author_engine 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 240a86f7ca8210f55b1000684999a28d178bb8aee05816924c09987279930acb
4
- data.tar.gz: b020c530b4e217ff3b2c6fa8f956eae3a7faef8c3e07de8226fdbd239a0adec9
3
+ metadata.gz: cdf3e00f6f0e8840311bc2fb5c77c7dfe56c0bf9de8efa7cb310468b649d4ff4
4
+ data.tar.gz: b32cfb362b8bdfe3c408ac8e113082c94c68faaa2475afbc91b8e97ffdee2ffe
5
5
  SHA512:
6
- metadata.gz: 979bd0fc59a1767dede59ca38f1ac63965beb8c538c95c25fd824d7d5c0b4449508d83f4dec6bc2e4b0d8c046f37f35c1995da4f2460e4d9562534d59b4e41fd
7
- data.tar.gz: d105fe0793a73d82bb36c69623a031ad0a45ffd0dfbae4027a55e4a77e05bcd75be360fc9fa8427080a2ab1dc4f993f87065a63b0fdbb7e1ec89c99dbc2649d6
6
+ metadata.gz: 0ddf727d607cd61cf2aa34551c8489cc8af8af44c26c263eed9faac1f0ac7ff0563ccbe8ea400a4762d050f08f6a76e40d874484770ee833d28340de3bdb66fd
7
+ data.tar.gz: 91335c5c61622bb198535bb62bd15b5ad3db5eb10f2aa290dfc3b7ff7bc05e96b5ed270834eca6ee25721d1ace97bd30c9587746eabaa3f4217cfd236a8d7560
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- author_engine (0.5.0)
4
+ author_engine (0.6.0)
5
5
  coderay (~> 1.1.2)
6
6
  gosu (~> 0.14.4)
7
7
  opal (~> 0.11.4)
@@ -2,8 +2,17 @@ class AuthorEngine
2
2
  class Loader < Container
3
3
  Project = Struct.new(:name, :block)
4
4
  def setup
5
- @root_directory = Dir.pwd#"#{Dir.home}/AuthorEngineProjects"
6
- Dir.mkdir(@root_directory) unless File.exists?(@root_directory)
5
+ if ARGV[0] && File.exists?(ARGV[0]) && Gosu.milliseconds < 1500
6
+ if ARGV[0].end_with?(".authorengine")
7
+ load(ARGV[0])
8
+ return
9
+ elsif File.directory?(ARGV[0])
10
+ @root_directory = ARGV[0]
11
+ else
12
+ puts "AuthorEngine: #{ARGV[0]} is not a compatible file."
13
+ end
14
+ end
15
+ @root_directory ||= Dir.pwd #"#{Dir.home}/AuthorEngineProjects"
7
16
 
8
17
  @list = []
9
18
  @files = Dir.glob(@root_directory+"/*.authorengine")
@@ -27,13 +36,9 @@ class AuthorEngine
27
36
  end
28
37
  @new_button.x = window.width - @new_button.width
29
38
 
30
- if ARGV[0] && File.exists?(ARGV[0])
31
- if ARGV[0].end_with?(".authorengine")
32
- load(ARGV[0])
33
- else
34
- puts "AuthorEngine: #{ARGV[0]} is not a compatible file."
35
- end
36
- end
39
+ @draw_caret = true
40
+ @last_caret_update = Gosu.milliseconds
41
+ @caret_interval = 500
37
42
  end
38
43
 
39
44
  def load(filename)
@@ -53,8 +58,23 @@ class AuthorEngine
53
58
  end
54
59
 
55
60
  def draw_inputter
61
+ caret_x = @font.text_width(window.text_input.text[0..window.text_input.caret_pos-1])
62
+ caret_x = 0 if window.text_input.caret_pos == 0
63
+ if Gosu.milliseconds > @last_caret_update + @caret_interval
64
+ @draw_caret = !@draw_caret
65
+ @last_caret_update = Gosu.milliseconds
66
+ end
67
+
56
68
  x = window.width/2 - @font.text_width(window.text_input.text+".authorengine")/2
69
+ error_x = window.width/2 - @font.text_width(window.text_input.text.strip+".authorengine already exists!")/2
57
70
  y = window.height/2 - @font.height/2
