reight 0.1.2

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 (58) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/release-gem.yml +62 -0
  3. data/.github/workflows/tag.yml +35 -0
  4. data/.github/workflows/test.yml +37 -0
  5. data/.github/workflows/utils.rb +56 -0
  6. data/.gitignore +1 -0
  7. data/ChangeLog.md +23 -0
  8. data/Gemfile +6 -0
  9. data/LICENSE +21 -0
  10. data/README.md +62 -0
  11. data/Rakefile +30 -0
  12. data/VERSION +1 -0
  13. data/bin/r8 +35 -0
  14. data/lib/reight/all.rb +59 -0
  15. data/lib/reight/app/map/brush.rb +27 -0
  16. data/lib/reight/app/map/brush_base.rb +54 -0
  17. data/lib/reight/app/map/canvas.rb +150 -0
  18. data/lib/reight/app/map/chips.rb +84 -0
  19. data/lib/reight/app/map/editor.rb +117 -0
  20. data/lib/reight/app/map/line.rb +35 -0
  21. data/lib/reight/app/map/rect.rb +29 -0
  22. data/lib/reight/app/map/tool.rb +32 -0
  23. data/lib/reight/app/map.rb +8 -0
  24. data/lib/reight/app/music/editor.rb +25 -0
  25. data/lib/reight/app/music.rb +1 -0
  26. data/lib/reight/app/navigator.rb +172 -0
  27. data/lib/reight/app/runner.rb +275 -0
  28. data/lib/reight/app/sound/editor.rb +25 -0
  29. data/lib/reight/app/sound.rb +1 -0
  30. data/lib/reight/app/sprite/brush.rb +43 -0
  31. data/lib/reight/app/sprite/canvas.rb +254 -0
  32. data/lib/reight/app/sprite/chips.rb +92 -0
  33. data/lib/reight/app/sprite/color.rb +30 -0
  34. data/lib/reight/app/sprite/editor.rb +272 -0
  35. data/lib/reight/app/sprite/fill.rb +45 -0
  36. data/lib/reight/app/sprite/line.rb +37 -0
  37. data/lib/reight/app/sprite/select.rb +58 -0
  38. data/lib/reight/app/sprite/shape.rb +43 -0
  39. data/lib/reight/app/sprite/tool.rb +27 -0
  40. data/lib/reight/app/sprite.rb +10 -0
  41. data/lib/reight/app.rb +123 -0
  42. data/lib/reight/button.rb +93 -0
  43. data/lib/reight/chip.rb +150 -0
  44. data/lib/reight/extension.rb +24 -0
  45. data/lib/reight/helpers.rb +69 -0
  46. data/lib/reight/history.rb +135 -0
  47. data/lib/reight/map.rb +264 -0
  48. data/lib/reight/project.rb +115 -0
  49. data/lib/reight/reight.rb +83 -0
  50. data/lib/reight.rb +18 -0
  51. data/reight.gemspec +40 -0
  52. data/res/icons.png +0 -0
  53. data/test/helper.rb +15 -0
  54. data/test/test_chip.rb +108 -0
  55. data/test/test_chip_list.rb +68 -0
  56. data/test/test_map.rb +232 -0
  57. data/test/test_map_chunk.rb +226 -0
  58. metadata +244 -0
