fantasy 0.1.13 → 0.1.17

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 (84) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +22 -1
  3. data/.yardopts +4 -0
  4. data/CHANGELOG.md +6 -0
  5. data/Gemfile +1 -1
  6. data/Gemfile.lock +55 -0
  7. data/README.md +14 -6
  8. data/docs/Actor.html +2737 -0
  9. data/docs/Background.html +961 -0
  10. data/docs/Camera.html +791 -0
  11. data/docs/Clock.html +753 -0
  12. data/docs/Color.html +776 -0
  13. data/docs/Coordinates.html +730 -0
  14. data/docs/Cursor.html +752 -0
  15. data/docs/Disk.html +236 -0
  16. data/docs/Draggable.html +198 -0
  17. data/docs/Fantasy.html +121 -0
  18. data/docs/Game.html +904 -0
  19. data/docs/Global.html +2791 -0
  20. data/docs/Gravitier.html +179 -0
  21. data/docs/HudImage.html +979 -0
  22. data/docs/HudText.html +1151 -0
  23. data/docs/Image.html +506 -0
  24. data/docs/Jumper.html +189 -0
  25. data/docs/Mouse.html +226 -0
  26. data/docs/MoveByCursor.html +374 -0
  27. data/docs/MoveByDirection.html +179 -0
  28. data/docs/Mover.html +305 -0
  29. data/docs/Music.html +524 -0
  30. data/docs/Shape.html +1057 -0
  31. data/docs/Sound.html +374 -0
  32. data/docs/Tilemap.html +491 -0
  33. data/docs/Tween.html +186 -0
  34. data/docs/UserInputs.html +879 -0
  35. data/docs/Utils.html +345 -0
  36. data/docs/_index.html +346 -0
  37. data/docs/class_list.html +51 -0
  38. data/docs/css/common.css +1 -0
  39. data/docs/css/full_list.css +58 -0
  40. data/docs/css/style.css +497 -0
  41. data/docs/file.CHANGELOG.html +121 -0
  42. data/docs/file.README.html +599 -0
  43. data/docs/file_list.html +61 -0
  44. data/docs/frames.html +17 -0
  45. data/docs/index.html +599 -0
  46. data/docs/js/app.js +314 -0
  47. data/docs/js/full_list.js +216 -0
  48. data/docs/js/jquery.js +4 -0
  49. data/docs/method_list.html +1931 -0
  50. data/docs/top-level-namespace.html +978 -0
  51. data/lib/fantasy/actor.rb +455 -123
  52. data/lib/fantasy/background.rb +109 -13
  53. data/lib/fantasy/base.rb +113 -1
  54. data/lib/fantasy/camera.rb +95 -11
  55. data/lib/fantasy/clock.rb +4 -2
  56. data/lib/fantasy/color.rb +158 -153
  57. data/lib/fantasy/coordinates.rb +5 -9
  58. data/lib/fantasy/cursor.rb +2 -1
  59. data/lib/fantasy/disk.rb +12 -8
  60. data/lib/fantasy/draggable.rb +22 -1
  61. data/lib/fantasy/global.rb +59 -31
  62. data/lib/fantasy/hud_image.rb +5 -3
  63. data/lib/fantasy/hud_text.rb +6 -2
  64. data/lib/fantasy/image.rb +12 -4
  65. data/lib/fantasy/includes/gravitier.rb +2 -0
  66. data/lib/fantasy/includes/jumper.rb +3 -2
  67. data/lib/fantasy/includes/log.rb +12 -0
  68. data/lib/fantasy/includes/move_by_cursors.rb +24 -15
  69. data/lib/fantasy/includes/move_by_direction.rb +2 -0
  70. data/lib/fantasy/includes/mover.rb +3 -0
  71. data/lib/fantasy/includes/user_inputs.rb +6 -0
  72. data/lib/fantasy/loop.rb +41 -44
  73. data/lib/fantasy/mouse.rb +2 -0
  74. data/lib/fantasy/music.rb +4 -2
  75. data/lib/fantasy/shape.rb +11 -2
  76. data/lib/fantasy/sound.rb +3 -1
  77. data/lib/fantasy/tilemap.rb +19 -13
  78. data/lib/fantasy/tween.rb +3 -1
  79. data/lib/fantasy/utils.rb +7 -13
  80. data/lib/fantasy/version.rb +1 -1
  81. data/lib/fantasy.rb +2 -0
  82. metadata +90 -4
  83. data/fantasy.gemspec +0 -40
  84. data/fonts/VT323-Regular.ttf +0 -0
