osb 1.0.3 → 1.1.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: dae3bb6b24e591176075dcfaeb67dc5e74c625c668e44dd91e8f35b660973222
4
- data.tar.gz: 30a7304fe4b0c47a81d9a55ea0cc91d5e60775ab06f01aa85e4d76c46b241c0c
3
+ metadata.gz: bfa8c9fd46a2c0c25b5ae9b9d88d1043e2f16a74b01ff0ae1f8f7561d448f7f0
4
+ data.tar.gz: 44a5c46b92e37bdb8f8f5694e8da2b28dabecc77d27793d43b15238ddc97382c
5
5
  SHA512:
6
- metadata.gz: 7fd1cc3edbf6bc0995eb04951ac640ca7d583130e36e46b8b9aa801cbbdbd040cbcd41f1b1ea2eff477361f9af39f7775487b80b65332737df0a7055830c877e
7
- data.tar.gz: 3d4a452f5fdc3002e544d5ffc32542fa850f792dc01e19da19a32ae506da120fccfdf49d68c2c6d25534320d9406bdf575a67d63c1b1904a98878f26be9c1424
6
+ metadata.gz: 96064b59c3bbca5233109df31a39cf1867a1c598bfdb4f5583f992af578d43c9b4659c46f81ef8fcedd870e112aaa415bc27e88c290025700d8f7ea35a26e812
7
+ data.tar.gz: 601067ee4dd65cd6781b0a8719e988b5ba4f7888760a9dc4069b1d5369bfe497358314cf97ed01b3be9aee48a6cca0368e9bd6d0b31e59b930be818ba8be920b
data/lib/osb/animation.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  module Osb
4
4
  # A moving image.
5
5
  class Animation
6
- # @api private
6
+ # @private
7
7
  attr_reader :commands, :layer
8
8
  include Commandable
9
9
 
data/lib/osb/assert.rb CHANGED
@@ -1,20 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Osb
4
- # @api private
4
+ # @private
5
5
  class TypeError < StandardError
6
6
  end
7
7
 
8
- # @api private
8
+ # @private
9
9
  class InvalidValueError < StandardError
10
10
  end
11
11
 
12
- # @api private
12
+ # @private
13
13
  module Internal
14
- # @api private
14
+ # @private
15
15
  Boolean = [TrueClass, FalseClass]
16
16
 
17
- # @api private
17
+ # @private
18
18
  class TypedArray
19
19
  # @param [Class] type
20
20
  def initialize(type)
@@ -30,16 +30,21 @@ module Osb
30
30
  end
31
31
  end
32
32
 
33
- # @api private
33
+ # @private
34
34
  # @type [Hash{Class => Hash{Class => Object}}]
35
- T = { Array => { Numeric => TypedArray.new(Numeric) } }
35
+ T = {
36
+ Array => {
37
+ Numeric => TypedArray.new(Numeric),
38
+ Integer => TypedArray.new(Integer)
39
+ }
40
+ }
36
41
 
37
42
  # Check if supplied argument is correctly typed.
38
43
  # @param [Object] arg
39
44
  # @param [BasicObject, Array, TypedArray] possible_types
40
45
  # @param [String] param_name
41
46
  # @return [void]
42
- # @api private
47
+ # @private
43
48
  def self.assert_type!(arg, possible_types, param_name)
44
49
  if possible_types.is_a?(Array)
45
50
  valid =
@@ -75,7 +80,7 @@ module Osb
75
80
  # @param [BasicObject, Array, Range] possible_values
76
81
  # @param [String] param_name
77
82
  # @return [void]
78
- # @api private
83
+ # @private
79
84
  def self.assert_value!(arg, possible_values, param_name)
80
85
  val =
81
86
  if arg.is_a?(String) && arg.empty?
@@ -113,7 +118,7 @@ module Osb
113
118
  # Ensure the file name extenstion is correct.
114
119
  # @param [String] file_name
115
120
  # @param [String, Array<String>] exts
116
- # @api private
121
+ # @private
117
122
  def self.assert_file_name_ext!(file_name, exts)
118
123
  if exts.is_a?(Array)
119
124
  exts_ = exts.join("|")
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Osb
2
4
  # Beatmap's background.
3
5
  class Background
4
- # @api private
6
+ # @private
5
7
  attr_reader :command
6
8
 
7
9
  # @param [String] file_name location of the background image relative to the beatmap directory.
data/lib/osb/color.rb CHANGED
@@ -11,36 +11,52 @@ module Osb
11
11
  # @attribute [rw] b
12
12
  # @return Blue value.
13
13
 
14
- # @param [Integer] r red value
14
+ # @param [Integer, String, Array<Integer>] r red value, a hex +String+,
15
+ # or an +Array+ of 3 +{Integer}+s.
15
16
  # @param [Integer] g green value
16
17
  # @param [Integer] b blue value