71
+
72
+ Gosu.draw_rect(x+caret_x, y, 2, @font.height, Gosu::Color::WHITE) if @draw_caret
73
+ if window.text_input.text.strip+".authorengine" == @name_exists
74
+ @font.draw_text(window.text_input.text.strip+".authorengine already exists!", error_x, y - 32, 0, 1,1, red) if @name_exists
75
+ else
76
+ @name_exists = false
77
+ end
58
78
  @font.draw_text(window.text_input.text+".authorengine", x, y, 0)
59
79
  end
60
80
 
@@ -100,12 +120,22 @@ class AuthorEngine
100
120
  @index = 0 if @list.size == 0
101
121
  @index = @index % @list.size-1 if @list.size != 0
102
122
  when Gosu::KbEnter, Gosu::KbReturn
123
+ filename = window.text_input.text.strip if window.text_input
124
+
103
125
  if @entering_name
104
- SaveFile.create(window.text_input.text.strip+".authorengine")
105
- load(window.text_input.text.strip+".authorengine")
126
+ if File.exists?(filename)
127
+ @name_exists = filename
128
+ else
129
+ window.text_input = nil
130
+ savefile = SaveFile.create(filename+".authorengine")
131
+ load(filename+".authorengine")
132
+ end
106
133
  else
107
134
  @list[@index].block.call if @list[@index]&.block
108
135
  end
136
+ when Gosu::KbEscape
137
+ @entering_name = false
138
+ window.text_input = nil
109
139
  end
110
140
  end
111
141
  end
@@ -1,14 +1,17 @@
1
1
  class AuthorEngine
2
2
  class Part
3
3
  module Common
4
+ # returns display width
4
5
  def width
5
6
  128
6
7
  end
7
8
 
9
+ # returns display height
8
10
  def height
9
11
  128
10
12
  end
11
13
 
14
+ # returns frames per seconds
12
15
  def fps
13
16
  if RUBY_ENGINE == "opal"
14
17
  AuthorEngine::GameRunner.instance.fps
@@ -17,6 +20,14 @@ class AuthorEngine
17
20
  end
18
21
  end
19
22
 
23
+ def distance(x1, y1, x2, y2)
24
+ dx = x2 - x1
25
+ dy = y2 - y1
26
+
27
+ Math.sqrt(dx * dx + dy * dy)
28
+ end
29
+
30
+ # returns number of milliseconds since game started
20
31
  def milliseconds
21
32
  if RUBY_ENGINE == "opal"
22
33
  @__initial_milliseconds ||= `performance.now()`
@@ -44,38 +44,18 @@ class AuthorEngine
44
44
  @game.init
45
45
 
46
46
  @show_touch_controls = false
47
+ @touch_joystick = TouchJoystick.new(x: 100, radius: 50, side: :left)
47
48
  @touch_buttons = []
48
- buttons = AuthorEngine::Part::OpalInput::BUTTONS
49
- key_states = AuthorEngine::Part::OpalInput::KEY_STATES
50
49
  @touch_buttons.push(
51
50
  TouchButton.new(
52
- label: "Up", color: @game.dark_gray, x: 137, y: `window.innerHeight/2 - #{125}`, width: 50, height: 50, side: :left,
53
- contact_proc: proc { key_states[buttons["up"]] = true}, no_contact_proc: proc { key_states[buttons["up"]] = false}
54
- ),
51
+ label: "X", color: @game.red, x: 50, width: 50, height: 50, side: :right, for_key: "x"
52
+ ),
55
53
  TouchButton.new(
56
- label: "Down", color: @game.dark_gray, x: 137, y: `window.innerHeight/2 + 25`, width: 50, height: 50, side: :left,
57
- contact_proc: proc { key_states[buttons["down"]] = true}, no_contact_proc: proc { key_states[buttons["down"]] = false}
58
- ),
59
-
60
- TouchButton.new(
61
- label: "Left", color: @game.black, x: 175, width: 50, height: 50, side: :left,
62
- contact_proc: proc { key_states[buttons["left"]] = true}, no_contact_proc: proc { key_states[buttons["left"]] = false}
63
- ),
64
- TouchButton.new(
65
- label: "Right", color: @game.black, x: 100, width: 50, height: 50, side: :left,
66
- contact_proc: proc { key_states[buttons["right"]] = true}, no_contact_proc: proc { key_states[buttons["right"]] = false}
67
- ),
68
-
69
-
70
- TouchButton.new(
71
- label: "X", color: @game.red, x: 50, width: 50, height: 50, side: :right,
72
- contact_proc: proc { key_states[buttons["x"]] = true}, no_contact_proc: proc { key_states[buttons["x"]] = false}
73
- ),
74
- TouchButton.new(
75
- label: "Y", color: @game.yellow, x: 125, width: 50, height: 50, side: :right,
76
- contact_proc: proc { key_states[buttons["y"]] = true}, no_contact_proc: proc { key_states[buttons["y"]] = false}
54
+ label: "Y", color: @game.yellow, x: 125, width: 50, height: 50, side: :right, for_key: "y"
77
55
  )