data/lib/fantasy/actor.rb CHANGED
@@ -1,3 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Represents one active entity in the game.
4
+ # An actor can be the Player sprite. Or one of the enemies. Or the bullet.
5
+ # An actor can also be a tree, or a wall
6
+ #
7
+ # @example Actor can be used directly
8
+ # player = Actor.new("warrior") # ./images/warrior.png
9
+ # player.position = Coordinates.new(100, 100)
10
+ # player.solid = true
11
+ # player.speed = 200
12
+ # player.layer = 1
13
+ # player.move_with_cursors
14
+ # player.collision_with = ["enemy", "bullets"] # default "all"
15
+ #
16
+ # player.on_collision do |other|
17
+ # if other.name == "enemy"
18
+ # player.destroy
19
+ # end
20
+ # end
21
+ #
22
+ # player.on_after_move do
23
+ # if player.position.x > Global.screen_width
24
+ # player.position.x = Global.screen_width
25
+ # end
26
+ #
27
+ # if player.position.x < 0
28
+ # player.position.x = 0
29
+ # end
30
+ # end
31
+ #
32
+ # @example Or in a subclass:
33
+ # class Player < Actor
34
+ # def initialize
35
+ # super("warrior") # ./images/warrior.png
36
+ # @position = Coordinates.new(100, 100)
37
+ # @solid = true
38
+ # @speed = 200
39
+ # @layer = 1
40
+ # @direction = Coordinates.zero
41
+ # @collision_with = ["enemy", "bullets"] # default "all"
42
+ # move_with_cursors
43
+ # end
44
+ #
45
+ # on_collision do |other|
46
+ # if other.name == "enemy"
47
+ # destroy
48
+ # end
49
+ # end
50
+ #
51
+ # on_after_move do
52
+ # if @position.x > Global.screen_width
53
+ # @position.x = Global.screen_width
54
+ # end
55
+ #
56
+ # if @position.x < 0
57
+ # @position.x = 0
58
+ # end
59
+ # end
60
+ # end
1
61
  class Actor
2
62
  include MoveByCursor
3
63
  include MoveByDirection
@@ -6,10 +66,173 @@ class Actor
6
66
  include Jumper
7
67
  include UserInputs
8
68
 
