author_engine 0.1.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -2
  3. data/API.md +20 -0
  4. data/Gemfile.lock +13 -2
  5. data/README.md +8 -4
  6. data/author_engine.gemspec +2 -1
  7. data/bin/author_engine +3 -1
  8. data/lib/author_engine/button.rb +1 -1
  9. data/lib/author_engine/cli.rb +21 -0
  10. data/lib/author_engine/collision_detection/collision_detection.rb +188 -0
  11. data/lib/author_engine/containers/loader.rb +8 -1
  12. data/lib/author_engine/game/common/parts/collision_detection.rb +33 -0
  13. data/lib/author_engine/game/game.rb +28 -2
  14. data/lib/author_engine/game/{parts → gosu/parts}/colors.rb +0 -0
  15. data/lib/author_engine/game/{parts → gosu/parts}/common.rb +0 -0
  16. data/lib/author_engine/game/{parts → gosu/parts}/graphics.rb +19 -1
  17. data/lib/author_engine/game/{parts → gosu/parts}/input.rb +0 -0
  18. data/lib/author_engine/game/opal/exporter.rb +157 -0
  19. data/lib/author_engine/game/opal/game_runner.rb +158 -0
  20. data/lib/author_engine/game/opal/parts/colors.rb +64 -0
  21. data/lib/author_engine/game/opal/parts/common.rb +13 -0
  22. data/lib/author_engine/game/opal/parts/graphics.rb +69 -0
  23. data/lib/author_engine/game/opal/parts/input.rb +30 -0
  24. data/lib/author_engine/image.rb +1 -0
  25. data/lib/author_engine/level_picker.rb +92 -0
  26. data/lib/author_engine/opal.rb +15 -0
  27. data/lib/author_engine/save_file.rb +49 -15
  28. data/lib/author_engine/sprite.rb +1 -2
  29. data/lib/author_engine/sprite_picker.rb +10 -3
  30. data/lib/author_engine/text.rb +2 -2
  31. data/lib/author_engine/version.rb +1 -1
  32. data/lib/author_engine/views/level_editor.rb +161 -0
  33. data/lib/author_engine/views/play_viewer.rb +1 -0
  34. data/lib/author_engine.rb +9 -4
  35. data/screenshots/code_editor.png +0 -0
  36. data/screenshots/play.png +0 -0
  37. data/screenshots/sprite_editor.png +0 -0
  38. metadata +36 -9
  39. data/test_3.authorengine +0 -519
  40. data/testing.authorengine +0 -578
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: aa9450a11aa87c07b6af55a87d7ebf3a68feaff0c671662e6662a14ebf45a004
4
- data.tar.gz: 52c74e684c1d5c402083b5dfa7a630ad99d445ec5cd786db9fc3a3d324f7c9a4
3
+ metadata.gz: 0ac3f16a1dc192379088c8f74dde1e6dab90e5edeefa35e1bcb810c7017b6833
4
+ data.tar.gz: '0860cdd4a930f02a6654fbde557148d8e460bb3666e8e87c10a64ba81dffcf6e'
5
5
  SHA512:
6
- metadata.gz: 5b0173f8357c12aadd3d0ac99e0a7b2c78d862d93bca6940d3f383be701e51d00dfd5c63ea7873fb7289778389846bbce2cdb5129c5e5bb6b8801895d0f5f018
7
- data.tar.gz: a59df8c02965376c9824385f756516a5af44a4c88854c748a8246bf6f2929a63048e6c1f040214cbd193b5837d91f1ca24ab0161d3d9a9e8984a16fd8aafec61
6
+ metadata.gz: 91dc4eace8dcb2c8f3edae461cef1a515e5616a915dd19f7598df02e0fd6f0bab0e45f75713b40869aefa803b9db6cda0f48b0279d9f91cb6cf4a5fa16c4caad
7
+ data.tar.gz: 6ff618da871039a3786b1bdab78977914d41c7c10e45101ac19865aaee3c7ab3f771af7caec2577bcfb35c03372b1dbf9dfdf0a0af669b45a9ec5a8b77af33b9
data/.gitignore CHANGED
@@ -7,5 +7,6 @@
7
7
  /spec/reports/
8
8
  /tmp/
9
9
 