78
56
  )
57
+
58
+ @fullscreen_button = TouchButton.new(label: "Fullscreen", color: @game.black, x: 50, y: 10, width: 100, height: 50, side: :right)
79
59
  touch_handler_setup
80
60
 
81
61
  return self
@@ -95,6 +75,8 @@ class AuthorEngine
95
75
  def run_game
96
76
  `window.requestAnimationFrame(function() {#{run_game}})` # placed here to ensure next frame is called even if draw or update throw an error
97
77
  `#{@game.canvas_context}.clearRect(0,0, window.innerWidth, window.innerHeight)`
78
+ `#{@game.canvas_context}.fillStyle = "#222"`
79
+ `#{@game.canvas_context}.fillRect(0,0, window.innerWidth, window.innerHeight)`
98
80
 
99
81
  @counted_frames+=1
100
82
 
@@ -139,22 +121,17 @@ class AuthorEngine
139
121
  end
140
122
 
141
123
  def draw_touch_controls
124
+ @fullscreen_button.draw
142
125
  @touch_buttons.each(&:draw)
126
+ @touch_joystick.draw
143
127
  end
144
128
 
145
129
  def update_touch_controls
146
- active_buttons = []
147
-
148
- @touch_buttons.each do |button|
149
- @current_touches.each do |id, touch|
150
- if touch.x.between?(button.x, button.x+button.width) && touch.y.between?(button.y, button.y+button.height)
151
- active_buttons << button
152
- button.active
153
- end
154
- end
155
- end
130
+ @touch_buttons.each { |button| button.trigger?(@current_touches) }
131
+ @touch_joystick.update(@current_touches)
132
+ end
156
133
 
157
- (@touch_buttons - active_buttons).each(&:inactive)
134
+ def reposition_touch_controls
158
135
  end
159
136
 
160
137
  def resize_canvas
@@ -173,6 +150,8 @@ class AuthorEngine
173
150
  `#{@game.canvas}.style.height = #{height}`
174
151
 
175
152
  `#{@game.canvas_context}.imageSmoothingEnabled = false`
153
+
154
+ reposition_touch_controls
176
155
  return nil
177
156
  end
178
157
 
@@ -221,19 +200,26 @@ class AuthorEngine
221
200
  return nil
222
201
  end
223
202
 
203
+ def fullscreen_changed
204
+ resize_canvas
205
+ end
206
+
224
207
  def show(update_interval = (1000.0 / 60))
225
208
  return unless RUBY_ENGINE == "opal"
226
209
 
227
210
  `window.addEventListener('resize', () => { #{resize_canvas} })`
228
- `document.addEventListener('keydown', (event) => { #{AuthorEngine::Part::OpalInput::KEY_STATES[`event.key`] = true} })`
211
+ `document.addEventListener('keydown', (event) => { #{@show_touch_controls = false; AuthorEngine::Part::OpalInput::KEY_STATES[`event.key`] = true} })`
229
212
  `document.addEventListener('keyup', (event) => { #{AuthorEngine::Part::OpalInput::KEY_STATES[`event.key`] = false} })`
230
213
 