9
- attr_reader :image, :moving_with_cursors
10
- attr_accessor :image_name, :position, :direction, :speed, :jump_force, :gravity, :solid, :scale, :name, :layer
69
+ # Coordinates object where x and y represent the position of the Actor in the World (no necessarily in the Screen).
70
+ #
71
+ # Default `Coordinates.zero`.
72
+ #
73
+ # @return [Coordinates] the actual position
74
+ #
75
+ # @example Setting position
76
+ # actor = Actor.new("image")
77
+ # actor.position = Coordinates.new(10, 20)
78
+ # actor.position.x # => 10
79
+ #
80
+ # @example Modify position
81
+ # actor.position.x += 1
82
+ # actor.position.x # => 11
83
+ attr_accessor :position
84
+
85
+ # Controls the direction in which this Actor will move in the next frame.
86
+ #
87
+ # Default `Coordinates.zero`.
88
+ #
89
+ # @return [Coordinates] the actual direction
90
+ #
91
+ # @note The the pixels per second is represented by the `@speed` attribute
92
+ #
93
+ # @example Set direction
94
+ # actor = Actor.new("image")
95
+ # actor.direction = Coordinates.right
96
+ attr_accessor :direction
97
+
98
+ # Controls the pixels per second which this Actor will move in the next frame.
99
+ #
100
+ # Default `0`.
101
+ #
102
+ # @return [Float] the actual speed
103
+ #
104
+ # @note The the direction is represented by the `@direction` attribute
105
+ #
106
+ # @example Set speed
107
+ # actor = Actor.new("image")
108
+ # actor.speed = 10
109
+ attr_accessor :speed
110
+
111
+ # Controls impulse this Actor will receive when the (see #jump) is triggered.
112
+ #
113
+ # Default `0`.
114
+ #
115
+ # @return [Float] the actual jump_force
116
+ #
117
+ # @note The the direction will be `Coordinates.up`
118
+ #
119
+ # @example Set jump_force
120
+ # actor = Actor.new("image")
121
+ # actor.jump_force = 100
122
+ attr_accessor :jump_force
123
+
124
+ # Controls constant force this Actor will receive on each frame.
125
+ #
126
+ # Default `0`.
127
+ #
128
+ # @return [Float] the actual gravity
129
+ #
130
+ # @note The the direction will be `Coordinates.down`
131
+ #
132
+ # @example Set gravity
133
+ # actor = Actor.new("image")
134
+ # actor.gravity = 10
135
+ attr_accessor :gravity
136
+
137
+ # When `true` the Actor will cause and respond to collisions.
138
+ #
139
+ # When `false` the Actor won't cause neither respond to collisions.
140
+ #
141
+ # Default `true`.
142
+ #
143
+ # @return [Boolean] the actual solid value
144
+ #
145
+ # @param solid [true, false] only true or false
146
+ #
147
+ # @example Set solid
148
+ # actor = Actor.new("image")
149
+ # actor.solid = false
150
+ attr_accessor :solid
151
+
152
+ # The value to scale the image of the Actor when drawn.
153
+ # If the value is `2` the image will rendered at double of size.
154
+ # If the value is `0.5` the image will rendered at half of size.
155
+ #
156
+ # @note this value affects the attributes `width` and `height`
157
+ #
158
+ # Default `1`.
159
+ #
160
+ # @return [Float] the actual scale
161
+ #
162
+ # @example Set scale
163
+ # actor = Actor.new("image")
164
+ # actor.scale = 6
165
+ attr_accessor :scale
166
+
167
+ # The value to internal name of this Actor.
168
+ #
169
+ # It is useful for collision management for example.
170
+ #
171
+ # Default (same as `image_name`).
172
+ #
173
+ # @return [String] the actual name
174
+ #
175
+ # @example Set name
176
+ # actor = Actor.new("image")
177
+ # actor.name = "spaceship"
178
+ #
179
+ # @example Used in collision trigger
180
+ # actor = Actor.new("image")
181
+ # actor.on_collision do |other|
182
+ # if other.name == "enemy"
183
+ # # damage
184
+ # end
185
+ # end
186
+ #
187
+ # @example Used with `collision_with`
188
+ # actor = Actor.new("image")
189
+ # actor.collision_with = ["enemy", "bullet"]
190
+ attr_accessor :name
191
+
192
+ # In which layer the image of the Actor is rendered.
193
+ # Smaller numbers are rendered behind higher numbers.
194
+ #
195
+ # Default `0`.
196
+ #
197
+ # @return [Integer] the actual layer
198
+ #
199
+ # @example Set layer
200
+ # actor = Actor.new("image")
201
+ # actor.layer = -1
202
+ attr_accessor :layer
203
+
204
+ # Array of strings (or "all").
205
+ # Represents with which other solid Actors this Actor collide.
206
+ #
207
+ # Default `"all"`.
208
+ #
209
+ # @return [Array, String] the actual list of names of Actors to collide with
210
+ #
211
+ # @example Set with which other Actors this Actor is colliding:
212
+ # actor = Actor.new("image")
213
+ # actor.collision_with = ["enemy", "bullet"]
214
+ #
215
+ # @example Set this Actors collides with all other Actors
216
+ # actor = Actor.new("image")
217
+ # actor.collision_with = "all"
11
218
  attr_accessor :collision_with
12
219
 
220
+ # Generate an Actor with all the default attribute values
221
+ # @example Generate an Actor
222
+ # actor = Actor.new("image")
223
+ # actor.position # => Coordinates.zero
224
+ # actor.direction # => Coordinates.zero
225
+ # actor.speed # => 0
226
+ # actor.scale # => 1
227
+ # actor.solid # => true
228
+ # actor.draggable_on_debug # => true
229
+ # actor.layer # => 0
230
+ # actor.gravity # => 0
231
+ # actor.jump_force # => 0
232
+ # actor.collision_with # => "all"
233
+ #
234
+ # @param image_name [string] the name of the image file from `./images/*`
235
+ # @return [Actor] the Actor
13
236
  def initialize(image_name)
