sscharter 0.8.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 77116185ddcbeeeff551d0584bc35b8a10da9f0d7fcbae95a520b95909c355dd
4
- data.tar.gz: 5594955220d37210bf710314b379e88d8a23a5a5174291c7d06c91cf25e37dee
3
+ metadata.gz: 648753f12016bdf6a3da2c7e82fde0fb3baa65b242e31a853f1cc113a30e2638
4
+ data.tar.gz: a24123b640ae77b7c49a507201c15bb7bd6a4b70fb2114254f61e2a0bbb4f185
5
5
  SHA512:
6
- metadata.gz: b67bc6805cf36ab1d69c4b85bbe0dce05d0db8b794caa6c8cc0cc8a7f2f28bb5c840688122527f2e2b44190740756020d4308a0ba0ca3bfbec570108cdf0a108
7
- data.tar.gz: 6523d03c7b22171f37829136a21e4a378947eed410dd9f22c825046c6ffdb71269392d65f6b6621cb7c43521e994a8b6fc1d11351d9141432898fd12e61e5dc9
6
+ metadata.gz: 173cbf0d5e9ab511990e03f467e2819a7c8717599b41e13dd675bb986b3c818ded32bb2b8ce47decad3bba984d57e1359ca56f48e5fa8da7b744e88c768b3853
7
+ data.tar.gz: e504b089535c14742597e026f497b7c03e662fdda16819b4e238f60c11e70523fe7cb361d61e907fb54bf58b6aa33fa6841b67f71ae1531571df8fa215c579bd
data/Gemfile CHANGED
@@ -3,3 +3,5 @@
3
3
  source "https://rubygems.org"
4
4
 
5
5
  gemspec
6
+
7
+ gem 'yard', path: 'gems/yard'
data/Gemfile.lock ADDED
@@ -0,0 +1 @@
1
+ lock/ruby-4.0.1-bundler-4.0.8-Gemfile.lock
data/Rakefile CHANGED
@@ -11,8 +11,14 @@ Rake::TestTask.new :test do |t|
11
11
  end
12
12
 
13
13
  YARD::Rake::YardocTask.new do |t|