17
- def initialize(r, g, b)
18
- Internal.assert_type!(r, Integer, "r")
19
- Internal.assert_value!(r, 0..255, "r")
18
+ def initialize(r, g = nil, b = nil)
19
+ Internal.assert_type!(
20
+ r,
21
+ [Integer, String, Internal::T[Array][Integer]],
22
+ "r"
23
+ )
24
+ if r.is_a?(Array)
25
+ if r.size != 3
26
+ raise InvalidValueError, "Must be an Array of 3 Integers."
27
+ end
28
+ @r = r[0]
29
+ @g = r[1]
30
+ @b = r[2]
31
+ elsif r.is_a?(String)
32
+ Color.from_hex(r)
33
+ else
34
+ Internal.assert_value!(r, 0..255, "r")
20
35
 
21
- Internal.assert_type!(g, Integer, "g")
22
- Internal.assert_value!(g, 0..255, "g")
36
+ Internal.assert_type!(g, Integer, "g")
37
+ Internal.assert_value!(g, 0..255, "g")
23
38
 
24
- Internal.assert_type!(b, Integer, "b")
25
- Internal.assert_value!(b, 0..255, "b")
39
+ Internal.assert_type!(b, Integer, "b")
40
+ Internal.assert_value!(b, 0..255, "b")
26
41
 
27
- @r = r
28
- @g = g
29
- @b = b
42
+ @r = r
43
+ @g = g
44
+ @b = b
45
+ end
30
46
  end
31
47
 
32
48
  # Returns whether 2 colors are not equal.
33
49
  # @param [Color] color
34
50
  def !=(color)
35
51
  Internal.assert_type!(color, Color, "color")
36
-
52
+
37
53
  color.r != self.r && color.g != self.g && color.b != self.b
38
54
  end
39
55
 
40
56
  # Converts an HSL color value to RGB.
41
- # @param [Integer] h
42
- # @param [Integer] s
43
- # @param [Integer] l
57
+ # @param [Integer] hue
58
+ # @param [Integer] saturation
59
+ # @param [Integer] lightness
44
60
  # @return [Color]
45
61
  def self.from_hsl(h, s, l)
46
62
  Internal.assert_type!(h, Integer, "h")
@@ -70,7 +86,7 @@ module Osb
70
86
  Color.new((r * 255).round, (g * 255).round, (b * 255).round)
71
87
  end
72
88
 
73
- # @api private
89
+ # @private
74
90
  # @param [Float] p
75
91
  # @param [Float] q
76
92
  # @param [Float] t_
@@ -84,7 +100,7 @@ module Osb
84
100
  return p
85
101
  end
86
102
 
87
- # Create a Color object from hex string.
103
+ # Create a +{Color}+ object from hex string.
88
104
  # @param [String] hex
89
105
  # @return [Color]
90
106
  def self.from_hex(hex)
@@ -95,4 +111,23 @@ module Osb
95
111
  components.collect { |component| component.to_i(16) }
96
112
  end
97
113
  end
114
+
115
+ # Create a new rgb +{Color}+.
116
+ # @param [Integer, String, Array<Integer>] r red value, a hex +String+,
117
+ # or an +Array+ of 3 +{Integer}+s.
118
+ # @param [Integer] g green value
119
+ # @param [Integer] b blue value
120
+ # @return [Color]
121
+ def rgb(r, g = nil, b = nil)
122
+ Color.new(r, g, b)
123
+ end
124
+
125
+ # Create a new hsl +{Color}+.
126
+ # @param [Integer] hue
127
+ # @param [Integer] saturation
128
+ # @param [Integer] lightness
129
+ # @return [Color]
130
+ def hsl(h, s, l)
131
+ Color.from_hsl(h, s, l)
132
+ end
98
133
  end
@@ -4,21 +4,21 @@ module Osb
4
4
  module Internal
5
5
  # @param [Integer] time
6
6
  # @return [void]
7
- # @api private
7
+ # @private
8
8
  def self.raise_if_invalid_start_time!(time)
9
9
  Internal.assert_type!(time, Integer, "start_time")
10
10
  end
11
11
 
12
12
  # @param [Integer] time
13
13
  # @return [void]
14
- # @api private
14
+ # @private
15
15
  def self.raise_if_invalid_end_time!(time)
16
16
  Internal.assert_type!(time, Integer, "end_time")
17
17
  end
18
18
 
19
19
  # @param [Integer] easing
20
20
  # @return [void]
21
- # @api private
21
+ # @private
22
22
  def self.raise_if_invalid_easing!(easing)
23
23
  Internal.assert_type!(easing, Integer, "easing")
24
24
  Internal.assert_value!(easing, Easing::ALL, "easing")
@@ -26,10 +26,12 @@ module Osb
26
26
  end
27
27
 
28
28
  module Commandable
29
+ # @private
29
30
  private def tab_level
30
31
  @is_in_trigger ? 2 : 1
31
32
  end
32
33
 
34
+ # @private
33
35
  private def raise_if_trigger_called!
