Class: Sunniesnow::Charter
- Inherits:
-
Object
- Object
- Sunniesnow::Charter
- Includes:
- BeatSeries
- Defined in:
- lib/sscharter/cli.rb,
lib/sscharter/charter.rb,
lib/sscharter/version.rb,
lib/sscharter/charter/beat.rb,
lib/sscharter/charter/check.rb,
lib/sscharter/charter/event.rb,
lib/sscharter/charter/group.rb,
lib/sscharter/charter/metadata.rb,
lib/sscharter/charter/tip_point.rb,
lib/sscharter/charter/basic_events.rb,
lib/sscharter/charter/events_manip.rb,
lib/sscharter/charter/story_events.rb
Defined Under Namespace
Modules: BeatSeries, CLI, Metronomic Classes: BeatState, Bookmark, BpmChangeList, Event, GroupState, OffsetError, TimeDependent, TipPointStart, Transform
Constant Summary collapse
- PROJECT_DIR =
The project directory.
File.(ENV['SSCHARTER_PROJECT_DIR'] ||= Dir.pwd).freeze
- VERSION =
"0.10.1"- COLORS =
Aliases for some colors that can be used with #difficulty_color.
{ easy: '#3eb9fd', normal: '#f19e56', hard: '#e75e74', master: '#8c68f3', special: '#f156ee' }.freeze
- DIRECTIONS =
Aliases for some directions that can be used with #flick.
{ right: %i[r], up_right: %i[ur ru], up: %i[u], up_left: %i[ul lu], left: %i[l], down_left: %i[dl ld], down: %i[d], down_right: %i[dr rd] }.each_with_object({ right: 0.0, up_right: Math::PI / 4, up: Math::PI / 2, up_left: Math::PI * 3 / 4, left: Math::PI, down_left: -Math::PI * 3 / 4, down: -Math::PI / 2, down_right: -Math::PI / 4 }) do |(direction_name, aliases), directions| aliases.each { directions[_1] = directions[direction_name] } end.freeze
- IMAGE_LAYER_ABOVE =
%i[none background bg_pattern hud fx judgement_line bg_notes notes circles tip_points fx_front].freeze
- IMAGE_LAYER_ABOVE_SET =
IMAGE_LAYER_ABOVE.to_set.freeze
- COORDINATE_SYSTEMS =
%i[canvas chart].freeze
- COORDINATE_SYSTEMS_SET =
COORDINATE_SYSTEMS.to_set.freeze
Class Attribute Summary collapse
-
.charts ⇒ Hash{String => Sunniesnow::Charter}
readonly
A hash containing all the charts opened by open.
Instance Attribute Summary collapse
-
#events ⇒ Array<Event>
readonly
An array of events.
Attributes included from BeatSeries
DSL Methods collapse
-
#artist(artist) ⇒ String
Set the artist of the music for the chart.
- #at(name, goto_beat: true, preserve_beat: false, update_mark: false, &block) ⇒ Array<Event>
-
#bg_note(x, y, duration_beats = 0, text = nil) ⇒ Event
Creates a background note at the given coordinates with the given duration and text.
-
#big_text(duration_beats = 0, text) ⇒ Event
Creates a big text.
-
#charter(charter) ⇒ String
Set the name of the chart author for the chart.
-
#checkerboard(duration_beats = 0) ⇒ Event
Creates a checkerboard background pattern.
-
#diamond_grid(duration_beats = 0) ⇒ Event
Creates a diamond grid background pattern.
-
#difficulty(difficulty) ⇒ String
Set the difficulty level for the chart.
-
#difficulty_color(difficulty_color) ⇒ String
Set the color of the difficulty for the chart.
-
#difficulty_name(difficulty_name) ⇒ String
Set the name of the difficulty for the chart.
-
#difficulty_sup(difficulty_sup) ⇒ String
Set the difficulty superscript for the chart.
-
#drag(x, y) ⇒ Event
(also: #d)
Creates a drag note at the given coordinates.
-
#duplicate(events, new_tip_points: true) ⇒ Array<Event>
Duplicate all events in a given array.
-
#flick(x, y, direction, text = '') ⇒ Event
(also: #f)
Creates a flick note at the given coordinates with the given direction and text.
-
#grid(duration_beats = 0) ⇒ Event
Creates a grid background pattern.
-
#group(preserve_beat: true, &block) ⇒ Array<Event>
The events created inside
block. -
#hexagon(duration_beats = 0) ⇒ Event
Creates a hexagon background pattern.
-
#hexagram(duration_beats = 0) ⇒ Event
Creates a hexagram background pattern.
-
#hold(x, y, duration_beats, text = '') ⇒ Event
(also: #h)
Creates a hold note at the given coordinates with the given duration and text.
-
#image(filename, x, y, duration_beats, width, height = nil, above: nil, coordinate_system: nil, mirrorable: nil) ⇒ Event
Creates an image event.
-
#mark(name) ⇒ Object
name. -
#offset(offset) ⇒ BpmChangeList
Set the offset.
-
#pentagon(duration_beats = 0) ⇒ Event
Creates a pentagon background pattern.
-
#remove(*events) ⇒ Array<Event>
Remove events from the chart.
-
#tap(x, y, text = '') ⇒ Event
(also: #t)
Creates a tap note at the given coordinates with the given text.
- #time_dependent(events, goto_beat: true, preserve_beat: false, &block) ⇒ Array<Event> (also: #td)
-
#tip_point_chain(x, y, relative_time = nil, relative: true, speed: nil, relative_beat: nil, beat_speed: nil, preserve_beat: true, &block) ⇒ Array<Event>
(also: #tp_chain)
Create a tip point to connect the notes created inside
block. -
#tip_point_drop(x, y, relative_time = nil, relative: true, speed: nil, relative_beat: nil, beat_speed: nil, preserve_beat: true, &block) ⇒ Array<Event>
(also: #tp_drop)
A tip point is created for each note created inside
block. -
#tip_point_none(preserve_beat: true, &block) ⇒ Array<Event>
(also: #tp_none)
Notes created inside
blockwill not be visited by any tip point. -
#title(title) ⇒ String
Set the title of the music for the chart.
-
#transform(events, &block) ⇒ Array<Event>
Transform all events in a given array in time and/or space.
-
#turntable(duration_beats = 0) ⇒ Event
Creates a turntable background pattern.
Methods included from BeatSeries
Class Method Summary collapse
-
.open(name, &block) ⇒ Charter
Create a new chart or open an existing chart for editing.
Instance Method Summary collapse
-
#check(notes_in_bound: true, bg_notes_in_bound: true) ⇒ void
Check the chart for potential issues.
- #current_group_state ⇒ GroupState
- #event(type, duration_beats = nil, **properties) ⇒ Event
- #init_beat_state ⇒ void
- #init_bookmarks ⇒ void
- #init_chart_info ⇒ void
- #init_group_state ⇒ void
-
#initialize(name) ⇒ Charter
constructor
Create a new chart.
- #inspect ⇒ String
- #output_json(*args, **opts) ⇒ String
- #push_tip_point_start(start_event) ⇒ void
- #restore_group_state(backup) ⇒ void
- #tip_point(mode, *args, preserve_beat: true, **opts, &block) ⇒ Object
-
#to_sunniesnow(live_reload_port: 31108, production: false) ⇒ Sunniesnow::Chart
See Sunniesnow::Chart#initialize for the arguments.
Methods included from BeatSeries
#current_beat_state, #restore_beat_state, #time_at
Constructor Details
Class Attribute Details
.charts ⇒ Hash{String => Sunniesnow::Charter} (readonly)
A hash containing all the charts opened by open. The keys are the names of the charts, and the values are the Sunniesnow::Charter objects.
12 13 14 |
# File 'lib/sscharter/charter.rb', line 12 def charts @charts end |
Instance Attribute Details
#events ⇒ Array<Event> (readonly)
An array of events.
377 378 379 |
# File 'lib/sscharter/charter/event.rb', line 377 def events @events end |
Class Method Details
.open(name, &block) ⇒ Charter
Create a new chart or open an existing chart for editing.
The name is used to check whether the chart already exists.
If a new chart needs to be created, it is added to charts.
The given block will be evaluated in the context of the chart
(inside the block, self is the same as the return value, a Sunniesnow::Charter instance).
This method is intended to be called at the top level of a Ruby script
to open a context for writing a Sunniesnow chart using the DSL.
In the examples in the documentation of other methods, it is assumed that they are run inside a block passed to this method.
34 35 36 37 38 |
# File 'lib/sscharter/charter.rb', line 34 def self.open name, &block result = @charts[name] ||= new name result.instance_eval &block if block result end |
Instance Method Details
#artist(artist) ⇒ String
Set the artist of the music for the chart. This will be reflected in the return value of #to_sunniesnow.
46 47 48 49 |
# File 'lib/sscharter/charter/metadata.rb', line 46 def artist artist raise ArgumentError, 'artist must be a string' unless artist.is_a? String @artist = artist end |
#at(name, goto_beat: true, preserve_beat: false, update_mark: false, &block) ⇒ Array<Event>
114 115 116 117 118 119 120 121 122 123 124 125 126 |
# File 'lib/sscharter/charter/group.rb', line 114 def at name, goto_beat: true, preserve_beat: false, update_mark: false, &block raise ArgumentError, 'no block given' unless block raise ArgumentError, "unknown bookmark #{name}" unless bookmark = @bookmarks[name] group_backup = current_group_state beat_backup = current_beat_state unless preserve_beat restore_group_state bookmark.group_state restore_beat_state bookmark.beat_state if goto_beat result = group &block mark name if update_mark restore_group_state group_backup restore_beat_state beat_backup unless preserve_beat result end |
#bg_note(x, y, duration_beats = 0, text = nil) ⇒ Event
Creates a background note at the given coordinates with the given duration and text.
The coordinates x and y must be numbers.
The argument duration_beats is the duration of the background note in beats.
It needs to be a non-negative Rational or Integer.
If it is a Float, it will be converted to a Rational, and a warning will be issued.
The argument text is the text to be displayed on the note
(it is converted to a string via to_s if it is not a string).
Both the duration_beats and the text arguments are optional.
When there are three arguments given in total,
the method determines whether the third is duration_beats or text based on its type.
Technically, this adds an event of type :bg_note to the chart at the current time
with properties containing the information provided by x, y, duration_beats, and text.
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 |
# File 'lib/sscharter/charter/basic_events.rb', line 184 def bg_note x, y, duration_beats = 0, text = nil if text.nil? if duration_beats.is_a? String text = duration_beats duration_beats = 0 else text = '' end end if !x.is_a?(Numeric) || !y.is_a?(Numeric) || !duration_beats.is_a?(Numeric) raise ArgumentError, 'x, y, and duration_beats must be numbers' end if duration_beats < 0 raise ArgumentError, 'duration must be non-negative' end if duration_beats.is_a? Float warn 'Rational is recommended over Float for duration_beats' end event :bg_note, duration_beats.to_r, x: x.to_f, y: y.to_f, text: text.to_s end |
#big_text(duration_beats = 0, text) ⇒ Event
Creates a big text.
The argument duration_beats is the duration of the big text in beats.
It needs to be a non-negative Rational or Integer.
If it is a Float, it will be converted to a Rational, and a warning will be issued.
The argument text is the text to be displayed.
Technically, this adds an event of type :big_text to the chart at the current time
with properties containing the information provided by duration_beats and text.
222 223 224 225 226 227 228 229 230 231 232 233 |
# File 'lib/sscharter/charter/basic_events.rb', line 222 def big_text duration_beats = 0, text unless duration_beats.is_a? Numeric raise ArgumentError, 'duration_beats must be a number' end if duration_beats < 0 raise ArgumentError, 'duration must be non-negative' end if duration_beats.is_a? Float warn 'Rational is recommended over Float for duration_beats' end event :big_text, duration_beats.to_r, text: text.to_s end |
#charter(charter) ⇒ String
Set the name of the chart author for the chart. This will be reflected in the return value of #to_sunniesnow.
57 58 59 60 |
# File 'lib/sscharter/charter/metadata.rb', line 57 def charter charter raise ArgumentError, 'charter must be a string' unless charter.is_a? String @charter = charter end |
#check(notes_in_bound: true, bg_notes_in_bound: true) ⇒ void
This method returns an undefined value.
Check the chart for potential issues.
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
# File 'lib/sscharter/charter/check.rb', line 9 def check( notes_in_bound: true, bg_notes_in_bound: true ) out_of_bound_events = [] if notes_in_bound || bg_notes_in_bound @events.each do |event| if %i[tap hold drag flick drag_flick].include?(event.type) && notes_in_bound || event.type == :bg_note && bg_notes_in_bound if event[:x] < -100-1e-10 || event[:x] > 100+1e-10 || event[:y] < -50-1e-10 || event[:y] > 50+1e-10 out_of_bound_events.push event end end end if notes_in_bound || bg_notes_in_bound if out_of_bound_events.empty? puts "===== All notes are in bound =====" else puts "===== Out-of-bound notes =====" out_of_bound_events.each do |event| p event puts "at time #{event.time}" puts 'defined at:' puts event.backtrace end end end nil end |
#checkerboard(duration_beats = 0) ⇒ Event
Creates a checkerboard background pattern.
The argument duration_beats is the duration of the background pattern in beats.
It needs to be a non-negative Rational or Integer.
If it is a Float, it will be converted to a Rational, and a warning will be issued.
Technically, this adds an event of type :checkerboard to the chart at the current time
with properties containing the information provided by duration_beats.
260 261 262 263 264 265 266 267 268 269 270 271 272 273 |
# File 'lib/sscharter/charter/basic_events.rb', line 260 %i[grid hexagon checkerboard diamond_grid pentagon turntable hexagram].each do |method_name| define_method method_name do |duration_beats = 0| unless duration_beats.is_a? Numeric raise ArgumentError, 'duration_beats must be a number' end if duration_beats < 0 raise ArgumentError, 'duration must be non-negative' end if duration_beats.is_a? Float warn 'Rational is recommended over Float for duration_beats' end event method_name, duration_beats.to_r end end |
#current_group_state ⇒ GroupState
Internal API.
62 63 64 65 66 67 68 69 70 71 72 |
# File 'lib/sscharter/charter/group.rb', line 62 def current_group_state GroupState.new( @tip_point_mode_stack.dup, @current_tip_point_stack.dup, @current_tip_point_group_stack.dup, @current_duplicate, @tip_point_start_stack.dup, @tip_point_start_to_add_stack.dup, @groups.dup ) end |
#diamond_grid(duration_beats = 0) ⇒ Event
Creates a diamond grid background pattern.
The argument duration_beats is the duration of the background pattern in beats.
It needs to be a non-negative Rational or Integer.
If it is a Float, it will be converted to a Rational, and a warning will be issued.
Technically, this adds an event of type :diamond_grid to the chart at the current time
with properties containing the information provided by duration_beats.
260 261 262 263 264 265 266 267 268 269 270 271 272 273 |
# File 'lib/sscharter/charter/basic_events.rb', line 260 %i[grid hexagon checkerboard diamond_grid pentagon turntable hexagram].each do |method_name| define_method method_name do |duration_beats = 0| unless duration_beats.is_a? Numeric raise ArgumentError, 'duration_beats must be a number' end if duration_beats < 0 raise ArgumentError, 'duration must be non-negative' end if duration_beats.is_a? Float warn 'Rational is recommended over Float for duration_beats' end event method_name, duration_beats.to_r end end |
#difficulty(difficulty) ⇒ String
Set the difficulty level for the chart. This will be reflected in the return value of #to_sunniesnow.
The argument difficulty should be a string representing the difficulty level.
Anything other than a string will be converted to a string using to_s.
111 112 113 |
# File 'lib/sscharter/charter/metadata.rb', line 111 def difficulty difficulty @difficulty = difficulty.to_s end |
#difficulty_color(difficulty_color) ⇒ String
Set the color of the difficulty for the chart. This will be reflected in the return value of #to_sunniesnow.
The argument difficulty_color can be a color name (a key of COLORS),
an RGB color in hexadecimal format (e.g. '#8c68f3', '#8CF'),
an RGB color in decimal format (e.g. 'rgb(140, 104, 243)'),
or an integer representing an RGB color (e.g. 0x8c68f3).
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
# File 'lib/sscharter/charter/metadata.rb', line 84 def difficulty_color difficulty_color @difficulty_color = case difficulty_color when Symbol COLORS[difficulty_color] when /^#[0-9a-fA-F]{6}$/ difficulty_color when /^#[0-9a-fA-F]{3}$/ _, r, g, b = difficulty_color.chars "##{r}#{r}#{g}#{g}#{b}#{b}" when /^rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/ r, g, b = $1, $2, $3 sprintf '#%02x%02x%02x', r.to_i, g.to_i, b.to_i when Integer sprintf '#%06x', difficulty_color % 0x1000000 else raise ArgumentError, 'unknown format of difficulty_color' end end |
#difficulty_name(difficulty_name) ⇒ String
Set the name of the difficulty for the chart. This will be reflected in the return value of #to_sunniesnow.
68 69 70 71 |
# File 'lib/sscharter/charter/metadata.rb', line 68 def difficulty_name difficulty_name raise ArgumentError, 'difficulty_name must be a string' unless difficulty_name.is_a? String @difficulty_name = difficulty_name end |
#difficulty_sup(difficulty_sup) ⇒ String
Set the difficulty superscript for the chart. This will be reflected in the return value of #to_sunniesnow.
The argument difficulty_sup should be a string representing the difficulty superscript.
Anything other than a string will be converted to a string using to_s.
123 124 125 |
# File 'lib/sscharter/charter/metadata.rb', line 123 def difficulty_sup difficulty_sup @difficulty_sup = difficulty_sup.to_s end |
#drag(x, y) ⇒ Event Also known as: d
Creates a drag note at the given coordinates.
The coordinates x and y must be numbers.
Technically, this adds an event of type :drag to the chart at the current time
with properties containing the information provided by x and y.
107 108 109 110 111 112 |
# File 'lib/sscharter/charter/basic_events.rb', line 107 def drag x, y if !x.is_a?(Numeric) || !y.is_a?(Numeric) raise ArgumentError, 'x and y must be numbers' end event :drag, x: x.to_f, y: y.to_f end |
#duplicate(events, new_tip_points: true) ⇒ Array<Event>
Duplicate all events in a given array.
This method is useful when you want to duplicate a set of events.
The argument events is an array of events to be duplicated.
The argument new_tip_points is a boolean indicating whether to create new tip points.
If it is true, new tip points will be created for the duplicated events.
If it is false, each duplicated event shares the same tip point as the original event.
139 140 141 142 143 144 145 146 147 148 149 150 151 |
# File 'lib/sscharter/charter/events_manip.rb', line 139 def duplicate events, new_tip_points: true result = [] events.each do |event| next if event.type == :placeholder && !new_tip_points result.push event = event.dup if event[:tip_point] && new_tip_points event[:tip_point] = "#@current_duplicate #{event[:tip_point]}" end @groups.each { _1.push event } end @current_duplicate += 1 if new_tip_points result end |
#event(type, duration_beats = nil, **properties) ⇒ Event
Internal API.
384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 |
# File 'lib/sscharter/charter/event.rb', line 384 def event type, duration_beats = nil, **properties raise OffsetError.new __method__ unless @bpm_changes event = Event.new type, @current_beat, duration_beats, @bpm_changes, **properties @groups.each { _1.push event } return event unless event.tip_pointable? case @tip_point_mode_stack.last when :chain if @tip_point_start_to_add_stack.last @current_tip_point_stack[-1] = @tip_point_peak @tip_point_peak += 1 end push_tip_point_start event @tip_point_start_to_add_stack[-1] = nil when :drop @current_tip_point_stack[-1] = @tip_point_peak @tip_point_peak += 1 push_tip_point_start event when :none # pass end event end |
#flick(x, y, direction, text = '') ⇒ Event Also known as: f
Creates a flick note at the given coordinates with the given direction and text.
The coordinates x and y must be numbers.
The argument direction is the direction of the flick note in radians or a symbol.
If it is a symbol, it should be one of the keys of DIRECTIONS
(which are :right, :up_right, etc., abbreviated as :r, :ur etc.).
If it is a number, it should be a number representing the angle in radians,
specifying the angle rorated anticlockwise starting from the positive x-direction.
The argument text is the text to be displayed on the note
(it is converted to a string via to_s if it is not a string).
Technically, this adds an event of type :flick to the chart at the current time
with properties containing the information provided by x, y, direction, and text.
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
# File 'lib/sscharter/charter/basic_events.rb', line 141 def flick x, y, direction, text = '' if !x.is_a?(Numeric) || !y.is_a?(Numeric) raise ArgumentError, 'x and y must be numbers' end if direction.is_a? Symbol direction = DIRECTIONS[direction] raise ArgumentError, "unknown direction #{direction}" unless direction elsif direction.is_a? Numeric warn 'Are you using degrees as angle unit instead of radians?' if direction != 0 && direction % 45 == 0 direction = direction.to_f else raise ArgumentError, 'direction must be a symbol or a number' end event :flick, x: x.to_f, y: y.to_f, angle: direction, text: text.to_s end |
#grid(duration_beats = 0) ⇒ Event
Creates a grid background pattern.
The argument duration_beats is the duration of the background pattern in beats.
It needs to be a non-negative Rational or Integer.
If it is a Float, it will be converted to a Rational, and a warning will be issued.
Technically, this adds an event of type :grid to the chart at the current time
with properties containing the information provided by duration_beats.
260 261 262 263 264 265 266 267 268 269 270 271 272 273 |
# File 'lib/sscharter/charter/basic_events.rb', line 260 %i[grid hexagon checkerboard diamond_grid pentagon turntable hexagram].each do |method_name| define_method method_name do |duration_beats = 0| unless duration_beats.is_a? Numeric raise ArgumentError, 'duration_beats must be a number' end if duration_beats < 0 raise ArgumentError, 'duration must be non-negative' end if duration_beats.is_a? Float warn 'Rational is recommended over Float for duration_beats' end event method_name, duration_beats.to_r end end |
#group(preserve_beat: true, &block) ⇒ Array<Event>
Returns the events created inside block.
91 92 93 94 95 96 97 98 99 |
# File 'lib/sscharter/charter/group.rb', line 91 def group preserve_beat: true, &block raise ArgumentError, 'no block given' unless block @groups.push result = [] beat_backup = current_beat_state unless preserve_beat instance_eval &block restore_beat_state beat_backup unless preserve_beat @groups.delete_if { result.equal? _1 } result end |
#hexagon(duration_beats = 0) ⇒ Event
Creates a hexagon background pattern.
The argument duration_beats is the duration of the background pattern in beats.
It needs to be a non-negative Rational or Integer.
If it is a Float, it will be converted to a Rational, and a warning will be issued.
Technically, this adds an event of type :hexagon to the chart at the current time
with properties containing the information provided by duration_beats.
260 261 262 263 264 265 266 267 268 269 270 271 272 273 |
# File 'lib/sscharter/charter/basic_events.rb', line 260 %i[grid hexagon checkerboard diamond_grid pentagon turntable hexagram].each do |method_name| define_method method_name do |duration_beats = 0| unless duration_beats.is_a? Numeric raise ArgumentError, 'duration_beats must be a number' end if duration_beats < 0 raise ArgumentError, 'duration must be non-negative' end if duration_beats.is_a? Float warn 'Rational is recommended over Float for duration_beats' end event method_name, duration_beats.to_r end end |
#hexagram(duration_beats = 0) ⇒ Event
Creates a hexagram background pattern.
The argument duration_beats is the duration of the background pattern in beats.
It needs to be a non-negative Rational or Integer.
If it is a Float, it will be converted to a Rational, and a warning will be issued.
Technically, this adds an event of type :hexagram to the chart at the current time
with properties containing the information provided by duration_beats.
260 261 262 263 264 265 266 267 268 269 270 271 272 273 |
# File 'lib/sscharter/charter/basic_events.rb', line 260 %i[grid hexagon checkerboard diamond_grid pentagon turntable hexagram].each do |method_name| define_method method_name do |duration_beats = 0| unless duration_beats.is_a? Numeric raise ArgumentError, 'duration_beats must be a number' end if duration_beats < 0 raise ArgumentError, 'duration must be non-negative' end if duration_beats.is_a? Float warn 'Rational is recommended over Float for duration_beats' end event method_name, duration_beats.to_r end end |
#hold(x, y, duration_beats, text = '') ⇒ Event Also known as: h
Creates a hold note at the given coordinates with the given duration and text.
The coordinates x and y must be numbers.
The argument duration_beats is the duration of the hold note in beats.
It needs to be a positive Rational or Integer.
If it is a Float, it will be converted to a Rational, and a warning will be issued.
The argument text is the text to be displayed on the note
(it is converted to a string via to_s if it is not a string).
Technically, this adds an event of type :hold to the chart at the current time
with properties containing the information provided by x, y, duration_beats, and text.
79 80 81 82 83 84 85 86 87 88 89 90 |
# File 'lib/sscharter/charter/basic_events.rb', line 79 def hold x, y, duration_beats, text = '' if !x.is_a?(Numeric) || !y.is_a?(Numeric) || !duration_beats.is_a?(Numeric) raise ArgumentError, 'x, y, and duration must be numbers' end if duration_beats <= 0 raise ArgumentError, 'duration must be positive' end if duration_beats.is_a? Float warn 'Rational is recommended over Float for duration_beats' end event :hold, duration_beats.to_r, x: x.to_f, y: y.to_f, text: text.to_s end |
#image(filename, x, y, duration_beats, width, height = nil, above: nil, coordinate_system: nil, mirrorable: nil) ⇒ Event
Creates an image event.
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
# File 'lib/sscharter/charter/story_events.rb', line 30 def image filename, x, y, duration_beats, width, height = nil, above: nil, coordinate_system: nil, mirrorable: nil raise ArgumentError, 'filename must be a string' unless filename.is_a? String raise ArgumentError, 'x and y must be numbers' unless x.is_a?(Numeric) && y.is_a?(Numeric) raise ArgumentError, 'duration_beats must be a number' unless duration_beats.is_a? Numeric raise ArgumentError, 'duration_beats must be non-negative' if duration_beats < 0 raise ArgumentError, 'width must be a number' unless width.is_a? Numeric raise ArgumentError, 'height must be a number' if !height.nil? && !height.is_a?(Numeric) raise ArgumentError, "unknown coordinate_system #{coordinate_system}" if !coordinate_system.nil? && !%i[chart screen].include?(coordinate_system) warn 'Rational is recommended over Float for duration_beats' if duration_beats.is_a? Float raise ArgumentError, "invalid above: #{above}" if !above.nil? && !IMAGE_LAYER_ABOVE_SET.include?(above) raise ArgumentError, "invalid coordinate_system: #{coordinate_system}" if !coordinate_system.nil? && !COORDINATE_SYSTEMS_SET.include?(coordinate_system) raise ArgumentError, "mirrorable must be a boolean" unless [nil, true, false].include? mirrorable additional_properties = {} additional_properties[:above] = above.snake_to_camel if above additional_properties[:coordinate_system] = coordinate_system.snake_to_camel if coordinate_system additional_properties[:mirrorable] = mirrorable unless mirrorable.nil? additional_properties[:height] = height.to_f if height event :image, duration_beats.to_r, filename: filename, x: x.to_f, y: y.to_f, width: width.to_f, **additional_properties end |
#init_beat_state ⇒ void
Internal API.
This method returns an undefined value.
296 297 298 299 |
# File 'lib/sscharter/charter/beat.rb', line 296 def init_beat_state @current_beat = nil @bpm_changes = nil end |
#init_bookmarks ⇒ void
Internal API.
This method returns an undefined value.
41 42 43 44 |
# File 'lib/sscharter/charter/group.rb', line 41 def init_bookmarks @bookmarks = {} nil end |
#init_chart_info ⇒ void
Internal API.
This method returns an undefined value.
16 17 18 19 20 21 22 23 24 25 |
# File 'lib/sscharter/charter/metadata.rb', line 16 def init_chart_info @difficulty_name = '' @difficulty_color = '#000000' @difficulty = '' @difficulty_sup = '' @title = '' @artist = '' @charter = '' @events = [] end |
#init_group_state ⇒ void
Internal API.
This method returns an undefined value.
48 49 50 51 52 53 54 55 56 57 58 |
# File 'lib/sscharter/charter/group.rb', line 48 def init_group_state @tip_point_mode_stack = [:none] @current_tip_point_stack = [] @current_tip_point_group_stack = [] @tip_point_peak = 0 @current_duplicate = 0 @tip_point_start_stack = [nil] @tip_point_start_to_add_stack = [nil] @groups = [@events] nil end |
#inspect ⇒ String
73 74 75 |
# File 'lib/sscharter/charter.rb', line 73 def inspect "#<Sunniesnow::Charter #@name>" end |
#mark(name) ⇒ Object
Returns name.
103 104 105 106 |
# File 'lib/sscharter/charter/group.rb', line 103 def mark name @bookmarks[name] = Bookmark.new current_beat_state, current_group_state name end |
#offset(offset) ⇒ BpmChangeList
Set the offset. This is the time in seconds of the zeroth beat. This method must be called before any other methods that require a beat, or an OffsetError will be raised.
After calling this method, the current beat (see Sunniesnow::Charter::BeatSeries#beat and Sunniesnow::Charter::BeatSeries#beat!) is set to zero, and a new BPM needs to be set using Sunniesnow::Charter::BeatSeries#bpm. Only after that can the time of any positive beat be calculated.
Though not commonly useful, this method can be called multiple times in a chart. A new call of this method does not affect the events and BPM changes set before. Technically, each event is associated with a BPM change list (see Sunniesnow::Charter::Metronomic#bpm_changes), and each call of this method creates a new BPM change list, which is used for the events set after.
326 327 328 329 330 |
# File 'lib/sscharter/charter/beat.rb', line 326 def offset offset raise ArgumentError, 'offset must be a number' unless offset.is_a? Numeric @current_beat = 0r @bpm_changes = BpmChangeList.new offset.to_f end |
#output_json(*args, **opts) ⇒ String
68 69 70 |
# File 'lib/sscharter/charter.rb', line 68 def output_json *args, **opts to_sunniesnow(**opts).to_json *args end |
#pentagon(duration_beats = 0) ⇒ Event
Creates a pentagon background pattern.
The argument duration_beats is the duration of the background pattern in beats.
It needs to be a non-negative Rational or Integer.
If it is a Float, it will be converted to a Rational, and a warning will be issued.
Technically, this adds an event of type :pentagon to the chart at the current time
with properties containing the information provided by duration_beats.
260 261 262 263 264 265 266 267 268 269 270 271 272 273 |
# File 'lib/sscharter/charter/basic_events.rb', line 260 %i[grid hexagon checkerboard diamond_grid pentagon turntable hexagram].each do |method_name| define_method method_name do |duration_beats = 0| unless duration_beats.is_a? Numeric raise ArgumentError, 'duration_beats must be a number' end if duration_beats < 0 raise ArgumentError, 'duration must be non-negative' end if duration_beats.is_a? Float warn 'Rational is recommended over Float for duration_beats' end event method_name, duration_beats.to_r end end |
#push_tip_point_start(start_event) ⇒ void
Internal API.
This method returns an undefined value.
95 96 97 98 99 100 101 102 103 104 |
# File 'lib/sscharter/charter/tip_point.rb', line 95 def push_tip_point_start start_event start_event[:tip_point] = @current_tip_point_stack.last.to_s tip_point_start = @tip_point_start_to_add_stack.last&.get_start_placeholder start_event return unless tip_point_start @groups.each do |group| group.push tip_point_start break if group.equal?(@current_tip_point_group_stack.last) && @tip_point_mode_stack.last != :drop end nil end |
#remove(*events) ⇒ Array<Event>
Remove events from the chart.
169 170 171 |
# File 'lib/sscharter/charter/events_manip.rb', line 169 def remove *events events.each { |event| @groups.each { _1.delete event } } end |
#restore_group_state(backup) ⇒ void
Internal API.
This method returns an undefined value.
77 78 79 80 81 82 83 84 85 |
# File 'lib/sscharter/charter/group.rb', line 77 def restore_group_state backup @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 |
#tap(x, y, text = '') ⇒ Event Also known as: t
Creates a tap note at the given coordinates with the given text.
The coordinates x and y must be numbers.
The argument text is the text to be displayed on the note
(it is converted to a string via to_s if it is not a string).
Technically, this adds an event of type :tap to the chart at the current time
with properties containing the information provided by x, y, and text.
48 49 50 51 52 53 |
# File 'lib/sscharter/charter/basic_events.rb', line 48 def tap x, y, text = '' if !x.is_a?(Numeric) || !y.is_a?(Numeric) raise ArgumentError, 'x and y must be numbers' end event :tap, x: x.to_f, y: y.to_f, text: text.to_s end |
#time_dependent(events, goto_beat: true, preserve_beat: false, &block) ⇒ Array<Event> Also known as: td
414 415 416 417 418 419 420 421 422 423 424 |
# File 'lib/sscharter/charter/event.rb', line 414 def time_dependent events, goto_beat: true, preserve_beat: false, &block raise ArgumentError, 'no block given' unless block events = [events] if events.is_a? Event beat_backup = current_beat_state if !goto_beat || !preserve_beat events.each do |event| event.time_dependent.restore_beat_state goto_beat ? event.beat_state : beat_backup event.time_dependent.instance_eval &block end restore_beat_state preserve_beat ? events.last.time_dependent.current_beat_state : beat_backup events end |
#tip_point(mode, *args, preserve_beat: true, **opts, &block) ⇒ Object
Internal API.
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
# File 'lib/sscharter/charter/tip_point.rb', line 108 def tip_point mode, *args, preserve_beat: true, **opts, &block @tip_point_mode_stack.push mode if mode == :none @tip_point_start_stack.push nil @tip_point_start_to_add_stack.push nil @current_tip_point_stack.push nil else if args.empty? && opts.empty? unless @tip_point_start_stack.last raise ArgumentError, 'cannot omit tip point arguments at top level or inside tip_point_none' end @tip_point_start_stack.push @tip_point_start_stack.last.dup else @tip_point_start_stack.push TipPointStart.new *args, **opts end @tip_point_start_to_add_stack.push @tip_point_start_stack.last @current_tip_point_stack.push nil end result = group preserve_beat: preserve_beat do @current_tip_point_group_stack.push @groups.last instance_eval &block end @tip_point_start_stack.pop @tip_point_start_to_add_stack.pop @tip_point_mode_stack.pop @current_tip_point_stack.pop @current_tip_point_group_stack.pop result end |
#tip_point_chain(x, y, relative_time, relative: true, preserve_beat: true, &block) ⇒ Array<Event> #tip_point_chain(x, y, speed:, relative: true, preserve_beat: true, &block) ⇒ Array<Event> #tip_point_chain(x, y, relative_beat:, relative: true, preserve_beat: true, &block) ⇒ Array<Event> #tip_point_chain(x, y, beat_speed:, relative: true, preserve_beat: true, &block) ⇒ Array<Event> #tip_point_chain(preserve_beat: true, &block) ⇒ Array<Event> Also known as: tp_chain
Create a tip point to connect the notes created inside block.
There are four overloads of this method for different ways to specify the time at which the tip point appears,
and there is another overload that totally omits the arguments for specifying
when and where the tip point appears and can only be used inside another tip point block.
This method is otherwise the same as #group.
If the methods #tp_chain, #tp_drop, and #tp_none are nested in block,
only the innermost one takes effect.
199 200 201 202 203 204 |
# File 'lib/sscharter/charter/tip_point.rb', line 199 %i[chain drop none].each do |mode| define_method "tip_point_#{mode}" do |*args, **opts, &block| tip_point mode, *args, **opts, &block end alias_method "tp_#{mode}", "tip_point_#{mode}" end |
#tip_point_drop(x, y, relative_time, relative: true, preserve_beat: true, &block) ⇒ Array<Event> #tip_point_drop(x, y, speed:, relative: true, preserve_beat: true, &block) ⇒ Array<Event> #tip_point_drop(x, y, relative_beat:, relative: true, preserve_beat: true, &block) ⇒ Array<Event> #tip_point_drop(x, y, beat_speed:, relative: true, preserve_beat: true, &block) ⇒ Array<Event> #tip_point_drop(preserve_beat: true, &block) ⇒ Array<Event> Also known as: tp_drop
A tip point is created for each note created inside block.
There are four overloads of this method for different ways to specify the time at which the tip point appears,
and there is another overload that totally omits the arguments for specifying
when and where the tip point appears and can only be used inside another tip point block.
This method is otherwise the same as #group.
If the methods #tp_chain, #tp_drop, and #tp_none are nested in block,
only the innermost one takes effect.
199 200 201 202 203 204 |
# File 'lib/sscharter/charter/tip_point.rb', line 199 %i[chain drop none].each do |mode| define_method "tip_point_#{mode}" do |*args, **opts, &block| tip_point mode, *args, **opts, &block end alias_method "tp_#{mode}", "tip_point_#{mode}" end |
#tip_point_none(preserve_beat: true, &block) ⇒ Array<Event> Also known as: tp_none
Notes created inside block will not be visited by any tip point.
This method is otherwise the same as #group.
199 200 201 202 203 204 |
# File 'lib/sscharter/charter/tip_point.rb', line 199 %i[chain drop none].each do |mode| define_method "tip_point_#{mode}" do |*args, **opts, &block| tip_point mode, *args, **opts, &block end alias_method "tp_#{mode}", "tip_point_#{mode}" end |
#title(title) ⇒ String
Set the title of the music for the chart. This will be reflected in the return value of #to_sunniesnow.
35 36 37 38 |
# File 'lib/sscharter/charter/metadata.rb', line 35 def title title raise ArgumentError, 'title must be a string' unless title.is_a? String @title = title end |
#to_sunniesnow(live_reload_port: 31108, production: false) ⇒ Sunniesnow::Chart
See Sunniesnow::Chart#initialize for the arguments.
54 55 56 57 58 59 60 61 62 63 64 65 |
# File 'lib/sscharter/charter.rb', line 54 def to_sunniesnow **opts result = Sunniesnow::Chart.new **opts result.title = @title result.artist = @artist result.charter = @charter result.difficulty_name = @difficulty_name result.difficulty_color = @difficulty_color result.difficulty = @difficulty result.difficulty_sup = @difficulty_sup @events.each { result.events.push _1.to_sunniesnow } result end |
#transform(events, &block) ⇒ Array<Event>
Transform all events in a given array in time and/or space. Space transformation does not affect background patterns.
158 159 160 161 162 163 164 |
# File 'lib/sscharter/charter/events_manip.rb', line 158 def transform events, &block raise ArgumentError, 'no block given' unless block events = [events] if events.is_a? Event transform = Transform.new transform.instance_eval &block events.each { transform.apply _1 } end |
#turntable(duration_beats = 0) ⇒ Event
Creates a turntable background pattern.
The argument duration_beats is the duration of the background pattern in beats.
It needs to be a non-negative Rational or Integer.
If it is a Float, it will be converted to a Rational, and a warning will be issued.
Technically, this adds an event of type :turntable to the chart at the current time
with properties containing the information provided by duration_beats.
260 261 262 263 264 265 266 267 268 269 270 271 272 273 |
# File 'lib/sscharter/charter/basic_events.rb', line 260 %i[grid hexagon checkerboard diamond_grid pentagon turntable hexagram].each do |method_name| define_method method_name do |duration_beats = 0| unless duration_beats.is_a? Numeric raise ArgumentError, 'duration_beats must be a number' end if duration_beats < 0 raise ArgumentError, 'duration must be non-negative' end if duration_beats.is_a? Float warn 'Rational is recommended over Float for duration_beats' end event method_name, duration_beats.to_r end end |