14
237
  @image_name = image_name
15
238
  @image = Image.new(image_name)
@@ -19,8 +242,7 @@ class Actor
19
242
  @speed = 0
20
243
  @scale = 1
21
244
 
22
- @moving_with_cursors = false
23
- @solid = false
245
+ @solid = true
24
246
  @draggable_on_debug = true
25
247
  @dragging = false
26
248
  @dragging_offset = nil
@@ -40,59 +262,53 @@ class Actor
40
262
  Global.actors << self
41
263
  end
42
264
 
265
+
266
+ # Set a new image to the Actor.
267
+ # @param image_name [String] The image file from `./images/*`
268
+ #
269
+ # @example Set a new image
270
+ # actor = Actor.new("player_walk")
271
+ # actor.image_name = "player_jump"
43
272
  def image=(image_name)
44
273
  @image = Image.new(image_name)
45
274
  end
46
275
 
276
+ # @return [Fixnum] the Actor width in pixels
47
277
  def width
48
278
  @image.width * @scale
49
279
  end
50
280
 
281
+ # @return [Fixnum] the Actor height in pixels
51
282
  def height
52
283
  @image.height * @scale
53
284
  end
54
285
 
286
+ # @return [Boolean] the value of `@solid`
287
+ def solid?
288
+ @solid
289
+ end
290
+
291
+ # @param value [Coordinates] set a new direction to the Actor.
292
+ # The vector is normalized
293
+ #
294
+ # @example Set a new direction
295
+ # actor = Actor.new("player_walking")
296
+ # actor.direction = Coordinates.up
55
297
  def direction=(value)
56
298
  @direction = value
57
299
  @direction = @direction.normalize unless @direction.zero?
58
300
  end
59
301
 
302
+ # @!visibility private
60
303
  def draw
61
304
  @image.draw(x: position_in_camera.x, y: position_in_camera.y, scale: @scale)
62
305
 
63
306
  draw_debug if Global.debug
64
307
  end
65
308
 
66
- def draw_debug
67
- Utils.draw_frame(position_in_camera.x, position_in_camera.y, width, height, 1, Gosu::Color::RED) if solid
68
- Global.pixel_fonts["medium"].draw_text("#{@position.x.floor},#{@position.y.floor}", position_in_camera.x, position_in_camera.y - 20, 1)
69
- end
70
-
71
- def position_in_camera
72
- @position - Global.camera.position
73
- end
74
-
75
- # TODO: make this work optimized
76
- # def position_top_left
77
- # @position - position_delta
78
- # end
79
-
80
- # def position_delta
81
- # case @alignment
82
- # when "top-left"
83
- # Coordinates.zero
84
- # when "center"
85
- # Coordinates.new(width/2, height/2)
86
- # else
87
- # raise "Actor.alignment value not valid '#{@alignment}'. Valid values: 'top-left, center'"
88
- # end
89
- # end
90
-
91
- # TODO: I made more of this code while I was with Covid
92
- # It looks horrible and it is crap
93
- # I'll improve it some day :)
309
+ # @!visibility private
94
310
  def move
95
- mouse_position = Global.mouse_position + Global.camera.position
311
+ mouse_position = Global.mouse_position + Camera.main.position
96
312
 
97
313
  if @draggable_on_debug && Global.debug && !@dragging && Gosu.button_down?(Gosu::MS_LEFT) && Utils.collision_at?(self, mouse_position.x, mouse_position.y)
98
314
  @dragging = true
@@ -126,28 +342,14 @@ class Actor
126
342
  manage_collisions(last_position)
127
343
  end
128
344
 
129
- # # Jump moving
130
- # unless @jump_force.zero?
131
- # last_position = @position
132
- # add_force_by_jump
133
- # apply_forces(max_speed: @jump_speed || @jump_force)
134
-
135
- # # Check collision after jump moving
136
- # if @solid && @position != last_position
137
- # if manage_collisions(last_position)
138
- # @jumping = false
139
- # end
140
- # end
141
- # end
142
-
143
345
  # Gravity force
144
- if !@gravity.zero?
346
+ unless @gravity.zero?
145
347
  add_force_by_gravity
146
348
  end
147
349
 
148
350
  # Apply forces
149
351
  last_position = @position