231
- `#{@game.canvas}.addEventListener('touchstart', (event) => { #{@show_touch_controls = true; handle_touch_start(`event`); puts "Touch started..."} })`
232
- `#{@game.canvas}.addEventListener('touchmove', (event) => { #{handle_touch_move(`event`); puts "Touch moved..."} })`
233
- `#{@game.canvas}.addEventListener('touchcancel', (event) => { #{handle_touch_cancel(`event`); puts "Touch canceled."} })`
234
- `#{@game.canvas}.addEventListener('touchend', (event) => { #{handle_touch_end(`event`); puts "Touch Ended."} })`
214
+ `#{@game.canvas}.addEventListener('touchstart', (event) => { #{@show_touch_controls = true; handle_touch_start(`event`)} })`
215
+ `#{@game.canvas}.addEventListener('touchmove', (event) => { #{handle_touch_move(`event`)} })`
216
+ `#{@game.canvas}.addEventListener('touchcancel', (event) => { #{handle_touch_cancel(`event`)} })`
217
+ `#{@game.canvas}.addEventListener('touchend', (event) => { #{handle_touch_end(`event`)} })`
218
+
219
+ `#{@game.canvas}.addEventListener('fullscreenchange', () => { #{fullscreen_changed} })`
235
220
 
236
221
  `document.getElementById('loading').style.display = "none"`
222
+
237
223
  `window.requestAnimationFrame(function() {#{run_game}})`
238
224
  return nil
239
225
  end
@@ -45,7 +45,7 @@ class AuthorEngine
45
45
  _level = AuthorEngine::GameRunner.instance.levels[level]
46
46
  raise "No level at '#{index}'!" unless _level
47
47
  raise "No sprite at '#{current_sprite}'!" unless AuthorEngine::GameRunner.instance.sprites[current_sprite]
48
- raise "No sprite at '#{current_sprite}'!" unless AuthorEngine::GameRunner.instance.sprites[replacement_sprite]
48
+ raise "No sprite at '#{replacement_sprite}'!" unless AuthorEngine::GameRunner.instance.sprites[replacement_sprite]
49
49
 
50
50
  _level.each {|sprite| sprite.sprite = replacement_sprite if sprite.sprite == current_sprite}
51
51
  end
@@ -1,9 +1,13 @@
1
1
  class AuthorEngine
2
2
  class TouchButton
3
3
  attr_reader :x, :y, :width, :height
4
- def initialize(label:, color:, x:, y: nil, width:, height:, side:, contact_proc:, no_contact_proc:)
4
+ def initialize(label:, color:, x:, y: nil, width:, height:, side:, for_key: nil, &block)
5
5
  @label, @color, @x, @y, @width, @height = label, color, x, y, width, height
6
- @side, @contact_proc, @no_contact_proc = side, contact_proc, no_contact_proc
6
+ @side, @for_key = side, for_key
7
+ @block = block
8
+
9
+ @buttons = AuthorEngine::Part::OpalInput::BUTTONS
10
+ @key_states = AuthorEngine::Part::OpalInput::KEY_STATES
7
11
 
8
12
  @game = AuthorEngine::GameRunner.instance.game
9
13
  @game_width = 128 * @game.scale
@@ -17,20 +21,46 @@ class AuthorEngine
17
21
  raise "side must be :left or :right"
18
22
  end
19
23
 
20
- @y = `window.innerHeight/2 - #{height}` unless @y.is_a?(Numeric)
24
+ @y = `window.innerHeight/2 - #{height/2}` unless @y.is_a?(Numeric)
21
25
  end
22
26
 
23
27
  def draw
24
28
  `#{@game.canvas_context}.fillStyle = #{@color}`
25
- `#{@game.canvas_context}.fillRect(#{@x}, #{@y}, #{width}, #{width})`
29
+ `#{@game.canvas_context}.fillRect(#{@x}, #{@y}, #{@width}, #{@height})`
30
+
31
+ font = "#{@height}px Connection, Consolas"
32
+ `#{@game.canvas_context}.font = #{font}`
33
+ `#{@game.canvas_context}.fillStyle = "white"`
34
+ `#{@game.canvas_context}.textBaseline = "top"`
35
+ `#{@game.canvas_context}.fillText(#{@label}, #{@x}, #{@y}, #{@width})`
36
+ end
37
+
38
+ def trigger?(touches)
39
+ triggered = false
40
+
41
+ touches.detect do |id, touch|
42
+ if touch.x.between?(@x, @x+@width) && touch.y.between?(@y, @y+@height)
43
+ triggered = true
44
+ end
45
+ end
46
+
47
+
48
+ if @for_key
49
+ active if triggered
50
+ inactive unless triggered
51
+ else
52
+ @block.call if @block && triggered
53
+ end
54
+
55
+ return triggered
26
56
  end