@@ -0,0 +1,84 @@
1
+ using Reight
2
+
3
+
4
+ class Reight::MapEditor::Chips
5
+
6
+ def initialize(app, chips, size = 8)
7
+ @app, @chips = app, chips
8
+ @offset = create_vector
9
+
10
+ @app.history.disable do
11
+ set_frame 0, 0, size, size
12
+ end
13
+ end
14
+
15
+ attr_reader :x, :y, :size
16
+
17
+ def chip = @chips.at(x, y, size, size)
18
+
19
+ def set_frame(x, y, w, h)
20
+ raise 'Chips: width != height' if w != h
21
+ @x = align_to_grid(x).clamp(0..@chips.image.width)
22
+ @y = align_to_grid(y).clamp(0..@chips.image.height)
23
+ @size = w
24
+ end
25
+
26
+ def draw()
27
+ sp = sprite
28
+
29
+ clip sp.x, sp.y, sp.w, sp.h
30
+ translate(*clamp_offset(@offset).to_a)
31
+ image @chips.image, 0, 0
32
+
33
+ no_fill
34
+ stroke 255, 255, 255
35
+ stroke_weight 1
36
+ rect @x, @y, @size, @size
37
+ end
38
+
39
+ def mouse_pressed(x, y)
40
+ @prev_pos = create_vector x, y
41
+ end
42
+
43
+ def mouse_released(x, y)
44
+ @offset = clamp_offset @offset
45
+ end
46
+
47
+ def mouse_dragged(x, y)
48
+ pos = create_vector x, y
49
+ @offset += pos - @prev_pos if @prev_pos
50
+ @prev_pos = pos
51
+ end
52
+
53
+ def mouse_clicked(x, y)
54
+ set_frame(
55
+ -@offset.x + align_to_grid(x),
56
+ -@offset.y + align_to_grid(y),
57
+ size,
58
+ size)
59
+ end
60
+
61
+ def sprite()
62
+ @sprite ||= Sprite.new.tap do |sp|
63
+ sp.draw {draw}
64
+ sp.mouse_pressed {mouse_pressed sp.mouse_x, sp.mouse_y}
65
+ sp.mouse_released {mouse_released sp.mouse_x, sp.mouse_y}
66
+ sp.mouse_dragged {mouse_dragged sp.mouse_x, sp.mouse_y}
67
+ sp.mouse_clicked {mouse_clicked sp.mouse_x, sp.mouse_y}
68
+ end
69
+ end
70
+
71
+ private
72
+
73
+ def clamp_offset(offset)
74
+ sp = sprite
75
+ x = offset.x.clamp([-(@chips.image.width - sp.w), 0].min..0)
76
+ y = offset.y.clamp([-(@chips.image.height - sp.h), 0].min..0)
77
+ create_vector align_to_grid(x), align_to_grid(y)
78
+ end
79
+
80
+ def align_to_grid(n)
81
+ n.to_i / 8 * 8
82
+ end
83
+
84
+ end# Chips
@@ -0,0 +1,117 @@
1
+ using Reight
2
+
3
+
4
+ class Reight::MapEditor < Reight::App
5
+
6
+ def canvas()
7
+ @canvas ||= Canvas.new self, project.maps.first, project.maps_json_path
8
+ end
9
+
10
+ def chips()
11
+ @chips ||= Chips.new self, project.chips
12
+ end
13
+
14
+ def activated()
15
+ super
16
+ history.disable do
17
+ tools[0].click
18
+ chip_sizes[0].click
19
+ end
20
+ end
21
+
22
+ def draw()
23
+ background 200
24
+ sprite(*sprites)
25
+ super
26
+ end
27
+
28
+ def key_pressed()
29
+ super
30
+ shift, ctrl, cmd = %i[shift control command].map {pressing? _1}
31
+ case key_code
32
+ when LEFT then canvas.x += SCREEN_WIDTH / 2
33
+ when RIGHT then canvas.x -= SCREEN_WIDTH / 2
34
+ when UP then canvas.y += SCREEN_HEIGHT / 2
35
+ when DOWN then canvas.y -= SCREEN_HEIGHT / 2
36
+ when :z then shift ? self.redo : undo if ctrl || cmd
37
+ when :b then brush.click
38
+ when :l then line.click
39
+ when :r then (shift ? fill_rect : stroke_rect).click
40
+ end
41
+ end
42
+
43
+ def window_resized()
44
+ super
45
+ chips.sprite.tap do |sp|
46
+ sp.x = SPACE
47
+ sp.y = NAVIGATOR_HEIGHT + SPACE
48
+ sp.w = CHIPS_WIDTH
49
+ sp.bottom = height - SPACE
50
+ end
51
+ tools.map {_1.sprite}.each.with_index do |sp, index|
52
+ sp.w = sp.h = BUTTON_SIZE
53
+ sp.x = chips.sprite.right + SPACE + (sp.w + 1) * index
54
+ sp.y = height - (SPACE + sp.h)
55
+ end
56
+ chip_sizes.reverse.map {_1.sprite}.each.with_index do |sp, index|
57
+ sp.w = sp.h = BUTTON_SIZE
58
+ sp.x = width - (SPACE + sp.w * (index + 1) + index)
59
+ sp.y = height - (SPACE + sp.h)
60
+ end
61
+ canvas.sprite.tap do |sp|
62
+ sp.x = chips.sprite.right + SPACE
63
+ sp.y = chips.sprite.y
64
+ sp.right = width - SPACE
65
+ sp.bottom = tools.first.sprite.top - SPACE
66
+ end
67
+ end
68
+
69
+ def undo(flash: true)
70
+ history.undo do |action|
71
+ case action
72
+ in [:put_chip, x, y, id] then canvas.map.delete x, y
73
+ in [:delete_chip, x, y, id] then canvas.map.put x, y, project.chips[id]
74
+ in [ :select, sel, _] then sel ? canvas.select(*sel) : canvas.deselect
75
+ in [:deselect, sel] then canvas.select(*sel)
76
+ end
77
+ self.flash 'Undo!' if flash
78
+ end
79
+ end
80
+
81
+ def redo(flash: true)
82
+ history.redo do |action|
83
+ case action
84
+ in [:put_chip, x, y, id] then canvas.map.put x, y, project.chips[id]
85
+ in [:delete_chip, x, y, id] then canvas.map.delete x, y
86
+ in [ :select, _, sel] then canvas.select(*sel)
87
+ in [:deselect, _] then canvas.deselect
88
+ end
89
+ self.flash 'Redo!' if flash
90
+ end
91
+ end
92
+
93
+ private
94
+
95
+ def sprites()
96
+ [canvas, chips, *chip_sizes, *tools]
97
+ .map(&:sprite) + super
98
+ end
99
+
100
+ def chip_sizes()
101
+ @chip_sizes ||= group(*[8, 16, 32].map {|size|
102
+ Reight::Button.new name: "#{size}x#{size}", label: size do
103
+ chips.set_frame chips.x, chips.y, size, size
104
+ end
105
+ })
106
+ end
107
+
108
+ def tools()
109
+ @tools ||= group brush, line, stroke_rect, fill_rect
110
+ end
111
+
112
+ def brush = @brush ||= Brush.new(self) {canvas.tool = _1}
113
+ def line = @line ||= Line.new(self) {canvas.tool = _1}
114
+ def stroke_rect = @stroke_rect ||= Rect.new(self, fill: false) {canvas.tool = _1}
115
+ def fill_rect = @fill_rect ||= Rect.new(self, fill: true) {canvas.tool = _1}
116
+
117
+ end# MapEditor
@@ -0,0 +1,35 @@
1
+ using Reight
2
+
3
+
4
+ class Reight::MapEditor::Line < Reight::MapEditor::BrushBase
5
+
6
+ def initialize(app, &block)
7
+ super app, icon: app.icon(3, 2, 8), &block
8
+ set_help left: name, right: 'Pick Chip'
9
+ end
10
+
11
+ def brush(cursor_from, cursor_to, chip)
12
+ result = false
13
+ canvas.begin_editing do
14
+ fromx, fromy = cursor_from[...2]
15
+ tox, toy = cursor_to[...2]
16
+ dx = fromx < tox ? chip.w : -chip.w
17
+ dy = fromy < toy ? chip.h : -chip.h
18
+ if (tox - fromx).abs > (toy - fromy).abs
19
+ (fromx..tox).step(dx).each do |x|
20
+ y = map x, fromx, tox, fromy, toy
21
+ y = y / chip.h * chip.h
22
+ result |= put_or_delete_chip x, y, chip
23
+ end
24
+ else
25
+ (fromy..toy).step(dy).each do |y|
26
+ x = map y, fromy, toy, fromx, tox
27
+ x = x / chip.w * chip.w
28
+ result |= put_or_delete_chip x, y, chip
29
+ end
30
+ end
31
+ end
32
+ result
33
+ end
34
+
35
+ end# Line
@@ -0,0 +1,29 @@
1
+ using Reight
2
+
3
+
4
+ class Reight::MapEditor::Rect < Reight::MapEditor::BrushBase
5
+
6
+ def initialize(app, fill:, &block)
7
+ @fill = fill
8
+ super app, icon: app.icon(fill ? 5 : 4, 2, 8), &block
9
+ set_help left: "#{fill ? 'Fill' : 'Stroke'} #{name}", right: 'Pick Chip'
10
+ end
11
+
12
+ def brush(cursor_from, cursor_to, chip)
13
+ result = false
14
+ canvas.begin_editing do
15
+ fromx, fromy = cursor_from[...2]
16
+ tox, toy = cursor_to[...2]
17
+ fromx, tox = tox, fromx if fromx > tox
18
+ fromy, toy = toy, fromy if fromy > toy
19
+ (fromy..toy).step(chip.h).each do |y|
20
+ (fromx..tox).step(chip.w).each do |x|
21
+ next if !@fill && fromx < x && x < tox && fromy < y && y < toy
22
+ result |= put_or_delete_chip x, y, chip
23
+ end
24
+ end
25
+ end
26
+ result
27
+ end
28
+
29
+ end# Rect
@@ -0,0 +1,32 @@
1
+ using Reight
2
+
3
+
4
+ class Reight::MapEditor::Tool < Reight::Button
5
+
6
+ def initialize(app, *a, **k, &b)
7
+ super(*a, **k, &b)
8
+ @app = app
9
+ end
10
+
11
+ attr_reader :app
12
+
13
+ def canvas = app.canvas
14
+
15
+ def history = app.history
16
+
17
+ def chips = app.chips
18
+
19
+ def name = self.class.name.split('::').last
20
+
21
+ def pick_chip(x, y)
22
+ chip = canvas.chip_at_cursor or return
23
+ chips.mouse_clicked chip.x, chip.y
24
+ end
25
+
26
+ def canvas_pressed( x, y, button) = nil
27
+ def canvas_released(x, y, button) = nil
28
+ def canvas_moved( x, y) = nil
29
+ def canvas_dragged( x, y, button) = nil
30
+ def canvas_clicked( x, y, button) = nil
31
+
32
+ end# Tool
@@ -0,0 +1,8 @@
1
+ require 'reight/app/map/editor'
2
+ require 'reight/app/map/canvas'
3
+ require 'reight/app/map/chips'
4
+ require 'reight/app/map/tool'
5
+ require 'reight/app/map/brush_base'
6
+ require 'reight/app/map/brush'
7
+ require 'reight/app/map/line'
8
+ require 'reight/app/map/rect'
@@ -0,0 +1,25 @@
1
+ using Reight
2
+
3
+
4
+ class Reight::MusicEditor < Reight::App
5
+
6
+ def draw()
7
+ background 200
8
+
9
+ push do
10
+ text_align LEFT, CENTER
11
+
12
+ text_size 10
13
+ fill 220; text "Music Editor", 150, 1, 200, height
14
+ fill 150; text "Music Editor", 150, 0, 200, height
15
+
16
+ text_size 8
17
+ dots = '.' * (frame_count / 60 % 4).to_i
18
+ fill 220; text "is under construction#{dots}", 150, 11, 200, height
19
+ fill 150; text "is under construction#{dots}", 150, 10, 200, height
20
+ end
21
+
22
+ super
23
+ end
24
+
25
+ end# MusicEditor
@@ -0,0 +1 @@
1
+ require 'reight/app/music/editor'
@@ -0,0 +1,172 @@
1
+ using Reight
2
+
3
+
4
+ class Reight::Navigator
5
+
6
+ def initialize(app)
7
+ @app, @visible = app, true
8
+ end
9
+
10
+ def flash(...) = message.flash(...)
11
+
12
+ def visible=(visible)
13
+ return if visible == @visible
14
+ @visible = visible
15
+ sprites.each {|sp| visible ? sp.show : sp.hide}
16
+ end
17
+
18
+ def visible? = @visible
19
+
20
+ def sprites()
21
+ [*app_buttons, *history_buttons, *edit_buttons, message]
22
+ .map &:sprite
23
+ end
24
+
25
+ def draw()
26
+ return unless visible?
27
+ fill 220
28
+ no_stroke
29
+ rect 0, 0, width, Reight::App::NAVIGATOR_HEIGHT
30
+ sprite(*sprites)
31
+ end
32
+
33
+ def key_pressed()
34
+ index = [F1, F2, F3, F4, F5].index(key_code)
35
+ app_buttons[index].click if index
36
+ end
37
+
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
43
+
44
+ space = Reight::App::SPACE
45
+ x = space
46
+
47
+ app_buttons.map {_1.sprite}.each do |sp|
48
+ sp.x = x + 1
49
+ x = sp.right
50
+ end
51
+ x += space
52
+
53
+ history_buttons.map {_1.sprite}.each do |sp|
54
+ sp.x = x + 1
55
+ x = sp.right
56
+ end
57
+ x += space unless history_buttons.empty?
58
+
59
+ edit_buttons.map {_1.sprite}.each do |sp|
60
+ sp.x = x + 1
61
+ x = sp.right
62
+ end
63
+ x += space unless edit_buttons.empty?
64
+
65
+ message.sprite.tap do |sp|
66
+ sp.x = x + space
67
+ sp.y = 0
68
+ sp.h = Reight::App::NAVIGATOR_HEIGHT
69
+ sp.right = width - space
70
+ end
71
+ end
72
+
73
+ private
74
+
75
+ 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
+ ]
93
+ end
94
+
95
+ def history_buttons()
96
+ @history_buttons ||= history_buttons? ? [
97
+ Reight::Button.new(name: 'Undo', icon: @app.icon(3, 1, 8)) {
98
+ @app.undo flash: false
99
+ }.tap {|b|
100
+ b.enabled? {@app.history.can_undo?}
101
+ },
102
+ Reight::Button.new(name: 'Redo', icon: @app.icon(4, 1, 8)) {
103
+ @app.redo flash: false
104
+ }.tap {|b|
105
+ b.enabled? {@app.history.can_redo?}
106
+ }
107
+ ] : []
108
+ end
109
+
110
+ def edit_buttons()
111
+ @edit_buttons ||= edit_buttons? ? [
112
+ Reight::Button.new(name: 'Cut', icon: @app.icon(0, 1, 8)) {
113
+ @app.cut flash: false
114
+ }.tap {|b|
115
+ b.enabled? {@app.can_cut?}
116
+ },
117
+ Reight::Button.new(name: 'Copy', icon: @app.icon(1, 1, 8)) {
118
+ @app.copy flash: false
119
+ }.tap {|b|
120
+ b.enabled? {@app.can_copy?}
121
+ },
122
+ Reight::Button.new(name: 'Paste', icon: @app.icon(2, 1, 8)) {
123
+ @app.paste flash: false
124
+ }.tap {|b|
125
+ b.enabled? {@app.can_paste?}
126
+ },
127
+ ] : []
128
+ end
129
+
130
+ def history_buttons? = @app.respond_to? :undo
131
+ def edit_buttons? = @app.respond_to? :cut
132
+
133
+ def message()
134
+ @message ||= Message.new
135
+ end
136
+
137
+ def switch_app(klass)
138
+ app = r8.apps.find {_1.class == klass}
139
+ r8.current = app if app
140
+ end
141
+
142
+ end# Navigator
143
+
144
+
145
+ class Reight::Navigator::Message
146
+
147
+ def initialize()
148
+ @priority = 0
149
+ end
150
+
151
+ attr_accessor :text
152
+
153
+ def flash(str, priority: 1)
154
+ return if priority < @priority
155
+ @text, @priority = str, priority
156
+ set_timeout 2, id: :message_flash do
157
+ @text, @priority = '', 0
158
+ end
159
+ end
160
+
161
+ def sprite()
162
+ @sprite ||= Sprite.new.tap do |sp|
163
+ sp.draw do
164
+ next unless @text
165
+ fill 100
166
+ text_align LEFT, CENTER
167
+ draw_text @text, 0, 0, sp.w, sp.h
168
+ end
169
+ end
170
+ end
171
+
172
+ end# Message