150
- apply_forces(max_speed: @speed)
352
+ apply_forces
151
353
 
152
354
  # Check collision after gravity moving
153
355
  if @solid && @position != last_position
@@ -159,145 +361,275 @@ class Actor
159
361
  on_after_move_do
160
362
  end
161
363
 
162
- def manage_collisions(last_position)
163
- @velocity ||= Coordinates.zero # In case it is not initialized yet
164
-
165
- collisions.each do |other|
166
- on_collision_do(other)
167
- other.on_collision_do(self)
168
-
169
- if other.position.y > (last_position.y + height)
170
- on_floor_do unless @on_floor
364
+ # Destroy this Actor is not longer, moved, or rendered, or cause any collision.
365
+ #
366
+ # @example Destroy an Actor
367
+ # actor = Actor.new("image")
368
+ # actor.destroy
369
+ def destroy
370
+ on_destroy_do
371
+ Global.actors.delete(self)
372
+ end
171
373
 
172
- @on_floor = true
173
- @jumping = false
174
- @velocity.y = 0
175
- end
374
+ # rubocop:disable Metrics/MethodLength
375
+ # rubocop:disable Metrics/AbcSize
176
376
 
177
- if other.position.y + other.height < last_position.y
178
- @velocity.y = 0
179
- end
180
- end
377
+ # @!visibility private
378
+ def clone
379
+ actor = self.class.new(@image_name)
380
+ actor.image_name = @image_name
381
+ actor.name = @name
382
+ actor.position = @position.clone
383
+ actor.direction = @direction.clone
181
384
 
182
- # # Reset position pixel by pixel
183
- # was_collision = false
184
- # while collisions.any? && @position != last_position
185
- # was_collision = true
186
- # last_position_direction = last_position - @position
187
- # @position += last_position_direction.normalize
188
- # end
189
- # was_collision
385
+ actor.speed = @speed
386
+ actor.scale = @scale
387
+ actor.solid = @solid
388
+ actor.layer = @layer
389
+ actor.gravity = @gravity
390
+ actor.jump_force = @jump_force
391
+ actor.collision_with = @collision_with
190
392
 
191
- if collisions.any? # we don't cache collisions because position may be changed on collision callback
192
- @position = last_position
393
+ actor.on_after_move_callback = @on_after_move_callback
394
+ actor.on_collision_callback = @on_collision_callback
395
+ actor.on_destroy_callback = @on_destroy_callback
396
+ actor.on_jumping_callback = @on_jumping_callback
397
+ actor.on_floor_callback = @on_floor_callback
193
398
 
194
- return true
195
- end
399
+ actor.on_cursor_down_callback = @on_cursor_down_callback
400
+ actor.on_cursor_up_callback = @on_cursor_up_callback
401
+ actor.on_cursor_left_callback = @on_cursor_left_callback
402
+ actor.on_cursor_right_callback = @on_cursor_right_callback
403
+ actor.on_space_bar_callback = @on_space_bar_callback
404
+ actor.on_mouse_button_left_callback = @on_mouse_button_left_callback
196
405
 
197
- false
198
- end
406
+ actor.on_click_callback = @on_click_callback
199
407
 
200
- def solid?
201
- @solid
408
+ actor
202
409
  end
410
+ # rubocop:enable Metrics/AbcSize
411
+ # rubocop:enable Metrics/MethodLength
203
412
 
204
413
  # Set callbacks
414
+
415
+ # The block to be executed after each frame
416
+ #
417
+ # @example Limit Actor movement horizontally
418
+ # actor = Actor.new("image")
419
+ # actor.on_after_move do
420
+ # if actor.position.x > 100
421
+ # actor.position.x = 100
422
+ # end
423
+ # end
205
424
  def on_after_move(&block)
206
425
  @on_after_move_callback = block
207
426
  end
208
427
 
428
+ # The block to be executed when Actor collides with another Actor
429
+ #
430
+ # @example Collision detected with _"bullet"_
431
+ # actor = Actor.new("image")
432
+ # actor.on_collision do |other|
433
+ # if other.name == "bullet"
434
+ # actor.destroy
435
+ # end
436
+ # end
209
437
  def on_collision(&block)
210
438
  @on_collision_callback = block
211
439
  end
212
440
 
