bevy 1.0.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.
Files changed (93) hide show
  1. checksums.yaml +7 -0
  2. data/Cargo.lock +4279 -0
  3. data/Cargo.toml +36 -0
  4. data/README.md +226 -0
  5. data/crates/bevy/Cargo.toml +52 -0
  6. data/crates/bevy/src/app.rs +43 -0
  7. data/crates/bevy/src/component.rs +111 -0
  8. data/crates/bevy/src/entity.rs +30 -0
  9. data/crates/bevy/src/error.rs +32 -0
  10. data/crates/bevy/src/event.rs +190 -0
  11. data/crates/bevy/src/input_bridge.rs +300 -0
  12. data/crates/bevy/src/lib.rs +42 -0
  13. data/crates/bevy/src/mesh_renderer.rs +328 -0
  14. data/crates/bevy/src/query.rs +53 -0
  15. data/crates/bevy/src/render_app.rs +689 -0
  16. data/crates/bevy/src/resource.rs +28 -0
  17. data/crates/bevy/src/schedule.rs +186 -0
  18. data/crates/bevy/src/sprite_renderer.rs +355 -0
  19. data/crates/bevy/src/system.rs +44 -0
  20. data/crates/bevy/src/text_renderer.rs +258 -0
  21. data/crates/bevy/src/types/color.rs +114 -0
  22. data/crates/bevy/src/types/dynamic.rs +131 -0
  23. data/crates/bevy/src/types/math.rs +260 -0
  24. data/crates/bevy/src/types/mod.rs +9 -0
  25. data/crates/bevy/src/types/transform.rs +166 -0
  26. data/crates/bevy/src/world.rs +163 -0
  27. data/crates/bevy_ruby_render/Cargo.toml +22 -0
  28. data/crates/bevy_ruby_render/src/asset.rs +360 -0
  29. data/crates/bevy_ruby_render/src/audio.rs +511 -0
  30. data/crates/bevy_ruby_render/src/camera.rs +365 -0
  31. data/crates/bevy_ruby_render/src/gamepad.rs +398 -0
  32. data/crates/bevy_ruby_render/src/lib.rs +26 -0
  33. data/crates/bevy_ruby_render/src/material.rs +310 -0
  34. data/crates/bevy_ruby_render/src/mesh.rs +491 -0
  35. data/crates/bevy_ruby_render/src/sprite.rs +289 -0
  36. data/ext/bevy/Cargo.toml +20 -0
  37. data/ext/bevy/extconf.rb +6 -0
  38. data/ext/bevy/src/conversions.rs +137 -0
  39. data/ext/bevy/src/lib.rs +29 -0
  40. data/ext/bevy/src/ruby_app.rs +65 -0
  41. data/ext/bevy/src/ruby_color.rs +149 -0
  42. data/ext/bevy/src/ruby_component.rs +189 -0
  43. data/ext/bevy/src/ruby_entity.rs +33 -0
  44. data/ext/bevy/src/ruby_math.rs +384 -0
  45. data/ext/bevy/src/ruby_query.rs +64 -0
  46. data/ext/bevy/src/ruby_render_app.rs +779 -0
  47. data/ext/bevy/src/ruby_system.rs +122 -0
  48. data/ext/bevy/src/ruby_world.rs +107 -0
  49. data/lib/bevy/animation.rb +597 -0
  50. data/lib/bevy/app.rb +675 -0
  51. data/lib/bevy/asset.rb +613 -0
  52. data/lib/bevy/audio.rb +545 -0
  53. data/lib/bevy/audio_effects.rb +224 -0
  54. data/lib/bevy/camera.rb +412 -0
  55. data/lib/bevy/component.rb +91 -0
  56. data/lib/bevy/diagnostics.rb +227 -0
  57. data/lib/bevy/ecs_advanced.rb +296 -0
  58. data/lib/bevy/event.rb +199 -0
  59. data/lib/bevy/gizmos.rb +158 -0
  60. data/lib/bevy/gltf.rb +227 -0
  61. data/lib/bevy/hierarchy.rb +444 -0
  62. data/lib/bevy/input.rb +514 -0
  63. data/lib/bevy/lighting.rb +369 -0
  64. data/lib/bevy/material.rb +248 -0
  65. data/lib/bevy/mesh.rb +257 -0
  66. data/lib/bevy/navigation.rb +344 -0
  67. data/lib/bevy/networking.rb +335 -0
  68. data/lib/bevy/particle.rb +337 -0
  69. data/lib/bevy/physics.rb +396 -0
  70. data/lib/bevy/plugins/default_plugins.rb +34 -0
  71. data/lib/bevy/plugins/input_plugin.rb +49 -0
  72. data/lib/bevy/reflect.rb +361 -0
  73. data/lib/bevy/render_graph.rb +210 -0
  74. data/lib/bevy/resource.rb +185 -0
  75. data/lib/bevy/scene.rb +254 -0
  76. data/lib/bevy/shader.rb +319 -0
  77. data/lib/bevy/shape.rb +195 -0
  78. data/lib/bevy/skeletal.rb +248 -0
  79. data/lib/bevy/sprite.rb +152 -0
  80. data/lib/bevy/sprite_sheet.rb +444 -0
  81. data/lib/bevy/state.rb +277 -0
  82. data/lib/bevy/system.rb +206 -0
  83. data/lib/bevy/text.rb +99 -0
  84. data/lib/bevy/text_advanced.rb +455 -0
  85. data/lib/bevy/timer.rb +147 -0
  86. data/lib/bevy/transform.rb +158 -0
  87. data/lib/bevy/ui.rb +454 -0
  88. data/lib/bevy/ui_advanced.rb +568 -0
  89. data/lib/bevy/version.rb +5 -0
  90. data/lib/bevy/visibility.rb +250 -0
  91. data/lib/bevy/window.rb +302 -0
  92. data/lib/bevy.rb +390 -0
  93. metadata +150 -0