27
57
 
28
58
  def active
29
- @contact_proc.call
59
+ @key_states[@buttons[@for_key]] = true
30
60
  end
31
61
 
32
62
  def inactive
33
- @no_contact_proc.call
63
+ @key_states[@buttons[@for_key]] = false
34
64
  end
35
65
  end
36
66
  end
@@ -1,12 +1,21 @@
1
1
  class AuthorEngine
2
2
  module TouchHandler
3
- Touch = Struct.new(:x, :y)
3
+ Touch = Struct.new(:origin_x, :origin_y, :x, :y)
4
4
  def touch_handler_setup
5
5
  @current_touches = {}
6
6
  end
7
7
 
8
8
  def copy_touch(touch)
9
- Touch.new(`touch.pageX`, `touch.pageY`)
9
+ Touch.new(`touch.pageX`, `touch.pageY`, `touch.pageX`, `touch.pageY`)
10
+ end
11
+
12
+ def set_touch(touch)
13
+ struct = @current_touches[`#{touch}.identifier`]
14
+
15
+ struct.x = `#{touch}.pageX`
16
+ struct.y = `#{touch}.pageY`
17
+
18
+ return nil
10
19
  end
11
20
 
12
21
  def handle_touch_start(event)
@@ -17,6 +26,14 @@ class AuthorEngine
17
26
  #{@current_touches[`touches[i].identifier`] = copy_touch(`touches[i]`)}
18
27
  }`
19
28
 
29
+ if @fullscreen_button && @fullscreen_button.trigger?(@current_touches)
30
+ `if (document.fullscreenElement == null && #{@game.canvas}.requestFullscreen) {
31
+ #{game.canvas}.requestFullscreen()
32
+ } else if(document.fullscreenElement != null && document.exitFullscreen) {
33
+ document.exitFullscreen()
34
+ } `
35
+ end
36
+
20
37
  return nil
21
38
  end
22
39
 
@@ -25,7 +42,7 @@ class AuthorEngine
25
42
 
26
43
  touches = `#{event}.changedTouches`
27
44
  `for (var i = 0; i < #{touches}.length; i++) {
28
- #{@current_touches[`touches[i].identifier`] = copy_touch(`touches[i]`)}
45
+ #{set_touch(`touches[i]`)}
29
46
  }`
30
47
 
31
48
  return nil
