reight 0.1.6 → 0.1.8

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 (55) hide show
  1. checksums.yaml +4 -4
  2. data/.github/PULL_REQUEST_TEMPLATE.md +12 -0
  3. data/CONTRIBUTING.md +7 -0
  4. data/ChangeLog.md +76 -0
  5. data/README.md +6 -5
  6. data/Rakefile +5 -2
  7. data/VERSION +1 -1
  8. data/bin/r8 +18 -8
  9. data/lib/reight/all.rb +7 -2
  10. data/lib/reight/app/chips.rb +124 -0
  11. data/lib/reight/app/map/brush.rb +1 -1
  12. data/lib/reight/app/map/brush_base.rb +3 -3
  13. data/lib/reight/app/map/canvas.rb +10 -15
  14. data/lib/reight/app/map/editor.rb +44 -17
  15. data/lib/reight/app/map/line.rb +5 -5
  16. data/lib/reight/app/map/rect.rb +2 -2
  17. data/lib/reight/app/map.rb +0 -1
  18. data/lib/reight/app/navigator.rb +33 -47
  19. data/lib/reight/app/runner.rb +123 -86
  20. data/lib/reight/app/sound/brush.rb +32 -0
  21. data/lib/reight/app/sound/canvas.rb +190 -0
  22. data/lib/reight/app/sound/editor.rb +170 -10
  23. data/lib/reight/app/sound/eraser.rb +28 -0
  24. data/lib/reight/app/sound/tool.rb +29 -0
  25. data/lib/reight/app/sound.rb +4 -0
  26. data/lib/reight/app/sprite/canvas.rb +23 -18
  27. data/lib/reight/app/sprite/color.rb +3 -1
  28. data/lib/reight/app/sprite/editor.rb +58 -47
  29. data/lib/reight/app/sprite/line.rb +1 -1
  30. data/lib/reight/app/sprite/shape.rb +1 -1
  31. data/lib/reight/app/sprite.rb +0 -1
  32. data/lib/reight/app.rb +24 -5
  33. data/lib/reight/button.rb +10 -7
  34. data/lib/reight/chip.rb +32 -6
  35. data/lib/reight/context.rb +168 -0
  36. data/lib/reight/helpers.rb +2 -2
  37. data/lib/reight/index.rb +87 -0
  38. data/lib/reight/map.rb +141 -11
  39. data/lib/reight/project.rb +45 -6
  40. data/lib/reight/reight.rb +11 -15
  41. data/lib/reight/sound.rb +238 -0
  42. data/lib/reight/sprite.rb +42 -0
  43. data/lib/reight/text.rb +124 -0
  44. data/lib/reight.rb +7 -3
  45. data/reight.gemspec +7 -7
  46. data/res/icons.png +0 -0
  47. data/test/helper.rb +16 -0
  48. data/test/test_map.rb +7 -7
  49. data/test/test_map_chunk.rb +6 -6
  50. data/test/test_sprite.rb +28 -0
  51. metadata +42 -32
  52. data/lib/reight/app/map/chips.rb +0 -84
  53. data/lib/reight/app/music/editor.rb +0 -25
  54. data/lib/reight/app/music.rb +0 -1
  55. data/lib/reight/app/sprite/chips.rb +0 -92
@@ -32,14 +32,15 @@ class Reight::Navigator
32
32
 
33
33
  def key_pressed()
34
34
  index = [F1, F2, F3, F4, F5].index(key_code)
35
- app_buttons[index].click if index
35
+ app_buttons[index]&.click if index
36
36
  end
37
37
 
38
38
  def window_resized()
39
- [app_buttons, history_buttons, edit_buttons].flatten.map(&:sprite).each do |sp|
40
- sp.w = sp.h = Reight::App::NAVIGATOR_HEIGHT
41
- sp.y = 0
42
- end
39
+ [app_buttons, history_buttons, edit_buttons]
40
+ .flatten.map(&:sprite).each do |sp|
41
+ sp.w = sp.h = Reight::App::NAVIGATOR_HEIGHT
42
+ sp.y = 0
43
+ end
43
44
 