34
36
  if @trigger_called
35
37
  raise RuntimeError, "Do not add commands after #trigger is called."
@@ -42,6 +44,7 @@ module Osb
42
44
  # @param [Integer] easing
43
45
  # @param [Numeric] start_opacity
44
46
  # @param [Numeric] end_opacity
47
+ # @return [void]
45
48
  def fade(
46
49
  start_time:,
47
50
  end_time: start_time,
@@ -71,6 +74,7 @@ module Osb
71
74
  # @param [Integer] easing
72
75
  # @param [Osb::Vector2, Array<Numeric>] start_position
73
76
  # @param [Osb::Vector2, Array<Numeric>] end_position
77
+ # @return [void]
74
78
  def move(
75
79
  start_time:,
76
80
  end_time: start_time,
@@ -84,12 +88,12 @@ module Osb
84
88
  Internal.raise_if_invalid_easing!(easing)
85
89
  Internal.assert_type!(
86
90
  start_position,
87
- [Osb::Vector2, T[Array][Numeric]],
91
+ [Osb::Vector2, Internal::T[Array][Numeric]],
88
92
  "start_position"
89
93
  )
90
94
  Internal.assert_type!(
91
95
  end_position,
92
- [Osb::Vector2, T[Array][Numeric]],
96
+ [Osb::Vector2, Internal::T[Array][Numeric]],
93
97
  "end_position"
94
98
  )
95
99
  if start_position.is_a?(Array)
@@ -100,8 +104,9 @@ module Osb
100
104
  tabs = " " * self.tab_level
101
105
  command =
102
106
  "#{tabs}M,#{start_time},#{end_time},#{start_position.x},#{start_position.y}"
103
- command += ",#{end_position.x},#{end_position.y}" if end_position !=
104
- start_position
107
+ if end_position != start_position
108
+ command += ",#{end_position.x},#{end_position.y}"
109
+ end
105
110
  @commands << command
106
111
  end
107
112
 
@@ -111,6 +116,7 @@ module Osb
111
116
  # @param [Integer] easing
112
117
  # @param [Numeric] start_x
113
118
  # @param [Numeric] end_x
119
+ # @return [void]
114
120
  def move_x(
115
121
  start_time:,
116
122
  end_time: start_time,
@@ -138,6 +144,7 @@ module Osb
138
144
  # @param [Integer] easing
139
145
  # @param [Numeric] start_y
140
146
  # @param [Numeric] end_y
147
+ # @return [void]
141
148
  def move_y(
142
149
  start_time:,
143
150
  end_time: start_time,
@@ -167,6 +174,7 @@ module Osb
167
174
  # @param [Integer] easing
168
175
  # @param [Numeric, Osb::Vector2, Array<Numeric>] start_scale
169
176
  # @param [Numeric, Osb::Vector2, Array<Numeric>] end_scale
177
+ # @return [void]
170
178
  def scale(
171
179
  start_time:,
172
180
  end_time: start_time,
@@ -180,12 +188,12 @@ module Osb
180
188
  Internal.raise_if_invalid_easing!(easing)
181
189
  Internal.assert_type!(
182
190
  start_scale,
183
- [Numeric, T[Array][Numeric], Osb::Vector2],
191
+ [Numeric, Internal::T[Array][Numeric], Osb::Vector2],
184
192
  "start_scale"
185
193
  )
186
194
  Internal.assert_type!(
187
195
  end_scale,
188
- [Numeric, T[Array][Numeric], Osb::Vector2],
196
+ [Numeric, Internal::T[Array][Numeric], Osb::Vector2],
189
197
  "end_scale"
190
198
  )
191
199
 
@@ -207,7 +215,6 @@ module Osb
207
215
  end
208
216
 
209
217
  start_scale = Osb::Vector2.new(start_scale) if start_scale.is_a?(Array)
210
-
211
218
  end_scale = Osb::Vector2.new(end_scale) if end_scale.is_a?(Array)
212
219
 
213
220
  command =
@@ -223,6 +230,7 @@ module Osb
223
230
  # @param [Integer] easing
224
231
  # @param [Float] start_angle start angle in radians.
225
232
  # @param [Float] end_angle end angle in radians.
233
+ # @return [void]
226
234
  def rotate(
227
235
  start_time:,
228
236
  end_time: start_time,
@@ -248,8 +256,9 @@ module Osb
248
256
  # @param [Integer] start_time
249
257
  # @param [Integer] end_time
250
258
  # @param [Integer] easing
251
- # @param [Osb::Color] start_color
252
- # @param [Osb::Color] end_color
259
+ # @param [Osb::Color, Array<Integer>] start_color
260
+ # @param [Osb::Color, Array<Integer>] end_color
261
+ # @return [void]
253
262
  def color(
254
263
  start_time:,
255
264
  end_time: start_time,
@@ -261,8 +270,19 @@ module Osb
261
270
  Internal.raise_if_invalid_start_time!(start_time)
262
271
  Internal.raise_if_invalid_end_time!(end_time)
263
272
  Internal.raise_if_invalid_easing!(easing)
264
- Internal.assert_type!(start_color, Osb::Color, "start_color")
265
- Internal.assert_type!(end_color, Osb::Color, "end_color")
273
+ Internal.assert_type!(
274
+ start_color,
275
+ [Osb::Color, Internal::T[Array][Integer]],
276
+ "start_color"
277
+ )
278
+ Internal.assert_type!(
279
+ end_color,
280
+ [Osb::Color, Internal::T[Array][Integer]],
281
+ "end_color"
282
+ )
283
+
284
+ start_color = Color.new(start_color) if start_color.is_a?(Array)
285
+ end_color = Color.new(end_color) if end_color.is_a?(Array)
266
286
 
267
287
  end_time = "" if start_time == end_time
268
288
  tabs = " " * self.tab_level
@@ -279,6 +299,7 @@ module Osb
279
299
  # @param [Integer] end_time
280
300
  # @param [Boolean] horizontally
281
301
  # @param [Boolean] vertically
302
+ # @return [void]
282
303
  def flip(start_time:, end_time:, horizontally: true, vertically: false)
283
304
  self.raise_if_trigger_called!
284
305
  Internal.raise_if_invalid_start_time!(start_time)
@@ -304,6 +325,7 @@ module Osb
304
325
  # Use additive-color blending instead of alpha-blending.
305
326
  # @param [Integer] start_time
306
327
  # @param [Integer] end_time
328
+ # @return [void]
307
329
  def additive_color_blending(start_time:, end_time:)
308
330
  self.raise_if_trigger_called!
309
331
  Internal.raise_if_invalid_start_time!(start_time)
@@ -338,7 +360,8 @@ module Osb
338
360
  # @param [String] on indicates the trigger condition. It can be "Failing" or "Passing".
339
361
  # @param [Integer] start_time the timestamp at which the trigger becomes valid.
340
362
  # @param [Integer] end_time the timestamp at which the trigger stops being valid.
341
- def trigger(on:, start_time:, end_time:)
363
+ # @return [void]
364
+ def trigger(on:, start_time:, end_time:, &blk)
342
365
  self.raise_if_trigger_called!
343
366
  Internal.raise_if_invalid_start_time!(start_time)
344
367
  Internal.raise_if_invalid_end_time!(end_time)
@@ -0,0 +1,252 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Osb
4
+ # @private
5
+ def current_object
6
+ return @sprite if @sprite
7
+ return @animation if @animation
8
+ return nil
9
+ end
10
+
11
+ # @private
12
+ def raise_unless_inside_object!
13
+ self.raise_unless_inside_storyboard!
14
+ unless self.current_object
15
+ raise RuntimeError,
16
+ "Do not call this method outside of a sprite/animation context."
17
+ end
18
+ end
19
+
20
+ # Change the opacity of the object (how transparent it is).
21
+ # @param [Integer] start_time
22
+ # @param [Integer] end_time
23
+ # @param [Integer] easing
24
+ # @param [Numeric] start_opacity
25
+ # @param [Numeric] end_opacity
26
+ # @return [void]
27
+ def fade(
28
+ start_time:,
29
+ end_time: start_time,
30
+ easing: Easing::Linear,
31
+ start_opacity:,
32
+ end_opacity: start_opacity
33
+ )
34
+ self.raise_unless_inside_object!
35
+ self.current_object.fade(
36
+ start_time: start_time,
37
+ end_time: end_time,
38
+ easing: easing,
39
+ start_opacity: start_opacity,
40
+ end_opacity: end_opacity
41
+ )
42
+ end
43
+
44
+ # Move the object to a new position in the play area.
45
+ # @param [Integer] start_time
46
+ # @param [Integer] end_time
47
+ # @param [Integer] easing
48
+ # @param [Osb::Vector2, Array<Numeric>] start_position
49
+ # @param [Osb::Vector2, Array<Numeric>] end_position
50
+ # @return [void]
51
+ def move(
52
+ start_time:,
53
+ end_time: start_time,
54
+ easing: Easing::Linear,
55
+ start_position:,
56
+ end_position: start_position
57
+ )
58
+ self.raise_unless_inside_object!
59
+ self.current_object.move(
60
+ start_time: start_time,
61
+ end_time: end_time,
62
+ easing: easing,
63
+ start_position: start_position,
64
+ end_position: end_position
65
+ )
66
+ end
67
+
68
+ # Move the object along the x axis.
69
+ # @param [Integer] start_time
70
+ # @param [Integer] end_time
71
+ # @param [Integer] easing
72
+ # @param [Numeric] start_x
73
+ # @param [Numeric] end_x
74
+ # @return [void]
75
+ def move_x(
76
+ start_time:,
77
+ end_time: start_time,
78
+ easing: Easing::Linear,
79
+ start_x:,
80
+ end_x: start_x
81
+ )
82
+ self.raise_unless_inside_object!
83
+ self.current_object.move_x(
84
+ start_time: start_time,
85
+ end_time: end_time,
86
+ easing: easing,
87
+ start_x: start_x,
88
+ end_x: end_x
89
+ )
90
+ end
91
+
92
+ # Move the object along the y axis.
93
+ # @param [Integer] start_time
94
+ # @param [Integer] end_time
95
+ # @param [Integer] easing
96
+ # @param [Numeric] start_y
97
+ # @param [Numeric] end_y
98
+ # @return [void]
99
+ def move_y(
100
+ start_time:,
101
+ end_time: start_time,
102
+ easing: Easing::Linear,
103
+ start_y:,
104
+ end_y: start_y
105
+ )
106
+ self.raise_unless_inside_object!
107
+ self.current_object.move_x(
108
+ start_time: start_time,
109
+ end_time: end_time,
110
+ easing: easing,
111
+ start_y: start_y,
112
+ end_y: end_y
113
+ )
114
+ end
115
+
116
+ # Change the size of the object relative to its original size. Will scale
117
+ # seperatedly if given +Osb::Vector2+s or +Array<Numeric>+s. The scaling is
118
+ # affected by the object's origin
119
+ # @param [Integer] start_time
120
+ # @param [Integer] end_time
121
+ # @param [Integer] easing
122
+ # @param [Numeric, Osb::Vector2, Array<Numeric>] start_scale
123
+ # @param [Numeric, Osb::Vector2, Array<Numeric>] end_scale
124
+ # @return [void]
125
+ def scale(
126
+ start_time:,
127
+ end_time: start_time,
128
+ easing: Easing::Linear,
129
+ start_scale:,
130
+ end_scale: start_scale
131
+ )
132
+ self.raise_unless_inside_object!
133
+ self.current_object.scale(
134
+ start_time: start_time,
135
+ end_time: end_time,
136
+ easing: easing,
137
+ start_scale: start_scale,
138
+ end_scale: end_scale
139
+ )
140
+ end
141
+
142
+ # Rotate the object around its origin.
143
+ # @param [Integer] start_time
144
+ # @param [Integer] end_time
145
+ # @param [Integer] easing
146
+ # @param [Float] start_angle start angle in radians.
147
+ # @param [Float] end_angle end angle in radians.
148
+ # @return [void]
149
+ def rotate(
150
+ start_time:,
151
+ end_time: start_time,
152
+ easing: Easing::Linear,
153
+ start_angle:,
154
+ end_angle: start_angle
155
+ )
156
+ self.raise_unless_inside_object!
157
+ self.current_object.rotate(
158
+ start_time: start_time,
159
+ end_time: end_time,
160
+ easing: easing,
161
+ start_angle: start_angle,
162
+ end_angle: end_angle
163
+ )
164
+ end
165
+
166
+ # The virtual light source colour on the object. The colours of the pixels on the object are determined subtractively.
167
+ # @param [Integer] start_time
168
+ # @param [Integer] end_time
169
+ # @param [Integer] easing
170
+ # @param [Osb::Color, Array<Integer>] start_color
171
+ # @param [Osb::Color, Array<Integer>] end_color
172
+ # @return [void]
173
+ def color(
174
+ start_time:,
175
+ end_time: start_time,
176
+ easing: Easing::Linear,
177
+ start_color:,
178
+ end_color: start_color
179
+ )
180
+ self.raise_unless_inside_object!
181
+ self.current_object.color(
182
+ start_time: start_time,
183
+ end_time: end_time,
184
+ easing: easing,
185
+ start_color: start_color,
186
+ end_color: end_color
187
+ )
188
+ end
189
+
190
+ # Flip the object horizontally or vertically.
191
+ # @param [Integer] start_time
192
+ # @param [Integer] end_time
193
+ # @param [Boolean] horizontally
194
+ # @param [Boolean] vertically
195
+ # @return [void]
196
+ def flip(start_time:, end_time:, horizontally: true, vertically: false)
197
+ self.raise_unless_inside_object!
198
+ self.current_object.flip(
199
+ start_time: start_time,
200
+ end_time: end_time,
201
+ horizontally: horizontally,
202
+ vertically: vertically
203
+ )
204
+ end
205
+
206
+ # Use additive-color blending instead of alpha-blending.
207
+ # @param [Integer] start_time
208
+ # @param [Integer] end_time
209
+ # @return [void]
210
+ def additive_color_blending(start_time:, end_time:)
211
+ self.raise_unless_inside_object!
212
+ self.current_object.additive_color_blending(
213
+ start_time: start_time,
214
+ end_time: end_time
215
+ )
216
+ end
217
+
218
+ # Add a group of commands on a specific condition.
219
+ # `#trigger` can only be called on an empty object declaration (no commands).
220
+ # Pass a block to this method call to specify which commands to run if
221
+ # the condition is met.
222
+ #
223
+ # @example
224
+ # img.trigger(on: "Passing", start_time: 0, end_time: 1000) do
225
+ # img.fade(start_time: 0, start_opacity: 0.5)
226
+ # end
227
+ #
228
+ # In addition to the "implicit" player feedback via the separate
229
+ # Pass/Fail layers, you can use one of several Trigger conditions
230
+ # to cause a series of events to happen whenever that condition is
231
+ # fulfilled within a certain time period.
232
+ # Note that `start_time` and `end_time` of any commands called inside
233
+ # the block become relative to the `start_time` and `end_time` of the
234
+ # `#trigger` command.
235
+ #
236
+ # While osu! supports trigger on hitsounds playing, we decided to not
237
+ # include it in because it is unreliable/unpredictable.
238
+ #
239
+ # @param [String] on indicates the trigger condition. It can be "Failing" or "Passing".
240
+ # @param [Integer] start_time the timestamp at which the trigger becomes valid.
241
+ # @param [Integer] end_time the timestamp at which the trigger stops being valid.
242
+ # @return [void]
243
+ def trigger(on:, start_time:, end_time:, &blk)
244
+ self.raise_unless_inside_object!
245
+ self.current_object.trigger(
246
+ on: on,
247
+ start_time: start_time,
248
+ end_time: end_time,
249
+ &blk
250
+ )
251
+ end
252
+ end
@@ -0,0 +1,141 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Osb
4
+ # @private
5
+ def raise_unless_inside_storyboard!
6
+ unless @storyboard
7
+ raise RuntimeError,
8
+ "Do not call this method outside of a storyboard context."
9
+ end
10
+ end
11
+
12
+ # Set the output directory of this storyboard.
13
+ # @param [String] path path to .osb or .osu file
14
+ def out_path(path)
15
+ self.raise_unless_inside_storyboard!
16
+ @out_path = path
17
+ end
18
+
19
+ # Create an osu! storyboard.
20
+ # @return [void]
21
+ def storyboard
22
+ if @storyboard
23
+ raise RuntimeError,
24
+ "Cannot create a new storyboard inside another storyboard."
25
+ end
26
+ @storyboard = Storyboard.new
27
+ yield
28
+ raise RuntimeError, "Output path is not set." unless @out_path
29
+ @storyboard.generate(@out_path)
30
+ @storyboard = nil
31
+ end
32
+
33
+ # Create a still image.
34
+ # @param [String] layer the layer the object appears on.
35
+ # @param [String] origin where on the image should osu! consider that image's origin (coordinate) to be.
36
+ # @param [String] file_path filename of the image.
37
+ # @param [Osb::Vector2, nil] initial_position where the object should be by default.
38
+ # @return [void]
39
+ def sprite(
40
+ layer: Layer::Background,
41
+ origin: Origin::Center,
42
+ file_path:,
43
+ initial_position: nil
44
+ )
45
+ self.raise_unless_inside_storyboard!
46
+
47
+ if @sprite || @animation
48
+ raise RuntimeError,
49
+ "Cannot create a new sprite inside another sprite/animation."
50
+ end
51
+ @sprite =
52
+ Sprite.new(
53
+ layer: layer,
54
+ origin: origin,
55
+ file_path: file_path,
56
+ initial_position: initial_position
57
+ )
58
+ @storyboard << @sprite
59
+ yield
60
+ @sprite = nil
61
+ end
62
+
63
+ # Group multiple objects for clarity.
64
+ # @return [void]
65
+ def group(name: nil)
66
+ end
67
+
68
+ # Create a moving image.
69
+ # @param [String] layer the layer the object appears on.
70
+ # @param [String] origin where on the image should osu! consider that image's origin (coordinate) to be.
71
+ # @param [String] file_path filename of the image.
72
+ # @param [Vector2, nil] initial_position where the object should be by default.
73
+ # @param [Integer] frame_count how many frames the animation has.
74
+ # @param [Integer] frame_delay how many milliseconds should be in between each frame.
75
+ # @param [Boolean] repeat if the animation should loop or not.
76
+ # @return [void]
77
+ def animation(
78
+ layer: Osb::Layer::Background,
79
+ origin: Osb::Origin::Center,
80
+ file_path:,
81
+ initial_position: nil,
82
+ frame_count:,
83
+ frame_delay:,
84
+ repeat: false
85
+ )
86
+ self.raise_unless_inside_storyboard!
87
+
88
+ if @sprite || @animation
89
+ raise RuntimeError,
90
+ "Cannot create a new animation inside another animation/animation."
91
+ end
92
+ @animation =
93
+ Animation.new(
94
+ layer: layer,
95
+ origin: origin,
96
+ file_path: file_path,
97
+ initial_position: initial_position,
98
+ frame_count: frame_count,
99
+ frame_delay: frame_delay,
100
+ repeat: repeat
101
+ )
102
+ @storyboard << @animation
103
+ yield
104
+ @animation = nil
105
+ end
106
+
107
+ # Set the background image for the beatmap.
108
+ # @param [String] file_name location of the background image relative to the beatmap directory.
109
+ # @return [void]
110
+ def background(file_path:)
111
+ self.raise_unless_inside_storyboard!
112
+
113
+ @storyboard << Background.new(file_path: file_path)
114
+ end
115
+
116
+ # Set the video for the beatmap.
117
+ # @param [String] file_name location of the video file relative to the beatmap directory.
118
+ # @param [Integer] start_time when the video starts.
119
+ # @return [void]
120
+ def video(file_path:, start_time:)
121
+ self.raise_unless_inside_storyboard!
122
+
123
+ @storyboard << Video.new(file_path: file_path, start_time: start_time)
124
+ end
125
+
126
+ # Add an audio sample to the storyboard.
127
+ # @param [Integer] time the timestamp that the sound should start playing.
128
+ # @param [String] layer the layer you want the sound to be on.
129
+ # @param [String] file_path filename of the audio.
130
+ # @param [Integer] volume indicate the relative loudness of the sound.
131
+ def sample(time:, layer:, file_path:, volume: 100)
132
+ self.raise_unless_inside_storyboard!
133
+
134
+ @storyboard << Sample.new(
135
+ time: time,
136
+ layer: layer,
137
+ file_path: file_path,
138
+ volume: volume
139
+ )
140
+ end
141
+ end
data/lib/osb/integer.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Integer
2
4
  # Does nothing. Just a way to tell people it's represented in milliseconds.
3
5
  # @return [Integer]
data/lib/osb/numeric.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Numeric
2
4
  # The degrees method is used to convert from degrees to radians.
3
5
  # @return [Float]
data/lib/osb/sample.rb CHANGED
@@ -1,6 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Osb
4
+ # Audio sample.
2
5
  class Sample
3
- # @api private
6
+ # @private
4
7
  attr_reader :command
5
8
 
6
9
  # @param [Integer] time the timestamp that the sound should start playing.
data/lib/osb/sprite.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  module Osb
4
4
  # A still image.
5
5
  class Sprite
6
- # @api private
6
+ # @private
7
7
  attr_reader :commands, :layer
8
8
  include Commandable
9
9
 
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Osb
4
- # @api private
4
+ # @private
5
5
  module Internal
6
- # @api private
6
+ # @private
7
7
  class LayerManager
8
8
  attr_reader :background,
9
9
  :foreground,
@@ -71,7 +71,7 @@ module Osb
71
71
  # +Osb::Storyboard+ object, but we recommend you to split the project into multiple
72
72
  # +Osb::Group+ so it will be easier to manage.
73
73
  class Group
74
- # @api private
74
+ # @private
75
75
  attr_reader :layers
76
76
 
77
77
  def initialize
@@ -115,11 +115,11 @@ module Osb
115
115
  end
116
116
  end
117
117
 
118
- # Represent a osu! storyboard. Each sprite or animation can be added directly
118
+ # Represent an osu! storyboard. Each sprite or animation can be added directly
119
119
  # to the storyboard instance, or through an intermediate group. A group can
120
120
  # have multiple nested groups in itself.
121
121
  class Storyboard
122
- # @api private
122
+ # @private
123
123
  attr_reader :layers
124
124
 
125
125
  def initialize
data/lib/osb/vector2.rb CHANGED
@@ -10,14 +10,16 @@ module Osb
10
10
  # @return y coordinate of this vector
11
11
 
12
12
  # @param [Numeric, Array<Numeric>] x
13
- # x coordinate of this +Vector2+, or an +Array+ of 2 numbers.
14
- # @param [Numeric] y y coordinate of this +Vector2+
13
+ # x coordinate of this +{Vector2}+, or an +Array+ of 2 numbers.
14
+ # @param [Numeric] y y coordinate of this +{Vector2}+
15
15
  def initialize(x = 0, y = 0)
16
16
  Internal.assert_type!(x, [Numeric, Internal::T[Array][Numeric]], "x")
17
17
  Internal.assert_type!(y, Numeric, "y")
18
18
 
19
19
  if x.is_a?(Array)
20
- raise InvalidValueError, "Must be an Array of 2 Numeric values." if x.size != 2
20
+ if x.size != 2
21
+ raise InvalidValueError, "Must be an Array of 2 Numeric values."
22
+ end
21
23
  @x = x[0]
22
24
  @y = x[1]
23
25
  else
@@ -26,7 +28,7 @@ module Osb
26
28
  end
27
29
  end
28
30
 
29
- # Add another +Vector2+ to this one.
31
+ # Add another +{Vector2}+ to this one.
30
32
  # @param [Vector2] vector
31
33
  # @return [Vector2]
32
34
  def +(vector)
@@ -34,7 +36,7 @@ module Osb
34
36
  Vector2.new(self.x + vector.x, self.y + vector.y)
35
37
  end
36
38
 
37
- # Subtract another +Vector2+ from this one.
39
+ # Subtract another +{Vector2}+ from this one.
38
40
  # @param [Vector2] vector
39
41
  # @return [Vector2]
40
42
  def -(vector)
@@ -42,7 +44,7 @@ module Osb
42
44
  Vector2.new(self.x - vector.x, self.y - vector.y)
43
45
  end
44
46
 
45
- # Returns whether two +Vector2+ are equal within tolerance
47
+ # Returns whether two +{Vector2}+ are equal within tolerance
46
48
  # @param [Vector2] vector
47
49
  # @return [Boolean]
48
50
  def ==(vector)
@@ -50,14 +52,14 @@ module Osb
50
52
  Math.fuzzy_equal(self.x, vector.x) && Math.fuzzy_equal(self.y, vector.y)
51
53
  end
52
54
 
53
- # Returns whether two +Vector2+ are not equal within tolerance
55
+ # Returns whether two +{Vector2}+ are not equal within tolerance
54
56
  # @param [Vector2] vector
55
57
  # @return [Boolean]
56
58
  def !=(vector)
57
59
  !(self == vector)
58
60
  end
59
61
 
60
- # Makes a copy of this +Vector2+.
62
+ # Makes a copy of this +{Vector2}+.
61
63
  # @return [Vector2]
62
64
  def clone
63
65
  Vector2.new(self.x, self.y)
@@ -69,16 +71,24 @@ module Osb
69
71
  [self.x, self.y]
70
72
  end
71
73
 
72
- # Returns a string representation of this +Vector2+.
74
+ # Returns a string representation of this +{Vector2}+.
73
75
  # @return [String]
74
76
  def to_s
75
77
  self.to_a.to_s
76
78
  end
77
79
 
78
- # Returns the length of this +Vector2+.
80
+ # Returns the length of this +{Vector2}+.
79
81
  # @return [Float]
80
82
  def length
81
83
  Math.sqrt(self.x**2 + self.y**2)
82
84
  end
83
85
  end
86
+
87
+ # Create a +{Vector2}+.
88
+ # @param [Numeric, Array<Numeric>] x
89
+ # x coordinate of this +{Vector2}+, or an +Array+ of 2 numbers.
90
+ # @param [Numeric] y y coordinate of this +{Vector2}+
91
+ def vec2(x = 0, y = 0)
92
+ Vector2.new(x, y)
93
+ end
84
94
  end
data/lib/osb/video.rb CHANGED
@@ -1,14 +1,14 @@
1
1
  module Osb
2
- # A video.
2
+ # Background video.
3
3
  class Video
4
- # @api private
4
+ # @private
5
5
  attr_reader :commands, :layer
6
6
 
7
- # @param [String] file_path location of the background image relative to the beatmap directory.
7
+ # @param [String] file_path location of the video file relative to the beatmap directory.
8
8
  # @param [Integer] start_time when the video starts.
9
9
  def initialize(file_path:, start_time:)
10
10
  Internal.assert_type!(file_path, String, "file_path")
11
- Internal.assert_file_name_ext!(file_path, %w[png jpg jpeg])
11
+ Internal.assert_file_name_ext!(file_path, "mp4")
12
12
  Internal.assert_type!(start_time, Integer, "start_time")
13
13
 
14
14
  @command = "1,#{start_time},\"#{file_path}\""
data/lib/osb.rb CHANGED
@@ -16,7 +16,12 @@ require_relative "osb/sample"
16
16
  require_relative "osb/video"
17
17
  require_relative "osb/background"
18
18
  require_relative "osb/storyboard"
19
+ require_relative "osb/dsl/object"
20
+ require_relative "osb/dsl/commands"
19
21
 
20
22
  module Osb
21
- VERSION = "1.0.3"
23
+ VERSION = "1.1.0"
22
24
  end
25
+
26
+ # Extend the main object with the DSL commands.
27
+ extend Osb
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: osb
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.3
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dinh Vu
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-08-19 00:00:00.000000000 Z
11
+ date: 2023-08-21 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: A simple framework for building osu! storyboard.
14
14
  email: dinhvu2509@gmail.com
@@ -22,6 +22,8 @@ files:
22
22
  - lib/osb/background.rb
23
23
  - lib/osb/color.rb
24
24
  - lib/osb/commandable.rb
25
+ - lib/osb/dsl/commands.rb
26
+ - lib/osb/dsl/object.rb
25
27
  - lib/osb/enums/easing.rb
26
28
  - lib/osb/enums/layer.rb
27
29
  - lib/osb/enums/origin.rb
@@ -52,7 +54,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
52
54
  - !ruby/object:Gem::Version
53
55
  version: '0'
54
56
  requirements: []
55
- rubygems_version: 3.1.6
57
+ rubygems_version: 3.4.18
56
58
  signing_key:
57
59
  specification_version: 4
58
60
  summary: osu! storyboard framework