14
- t.files.concat %w[README.md]
15
- t.options.concat %w[--title sscharter --output-dir doc --exclude lib/sscharter/cli.rb --readme]
14
+ t.options.concat %w[
15
+ --title sscharter
16
+ --output-dir doc
17
+ --exclude lib/sscharter/cli.rb
18
+ --readme README.md
19
+ --files tutorial/*.md
20
+ --type-tag yieldself:Yield\ Self
21
+ ]
16
22
  end
17
23
 
18
24
  task default: :test
@@ -11,24 +11,66 @@ class Sunniesnow::Chart
11
11
  using Sunniesnow::Utils
12
12
 
13
13
  # The schema URL for the output JSON.
14
- # This will be set as the `$schema` property in the generated JSON.
14
+ # This will be set as the +$schema+ property in the generated JSON.
15
15
  SCHEMA = 'https://sunniesnow.github.io/schema/chart-1.0.json'
16
16
 
17
17
  # The title of the music.
18
18
  # This is one of the metadata of the chart which will be reflected in the generated JSON.
19
19
  # Also, see
20
- # {chart file specifications}[https://sunniesnow.github.io/doc/chart.html#title]
20
+ # {https://sunniesnow.github.io/doc/chart.html#chart-title chart file specifications}
21
21
  # for more info.
22
+ # @return [String]
22
23
  attr_accessor :title
23
-
24
+
25
+ # The artist of the music.
26
+ # This is one of the metadata of the chart which will be reflected in the generated JSON.
27
+ # Also, see
28
+ # {https://sunniesnow.github.io/doc/chart.html#chart-artist chart file specifications}
29
+ # for more info.
30
+ # @return [String]
24
31
  attr_accessor :artist
32
+
33
+ # The author of the chart.
34
+ # This is one of the metadata of the chart which will be reflected in the generated JSON.
35
+ # Also, see
36
+ # {https://sunniesnow.github.io/doc/chart.html#chart-charter chart file specifications}
37
+ # for more info.
38
+ # @return [String]
25
39
  attr_accessor :charter
40
+
41
+ # The difficulty name of the chart.
42
+ # This is one of the metadata of the chart which will be reflected in the generated JSON.
43
+ # Also, see
44
+ # {https://sunniesnow.github.io/doc/chart.html#chart-difficulty-name chart file specifications}
45
+ # for more info.
46
+ # @return [String]
26
47
  attr_accessor :difficulty_name
48
+
49
+ # The difficulty color of the chart.
50
+ # This is one of the metadata of the chart which will be reflected in the generated JSON.
51
+ # Also, see
52
+ # {https://sunniesnow.github.io/doc/chart.html#chart-difficulty-color chart file specifications}
53
+ # for more info.
54
+ # @return [String]
27
55
  attr_accessor :difficulty_color
56
+
57
+ # The difficulty of the chart.
58
+ # This is one of the metadata of the chart which will be reflected in the generated JSON.
59
+ # Also, see
60
+ # {https://sunniesnow.github.io/doc/chart.html#chart-difficulty chart file specifications}
61
+ # for more info.
62
+ # @return [String]
28
63
  attr_accessor :difficulty
64
+
65
+ # The difficulty superscript of the chart.
66
+ # This is one of the metadata of the chart which will be reflected in the generated JSON.
67
+ # Also, see
68
+ # {https://sunniesnow.github.io/doc/chart.html#chart-difficulty-sup chart file specifications}
69
+ # for more info.
70
+ # @return [String]
29
71
  attr_accessor :difficulty_sup
30
72
 
31
- # An array of Sunniesnow::Event.
73
+ # @return [Array<Sunniesnow::Event>]
32
74
  attr_reader :events
33
75
 
34
76
  # @param [Integer] live_reload_port The port to use for live reload.
@@ -50,10 +92,10 @@ class Sunniesnow::Chart
50
92
  @production = production
51
93
  end
52
94
 
53
- ##
54
95
  # Convert to JSON.
55
96
  # A Sunniesnow chart is always a JSON file in the level file.
56
97
  # This method is used to generate that JSON file.
98
+ # @return [String]
57
99
  def to_json *args
58
100
  hash = {
59
101
  '$schema': SCHEMA,
@@ -70,13 +112,13 @@ class Sunniesnow::Chart
70
112
  version: Sunniesnow::Charter::VERSION,
71
113
  port: @live_reload_port
72
114
  } unless @production
73
- hash.to_json
115
+ hash.to_json *args
74
116
  end
75
117
  end
76
118
 
77
119
  class Sunniesnow::Event
78
-
79
- attr_accessor :time, :type
120
+
121
+ attr_accessor :time, :type, :time_dependent
80
122
  attr_reader :properties
81
123
 
82
124
  def initialize time, type, **properties
@@ -94,6 +136,8 @@ class Sunniesnow::Event
94
136
  end
95
137
 
96
138
  def to_json *args
97
- {time: @time, type: @type, properties: @properties}.to_json
139
+ result = {time: @time, type: @type, properties: @properties}
140
+ result[:timeDependent] = @time_dependent if @time_dependent
141
+ result.to_json *args
98
142
  end
99
143
  end
@@ -0,0 +1,277 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Sunniesnow::Charter
4
+
5
+ # Aliases for some directions that can be used with {#flick}.
6
+ DIRECTIONS = {
7
+ right: %i[r],
8
+ up_right: %i[ur ru],
9
+ up: %i[u],
10
+ up_left: %i[ul lu],
11
+ left: %i[l],
12
+ down_left: %i[dl ld],
13
+ down: %i[d],
14
+ down_right: %i[dr rd]
15
+ }.each_with_object({
16
+ right: 0.0,
17
+ up_right: Math::PI / 4,
18
+ up: Math::PI / 2,
19
+ up_left: Math::PI * 3 / 4,
20
+ left: Math::PI,
21
+ down_left: -Math::PI * 3 / 4,
22
+ down: -Math::PI / 2,
23
+ down_right: -Math::PI / 4
24
+ }) do |(direction_name, aliases), directions|
25
+ aliases.each { directions[_1] = directions[direction_name] }
26
+ end.freeze
27
+
28
+ # @!group DSL Methods
29
+
30
+ # Creates a tap note at the given coordinates with the given text.
31
+ # The coordinates +x+ and +y+ must be numbers.
32
+ # The argument +text+ is the text to be displayed on the note
33
+ # (it is converted to a string via +to_s+ if it is not a string).
34
+ #
35
+ # Technically, this adds an event of type +:tap+ to the chart at the current time
36
+ # with properties containing the information provided by +x+, +y+, and +text+.
37
+ # @param x [Numeric] the x-coordinate of the note.
38
+ # @param y [Numeric] the y-coordinate of the note.
39
+ # @param text [String] the text to be displayed on the note.
40
+ # @return [Event] the event representing the tap note.
41
+ # @raise [ArgumentError] if +x+ or +y+ is not a number.
42
+ # @example
43
+ # offset 0.1; bpm 120
44
+ # t 0, 0, 'Hello'
45
+ # t 50, 0, 'world'
46
+ # # Now there are two simultaneous tap notes at (0, 0) and (50, 0)
47
+ # # with texts 'Hello' and 'world' respectively
48
+ def tap x, y, text = ''
49
+ if !x.is_a?(Numeric) || !y.is_a?(Numeric)
50
+ raise ArgumentError, 'x and y must be numbers'
51
+ end
52
+ event :tap, x: x.to_f, y: y.to_f, text: text.to_s
53
+ end
54
+ alias t tap
55
+
56
+ # Creates a hold note at the given coordinates with the given duration and text.
57
+ # The coordinates +x+ and +y+ must be numbers.
58
+ # The argument +duration_beats+ is the duration of the hold note in beats.
59
+ # It needs to be a positive Rational or Integer.
60
+ # If it is a Float, it will be converted to a Rational, and a warning will be issued.
61
+ # The argument +text+ is the text to be displayed on the note
62
+ # (it is converted to a string via +to_s+ if it is not a string).
63
+ #
64
+ # Technically, this adds an event of type +:hold+ to the chart at the current time
65
+ # with properties containing the information provided by +x+, +y+, +duration_beats+, and +text+.
66
+ # @param x [Numeric] the x-coordinate of the note.
67
+ # @param y [Numeric] the y-coordinate of the note.
68
+ # @param duration_beats [Rational, Integer] the duration of the hold note in beats.
69
+ # @param text [String] the text to be displayed on the note.
70
+ # @return [Event] the event representing the hold note.
71
+ # @raise [ArgumentError] if +x+, +y+, or +duration_beats+ is not a number,
72
+ # or if +duration_beats+ is not positive.
73
+ # @example
74
+ # offset 0.1; bpm 120
75
+ # h 0, 0, 1, 'Hello'
76
+ # h 50, 0, 2, 'world'
77
+ # # Now there are two hold notes at (0, 0) and (50, 0)
78
+ # # with durations 1 and 2 beats and texts 'Hello' and 'world' respectively
79
+ def hold x, y, duration_beats, text = ''
80
+ if !x.is_a?(Numeric) || !y.is_a?(Numeric) || !duration_beats.is_a?(Numeric)
81
+ raise ArgumentError, 'x, y, and duration must be numbers'
82
+ end
83
+ if duration_beats <= 0
84
+ raise ArgumentError, 'duration must be positive'
85
+ end
86
+ if duration_beats.is_a? Float
87
+ warn 'Rational is recommended over Float for duration_beats'
88
+ end
89
+ event :hold, duration_beats.to_r, x: x.to_f, y: y.to_f, text: text.to_s
90
+ end
91
+ alias h hold
92
+
93
+ # Creates a drag note at the given coordinates.
94
+ # The coordinates +x+ and +y+ must be numbers.
95
+ #
96
+ # Technically, this adds an event of type +:drag+ to the chart at the current time
97
+ # with properties containing the information provided by +x+ and +y+.
98
+ # @param x [Numeric] the x-coordinate of the note.
99
+ # @param y [Numeric] the y-coordinate of the note.
100
+ # @return [Event] the event representing the drag note.
101
+ # @raise [ArgumentError] if +x+ or +y+ is not a number.
102
+ # @example
103
+ # offset 0.1; bpm 120
104
+ # d 0, 0
105
+ # d 50, 0
106
+ # # Now there are two drag notes at (0, 0) and (50, 0)
107
+ def drag x, y
108
+ if !x.is_a?(Numeric) || !y.is_a?(Numeric)
109
+ raise ArgumentError, 'x and y must be numbers'
110
+ end
111
+ event :drag, x: x.to_f, y: y.to_f
112
+ end
113
+ alias d drag
114
+
115
+ # Creates a flick note at the given coordinates with the given direction and text.
116
+ # The coordinates +x+ and +y+ must be numbers.
117
+ # The argument +direction+ is the direction of the flick note in radians or a symbol.
118
+ # If it is a symbol, it should be one of the keys of {DIRECTIONS}
119
+ # (which are +:right+, +:up_right+, etc., abbreviated as +:r+, +:ur+ etc.).
120
+ # If it is a number, it should be a number representing the angle in radians,
121
+ # specifying the angle rorated anticlockwise starting from the positive x-direction.
122
+ # The argument +text+ is the text to be displayed on the note
123
+ # (it is converted to a string via +to_s+ if it is not a string).
124
+ #
125
+ # Technically, this adds an event of type +:flick+ to the chart at the current time
126
+ # with properties containing the information provided by +x+, +y+, +direction+, and +text+.
127
+ # @param x [Numeric] the x-coordinate of the note.
128
+ # @param y [Numeric] the y-coordinate of the note.
129
+ # @param direction [Numeric, Symbol] the direction of the flick note in radians or a symbol.
130
+ # @param text [String] the text to be displayed on the note.
131
+ # @return [Event] the event representing the flick note.
132
+ # @raise [ArgumentError] if +x+ or +y+ is not a number,
133
+ # if +direction+ is not a symbol or a number,
134
+ # or if the direction is a symbol that does not represent a known direction.
135
+ # @example
136
+ # offset 0.1; bpm 120
137
+ # f 0, 0, :r, 'Hello'
138
+ # f 50, 0, Math::PI / 4, 'world'
139
+ # # Now there are two flick notes at (0, 0) and (50, 0)
140
+ # # with directions right and up right and texts 'Hello' and 'world' respectively
141
+ def flick x, y, direction, text = ''
142
+ if !x.is_a?(Numeric) || !y.is_a?(Numeric)
143
+ raise ArgumentError, 'x and y must be numbers'
144
+ end
145
+ if direction.is_a? Symbol
146
+ direction = DIRECTIONS[direction]
147
+ raise ArgumentError, "unknown direction #{direction}" unless direction
148
+ elsif direction.is_a? Numeric
149
+ warn 'Are you using degrees as angle unit instead of radians?' if direction != 0 && direction % 45 == 0
150
+ direction = direction.to_f
151
+ else
152
+ raise ArgumentError, 'direction must be a symbol or a number'
153
+ end
154
+ event :flick, x: x.to_f, y: y.to_f, angle: direction, text: text.to_s
155
+ end
156
+ alias f flick
157
+
158
+ # Creates a background note at the given coordinates with the given duration and text.
159
+ # The coordinates +x+ and +y+ must be numbers.
160
+ # The argument +duration_beats+ is the duration of the background note in beats.
161
+ # It needs to be a non-negative Rational or Integer.
162
+ # If it is a Float, it will be converted to a Rational, and a warning will be issued.
163
+ # The argument +text+ is the text to be displayed on the note
164
+ # (it is converted to a string via +to_s+ if it is not a string).
165
+ #
166
+ # Both the +duration_beats+ and the +text+ arguments are optional.
167
+ # When there are three arguments given in total,
168
+ # the method determines whether the third is +duration_beats+ or +text+ based on its type.
169
+ #
170
+ # Technically, this adds an event of type +:bg_note+ to the chart at the current time
171
+ # with properties containing the information provided by +x+, +y+, +duration_beats+, and +text+.
172
+ # @param x [Numeric] the x-coordinate of the note.
173
+ # @param y [Numeric] the y-coordinate of the note.
174
+ # @param duration_beats [Rational, Integer] the duration of the background note in beats.
175
+ # @param text [String] the text to be displayed on the note.
176
+ # @return [Event] the event representing the background note.
177
+ # @raise [ArgumentError] if +x+, +y+, or +duration_beats+ is not a number,
178
+ # or if +duration_beats+ is negative.
179
+ # @example
180
+ # offset 0.1; bpm 120
181
+ # bg_note 0, 0, 1, 'Hello' # duration is 1, text is 'Hello'
182
+ # bg_note 50, 0, 'world' # duration is 0, text is 'world'
183
+ # bg_note -50, 0, 2 # duration is 2, text is ''
184
+ def bg_note x, y, duration_beats = 0, text = nil
185
+ if text.nil?
186
+ if duration_beats.is_a? String
187
+ text = duration_beats
188
+ duration_beats = 0
189
+ else
190
+ text = ''
191
+ end
192
+ end
193
+ if !x.is_a?(Numeric) || !y.is_a?(Numeric) || !duration_beats.is_a?(Numeric)
194
+ raise ArgumentError, 'x, y, and duration_beats must be numbers'
195
+ end
196
+ if duration_beats < 0
197
+ raise ArgumentError, 'duration must be non-negative'
198
+ end
199
+ if duration_beats.is_a? Float
200
+ warn 'Rational is recommended over Float for duration_beats'
201
+ end
202
+ event :bg_note, duration_beats.to_r, x: x.to_f, y: y.to_f, text: text.to_s
203
+ end
204
+
205
+ # Creates a big text.
206
+ # The argument +duration_beats+ is the duration of the big text in beats.
207
+ # It needs to be a non-negative Rational or Integer.
208
+ # If it is a Float, it will be converted to a Rational, and a warning will be issued.
209
+ # The argument +text+ is the text to be displayed.
210
+ #
211
+ # Technically, this adds an event of type +:big_text+ to the chart at the current time
212
+ # with properties containing the information provided by +duration_beats+ and +text+.
213
+ # @param duration_beats [Rational, Integer] the duration of the big text in beats.
214
+ # @param text [String] the text to be displayed.
215
+ # @return [Event] the event representing the big text.
216
+ # @raise [ArgumentError] if +duration_beats+ is not a number or is negative.
217
+ # @example
218
+ # offset 0.1; bpm 120
219
+ # big_text 1, 'Hello, world!' # duration is 1, text is 'Hello, world!'
220
+ # b 1
221
+ # big_text 'Goodbye!' # duration is 0, text is 'Goodbye!'
222
+ def big_text duration_beats = 0, text
223
+ unless duration_beats.is_a? Numeric
224
+ raise ArgumentError, 'duration_beats must be a number'
225
+ end
226
+ if duration_beats < 0
227
+ raise ArgumentError, 'duration must be non-negative'
228
+ end
229
+ if duration_beats.is_a? Float
230
+ warn 'Rational is recommended over Float for duration_beats'
231
+ end
232
+ event :big_text, duration_beats.to_r, text: text.to_s
233
+ end
234
+
235
+ # @!parse
236
+ # # @!macro [attach] bg_pattern
237
+ # # @!method $1(duration_beats = 0)
238
+ # # Creates a $2 background pattern.
239
+ # # The argument +duration_beats+ is the duration of the background pattern in beats.
240
+ # # It needs to be a non-negative Rational or Integer.
241
+ # # If it is a Float, it will be converted to a Rational, and a warning will be issued.
242
+ # #
243
+ # # Technically, this adds an event of type +:$1+ to the chart at the current time
244
+ # # with properties containing the information provided by +duration_beats+.
245
+ # # @param duration_beats [Rational, Integer] the duration of the background pattern in beats.
246
+ # # @return [Event] the event representing the background pattern.
247
+ # # @raise [ArgumentError] if +duration_beats+ is not a number or is negative.
248
+ # # @example
249
+ # # offset 0.1; bpm 120
250
+ # # $1 1 # duration is 1
251
+ # # b 2
252
+ # # $1 2 # duration is 2
253
+ # bg_pattern :grid, 'grid'
254
+ # bg_pattern :hexagon, 'hexagon'
255
+ # bg_pattern :checkerboard, 'checkerboard'
256
+ # bg_pattern :diamond_grid, 'diamond grid'
257
+ # bg_pattern :pentagon, 'pentagon'
258
+ # bg_pattern :turntable, 'turntable'
259
+ # bg_pattern :hexagram, 'hexagram'
260
+ %i[grid hexagon checkerboard diamond_grid pentagon turntable hexagram].each do |method_name|
261
+ define_method method_name do |duration_beats = 0|
262
+ unless duration_beats.is_a? Numeric
263
+ raise ArgumentError, 'duration_beats must be a number'
264
+ end
265
+ if duration_beats < 0
266
+ raise ArgumentError, 'duration must be non-negative'
267
+ end
268
+ if duration_beats.is_a? Float
269
+ warn 'Rational is recommended over Float for duration_beats'
270
+ end
271
+ event method_name, duration_beats.to_r
272
+ end
273
+ end
274
+
275
+ # @!endgroup
276
+
277
+ end