44
45
  space = Reight::App::SPACE
45
46
  x = space
@@ -47,20 +48,23 @@ class Reight::Navigator
47
48
  app_buttons.map {_1.sprite}.each do |sp|
48
49
  sp.x = x + 1
49
50
  x = sp.right
51
+ end.tap do
52
+ x += space unless _1.empty?
50
53
  end
51
- x += space
52
54
 
53
55
  history_buttons.map {_1.sprite}.each do |sp|
54
56
  sp.x = x + 1
55
57
  x = sp.right
58
+ end.tap do
59
+ x += space unless _1.empty?
56
60
  end
57
- x += space unless history_buttons.empty?
58
61
 
59
62
  edit_buttons.map {_1.sprite}.each do |sp|
60
63
  sp.x = x + 1
61
64
  x = sp.right
65
+ end.tap do
66
+ x += space unless _1.empty?
62
67
  end
63
- x += space unless edit_buttons.empty?
64
68
 
65
69
  message.sprite.tap do |sp|
66
70
  sp.x = x + space
@@ -73,72 +77,54 @@ class Reight::Navigator
73
77
  private
74
78
 
75
79
  def app_buttons()
76
- @app_buttons ||= [
77
- Reight::Button.new(name: 'Run', icon: @app.icon(0, 0, 8)) {
78
- switch_app Reight::Runner
79
- },
80
- Reight::Button.new(name: 'Sprite Editor', icon: @app.icon(1, 0, 8)) {
81
- switch_app Reight::SpriteEditor
82
- },
83
- Reight::Button.new(name: 'Map Editor', icon: @app.icon(2, 0, 8)) {
84
- switch_app Reight::MapEditor
85
- },
86
- Reight::Button.new(name: 'Sound Editor', icon: @app.icon(3, 0, 8)) {
87
- switch_app Reight::SoundEditor
88
- },
89
- Reight::Button.new(name: 'Music Editor', icon: @app.icon(4, 0, 8)) {
90
- switch_app Reight::MusicEditor
91
- },
92
- ]
80
+ @app_buttons ||= r8.apps.map.with_index {|app, index|
81
+ Reight::Button.new(name: app.label, icon: app.icon(index, 0, 8)) {
82
+ r8.current = app
83
+ }
84
+ }
93
85
  end
94
86
 
95
87
  def history_buttons()