441
+ # The block to be executed before the Actor is destroyed
442
+ #
443
+ # @example Executes when destroyed
444
+ # actor = Actor.new("image")
445
+ # actor.on_destroy do
446
+ # Sound.play("explosion")
447
+ # end
213
448
  def on_destroy(&block)
214
449
  @on_destroy_callback = block
215
450
  end
216
451
 
452
+ # The block to be executed when the Actor starts jumping
453
+ #
454
+ # @example Change image when jumping
455
+ # actor = Actor.new("walk")
456
+ # actor.on_jumping do
457
+ # actor.image_name = "jump"
458
+ # end
217
459
  def on_jumping(&block)
218
460
  @on_jumping_callback = block
219
461
  end
220
462
 
463
+ # The block to be executed when the Actor touches floor
464
+ #
465
+ # @example Change image when jumping
466
+ # actor = Actor.new("walk")
467
+ # actor.on_floor do
468
+ # actor.image_name = "land"
469
+ # Clock.new { actor.image_name = "walk" }.run_on(seconds: 0.8)
470
+ # end
221
471
  def on_floor(&block)
222
472
  @on_floor_callback = block
223
473
  end
224
474
 
225
-
226
475
  # Execute callbacks
476
+
477
+ # This method is triggered after each frame
478
+ #
479
+ # @example Limit Actor movement horizontally
480
+ # class Player < Actor
481
+ # def on_after_move_do
482
+ # if @position.x > 100
483
+ # @position.x = 100
484
+ # end
485
+ # end
486
+ # end
227
487
  def on_after_move_do
228
488
  instance_exec(&@on_after_move_callback) unless @on_after_move_callback.nil?
229
489
  end
230
490
 
491
+ # This method is triggered when Actor collides with another Actor
492
+ #
493
+ # @example Limit Actor movement horizontally
494
+ # class Player < Actor
495
+ # def on_collision_do(other)
496
+ # if other.name == "bullet"
497
+ # destroy
498
+ # end
499
+ # end
500
+ # end
231
501
  def on_collision_do(other)
232
502
  instance_exec(other, &@on_collision_callback) unless @on_collision_callback.nil?
233
503
  end
234
504
 
505
+ # This method is triggered before the Actor is destroyed
506
+ #
507
+ # @example Executes when destroyed
508
+ # class Player < Actor
509
+ # def on_destroy_do
510
+ # Sound.play("explosion")
511
+ # end
512
+ # end
235
513
  def on_destroy_do
236
514
  instance_exec(&@on_destroy_callback) unless @on_destroy_callback.nil?
237
515
  end
238
516
 
517
+ # This method is triggered when the Actor starts jumping
518
+ #
519
+ # @example Change image when jumping
520
+ # class Player < Actor
521
+ # def on_jumping_do
522
+ # self.image_name = "jump"
523
+ # end
524
+ # end
239
525
  def on_jumping_do
240
526
  instance_exec(&@on_jumping_callback) unless @on_jumping_callback.nil?
241
527
  end
242
528
 
529
+ # This method is triggered when the Actor touches floor
530
+ #
531
+ # @example Change image when jumping
532
+ # class Player < Actor
533
+ # def.on_floor_do
534
+ # self.image_name = "land"
535
+ # Clock.new { self.image_name = "walk" }.run_on(seconds: 0.8)
536
+ # end
537
+ # end
243
538
  def on_floor_do
244
539
  instance_exec(&@on_floor_callback) unless @on_floor_callback.nil?
245
540
  end
246
541
 
247
542
 
248
- def collisions
249
- Global.actors.reject { |e| e == self }.select { |e| e.solid? }.select do |other|
250
- if(
251
- (@collision_with == "all" || @collision_with.include?(other.name)) &&
252
- (other.collision_with == "all" || other.collision_with.include?(self.name))
253
- )
254
- Utils.collision? self, other
255
- end
256
- end
543
+ protected
544
+
545
+ attr_accessor :image_name
546
+
547
+ # TODO: make this work optimized
548
+ # def position_top_left
549
+ # @position - position_delta
550
+ # end
551
+
552
+ # def position_delta
553
+ # case @alignment
554
+ # when "top-left"
555
+ # Coordinates.zero
556
+ # when "center"
557
+ # Coordinates.new(width/2, height/2)
558
+ # else
559
+ # raise "Actor.alignment value not valid '#{@alignment}'. Valid values: 'top-left, center'"
560
+ # end
561
+ # end
562
+
563
+ def draw_debug
564
+ Shape.rectangle(
565
+ position: position_in_camera,
566
+ width: width,
567
+ height: height,
568
+ color: Color.palette.transparent,
569
+ stroke_color: Color.palette.red,
570
+ stroke: 1
571
+ ) if solid
572
+
573
+ Global.pixel_fonts["medium"].draw_text("#{@position.x.floor},#{@position.y.floor}", position_in_camera.x, position_in_camera.y - 20, 1)
257
574
  end
