diff --git a/Gemfile.lock b/Gemfile.lock index 364ec95..1c89577 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - sscharter (0.6.2) + sscharter (0.7.0) concurrent-ruby (~> 1.3) em-websocket (~> 0.5) filewatcher (~> 2.0) @@ -14,7 +14,7 @@ GEM specs: addressable (2.8.7) public_suffix (>= 2.0.2, < 7.0) - concurrent-ruby (1.3.3) + concurrent-ruby (1.3.4) em-websocket (0.5.3) eventmachine (>= 0.12.9) http_parser.rb (~> 0) @@ -24,7 +24,7 @@ GEM http_parser.rb (0.8.0) launchy (2.5.2) addressable (~> 2.8) - minitest (5.24.1) + minitest (5.25.1) module_methods (0.1.0) public_suffix (6.0.1) rake (13.2.1) diff --git a/lib/sscharter.rb b/lib/sscharter.rb index 19734d3..c879fd4 100644 --- a/lib/sscharter.rb +++ b/lib/sscharter.rb @@ -324,6 +324,7 @@ def initialize name @name = name init_chart_info init_state + init_bookmarks end def init_chart_info @@ -337,8 +338,11 @@ def init_chart_info @events = [] end + def init_bookmarks + @bookmarks = {} + end + def init_state - @current_offset = nil @current_beat = nil @bpm_changes = nil @tip_point_mode_stack = [:none] @@ -399,9 +403,8 @@ def difficulty_sup difficulty_sup def offset offset raise ArgumentError, 'offset must be a number' unless offset.is_a? Numeric - @current_offset = offset.to_f @current_beat = 0r - @bpm_changes = BpmChangeList.new @current_offset + @bpm_changes = BpmChangeList.new offset.to_f end def bpm bpm @@ -449,24 +452,68 @@ def time_at beat = @current_beat alias_method "tp_#{mode}", "tip_point_#{mode}" end + def backup_beat + {current_beat: @current_beat, bpm_changes: @bpm_changes} + end + + def restore_beat backup + @current_beat = backup[:current_beat] + @bpm_changes = backup[:bpm_changes] + end + def group preserve_beat: true, &block raise ArgumentError, 'no block given' unless block @groups.push result = [] - unless preserve_beat - last_beat = @current_beat - last_offset = @current_offset - last_bpm_changes = @bpm_changes - end + beat_backup = backup_beat unless preserve_beat instance_eval &block - unless preserve_beat - @current_beat = last_beat - @current_offset = last_offset - @bpm_changes = last_bpm_changes - end + restore_beat beat_backup unless preserve_beat @groups.delete_if { result.equal? _1 } result end + def backup_state + { + current_beat: @current_beat, + bpm_changes: @bpm_changes, + tip_point_mode_stack: @tip_point_mode_stack.dup, + current_tip_point_stack: @current_tip_point_stack.dup, + current_tip_point_group_stack: @current_tip_point_group_stack.dup, + current_duplicate: @current_duplicate, + tip_point_start_to_add_stack: @tip_point_start_to_add_stack.dup, + groups: @groups.dup + } + end + + def restore_state backup + @current_beat = backup[:current_beat] + @bpm_changes = backup[:bpm_changes] + @tip_point_mode_stack = backup[:tip_point_mode_stack] + @current_tip_point_stack = backup[:current_tip_point_stack] + @current_tip_point_group_stack = backup[:current_tip_point_group_stack] + @current_duplicate = backup[:current_duplicate] + @tip_point_start_to_add_stack = backup[:tip_point_start_to_add_stack] + @groups = backup[:groups] + nil + end + + def mark name + @bookmarks[name] = backup_state + name + end + + def at name, preserve_beat: false, update_mark: false, &block + raise ArgumentError, 'no block given' unless block + raise ArgumentError, "unknown bookmark #{name}" unless bookmark = @bookmarks[name] + backup = backup_state + restore_state bookmark + result = group &block + mark name if update_mark + beat_backup = backup_beat if preserve_beat + restore_state backup + restore_beat beat_backup if preserve_beat + result + end + def tip_point mode, *args, preserve_beat: true, **opts, &block @tip_point_mode_stack.push mode if mode == :none diff --git a/lib/sscharter/version.rb b/lib/sscharter/version.rb index 3911abc..9f13316 100644 --- a/lib/sscharter/version.rb +++ b/lib/sscharter/version.rb @@ -2,6 +2,6 @@ module Sunniesnow class Charter - VERSION = "0.6.2" + VERSION = "0.7.0" end end diff --git a/test/test_sscharter.rb b/test/test_sscharter.rb index 78abf30..4a06918 100644 --- a/test/test_sscharter.rb +++ b/test/test_sscharter.rb @@ -84,7 +84,6 @@ def test_offset_and_beat assert_raises(Charter::OffsetError) { chart.event :placeholder } chart.offset offset = rand - assert_equal offset, chart.instance_variable_get(:@current_offset) assert_equal 0r, chart.instance_variable_get(:@current_beat) chart.beat 10 assert_equal 10r, chart.instance_variable_get(:@current_beat) @@ -595,4 +594,61 @@ def test_remove_and_duplicate assert_equal chart.events, [*group1, *group2] end + def test_mark + chart = Charter.open __method__ + chart.offset offset = rand + chart.bpm bpm = rand * 300 + + note1 = note2 = current_beat = last_beat = nil + group1 = chart.tp_chain rand, rand, rand do + note1 = t rand(100), rand(100) + last_beat = b 1 + mark :test1 + end + group2 = chart.at :test1, preserve_beat: true do + note2 = t rand(100), rand(100) + current_beat = b 1 + end + note3 = chart.t rand(100), rand(100) + assert_equal group1.length, 3 + assert_equal group2, [note2] + assert_equal note1[:tip_point], note2[:tip_point] + assert_equal note2.beat, last_beat + assert_equal note3.beat, current_beat + + note4 = nil + chart.mark :test2 + group4 = chart.at :test2 do + note4 = t rand(100), rand(100) + b 1 + end + note5 = chart.t rand(100), rand(100) + assert_equal group4, [note4] + assert_equal note4.beat, current_beat + assert_equal note5.beat, current_beat + end + + def test_mark_and_tp_drop + chart = Charter.open __method__ + chart.offset offset = rand + chart.bpm bpm = rand * 300 + + note1 = note2 = note3 = nil + group1 = chart.tp_drop rand, rand, rand do + note1 = t rand(100), rand(100) + b 1 + mark :test1 + end + group2 = chart.at :test1 do + note2 = t rand(100), rand(100) + b 1 + end + group3 = chart.tp_chain rand, rand, rand do + note3 = t rand(100), rand(100) + b 1 + end + assert_equal group1.length, 4 + assert_equal [note1, note2, note3].map { _1[:tip_point] }.uniq.length, 3 + end + end