96
- @history_buttons ||= history_buttons? ? [
97
- Reight::Button.new(name: 'Undo', icon: @app.icon(3, 1, 8)) {
88
+ @history_buttons ||= [].tap do |buttons|
89
+ next unless @app.respond_to? :undo
90
+ buttons << Reight::Button.new(name: 'Undo', icon: @app.icon(3, 1, 8)) {
98
91
  @app.undo flash: false
99
92
  }.tap {|b|
100
93
  b.enabled? {@app.history.can_undo?}
101
- },
102
- Reight::Button.new(name: 'Redo', icon: @app.icon(4, 1, 8)) {
94
+ }
95
+ buttons << Reight::Button.new(name: 'Redo', icon: @app.icon(4, 1, 8)) {
103
96
  @app.redo flash: false
104
97
  }.tap {|b|
105
98
  b.enabled? {@app.history.can_redo?}
106
99
  }
107
- ] : []
100
+ end
108
101
  end
109
102
 
110
103
  def edit_buttons()
111
- @edit_buttons ||= edit_buttons? ? [
112
- Reight::Button.new(name: 'Cut', icon: @app.icon(0, 1, 8)) {
104
+ @edit_buttons ||= [].tap do |buttons|
105
+ next unless @app.respond_to? :paste
106
+ buttons << Reight::Button.new(name: 'Cut', icon: @app.icon(0, 1, 8)) {
113
107
  @app.cut flash: false
114
108
  }.tap {|b|
115
109
  b.enabled? {@app.can_cut?}
116
- },
117
- Reight::Button.new(name: 'Copy', icon: @app.icon(1, 1, 8)) {
110
+ }
111
+ buttons << Reight::Button.new(name: 'Copy', icon: @app.icon(1, 1, 8)) {
118
112
  @app.copy flash: false
119
113
  }.tap {|b|
120
114
  b.enabled? {@app.can_copy?}
121
- },
122
- Reight::Button.new(name: 'Paste', icon: @app.icon(2, 1, 8)) {
115
+ }
116
+ buttons << Reight::Button.new(name: 'Paste', icon: @app.icon(2, 1, 8)) {
123
117
  @app.paste flash: false
124
118
  }.tap {|b|
125
119
  b.enabled? {@app.can_paste?}
126
- },
127
- ] : []
120
+ }
121
+ end
128
122
  end
129
123
 
130
- def history_buttons? = @app.respond_to? :undo
131
- def edit_buttons? = @app.respond_to? :cut
132
-
133
124
  def message()
134
125
  @message ||= Message.new
135
126
  end
136
127
 
137
- def switch_app(klass)
138
- app = r8.apps.find {_1.class == klass}
139
- r8.current = app if app
140
- end
141
-
142
128
  end# Navigator
143
129
 
144
130
 
@@ -159,7 +145,7 @@ class Reight::Navigator::Message
159
145
  end
160
146
 
161
147
  def sprite()
162
- @sprite ||= Sprite.new.tap do |sp|
148
+ @sprite ||= RubySketch::Sprite.new.tap do |sp|
163
149
  sp.draw do
164
150
  next unless @text
165
151
  fill 100
@@ -1,117 +1,148 @@
1
- using Reight
1
+ class Reight::Runner < Reight::App
2
2
 
3
+ include Xot::Inspectable
3
4
 
4
- class Reight::Runner < Reight::App
5
+ ROOT_CONTEXT = Reight::CONTEXT__
5
6
 
6
7
  TEMPORARY_HASH = {}
7
8
 
9
+ def label = 'Run'
10
+
11
+ def setup()
12
+ navigator.visible = false
13
+ end
14
+
8
15
  def activated()
9
- super
10
16
  run force: true
11
- @context.call_activated__
17
+ @context.call_activated__ {|&b| call_event(ignore_pause: true, &b)}
18
+ super
12
19
  end
13
20
 
14
21
  def deactivated()
15
22
  super
16
- @context.call_deactivated__
23
+ @context.call_deactivated__ {|&b| call_event(ignore_pause: true, &b)}
17
24
  pause
25
+ cleanup
18
26
  end
19
27
 
20
28
  def draw()
21
- push do
22
- @context&.call_draw__ unless paused?
29
+ return unless @context
30
+ @initial_resize ||= true.tap do
31
+ call_event {@context.size ROOT_CONTEXT.width, ROOT_CONTEXT.height}
32
+ end
33
+ @context.call_draw__ {|push: true, &b| call_event(push: push, &b)}
34
+ if canvasFrame = @context.canvasFrame__
35
+ ROOT_CONTEXT.background 0
36
+ ROOT_CONTEXT.image @context, *canvasFrame
37
+ else
38
+ ROOT_CONTEXT.image @context, 0, 0
23
39
  end
24
40
  super
25
41
  end
26
42
 
27
43
  def key_pressed()
28
44
  super
29
- @context&.key_pressed unless paused?
45
+ call_event {@context.key_pressed}
30
46
  end
31
47
 
32
48
  def key_released()
33
49
  super
34
- @context&.key_released unless paused?
50
+ call_event {@context.key_released}
35
51
  end
36
52
 
37
53
  def key_typed()
38
54
  super
39
- @context&.key_typed unless paused?
55
+ call_event {@context.key_typed}
40
56
  end
41
57
 
42
58
  def mouse_pressed()
43
59
  super
44
- @context&.mouse_pressed unless paused?
60
+ call_event {@context.mouse_pressed}
45
61
  end
46
62
 
47
63
  def mouse_released()
48
64
  super
49
- @context&.mouse_released unless paused?
65
+ call_event {@context.mouse_released}
50
66
  end
51
67
 
52
68
  def mouse_moved()
53
69
  super
54
- @context&.mouse_moved unless paused?
55
- navigator.visible = mouse_y < NAVIGATOR_HEIGHT
70
+ navigator.visible = ROOT_CONTEXT.mouse_y < NAVIGATOR_HEIGHT if r8.edit?
71
+ call_event {@context.mouse_moved}
56
72
  end
57
73
 
58
74
  def mouse_dragged()
59
75
  super
60
- @context&.mouse_dragged unless paused?
76
+ call_event {@context.mouse_dragged}
61
77
  end
62
78
 
63
79
  def mouse_clicked()
64
80
  super
65
- @context&.mouse_clicked unless paused?
81
+ call_event {@context.mouse_clicked}
66
82
  end
67
83
 
68
84
  def double_clicked()
69
85
  super
70
- @context&.double_clicked unless paused?
86
+ call_event {@context.double_clicked}
71
87
  end
72
88
 
73
89
  def mouse_wheel()
74
90
  super
75
- @context&.mouse_wheel unless paused?
91
+ call_event {@context.mouse_wheel}
76
92
  end
77
93
 
78
94
  def touch_started()
79
95
  super
80
- @context&.touch_started unless paused?
96
+ call_event {@context.touch_started}
81
97
  end
82
98
 
83
99
  def touch_ended()
84
100
  super
85
- @context&.touch_ended unless paused?
101
+ call_event {@context.touch_ended}
86
102
  end
87
103
 
88
104
  def touch_moved()
89
105
  super
90
- @context&.touch_moved unless paused?
106
+ call_event {@context.touch_moved}
91
107
  end
92
108
 
93
109
  def window_moved()
94
110
  super
95
- @context&.window_moved
111
+ call_event(ignore_pause: true) {@context.window_moved}
96
112
  end
97
113
 
98
114
  def window_resized()
99
115
  super
100
- @context&.window_resized
116
+ call_event(ignore_pause: true) {@context.window_resized}
101
117
  end
102
118
 
103
119
  private
104
120
 
121
+ def call_event(push: true, ignore_pause: false, &block)
122
+ if @context
123
+ @context.beginDraw__
124
+ @context.push if push
125
+ block.call unless paused?
126
+ end
127
+ rescue ScriptError, StandardError => e
128
+ puts e.full_message
129
+ ensure
130
+ if @context
131
+ @context.pop if push
132
+ @context.endDraw__
133
+ end
134
+ end
135
+
105
136
  def running? = @context && !@paused
106
137
 
107
138
  def paused? = @context && @paused
108
139
 
109
140
  def run(force: false)
110
141
  return pause false if paused? && !force
111
- cleanup
112
142
  backup_global_vars
113
143
  @context = create_context
114
144
  @paused = false
145
+ Processing::Context.setContext__ @context
115
146
  begin_wrapping_user_classes @context
116
147
  eval_user_script @context, project.code_paths.zip(project.codes).to_h
117
148
  end
@@ -121,9 +152,12 @@ class Reight::Runner < Reight::App
121
152
  end
122
153
 
123
154
  def cleanup()
124
- remove_world @context.sprite_world__ if @context
155
+ ROOT_CONTEXT.remove_world @context.spriteWorld__ if @context
156
+ Processing::Context.setContext__ nil
125
157
  @context = nil
126
158
  end_wrapping_user_classes
159
+ clear_all_timers
160
+ project.clear_all_sprites
127
161
  restore_global_vars
128
162
  GC.enable
129
163
  GC.start
@@ -131,55 +165,56 @@ class Reight::Runner < Reight::App
131
165
 
132
166
  def create_context()
133
167
  klass = Class.new do
134
- def project = @project__
168
+ include Reight::Context
135
169
 
136
- def sprite_world__ = @sprite_world__ ||= SpriteWorld.new(pixels_per_meter: 5)
137
-
138
- def call_activated__()
139
- add_world sprite_world__
140
- activated
170
+ def call_activated__(&caller)
171
+ add_world spriteWorld__
172
+ caller.call {activated}
141
173
  end
142
174
 
143
- def call_deactivated__()
144
- deactivated
145
- remove_world sprite_world__
175
+ def call_deactivated__(&caller)
176
+ caller.call {deactivated}
177
+ remove_world spriteWorld__
146
178
  @background_cleared__ = false
147
179
  end
148
180
 
149
- def call_draw__()
150
- unless @setup_done__
151
- @setup_done__ = true
152
- setup
153
- end
154
- unless @background_cleared__
155
- @background_cleared__ = true
156
- background 100, 100, 100
157
- end
158
- draw
181
+ def call_draw__(&caller)
182
+ @setup_done__ ||= true.tap {caller.call(push: false) {setup}}
183
+ @background_cleared__ ||= true.tap {caller.call {background 100}}
184
+ caller.call {draw}
159
185
  end
160
186
 
161
- def createSprite(...) = sprite_world__.createSprite(...)
162
- def addSprite(...) = sprite_world__.addSprite(...)
163
- def removeSprite(...) = sprite_world__.removeSprite(...)
164
- def gravity(...) = sprite_world__.gravity(...)
165
- end
166
- Processing.to_snake_case__(
167
- %i[activated deactivated] + Processing::EVENT_NAMES__
168
- ).each do |camel, snake|
169
- klass.class_eval <<~END
170
- def #{camel}(&block)
171
- if block
172
- @#{camel}_block__ = block
173
- else
174
- @#{camel}_block__&.call
187
+ def inspect()
188
+ "#<#{self.class.name}:0x#{object_id}>"
189
+ end
190
+
191
+ methods = (instance_methods - Object.instance_methods)
192
+ .reject {_1.end_with? '__'}
193
+ Processing.to_snake_case__(methods).each do |camel, snake|
194
+ alias_method snake, camel if snake != camel
195
+ end
196
+
197
+ Processing.to_snake_case__(
198
+ %i[activated deactivated] + Processing::EVENT_NAMES__
199
+ ).each do |camel, snake|
200
+ class_eval <<~END
201
+ def #{camel}(&block)
202
+ if block
203
+ @#{camel}_block__ = block
204
+ else
205
+ @#{camel}_block__&.call
206
+ end
175
207
  end
176
- end
177
- END
178
- klass.alias_method snake, camel if snake != camel
179
- end
180
- klass.new.tap do |context|
181
- context.instance_variable_set :@project__, project
208
+ END
209
+ alias_method snake, camel if snake != camel
210
+ end
211
+
212
+ Processing.funcs__(ROOT_CONTEXT).each do |func|
213
+ next if method_defined? func
214
+ define_method(func) {|*a, **k, &b| ROOT_CONTEXT.__send__ func, *a, **k, &b}
215
+ end
182
216
  end
217
+ klass.new(ROOT_CONTEXT, project)
183
218
  end
184
219
 
185
220
  def begin_wrapping_user_classes(context)
@@ -207,27 +242,17 @@ class Reight::Runner < Reight::App
207
242
 
208
243
  def create_user_class_wrapper(context)
209
244
  Module.new.tap do |wrapper|
210
- wrap_methods context, wrapper
211
- end
212
- end
213
-
214
- def wrap_methods(context, klass)
215
- klass.define_method :respond_to_missing? do |name, include_private = false|
216
- context.respond_to?(name, false) || super(name, include_private)
217
- end
218
- klass.define_method :method_missing do |name, *args, **kwargs, &block|
219
- if context.respond_to? name
220
- klass.define_method(name) {|*a, **k, &b| context.public_send name, *a, **k, &b}
221
- context.public_send name, *args, **kwargs, &block
222
- else
223
- super name, *args, **kwargs, &block
245
+ wrapper.define_method :respond_to_missing? do |name, include_private = false|
246
+ context.respond_to?(name, false) || super(name, include_private)
247
+ end
248
+ wrapper.define_method :method_missing do |name, *args, **kwargs, &block|
249
+ if context.respond_to? name
250
+ wrapper.define_method(name) {|*a, **k, &b| context.public_send name, *a, **k, &b}
251
+ context.public_send name, *args, **kwargs, &block
252
+ else
253
+ super name, *args, **kwargs, &block
254
+ end
224
255
  end
225
- end
226
- Processing.to_snake_case__(%i[
227
- project createSprite addSprite removeSprite gravity
228
- ]).each do |camel, snake|
229
- klass.define_method(camel) {|*a, **k, &b| context.public_send camel, *a, **k, &b}
230
- klass.alias_method snake, camel if snake != camel
231
256
  end
232
257
  end
233
258
 
@@ -237,19 +262,23 @@ class Reight::Runner < Reight::App
237
262
  ::Reight::Runner::TEMPORARY_HASH[:params] => {context:, codes:}
238
263
  codes.each {|path, code| context.instance_eval code, path if code}
239
264
  END
265
+ rescue ScriptError, StandardError => e
266
+ puts e.full_message
240
267
  ensure
241
268
  TEMPORARY_HASH.delete :params
242
269
  end
243
270
 
271
+ EXCLUDE_GLOBAL_VARS = [:$FILENAME]
272
+
244
273
  def backup_global_vars()
245
- @global_vars = global_variables
274
+ @global_vars = (global_variables - EXCLUDE_GLOBAL_VARS)
246
275
  .each.with_object({}) {|name, hash| hash[name] = eval name.to_s}
247
276
  .freeze
248
277
  end
249
278
 
250
279
  def restore_global_vars()
251
280
  return unless @global_vars
252
- global_variables
281
+ (global_variables - EXCLUDE_GLOBAL_VARS)
253
282
  .map {|name| [name, eval(name.to_s)]}
254
283
  .select {|name, value| value != nil && @global_vars[name] == nil}
255
284
  .each {|name, value| global_var_set name, nil}
@@ -267,4 +296,12 @@ class Reight::Runner < Reight::App
267
296
  TEMPORARY_HASH.delete :value
268
297
  end
269
298
 
299
+ def clear_all_timers()
300
+ prefix = Reight::Context::TIMER_PREFIX__
301
+ ROOT_CONTEXT.instance_eval do
302
+ @timers__ .delete_if {|id| id in [prefix, _]}
303
+ @firingTimers__.delete_if {|id| id in [prefix, _]}
304
+ end
305
+ end
306
+
270
307
  end# Runner
@@ -0,0 +1,32 @@
1
+ class Reight::SoundEditor::Brush < Reight::SoundEditor::Tool
2
+
3
+ def initialize(app, &block)
4
+ super app, icon: app.icon(1, 2, 8), &block
5
+ set_help left: name, right: 'Pick Tone'
6
+ end
7
+
8
+ def brush(x, y, button)
9
+ canvas.put x, y
10
+ end
11
+
12
+ def canvas_pressed(x, y, button)
13
+ return unless button == LEFT
14
+ canvas.begin_editing
15
+ brush x, y, button
16
+ end
17
+
18
+ def canvas_released(x, y, button)
19
+ return unless button == LEFT
20
+ canvas.end_editing
21
+ end
22
+
23
+ def canvas_dragged(x, y, button)
24
+ return unless button == LEFT
25
+ brush x, y, button
26
+ end
27
+
28
+ def canvas_clicked(x, y, button)
29
+ pick_tone x, y if button == RIGHT
30
+ end
31
+
32
+ end# Brush