10
- _*.rb
11
- *.authorengine
10
+ **/_*.rb
11
+ **/*.authorengine
12
+ **/*.html
data/API.md CHANGED
@@ -38,10 +38,30 @@ end
38
38
  ### milliseconds
39
39
  Milliseconds since game started
40
40
 
41
+ # CollisionDetection
42
+ ### bounding_box(sprite_index)
43
+ returns the bounding_box for sprite
44
+ ### sprite_vs_sprite(sprite_index, sprite_x, sprite_y, target_sprite_index, target_x, target_y)
45
+ determine whether 2 sprites are colliding
46
+ ### sprite_vs_level(sprite_index, sprite_x, sprite_y, level)
47
+ returns an array of level tiles that sprite is colliding with. returns an empty array if non are colliding.
48
+ ### colliding_edge(sprite_index, sprite_x, sprite_y, target_sprite_index, target_x, target_y)
49
+ determine the edge of which sprite is colliding with with target
50
+ ### draw_sprite_box(sprite_index, sprite_x, sprite_y)
51
+ draws sprites bounding box
52
+ ### draw_level_boxes(level_index)
53
+ draws bounding boxes around each tile of level
54
+ ### render_bounding_box(sprite_index, box, sprite_x, sprite_y, edges = {}, z = Float::INFINITY)
55
+ draw bounding box around sprite with optional edges from colliding_edges to show colliding edges
56
+
41
57
  # Graphics
42
58
  ### rect(x = 0, y = 0, width = 1, height = 1, color = white, z = 0)
43
59
  ### sprite(sprite_sheet_index, x = 0, y = 0, z = 0, color = white)
44
60
  ### text(string, x = 0, y = 0, size = 4, z = 0, color = white)
61
+ ### level(level_index, z = 0)
62
+
63
+ ### swap(level_index, current_sprite, replacement_sprite)
64
+ Swap level's current_sprite with replacement_sprite
45
65
 
46
66
  ### translate(x, y, &block)
47
67
  Translate the contents of block by x and y
data/Gemfile.lock CHANGED
@@ -1,17 +1,28 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- author_engine (0.1.0)
4
+ author_engine (0.3.0)
5
5
  coderay (~> 1.1.2)
6
6
  gosu (~> 0.14.4)
7
+ opal (~> 0.11.4)
7
8
 
8
9
  GEM
9
10
  remote: https://rubygems.org/
10
11
  specs:
12
+ ast (2.4.0)
11
13
  coderay (1.1.2)
12
14
  gosu (0.14.4)
15
+ hike (1.2.3)
13
16
  minitest (5.11.3)
17
+ opal (0.11.4)
18
+ ast (>= 2.3.0)
19
+ hike (~> 1.2)
20
+ parser (= 2.3.3.1)
21
+ sourcemap (~> 0.1.0)
22
+ parser (2.3.3.1)
23
+ ast (~> 2.2)
14
24
  rake (10.5.0)
25
+ sourcemap (0.1.1)
15
26
 
16
27
  PLATFORMS
17
28
  ruby
@@ -23,4 +34,4 @@ DEPENDENCIES
23
34
  rake (~> 10.0)
24
35
 
25
36
  BUNDLED WITH
26
- 1.16.3
37
+ 1.17.2
data/README.md CHANGED
@@ -8,19 +8,23 @@ A virtual console¹ that you code in Ruby.
8
8
  `gem install author_engine`
9
9
 
10
10
  ## Run