@@ -0,0 +1,122 @@
1
+ class AuthorEngine
2
+ class TouchJoystick
3
+ def initialize(x:, y: nil, radius:, side:, background: nil, color: nil)
4
+ @x, @y, @radius, @side, @background, @color = x, y, radius, side, background, color
5
+
6
+ @buttons = AuthorEngine::Part::OpalInput::BUTTONS
7
+ @key_states = AuthorEngine::Part::OpalInput::KEY_STATES
8
+
9
+ @game = AuthorEngine::GameRunner.instance.game
10
+ @game_width = 128 * @game.scale
11
+ @game_x = `window.innerWidth/2 - #{@game_width/2}`
12
+
13
+ if @side == :left
14
+ @x = @game_x-@x
15
+ elsif @side == :right
16
+ @x = @game_x+@game_width+@x
17
+ else
18
+ raise "side must be :left or :right"
19
+ end
20
+
21
+ @y = `window.innerHeight/2` unless @y.is_a?(Numeric)
22
+ @color = @game.dark_gray unless @color
23
+ @background = @game.light_gray unless @background
24
+
25
+ @joystick_x, @joystick_y, @joystick_radius = @x, @y, @radius/2
26
+ end
27
+
28
+ def draw
29
+ # Background
30
+ `#{@game.canvas_context}.fillStyle = #{@background}`
31
+ `#{@game.canvas_context}.beginPath()`
32
+ `#{@game.canvas_context}.arc(#{@x}, #{@y}, #{@radius}, 0, 2 * Math.PI)`
33
+ `#{@game.canvas_context}.fill()`
34
+
35
+ # Joystick
36
+ `#{@game.canvas_context}.beginPath()`
37
+ `#{@game.canvas_context}.fillStyle = #{@color}`
38
+ `#{@game.canvas_context}.arc(#{@joystick_x}, #{@joystick_y}, #{@joystick_radius}, 0, 2 * Math.PI)`
39
+ `#{@game.canvas_context}.fill()`
40
+
41
+ return nil
42
+ end
43
+
44
+ def circles_collide?(x,y, radius, x2,y2, radius2)
45
+ radii = radius + radius2
46
+
47
+ if @game.distance(x,y, x2,y2) < radii
48
+ return true
49
+ else
50
+ return false
51
+ end
52
+ end
53
+
54
+ def update(touches)
55
+ touch_detected = false
56
+
57
+ touches.detect do |id, touch|
58
+ if circles_collide?(@x, @y, @radius, touch.origin_x, touch.origin_y, 1)
59
+ touch_detected = true
60
+
61
+ _distance = @game.distance(@x,@y, touch.x,touch.y).clamp(0, @radius)
62
+ _direction = Math.atan2(touch.y - @y, touch.x - @x)
63
+
64
+ @joystick_x = @x +(_distance * Math.cos(_direction))
65
+ @joystick_y = @y +(_distance * Math.sin(_direction))
66
+
67
+ return true
68
+ end
69
+ end
70
+
71
+
72
+ unless touch_detected
73
+ @joystick_x = @x
74
+ @joystick_y = @y
75
+ end
76
+
77
+ trigger_input
78
+
79
+ return nil
80
+ end
81
+
82
+ def trigger_input(threshold = 0.35)
83
+ threshold = @radius * threshold
84
+
85
+ if @joystick_x != @x || @joystick_y != @y
86
+ if (@x - @joystick_x) < -threshold
87
+ set("right", true)
88
+ else
89
+ set("right", false)
90
+ end
91
+
92
+ if (@x - @joystick_x) > threshold
93
+ set("left", true)
94
+ else
95
+ set("left", false)
96
+ end
97
+
98
+ if (@y - @joystick_y) < -threshold
99
+ set("down", true)
100
+ else
101
+ set("down", false)
102
+ end
103
+
104
+ if (@y - @joystick_y) > threshold
105
+ set("up", true)
106
+ else
107
+ set("up", false)
108
+ end
109
+
110
+ else
111
+ set("up", false)
112
+ set("down", false)
113
+ set("left", false)
114
+ set("right", false)
115
+ end
116
+ end
117
+
118
+ def set(key, boolean)
119
+ @key_states[@buttons[key]] = boolean
120
+ end
121
+ end
122
+ end
@@ -14,4 +14,5 @@ require_relative "game/game"
14
14
  require_relative "save_file"
15
15
  require_relative "game/opal/touch_handler"
16
16
  require_relative "game/opal/touch_button"
17
+ require_relative "game/opal/touch_joystick"
17
18
  require_relative "game/opal/game_runner"
@@ -1,3 +1,3 @@
1
1
  class AuthorEngine
2
- VERSION = "0.5.0"
2
+ VERSION = "0.6.0"
3
3
  end
@@ -60,7 +60,11 @@ class AuthorEngine
60
60
  end
61
61
  end
62
62
 
63
- import_spritesheet(window.container.savefile.sprites)
63
+ if window.container.savefile.sprites.to_blob.length < 4
64
+ import_spritesheet(spritesheet)
65
+ else
66
+ import_spritesheet(window.container.savefile.sprites)
67
+ end
64
68
  end
65
69
 
66
70
  def focus
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: author_engine
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cyberarm
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-12-23 00:00:00.000000000 Z
11
+ date: 2018-12-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: gosu
@@ -148,6 +148,7 @@ files:
148
148
  - lib/author_engine/game/opal/parts/input.rb
149
149
  - lib/author_engine/game/opal/touch_button.rb
150
150
  - lib/author_engine/game/opal/touch_handler.rb
151
+ - lib/author_engine/game/opal/touch_joystick.rb
151
152
  - lib/author_engine/image.rb
152
153
  - lib/author_engine/level_picker.rb
153
154
  - lib/author_engine/opal.rb