fantasy 0.1.13 → 0.1.17

Sign up to get free protection for your applications and to get access to all the features.
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