258
575
 
259
- def destroy
260
- on_destroy_do
261
- Global.actors.delete(self)
576
+ def position_in_camera
577
+ @position - Camera.main.position
262
578
  end
263
579
 
264
- def clone
265
- actor = self.class.new(@image_name)
266
- actor.image_name = @image_name
267
- actor.name = @name
268
- actor.position = @position.clone
269
- actor.direction = @direction.clone
580
+ def manage_collisions(last_position)
581
+ @velocity ||= Coordinates.zero # In case it is not initialized yet
270
582
 
271
- actor.speed = @speed
272
- actor.scale = @scale
273
- actor.moving_with_cursors if @moving_with_cursors
274
- actor.solid = @solid
275
- actor.layer = @layer
276
- actor.gravity = @gravity
277
- actor.jump_force = @jump_force
278
- actor.collision_with = @collision_with
583
+ collisions.each do |other|
584
+ on_collision_do(other)
585
+ other.on_collision_do(self)
279
586
 
280
- actor.on_after_move_callback = @on_after_move_callback
281
- actor.on_collision_callback = @on_collision_callback
282
- actor.on_destroy_callback = @on_destroy_callback
283
- actor.on_jumping_callback = @on_jumping_callback
284
- actor.on_floor_callback = @on_floor_callback
587
+ if other.position.y >= (last_position.y + height)
588
+ on_floor_do unless @on_floor
285
589
 
286
- actor.on_cursor_down_callback = @on_cursor_down_callback
287
- actor.on_cursor_up_callback = @on_cursor_up_callback
288
- actor.on_cursor_left_callback = @on_cursor_left_callback
289
- actor.on_cursor_right_callback = @on_cursor_right_callback
290
- actor.on_space_bar_callback = @on_space_bar_callback
291
- actor.on_mouse_button_left_callback = @on_mouse_button_left_callback
590
+ @on_floor = true
591
+ @jumping = false
592
+ @velocity.y = 0
593
+ end
292
594
 
293
- actor.on_click_callback = @on_click_callback
595
+ if other.position.y + other.height <= last_position.y
596
+ @velocity.y = 0
597
+ end
598
+ end
294
599
 
295
- actor
600
+ # # Reset position pixel by pixel
601
+ # was_collision = false
602
+ # while collisions.any? && @position != last_position
603
+ # was_collision = true
604
+ # last_position_direction = last_position - @position
605
+ # @position += last_position_direction.normalize
606
+ # end
607
+ # was_collision
608
+
609
+ if collisions.any? # we don't cache collisions because position may be changed on collision callback
610
+ @position = last_position
611
+
612
+ return true
613
+ end
614
+
615
+ false
616
+ end
617
+
618
+ # rubocop:disable Style/Next
619
+ def collisions
620
+ Global.actors.reject { |e| e == self }.select(&:solid?).select do |other|
621
+ if(
622
+ (@collision_with == "all" || @collision_with.include?(other.name)) &&
623
+ (other.collision_with == "all" || other.collision_with.include?(name))
624
+ )
625
+ Utils.collision? self, other
626
+ end
627
+ end
296
628
  end
629
+ # rubocop:enable Style/Next
297
630
 
298
631
  protected
299
632
 
300
633
  attr_accessor :on_after_move_callback, :on_collision_callback, :on_destroy_callback, :on_jumping_callback, :on_floor_callback
301
- attr_accessor :on_cursor_down_callback, :on_cursor_up_callback, :on_cursor_left_callback, :on_cursor_right_callback, :on_space_bar_callback, :on_mouse_button_left_callback
302
634
  attr_accessor :on_click_callback
303
635
  end