@@ -0,0 +1,444 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bevy
4
+ module Anchor
5
+ CENTER = :center
6
+ TOP_LEFT = :top_left
7
+ TOP_CENTER = :top_center
8
+ TOP_RIGHT = :top_right
9
+ CENTER_LEFT = :center_left
10
+ CENTER_RIGHT = :center_right
11
+ BOTTOM_LEFT = :bottom_left
12
+ BOTTOM_CENTER = :bottom_center
13
+ BOTTOM_RIGHT = :bottom_right
14
+ end
15
+
16
+ class TextureAtlasLayout
17
+ attr_reader :size, :tile_size, :columns, :rows, :padding, :offset
18
+ attr_reader :textures
19
+
20
+ def initialize(
21
+ tile_size:,
22
+ columns:,
23
+ rows:,
24
+ padding: nil,
25
+ offset: nil
26
+ )
27
+ @tile_size = tile_size
28
+ @columns = columns
29
+ @rows = rows
30
+ @padding = padding || Vec2.zero
31
+ @offset = offset || Vec2.zero
32
+ @size = Vec2.new(
33
+ @tile_size.x * @columns + @padding.x * (@columns - 1) + @offset.x * 2,
34
+ @tile_size.y * @rows + @padding.y * (@rows - 1) + @offset.y * 2
35
+ )
36
+ @textures = build_texture_rects
37
+ end
38
+
39
+ def self.from_grid(tile_size:, columns:, rows:, padding: nil, offset: nil)
40
+ new(
41
+ tile_size: tile_size,
42
+ columns: columns,
43
+ rows: rows,
44
+ padding: padding,
45
+ offset: offset
46
+ )
47
+ end
48
+
49
+ def tile_count
50
+ @columns * @rows
51
+ end
52
+
53
+ def get_texture_rect(index)
54
+ return nil if index < 0 || index >= tile_count
55
+
56
+ @textures[index]
57
+ end
58
+
59
+ def index_for(column, row)
60
+ return nil if column < 0 || column >= @columns
61
+ return nil if row < 0 || row >= @rows
62
+
63
+ row * @columns + column
64
+ end
65
+
66
+ def column_row_for(index)
67
+ return nil if index < 0 || index >= tile_count
68
+
69
+ row = index / @columns
70
+ column = index % @columns
71
+ [column, row]
72
+ end
73
+
74
+ def type_name
75
+ 'TextureAtlasLayout'
76
+ end
77
+
78
+ private
79
+
80
+ def build_texture_rects
81
+ rects = []
82
+ @rows.times do |row|
83
+ @columns.times do |col|
84
+ x = @offset.x + col * (@tile_size.x + @padding.x)
85
+ y = @offset.y + row * (@tile_size.y + @padding.y)
86
+ rects << Rect.new(
87
+ min: Vec2.new(x, y),
88
+ max: Vec2.new(x + @tile_size.x, y + @tile_size.y)
89
+ )
90
+ end
91
+ end
92
+ rects
93
+ end
94
+ end
95
+
96
+ class TextureAtlas
97
+ attr_reader :layout, :image
98
+
99
+ def initialize(layout:, image:)
100
+ @layout = layout
101
+ @image = image
102
+ end
103
+
104
+ def tile_count
105
+ @layout.tile_count
106
+ end
107
+
108
+ def get_texture_rect(index)
109
+ @layout.get_texture_rect(index)
110
+ end
111
+
112
+ def type_name
113
+ 'TextureAtlas'
114
+ end
115
+ end
116
+
117
+ class TextureAtlasSprite
118
+ attr_accessor :index, :color, :flip_x, :flip_y, :custom_size, :anchor
119
+
120
+ def initialize(
121
+ index: 0,
122
+ color: nil,
123
+ flip_x: false,
124
+ flip_y: false,
125
+ custom_size: nil,
126
+ anchor: nil
127
+ )
128
+ @index = index
129
+ @color = color || Color.white
130
+ @flip_x = flip_x
131
+ @flip_y = flip_y
132
+ @custom_size = custom_size
133
+ @anchor = anchor || Anchor::CENTER
134
+ end
135
+
136
+ def type_name
137
+ 'TextureAtlasSprite'
138
+ end
139
+ end
140
+
141
+ class SpriteSheetBundle
142
+ attr_reader :sprite, :atlas, :transform, :global_transform, :visibility
143
+
144
+ def initialize(
145
+ sprite: nil,
146
+ atlas: nil,
147
+ transform: nil
148
+ )
149
+ @sprite = sprite || TextureAtlasSprite.new
150
+ @atlas = atlas
151
+ @transform = transform || Transform.identity
152
+ @global_transform = GlobalTransform.from_transform(@transform)
153
+ @visibility = Visibility.new
154
+ end
155
+
156
+ def components
157
+ [@sprite, @atlas, @transform, @global_transform, @visibility]
158
+ end
159
+
160
+ def type_name
161
+ 'SpriteSheetBundle'
162
+ end
163
+ end
164
+
165
+ class AnimatedSprite
166
+ attr_accessor :frames, :current_frame, :timer, :looping, :playing
167
+
168
+ def initialize(
169
+ frames: [],
170
+ frame_duration: 0.1,
171
+ looping: true
172
+ )
173
+ @frames = frames
174
+ @frame_duration = frame_duration
175
+ @current_frame = 0
176
+ @timer = 0.0
177
+ @looping = looping
178
+ @playing = true
179
+ end
180
+
181
+ def frame_duration
182
+ @frame_duration
183
+ end
184
+
185
+ def frame_duration=(value)
186
+ @frame_duration = value.to_f
187
+ end
188
+
189
+ def fps
190
+ 1.0 / @frame_duration
191
+ end
192
+
193
+ def fps=(value)
194
+ @frame_duration = 1.0 / value.to_f
195
+ end
196
+
197
+ def update(delta)
198
+ return unless @playing
199
+ return if @frames.empty?
200
+
201
+ @timer += delta
202
+
203
+ while @timer >= @frame_duration
204
+ @timer -= @frame_duration
205
+ @current_frame += 1
206
+
207
+ if @current_frame >= @frames.size
208
+ if @looping
209
+ @current_frame = 0
210
+ else
211
+ @current_frame = @frames.size - 1
212
+ @playing = false
213
+ end
214
+ end
215
+ end
216
+ end
217
+
218
+ def current_index
219
+ return 0 if @frames.empty?
220
+
221
+ @frames[@current_frame]
222
+ end
223
+
224
+ def play
225
+ @playing = true
226
+ end
227
+
228
+ def pause
229
+ @playing = false
230
+ end
231
+
232
+ def stop
233
+ @playing = false
234
+ @current_frame = 0
235
+ @timer = 0.0
236
+ end
237
+
238
+ def reset
239
+ @current_frame = 0
240
+ @timer = 0.0
241
+ end
242
+
243
+ def finished?
244
+ !@looping && @current_frame >= @frames.size - 1 && @timer >= @frame_duration
245
+ end
246
+
247
+ def type_name
248
+ 'AnimatedSprite'
249
+ end
250
+ end
251
+
252
+ class SpriteSheetAnimation
253
+ attr_reader :name, :frames, :frame_duration, :looping
254
+
255
+ def initialize(name:, frames:, frame_duration: 0.1, looping: true)
256
+ @name = name
257
+ @frames = frames
258
+ @frame_duration = frame_duration
259
+ @looping = looping
260
+ end
261
+
262
+ def to_animated_sprite
263
+ AnimatedSprite.new(
264
+ frames: @frames,
265
+ frame_duration: @frame_duration,
266
+ looping: @looping
267
+ )
268
+ end
269
+
270
+ def type_name
271
+ 'SpriteSheetAnimation'
272
+ end
273
+ end
274
+
275
+ class AnimationLibrary
276
+ attr_reader :animations
277
+
278
+ def initialize
279
+ @animations = {}
280
+ end
281
+
282
+ def add(animation)
283
+ @animations[animation.name] = animation
284
+ self
285
+ end
286
+
287
+ def get(name)
288
+ @animations[name]
289
+ end
290
+
291
+ def remove(name)
292
+ @animations.delete(name)
293
+ end
294
+
295
+ def names
296
+ @animations.keys
297
+ end
298
+
299
+ def type_name
300
+ 'AnimationLibrary'
301
+ end
302
+ end
303
+
304
+ class Rect
305
+ attr_accessor :min, :max
306
+
307
+ def initialize(min:, max:)
308
+ @min = min
309
+ @max = max
310
+ end
311
+
312
+ def width
313
+ @max.x - @min.x
314
+ end
315
+
316
+ def height
317
+ @max.y - @min.y
318
+ end
319
+
320
+ def size
321
+ Vec2.new(width, height)
322
+ end
323
+
324
+ def center
325
+ Vec2.new(
326
+ (@min.x + @max.x) / 2.0,
327
+ (@min.y + @max.y) / 2.0
328
+ )
329
+ end
330
+
331
+ def contains?(point)
332
+ point.x >= @min.x && point.x <= @max.x &&
333
+ point.y >= @min.y && point.y <= @max.y
334
+ end
335
+
336
+ def intersects?(other)
337
+ @min.x < other.max.x && @max.x > other.min.x &&
338
+ @min.y < other.max.y && @max.y > other.min.y
339
+ end
340
+
341
+ def type_name
342
+ 'Rect'
343
+ end
344
+ end
345
+
346
+ class NineSlice
347
+ attr_reader :border_left, :border_right, :border_top, :border_bottom
348
+ attr_accessor :center_scale_mode
349
+
350
+ STRETCH = :stretch
351
+ TILE = :tile
352
+
353
+ def initialize(
354
+ border_left: 0.0,
355
+ border_right: 0.0,
356
+ border_top: 0.0,
357
+ border_bottom: 0.0,
358
+ center_scale_mode: STRETCH
359
+ )
360
+ @border_left = border_left.to_f
361
+ @border_right = border_right.to_f
362
+ @border_top = border_top.to_f
363
+ @border_bottom = border_bottom.to_f
364
+ @center_scale_mode = center_scale_mode
365
+ end
366
+
367
+ def self.from_single_border(border)
368
+ new(
369
+ border_left: border,
370
+ border_right: border,
371
+ border_top: border,
372
+ border_bottom: border
373
+ )
374
+ end
375
+
376
+ def slices_for(source_rect, target_size)
377
+ source_w = source_rect.width
378
+ source_h = source_rect.height
379
+ target_w = target_size.x
380
+ target_h = target_size.y
381
+
382
+ center_source_w = source_w - @border_left - @border_right
383
+ center_source_h = source_h - @border_top - @border_bottom
384
+ center_target_w = target_w - @border_left - @border_right
385
+ center_target_h = target_h - @border_top - @border_bottom
386
+
387
+ {
388
+ top_left: slice_rect(source_rect.min.x, source_rect.min.y, @border_left, @border_top),
389
+ top: slice_rect(source_rect.min.x + @border_left, source_rect.min.y, center_source_w, @border_top),
390
+ top_right: slice_rect(source_rect.max.x - @border_right, source_rect.min.y, @border_right, @border_top),
391
+ left: slice_rect(source_rect.min.x, source_rect.min.y + @border_top, @border_left, center_source_h),
392
+ center: slice_rect(source_rect.min.x + @border_left, source_rect.min.y + @border_top, center_source_w, center_source_h),
393
+ right: slice_rect(source_rect.max.x - @border_right, source_rect.min.y + @border_top, @border_right, center_source_h),
394
+ bottom_left: slice_rect(source_rect.min.x, source_rect.max.y - @border_bottom, @border_left, @border_bottom),
395
+ bottom: slice_rect(source_rect.min.x + @border_left, source_rect.max.y - @border_bottom, center_source_w, @border_bottom),
396
+ bottom_right: slice_rect(source_rect.max.x - @border_right, source_rect.max.y - @border_bottom, @border_right, @border_bottom)
397
+ }
398
+ end
399
+
400
+ def type_name
401
+ 'NineSlice'
402
+ end
403
+
404
+ private
405
+
406
+ def slice_rect(x, y, width, height)
407
+ Rect.new(
408
+ min: Vec2.new(x, y),
409
+ max: Vec2.new(x + width, y + height)
410
+ )
411
+ end
412
+ end
413
+
414
+ class TiledSprite
415
+ attr_accessor :tile_size, :repeat_x, :repeat_y, :spacing
416
+
417
+ def initialize(
418
+ tile_size:,
419
+ repeat_x: 1,
420
+ repeat_y: 1,
421
+ spacing: nil
422
+ )
423
+ @tile_size = tile_size
424
+ @repeat_x = repeat_x
425
+ @repeat_y = repeat_y
426
+ @spacing = spacing || Vec2.zero
427
+ end
428
+
429
+ def total_size
430
+ Vec2.new(
431
+ @tile_size.x * @repeat_x + @spacing.x * (@repeat_x - 1),
432
+ @tile_size.y * @repeat_y + @spacing.y * (@repeat_y - 1)
433
+ )
434
+ end
435
+
436
+ def tile_count
437
+ @repeat_x * @repeat_y
438
+ end
439
+
440
+ def type_name
441
+ 'TiledSprite'
442
+ end
443
+ end
444
+ end
data/lib/bevy/state.rb ADDED
@@ -0,0 +1,277 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bevy
4
+ class State
5
+ attr_reader :current, :previous, :pending
6
+
7
+ def initialize(initial_state)
8
+ @current = initial_state
9
+ @previous = nil
10
+ @pending = nil
11
+ @on_enter_callbacks = Hash.new { |h, k| h[k] = [] }
12
+ @on_exit_callbacks = Hash.new { |h, k| h[k] = [] }
13
+ end
14
+
15
+ def set(new_state)
16
+ @pending = new_state unless new_state == @current
17
+ end
18
+
19
+ def is?(state)
20
+ @current == state
21
+ end
22
+
23
+ def was?(state)
24
+ @previous == state
25
+ end
26
+
27
+ def changed?
28
+ @current != @previous
29
+ end
30
+
31
+ def just_entered?(state)
32
+ changed? && @current == state
33
+ end
34
+
35
+ def just_exited?(state)
36
+ changed? && @previous == state
37
+ end
38
+
39
+ def on_enter(state, &block)
40
+ @on_enter_callbacks[state] << block
41
+ end
42
+
43
+ def on_exit(state, &block)
44
+ @on_exit_callbacks[state] << block
45
+ end
46
+
47
+ def apply_transition(context = nil)
48
+ return false unless @pending
49
+
50
+ @previous = @current
51
+ old_state = @current
52
+ @current = @pending
53
+ @pending = nil
54
+
55
+ @on_exit_callbacks[old_state].each do |callback|
56
+ if context
57
+ callback.call(context)
58
+ else
59
+ callback.call
60
+ end
61
+ end
62
+
63
+ @on_enter_callbacks[@current].each do |callback|
64
+ if context
65
+ callback.call(context)
66
+ else
67
+ callback.call
68
+ end
69
+ end
70
+
71
+ true
72
+ end
73
+
74
+ def clear_change
75
+ @previous = @current
76
+ end
77
+
78
+ def type_name
79
+ 'State'
80
+ end
81
+ end
82
+
83
+ class NextState
84
+ attr_accessor :state
85
+
86
+ def initialize(state = nil)
87
+ @state = state
88
+ end
89
+
90
+ def set(new_state)
91
+ @state = new_state
92
+ end
93
+
94
+ def clear
95
+ @state = nil
96
+ end
97
+
98
+ def pending?
99
+ !@state.nil?
100
+ end
101
+
102
+ def take
103
+ s = @state
104
+ @state = nil
105
+ s
106
+ end
107
+
108
+ def type_name
109
+ 'NextState'
110
+ end
111
+ end
112
+
113
+ class StateStack
114
+ def initialize(initial_state = nil)
115
+ @stack = initial_state ? [initial_state] : []
116
+ @on_push_callbacks = Hash.new { |h, k| h[k] = [] }
117
+ @on_pop_callbacks = Hash.new { |h, k| h[k] = [] }
118
+ end
119
+
120
+ def current
121
+ @stack.last
122
+ end
123
+
124
+ def empty?
125
+ @stack.empty?
126
+ end
127
+
128
+ def size
129
+ @stack.size
130
+ end
131
+
132
+ def push(state, context = nil)
133
+ @stack.push(state)
134
+ @on_push_callbacks[state].each do |callback|
135
+ context ? callback.call(context) : callback.call
136
+ end
137
+ end
138
+
139
+ def pop(context = nil)
140
+ return nil if @stack.empty?
141
+
142
+ state = @stack.pop
143
+ @on_pop_callbacks[state].each do |callback|
144
+ context ? callback.call(context) : callback.call
145
+ end
146
+ state
147
+ end
148
+
149
+ def replace(new_state, context = nil)
150
+ pop(context)
151
+ push(new_state, context)
152
+ end
153
+
154
+ def clear(context = nil)
155
+ pop(context) until @stack.empty?
156
+ end
157
+
158
+ def on_push(state, &block)
159
+ @on_push_callbacks[state] << block
160
+ end
161
+
162
+ def on_pop(state, &block)
163
+ @on_pop_callbacks[state] << block
164
+ end
165
+
166
+ def include?(state)
167
+ @stack.include?(state)
168
+ end
169
+
170
+ def type_name
171
+ 'StateStack'
172
+ end
173
+ end
174
+
175
+ class StateMachine
176
+ attr_reader :states, :current_state, :transitions
177
+
178
+ def initialize
179
+ @states = {}
180
+ @current_state = nil
181
+ @transitions = Hash.new { |h, k| h[k] = {} }
182
+ @on_enter_callbacks = Hash.new { |h, k| h[k] = [] }
183
+ @on_exit_callbacks = Hash.new { |h, k| h[k] = [] }
184
+ @on_update_callbacks = Hash.new { |h, k| h[k] = [] }
185
+ end
186
+
187
+ def add_state(name, &block)
188
+ @states[name] = block || -> {}
189
+ self
190
+ end
191
+
192
+ def add_transition(from, event, to)
193
+ @transitions[from][event] = to
194
+ self
195
+ end
196
+
197
+ def set_initial(state)
198
+ @current_state = state
199
+ self
200
+ end
201
+
202
+ def trigger(event, context = nil)
203
+ return false unless @current_state
204
+
205
+ next_state = @transitions[@current_state][event]
206
+ return false unless next_state
207
+
208
+ transition_to(next_state, context)
209
+ end
210
+
211
+ def transition_to(new_state, context = nil)
212
+ return false unless @states.key?(new_state)
213
+ return false if new_state == @current_state
214
+
215
+ old_state = @current_state
216
+
217
+ @on_exit_callbacks[old_state].each do |callback|
218
+ context ? callback.call(context) : callback.call
219
+ end if old_state
220
+
221
+ @current_state = new_state
222
+
223
+ @on_enter_callbacks[new_state].each do |callback|
224
+ context ? callback.call(context) : callback.call
225
+ end
226
+
227
+ true
228
+ end
229
+
230
+ def update(context = nil)
231
+ return unless @current_state
232
+
233
+ @on_update_callbacks[@current_state].each do |callback|
234
+ context ? callback.call(context) : callback.call
235
+ end
236
+ end
237
+
238
+ def on_enter(state, &block)
239
+ @on_enter_callbacks[state] << block
240
+ self
241
+ end
242
+
243
+ def on_exit(state, &block)
244
+ @on_exit_callbacks[state] << block
245
+ self
246
+ end
247
+
248
+ def on_update(state, &block)
249
+ @on_update_callbacks[state] << block
250
+ self
251
+ end
252
+
253
+ def can_trigger?(event)
254
+ @current_state && @transitions[@current_state].key?(event)
255
+ end
256
+
257
+ def available_events
258
+ return [] unless @current_state
259
+
260
+ @transitions[@current_state].keys
261
+ end
262
+
263
+ def is?(state)
264
+ @current_state == state
265
+ end
266
+
267
+ def type_name
268
+ 'StateMachine'
269
+ end
270
+ end
271
+
272
+ module StatePlugin
273
+ def self.build(app)
274
+ app
275
+ end
276
+ end
277
+ end