11
- `author_engine`
11
+ `author_engine [filename] [options]`
12
+ - Run AuthorEngine editor with optional filename argument to open project.
13
+
14
+ `author_engine export filename`
15
+ - Export project to web using [Opal](https://opalrb.com).
12
16
  ### Options
13
17
  `--native` Open AuthorEngine in fullscreen and maximized.
14
18
 
15
19
  # Interface
16
20
  ## Play
17
- ![play_view](#)
21
+ ![play_view](https://raw.githubusercontent.com/cyberarm/author_engineV2/master/screenshots/play.png)
18
22
  ## Sprite Editor
19
- ![sprite_editor_view](#)
23
+ ![sprite_editor_view](https://raw.githubusercontent.com/cyberarm/author_engineV2/master/screenshots/sprite_editor.png)
20
24
  ## Level Editor
21
25
  ![level_editor_view](#)
22
26
  ## Code Editor
23
- ![code_editor_view](#)
27
+ ![code_editor_view](https://raw.githubusercontent.com/cyberarm/author_engineV2/master/screenshots/code_editor.png)
24
28
 
25
29
 
26
30
  ¹: Does not directly run any kind of assembly
@@ -30,10 +30,11 @@ Gem::Specification.new do |spec|
30
30
  end
31
31
  spec.bindir = "bin"
32
32
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
33
- spec.require_paths = ["lib"]
33
+ spec.require_paths = ["lib", "assets"]
34
34
 
35
35
  spec.add_dependency "gosu", "~> 0.14.4"
36
36
  spec.add_dependency "coderay", "~> 1.1.2"
37
+ spec.add_dependency "opal", "~> 0.11.4"
37
38
 
38
39
  spec.add_development_dependency "bundler", "~> 1.16"
39
40
  spec.add_development_dependency "rake", "~> 10.0"
data/bin/author_engine CHANGED
@@ -1,4 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require "author_engine"
4
- AuthorEngine::Window.new.show
4
+ require "author_engine/cli"
5
+
6
+ AuthorEngine::CLI.new
@@ -111,7 +111,7 @@ class AuthorEngine
111
111
  @image.draw(@x+@x_padding, @y+@y_padding, @z, (1 * window.square_scale), (1 * window.square_scale))
112
112
 
113
113
  else
114
- raise "Nothing to draw! (text and image were nil or invalid types)"
114
+ raise "Nothing to draw! (label and image were nil or invalid types)"
115
115
  end
116
116
  end
117
117
 
@@ -0,0 +1,21 @@
1
+ class AuthorEngine
2
+ class CLI
3
+ def initialize
4
+ if ARGV[0] && ARGV[0] == "export"
5
+ if ARGV[1] && ARGV[1].end_with?(".authorengine")
6
+ require "author_engine/game/opal/exporter"
7
+
8
+ AuthorEngine::OpalExporter.new(project_file: ARGV[1])
9
+ else
10
+ puts "author_engine export project [exported_name]"
11
+ end
12
+
13
+ elsif ARGV[0] && ARGV[0].end_with?(".authorengine")
14
+ # The Loader Container handles loading projects
15
+ AuthorEngine::Window.new.show
16
+ else
17
+ AuthorEngine::Window.new.show
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,188 @@
1
+ class AuthorEngine
2
+ class CollisionDetection
3
+ Color = Struct.new(:red, :green, :blue, :alpha)
4
+ BoundingBox = Struct.new(:x, :y, :width, :height)
5
+
6
+ def initialize(game_sprites, game_levels)
7
+ @game_sprites = game_sprites
8
+ @game_levels = game_levels
9
+
10
+ @sprites= []
11
+ @levels = []
12
+
13
+ @known_collisions = []
14
+ end
15
+
16
+ def clear
17
+ @known_collisions.clear
18
+ end
19
+
20
+ def add_sprite(image_or_blob)
21
+ blob = nil
22
+ if RUBY_ENGINE != "opal"
23
+ blob = image_or_blob.to_blob
24
+ else
25
+ blob = []
26
+ `#{image_or_blob}.forEach(function(value) {#{blob << `value`}})`#.each {|n| blob << n}
27
+ end
28
+
29
+ @sprites << {blob: blob, box: bounding_box(blob)}
30
+ end
31
+
32
+ def add_level(level_array)
33
+ @levels << level_array # TODO: Put level's into an optimized structure for fast quadrant look-ups
34
+ end
35
+
36
+ def box(sprite_index)
37
+ @sprites[sprite_index][:box]
38
+ end
39
+
40
+ def sprite_vs_sprite(sprite_index, sprite_x, sprite_y, target_sprite_index, target_x, target_y)
41
+ bounding_boxes_intersect?(box(sprite_index), sprite_x, sprite_y, box(target_sprite_index), target_x, target_y)
42
+ end
43
+
44
+ def sprite_vs_level(sprite_index, sprite_x, sprite_y, level)
45
+ detected = []
46
+
47
+ collider = box(sprite_index)
48
+ @levels[level].each do |sprite|
49
+ if bounding_boxes_intersect?(collider, sprite_x, sprite_y, box(sprite.sprite), sprite.x, sprite.y)
50
+ detected << sprite
51
+ end
52
+ end
53
+
54
+ return detected
55
+ end
56
+
57
+ def colliding_edge(sprite_index, sprite_x, sprite_y, target_sprite_index, target_x, target_y)
58
+ sprite_box = box(sprite_index)
59
+ target_box = box(target_sprite_index)
60
+
61
+ edges = {top: false, left: false, right: false, bottom: false}
62
+
63
+ # https://gamedev.stackexchange.com/a/24091
64
+ wy = (sprite_box.width + target_box.width) * ((sprite_y - sprite_box.height) - (target_y - target_box.height/2));
65
+ hx = (sprite_box.height + target_box.height) * ((sprite_x - sprite_box.width) - (target_x - target_box.height/2));
66
+
67
+ if (wy > hx)
68
+ if (wy > -hx)
69
+ edges[:bottom] = true
70
+ else
71
+ edges[:left] = true
72
+ end
73
+ else
74
+ if (wy > -hx)
75
+ edges[:right] = true
76
+ else
77
+ edges[:top] = true
78
+ end
79
+ end
80
+
81
+ return edges
82
+ end
83
+
84
+ def debug_draw_sprite(sprite_index, sprite_x, sprite_y)
85
+ render_bounding_box(sprite_index, box(sprite_index), sprite_x, sprite_y)
86
+ end
87
+
88
+ def debug_draw_level(level_index)
89
+ @levels[level_index].each do |sprite|
90
+ render_bounding_box(sprite.sprite, box(sprite.sprite), sprite.x, sprite.y)
91
+ end
92
+ end
93
+
94
+ def render_bounding_box(sprite_index, box, sprite_x, sprite_y, edges = {}, z = Float::INFINITY, color = 0xc800ff00, collision_color = 0xc8ff00ff)
95
+ if RUBY_ENGINE == "opal"
96
+ color = "green"
97
+ collision_color = "red"
98
+ end
99
+ paint_color = color
100
+ # EDGE: TOP
101
+ # TOP LEFT TO TOP RIGHT
102
+ if edges[:top] then paint_color = collision_color; else paint_color = color; end
103
+ draw_line(
104
+ box.x + sprite_x, box.y + sprite_y,
105
+ box.x + sprite_x + box.width, box.y + sprite_y,
106
+ paint_color, z
107
+ )
108
+
109
+ # EDGE: RIGHT
110
+ # TOP RIGHT TO BOTTOM RIGHT
111
+ if edges[:right] then paint_color = collision_color; else paint_color = color; end
112
+ draw_line(
113
+ box.x + sprite_x + box.width, box.y + sprite_y,
114
+ box.x + sprite_x + box.width, box.y + sprite_y + box.height,
115
+ paint_color, z
116
+ )
117
+
118
+ # EDGE: BOTTOM
119
+ # BOTTOM RIGHT to BOTTOM LEFT
120
+ if edges[:bottom] then paint_color = collision_color; else paint_color = color; end
121
+ draw_line(
122
+ box.x + sprite_x + box.width, box.y + sprite_y + box.height,
123
+ box.x + sprite_x, box.y + sprite_y + box.height,
124
+ paint_color, z
125
+ )
126
+
127
+ # EDGE: LEFT
128
+ # BOTTOM LEFT TO TOP LEFT
129
+ if edges[:left] then paint_color = collision_color; else paint_color = color; end
130
+ draw_line(
131
+ box.x + sprite_x, box.y + sprite_y + box.height,
132
+ box.x + sprite_x, box.y + sprite_y,
133
+ paint_color, z
134
+ )
135
+ end
136
+
137
+ private
138
+ def bounding_boxes_intersect?(a, a_x, a_y, b, b_x, b_y)
139
+ (a.x + a_x) <= (b.x + b_x + b.width) && (a.x + a_x + a.width) >= (b.x + b_x) &&
140
+ (a.y + a_y) <= (b.y + b_y + b.height) && (a.y + a_y + a.height) >= (b.y + b_y)
141
+ end
142
+
143
+ def draw_line(x, y, x2, y2, color, z = 0)
144
+ if RUBY_ENGINE == "opal"
145
+ `#{AuthorEngine::GameRunner.instance.game.canvas_context}.strokeStyle = #{color}`
146
+ `#{AuthorEngine::GameRunner.instance.game.canvas_context}.lineWidth = 1`
147
+
148
+ `#{AuthorEngine::GameRunner.instance.game.canvas_context}.beginPath()`
149
+ `#{AuthorEngine::GameRunner.instance.game.canvas_context}.moveTo(#{x}, #{y})`
150
+ `#{AuthorEngine::GameRunner.instance.game.canvas_context}.lineTo(#{x2}, #{y2})`
151
+ `#{AuthorEngine::GameRunner.instance.game.canvas_context}.stroke()`
152
+ else
153
+ Gosu.draw_line(x, y, color, x2, y2, color, z)
154
+ end
155
+ end
156
+
157
+ # returns alpha value of pixel at x and y
158
+ def solid_at?(blob, x, y)
159
+ width = 16
160
+
161
+ blob[(y * width + x) * 4 + 3].ord
162
+ end
163
+
164
+ def bounding_box(blob, size = 16)
165
+ box = BoundingBox.new(size, size, 0, 0)
166
+ size.times do |y|
167
+ size.times do |x|
168
+ if solid_at?(blob, x, y) > 0
169
+ box.x = x if x < box.x
170
+ box.y = y if y < box.y
171
+ box.width = x if x > box.width
172
+ box.height = y if y > box.height
173
+ end
174
+ end
175
+ end
176
+
177
+ # Correct width/height
178
+ box.width -= box.x
179
+ box.height -= box.y
180
+
181
+ # Correct off-by-1
182
+ box.width += 1
183
+ box.height += 1
184
+
185
+ return box
186
+ end
187
+ end
188
+ end
@@ -26,6 +26,14 @@ class AuthorEngine
26
26
  @entering_name = true
27
27
  end
28
28
  @new_button.x = window.width - @new_button.width
29
+
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
29
37
  end
30
38
 
31
39
  def load(filename)
@@ -54,7 +62,6 @@ class AuthorEngine
54
62
  Gosu.draw_rect(0, @new_button.y, window.width, @new_button.height, @header_color)
55
63
  @font.draw_text("AuthorEngine", 1*window.square_scale,@font.height/2,0)
56
64
  @new_button.draw
57
- @font.draw_text(@list[@index].name, 0, 0, 0) if @list[@index]
58
65
 
59
66
  Gosu.clip_to(0, @font.height*2, window.width, window.height-(@font.height*4)) do
60
67
  y = (window.height/2-@font.height) - (@height/2)
@@ -0,0 +1,33 @@
1
+ class AuthorEngine
2
+ class Part
3
+ module CollisionDetection
4
+ def bounding_box(sprite_index)
5
+ @collision_detection.box(sprite_index)
6
+ end
7
+
8
+ def colliding_edge(sprite_index, sprite_x, sprite_y, target_sprite_index, target_x, target_y)
9
+ @collision_detection.colliding_edge(sprite_index, sprite_x, sprite_y, target_sprite_index, target_x, target_y)
10
+ end
11
+
12
+ def sprite_vs_sprite(sprite_index, sprite_x, sprite_y, target_sprite_index, target_x, target_y)
13
+ @collision_detection.sprite_vs_sprite(sprite_index, sprite_x, sprite_y, target_sprite_index, target_x, target_y)
14
+ end
15
+
16
+ def sprite_vs_level(sprite_index, sprite_x, sprite_y, level)
17
+ @collision_detection.sprite_vs_level(sprite_index, sprite_x, sprite_y, level)
18
+ end
19
+
20
+ def draw_sprite_box(sprite_index, sprite_x, sprite_y)
21
+ @collision_detection.debug_draw_sprite(sprite_index, sprite_x, sprite_y)
22
+ end
23
+
24
+ def draw_level_boxes(level_index)
25
+ @collision_detection.debug_draw_level(level_index)
26
+ end
27
+
28
+ def render_bounding_box(sprite_index, box, sprite_x, sprite_y, edges = {}, z = Float::INFINITY)
29
+ @collision_detection.render_bounding_box(sprite_index, box, sprite_x, sprite_y, edges, z, nil, nil)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -1,17 +1,43 @@
1
1
  class AuthorEngine
2
2
  class Game
3
3
  include AuthorEngine::Part::Common
4
+ include AuthorEngine::Part::CollisionDetection
4
5
  include AuthorEngine::Part::Colors
5
6
  include AuthorEngine::Part::Graphics
6
7
  include AuthorEngine::Part::Input
7
8
 
9
+ attr_accessor :scale, :canvas, :canvas_context
10
+ attr_accessor :collision_detection
8
11
  def initialize(code:)
9
- @background_color = Gosu::Color::BLACK
12
+ if RUBY_ENGINE == "opal"
13
+ @scale = 1.0
14
+ @canvas = `document.getElementById('canvas')`
15
+ @canvas_context = `#{@canvas}.getContext('2d')`
16
+ end
17
+
18
+ if RUBY_ENGINE != "opal"
19
+ @sprites = SpriteEditor.instance.sprites
20
+
21
+ @levels = []
22
+ # Create a "Deep Copy" to allow for swapping of a level's sprites without corrupting LevelEditor's version
23
+ LevelEditor.instance.levels.each do |level|
24
+ @levels << level.sort_by {|sprite| sprite.z}.map {|sprite| sprite.dup}
25
+ end
26
+ size = 16
27
+ @levels.each {|level| level.each {|sprite| sprite.x = sprite.x * size; sprite.y = sprite.y * size}}
28
+
29
+ @collision_detection = CollisionDetection.new(@sprites, @levels)
30
+
31
+ @sprites.each {|sprite| @collision_detection.add_sprite(sprite) }
32
+ @levels.each {|level| @collision_detection.add_level(level) }
33
+ end
34
+
35
+ @background_color = black
10
36
  self.instance_eval(code)
11
37
  end
12
38
 
13
39
  def draw_background
14
- Gosu.draw_rect(0, 0, Window::VIEW_WIDTH, Window::VIEW_HEIGHT, @background_color)
40
+ rect(0, 0, width, height, @background_color)
15
41
  end
16
42
 
17
43
  def init
@@ -20,11 +20,29 @@ class AuthorEngine
20
20
  end
21
21
 
22
22
  def sprite(index, x = 0, y = 0, z = 0, alpha = 255)
23
- image = SpriteEditor.instance.sprites[index]
23
+ image = @sprites[index]
24
24
  raise "No sprite at '#{index}'!" unless image
25
25
  image.draw(x, y, z, 1,1, Gosu::Color.rgba(255,255,255, alpha))
26
26
  end
27
27
 
28
+ def level(index, z = 0)
29
+ _level = @levels[index]
30
+ raise "No level at '#{index}'!" unless _level
31
+
32
+ _level.each do |sprite|
33
+ sprite(sprite.sprite, sprite.x, sprite.y, z)
34
+ end
35
+ end
36
+
37
+ def swap(level, current_sprite, replacement_sprite)
38
+ _level = @levels[level]
39
+ raise "No level at '#{index}'!" unless _level
40
+ raise "No sprite at '#{current_sprite}'!" unless @sprites[current_sprite]
41
+ raise "No sprite at '#{current_sprite}'!" unless @sprites[replacement_sprite]
42
+
43
+ _level.each {|sprite| sprite.sprite = replacement_sprite if sprite.sprite == current_sprite}
44
+ end
45
+
28
46
  def translate(x, y, &block)
29
47
  Gosu.translate(x, y) do
30
48
  block.call if block
@@ -0,0 +1,157 @@
1
+ require "opal"
2
+ require "fileutils"
3
+
4
+ class AuthorEngine
5
+ class OpalExporter
6
+ def initialize(project_file:)
7
+ @project_file = project_file
8
+
9
+ save(export)
10
+ end
11
+
12
+ def project_name
13
+ name = File.basename(@project_file, ".authorengine")
14
+ return name.split("_").map {|n| n.capitalize}.join(" ")
15
+ end
16
+
17
+ def stylesheet
18
+ %{
19
+ @font-face { font-family: Connection; src: url('fonts/Connection.otf'); }
20
+ @font-face { font-family: ConnectionBold; src: url('fonts/ConnectionBold.otf'); }
21
+
22
+ body {
23
+ margin: 0;
24
+ padding: 0;
25
+ background: #222;
26
+ }
27
+ #canvas {
28
+ display: block;
29
+ margin: 0 auto;
30
+ cursor: none;
31
+ }
32
+ #loading {
33
+ font-family: Connection, sans serif;
34
+ color: white;
35
+ text-align: center;
36
+
37
+ position: absolute;
38
+ top: 0;
39
+ bottom: 0;
40
+ left: 0;
41
+ right: 0;
42
+ width: 50%;
43
+ height: 30%;
44
+ margin: auto;
45
+ }
46
+ }
47
+ end
48
+
49
+ def project
50
+ %{
51
+ var projectString = `#{File.open(@project_file).read}`;
52
+ }
53
+ end
54
+
55
+ def game_runtime
56
+ program = %{
57
+ require "opal"
58
+ require "opal-parser"
59
+ require "author_engine/opal"
60
+
61
+ `var callback = function(){
62
+ \#{AuthorEngine::GameRunner.new(`projectString`).show}
63
+ };
64
+
65
+ if (
66
+ document.readyState === "complete" ||
67
+ (document.readyState !== "loading" && !document.documentElement.doScroll)
68
+ ) {
69
+ callback();
70
+ } else {
71
+ document.addEventListener("DOMContentLoaded", callback);
72
+ }`
73
+ }
74
+
75
+ builder = Opal::Builder.new
76
+ base_path = File.expand_path("../../../..", __FILE__)
77
+ builder.append_paths("#{base_path}")
78
+
79
+ puts "Transpiling to JavaScript using Opal..."
80
+
81
+
82
+ builder.build("opal")
83
+ builder.build("opal-parser")
84
+ builder.build_require("author_engine/opal")
85
+
86
+ builder.build_str(program, "(inline)").to_s
87
+ end
88
+
89
+ def template
90
+ %{
91
+ <!doctype html5>
92
+ <html>
93
+ <head>
94
+ <meta charset="utf-8" />
95
+ <title>#{project_name} | AuthorEngine</title>
96
+ </head>
97
+ <style>
98
+ #{stylesheet}
99
+ </style>
100
+ <body>
101
+ <h1 id="loading">Loading...</h1>
102
+ <canvas id="canvas">
103
+ <h1>Your Browser Does Not Support HTML5 Canvas!</h1>
104
+ </canvas>
105
+
106
+ <script>
107
+ // Add a small delay before loading application in order to finish loading page and show "Loading..."
108
+ window.setTimeout(function() {
109
+ var script = document.createElement('script');
110
+ script.src = "application.js";
111
+
112
+ document.head.appendChild(script);
113
+ }, 500);
114
+ </script>
115
+ </body>
116
+ </html>
117
+ }
118
+ end
119
+
120
+ def export
121
+ template
122
+ end
123
+
124
+ def save(string)
125
+ filename = File.basename(@project_file)
126
+ directory = File.expand_path(@project_file.sub(filename, ''))
127
+ name = filename.sub(".authorengine", "")
128
+
129
+ export_path = "#{directory}/#{name}"
130
+ unless File.exists?(export_path)
131
+ Dir.mkdir(export_path)
132
+ unless File.exists?("#{export_path}/fonts")
133
+ Dir.mkdir("#{export_path}/fonts")
134
+ end
135
+ end
136
+
137
+ puts "Saving to \"#{export_path}\""
138
+ File.open("#{export_path}/#{name}.html", "w") do |file|
139
+ file.write(string)
140
+ end
141
+
142
+ File.open("#{export_path}/application.js", "w") do |file|
143
+ file.write(project)
144
+ file.write("\n\n\n")
145
+ file.write(game_runtime)
146
+ end
147
+
148
+ fonts_path = "#{File.expand_path("../../../../../", __FILE__)}/assets/fonts"
149
+ font_files = Dir.glob("#{fonts_path}/*")
150
+ font_files.each do |file|
151
+ FileUtils.cp(file, "#{export_path}/fonts/#{File.basename(file)}")
152
+ end
153
+
154
+ puts "Saved."
155
+ end
156
+ end
157
+ end