chichilku3 14.0.3 → 15.0.1

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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/bin/chichilku3 +1 -0
  3. data/bin/chichilku3-server +1 -0
  4. data/lib/client/chichilku3.rb +3 -1
  5. data/lib/client/client.rb +112 -59
  6. data/lib/client/client_cfg.rb +2 -1
  7. data/lib/client/gui.rb +219 -197
  8. data/lib/client/img/stick128/arm64/arm0.png +0 -0
  9. data/lib/client/img/stick128/arm64/arm1.png +0 -0
  10. data/lib/client/img/stick128/arm64/arm2.png +0 -0
  11. data/lib/client/img/stick128/arm64/arm3.png +0 -0
  12. data/lib/client/img/stick128/noarms/stick0.png +0 -0
  13. data/lib/client/img/stick128/noarms/stick1.png +0 -0
  14. data/lib/client/img/stick128/noarms/stick2.png +0 -0
  15. data/lib/client/img/stick128/noarms/stick3.png +0 -0
  16. data/lib/client/img/stick128/noarms/stick4.png +0 -0
  17. data/lib/client/img/stick128/noarms/stick5.png +0 -0
  18. data/lib/client/keys.rb +29 -0
  19. data/lib/client/particles.rb +54 -0
  20. data/lib/client/scoreboard.rb +29 -27
  21. data/lib/{client → external/gosu}/text.rb +29 -38
  22. data/lib/external/rubyzip/recursive.rb +58 -0
  23. data/lib/server/chichilku3_server.rb +169 -64
  24. data/lib/server/gamelogic.rb +107 -51
  25. data/lib/server/server_cfg.rb +2 -0
  26. data/lib/share/config.rb +21 -7
  27. data/lib/share/console.rb +22 -5
  28. data/lib/share/game_map.rb +279 -0
  29. data/lib/share/math.rb +14 -0
  30. data/lib/share/network.rb +47 -42
  31. data/lib/share/player.rb +168 -105
  32. data/lib/share/projectile.rb +97 -98
  33. data/lib/share/string.rb +24 -0
  34. data/server.json +3 -2
  35. metadata +49 -23
  36. data/lib/client/img/battle1024x576.png +0 -0
  37. data/lib/client/img/grass1024x512.png +0 -0
  38. data/lib/client/img/stick128/stick_noarms.png +0 -0
  39. data/lib/client/test.rb +0 -39
data/lib/share/player.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Player used by Client and Server
2
4
  require_relative 'console'
3
5
  require_relative 'network'
@@ -6,26 +8,29 @@ require_relative 'projectile'
6
8
  SPAWN_X = 512
7
9
  SPAWN_Y = 100
8
10
 
11
+ # Player objects represent stick figures
9
12
  class Player
10
- attr_accessor :x, :y, :dy, :dx, :id, :name, :score, :state, :dead, :dead_ticks, :was_crouching
11
- attr_accessor :aimX, :aimY, :projectile, :fire_ticks
13
+ attr_accessor :x, :y, :dy, :dx, :id, :name, :score, :state, :dead, :dead_ticks,
14
+ :was_crouching, :wants_crouch, :aim_x, :aim_y,
15
+ :projectile, :fire_ticks, :map_download
12
16
  attr_reader :collide, :collide_str, :img_index, :version, :w, :h
13
17
 
14
18
  def initialize(id, score, x = nil, y = nil, name = 'def', version = nil, ip = nil)
15
19
  @id = id
16
20
  @x = x.nil? ? SPAWN_X : x
17
21
  @y = y.nil? ? SPAWN_Y : y
18
- @w = TILE_SIZE / 2
19
- @h = TILE_SIZE
20
- @aimX = 0
21
- @aimY = 0
22
+ @w = PLAYER_SIZE / 2
23
+ @h = PLAYER_SIZE
24
+ @aim_x = 0
25
+ @aim_y = 0
22
26
  @projectile = Projectile.new
23
27
  @dx = 0
24
28
  @dy = 0
25
29
  @health = 3
26
- @collide = {up: false, down: false, right: false, left: false}
27
- @state = {bleeding: false, crouching: false, fire: 0}
30
+ @collide = { up: false, down: false, right: false, left: false }
31
+ @state = { bleeding: false, crouching: false, fire: 0 }
28
32
  @was_crouching = false
33
+ @wants_crouch = false
29
34
  @name = name
30
35
  @score = score
31
36
  @dead = false # only used by server for now
@@ -41,6 +46,7 @@ class Player
41
46
  @not_changed_y = 0
42
47
 
43
48
  # used by server
49
+ @map_download = -2
44
50
  @version = version
45
51
  @ip = ip
46
52
  end
@@ -56,9 +62,8 @@ class Player
56
62
 
57
63
  def update_img
58
64
  return if @tick % 5 != 0
59
- if @x != @last_x
60
- new_x = true
61
- end
65
+
66
+ new_x = true if @x != @last_x
62
67
  if @y != @last_y
63
68
  new_y = true
64
69
  @not_changed_y = 0
@@ -90,13 +95,13 @@ class Player
90
95
  players.find { |player| id == player.id }
91
96
  end
92
97
 
93
- def self.update_player(players, id, x, y, score, aimX, aimY)
98
+ def self.update_player(players, id, x, y, score, aim_x, aim_y)
94
99
  player = get_player_by_id(players, id)
95
100
  player.x = x
96
101
  player.y = y
97
102
  player.score = score
98
- player.aimX = aimX
99
- player.aimY = aimY
103
+ player.aim_x = aim_x
104
+ player.aim_y = aim_y
100
105
  player
101
106
  end
102
107
 
@@ -104,42 +109,53 @@ class Player
104
109
  # server only #
105
110
  ###############
106
111
 
107
- def set_name(name)
108
- @name = name
109
- end
110
-
111
112
  def tick
112
113
  move_x(@dx)
113
114
  move_y(@dy)
114
115
  @dx = normalize_zero(@dx)
115
- @dy = normalize_zero(@dy)
116
- check_out_of_world
117
- if @bleed_ticks > 0
118
- @bleed_ticks -= 1
119
- self.state[:bleeding] = @bleed_ticks.zero? == false
120
- end
116
+ # @dy = normalize_zero(@dy)
117
+ check_out_of_game_map
118
+ return unless @bleed_ticks.positive?
119
+
120
+ @bleed_ticks -= 1
121
+ state[:bleeding] = @bleed_ticks.zero? == false
122
+ end
123
+
124
+ def crouch!
125
+ @state[:crouching] = true
126
+ @w = PLAYER_SIZE
127
+ @h = PLAYER_SIZE / 2
128
+ end
129
+
130
+ def stop_crouch!
131
+ @state[:crouching] = false
132
+ @w = PLAYER_SIZE / 2
133
+ @h = PLAYER_SIZE
134
+ end
135
+
136
+ def crouching?
137
+ @state[:crouching]
121
138
  end
122
139
 
123
140
  def check_player_collide(other)
124
141
  # $console.log "x: #{@x} y: #{@y} ox: #{other.x} oy: #{other.y}"
125
142
  # x crash is more rare so make it the outer condition
126
- if other.x + other.w > @x && other.x < @x + @w
127
- if other.y + other.h > @y && other.y < @y + @h
128
- # $console.log "collide!"
129
- return @x < other.x ? -7 : 7
130
- end
143
+ if other.x + other.w > @x && other.x < @x + @w && (other.y + other.h > @y && other.y < @y + @h)
144
+ # $console.log "collide!"
145
+ return @x < other.x ? -7 : 7
131
146
  end
132
- return 0
147
+
148
+ 0
133
149
  end
134
150
 
135
151
  def damage(attacker)
136
152
  @bleed_ticks = 3
137
153
  @health -= 1
138
- $console.log "player='#{attacker.name}' damaged player='#{@name}'"
154
+ $console.log "'#{attacker.id}:#{attacker.name}' damaged '#{@id}:#{@name}'"
139
155
  die(attacker) if @health <= 0
140
156
  end
141
157
 
142
- # def check_out_of_world #die
158
+ # def check_out_of_game_map #die
143
159
  # # y
144
160
  # if @y < 0
145
161
  # die
@@ -153,15 +169,16 @@ class Player
153
169
  # die
154
170
  # end
155
171
  # end
156
- def check_out_of_world # swap size
172
+ # swap size
173
+ def check_out_of_game_map
157
174
  # y
158
- if @y < 0
175
+ if @y.negative?
159
176
  die
160
- elsif @y > WINDOW_SIZE_Y
177
+ elsif @y >= WINDOW_SIZE_Y - TILE_SIZE
161
178
  die
162
179
  end
163
180
  # x ( comment me out to add the glitch feature agian )
164
- if @x < 0
181
+ if @x.negative?
165
182
  @x = WINDOW_SIZE_X - @w - 2
166
183
  elsif @x > WINDOW_SIZE_X - @w - 1
167
184
  @x = 0
@@ -170,32 +187,76 @@ class Player
170
187
 
171
188
  def die(killer = nil)
172
189
  if killer.nil?
173
- $console.log("[death] id=#{@id} name='#{@name}'")
190
+ $console.log("player ID=#{@id} name='#{@name}' died")
174
191
  else
175
- if killer.id == self.id
176
- killer.score = (killer.score - 1).clamp(0, NET_MAX_INT)
192
+ if killer.id == id
193
+ killer.score -= 1
177
194
  else
178
195
  killer.score += 1
179
196
  end
180
- $console.log("[kill] id=#{@id} name='#{@name}' killer='#{killer.name}'")
197
+ killer.score = killer.score.clamp(0, NET_MAX_INT)
198
+ $console.log("player '#{@id}:#{@name}' was killed by '#{killer.id}:#{killer.name}'")
181
199
  end
182
200
  @x = SPAWN_X
183
201
  @y = SPAWN_Y
184
202
  @health = 3
185
203
  end
186
204
 
187
- #TODO: check for collision before update
205
+ # TODO: check for collision before update
188
206
  # if move_left or move_right set u on a collided field
189
207
  # dont update the position or slow down speed
190
208
  # idk make sure to not get stuck in walls
191
- def move_left
209
+
210
+ def check_move_left(game_map)
211
+ # left bottom
212
+ col = game_map.collision?(@x / TILE_SIZE, (@y + @h - 1) / TILE_SIZE)
213
+ if col
214
+ @x = (col[:x] + 1) * TILE_SIZE
215
+ @x += 1
216
+ do_collide(:left, true)
217
+ end
218
+
219
+ # left top
220
+ col = game_map.collision?(@x / TILE_SIZE, (@y + 1) / TILE_SIZE)
221
+ if col
222
+ @x = (col[:x] + 1) * TILE_SIZE
223
+ @x += 1
224
+ do_collide(:left, true)
225
+ end
226
+ nil
227
+ end
228
+
229
+ def check_move_right(game_map)
230
+ # right bottom
231
+ col = game_map.collision?((@x + @w) / TILE_SIZE, (@y + @h - 1) / TILE_SIZE)
232
+ if col
233
+ @x = col[:x] * TILE_SIZE
234
+ @x -= crouching? ? PLAYER_SIZE : PLAYER_SIZE / 2
235
+ @x -= 1
236
+ do_collide(:right, true)
237
+ end
238
+
239
+ # right top
240
+ col = game_map.collision?((@x + @w) / TILE_SIZE, (@y + 1) / TILE_SIZE)
241
+ if col
242
+ @x = col[:x] * TILE_SIZE
243
+ @x -= crouching? ? PLAYER_SIZE : PLAYER_SIZE / 2
244
+ @x -= 1
245
+ do_collide(:right, true)
246
+ end
247
+ nil
248
+ end
249
+
250
+ def move_left(game_map)
192
251
  # @dx = -8
193
- @x -= state[:crouching] ? 4 : 8
252
+ @x -= crouching? ? 4 : 8
253
+ check_move_left(game_map)
194
254
  end
195
255
 
196
- def move_right
256
+ def move_right(game_map)
197
257
  # @dx = 8
198
- @x += state[:crouching] ? 4 : 8
258
+ @x += crouching? ? 4 : 8
259
+ check_move_right(game_map)
199
260
  end
200
261
 
201
262
  def apply_force(x, y)
@@ -204,13 +265,13 @@ class Player
204
265
  end
205
266
 
206
267
  def do_jump
207
- return if !@collide[:down]
268
+ return unless @collide[:down]
208
269
 
209
- if @dead
210
- @dy = -5
211
- else
212
- @dy = state[:crouching] ? -20 : -30
213
- end
270
+ @dy = if @dead
271
+ -5
272
+ else
273
+ crouching? ? -15 : -20
274
+ end
214
275
  end
215
276
 
216
277
  def add_score(score = 1)
@@ -225,20 +286,20 @@ class Player
225
286
  end
226
287
 
227
288
  def do_collide(position, value)
228
- if position == :right && @dx > 0
289
+ if position == :right && @dx.positive?
229
290
  @dx = 0
230
- elsif position == :left && @dx < 0
291
+ elsif position == :left && @dx.negative?
231
292
  @dx = 0
232
- elsif position == :down && @dy > 0
293
+ elsif position == :down && @dy.positive?
233
294
  @dy = 0
234
- elsif position == :up && @dy < 0
295
+ elsif position == :up && @dy.negative?
235
296
  @dy = 0
236
297
  end
237
298
  @collide[position] = value
238
299
  end
239
300
 
240
301
  def reset_collide
241
- @collide = {up: false, down: false, right: false, left: false}
302
+ @collide = { up: false, down: false, right: false, left: false }
242
303
  end
243
304
 
244
305
  ##
@@ -249,83 +310,84 @@ class Player
249
310
  # @return [String] partial network packet
250
311
 
251
312
  def to_n_pck
252
- name = @name.ljust(NAME_LEN, '_')
313
+ name = @name.ljust(NAME_LEN, ' ')
253
314
  "#{@id.to_s(16)}#{net_pack_int(@score)}#{name}"
254
315
  end
255
316
 
256
317
  def to_s
257
- pos="#{net_pack_bigint(@x, 2)}#{net_pack_bigint(@y, 2)}"
258
- proj=@projectile.r.to_i.to_s # hack nil to "0"
259
- fake_y = @projectile.y > 0 ? @projectile.y : 0
260
- proj+="#{net_pack_bigint(@projectile.x, 2)}#{net_pack_bigint(fake_y, 2)}"
261
- aim="#{net_pack_bigint(@aimX, 2)}#{net_pack_bigint(@aimY, 2)}"
262
- "#{@id.to_s(16)}#{net_pack_int(@score)}#{state_to_net()}#{proj}#{aim}#{pos}"
318
+ pos = "#{net_pack_bigint(@x, 2)}#{net_pack_bigint(@y, 2)}"
319
+ proj = @projectile.r.to_i.to_s # HACK: nil to "0"
320
+ fake_y = @projectile.y.positive? ? @projectile.y : 0
321
+ proj += "#{net_pack_bigint(@projectile.x, 2)}#{net_pack_bigint(fake_y, 2)}"
322
+ aim = "#{net_pack_bigint(@aim_x, 2)}#{net_pack_bigint(@aim_y, 2)}"
323
+ "#{@id.to_s(16)}#{net_pack_int(@score)}#{state_to_net}#{proj}#{aim}#{pos}"
263
324
  end
264
325
 
265
326
  def state_to_net
266
- @w = TILE_SIZE / 2
267
- @h = TILE_SIZE
268
- if @state[:bleeding] && @state[:crouching]
269
- "s"
327
+ @w = PLAYER_SIZE / 2
328
+ @h = PLAYER_SIZE
329
+ if @state[:bleeding] && crouching?
330
+ 's'
270
331
  elsif @state[:bleeding] && @state[:fire] == 1
271
- "x"
332
+ 'x'
272
333
  elsif @state[:bleeding] && @state[:fire] == 2
273
- "y"
334
+ 'y'
274
335
  elsif @state[:bleeding] && @state[:fire] == 3
275
- "z"
336
+ 'z'
276
337
  elsif @state[:bleeding]
277
- "b"
278
- elsif @state[:crouching]
279
- @w = TILE_SIZE
280
- @h = TILE_SIZE / 2
281
- "c"
338
+ 'b'
339
+ elsif crouching?
340
+ @w = PLAYER_SIZE
341
+ @h = PLAYER_SIZE / 2
342
+ 'c'
282
343
  elsif @state[:fire] == 1
283
- "1"
344
+ '1'
284
345
  elsif @state[:fire] == 2
285
- "2"
346
+ '2'
286
347
  elsif @state[:fire] == 3
287
- "3"
348
+ '3'
288
349
  else
289
- "0"
350
+ '0'
290
351
  end
291
352
  end
292
353
 
293
354
  def net_to_state(net)
294
- if net == "b"
295
- @state = {bleeding: true, crouching: false, fire: 0}
296
- elsif net == "c"
297
- @state = {bleeding: false, crouching: true, fire: 0}
298
- elsif net == "s"
299
- @state = {bleeding: true, crouching: true, fire: 0}
300
- elsif net == "x"
301
- @state = {bleeding: true, crouching: false, fire: 1}
302
- elsif net == "y"
303
- @state = {bleeding: true, crouching: false, fire: 2}
304
- elsif net == "z"
305
- @state = {bleeding: true, crouching: false, fire: 3}
306
- elsif net == "1"
307
- @state = {bleeding: false, crouching: false, fire: 1}
308
- elsif net == "2"
309
- @state = {bleeding: false, crouching: false, fire: 2}
310
- elsif net == "3"
311
- @state = {bleeding: false, crouching: false, fire: 3}
312
- else
313
- @state = {bleeding: false, crouching: false, fire: 0}
314
- end
355
+ @state = case net
356
+ when 'b'
357
+ { bleeding: true, crouching: false, fire: 0 }
358
+ when 'c'
359
+ { bleeding: false, crouching: true, fire: 0 }
360
+ when 's'
361
+ { bleeding: true, crouching: true, fire: 0 }
362
+ when 'x'
363
+ { bleeding: true, crouching: false, fire: 1 }
364
+ when 'y'
365
+ { bleeding: true, crouching: false, fire: 2 }
366
+ when 'z'
367
+ { bleeding: true, crouching: false, fire: 3 }
368
+ when '1'
369
+ { bleeding: false, crouching: false, fire: 1 }
370
+ when '2'
371
+ { bleeding: false, crouching: false, fire: 2 }
372
+ when '3'
373
+ { bleeding: false, crouching: false, fire: 3 }
374
+ else
375
+ { bleeding: false, crouching: false, fire: 0 }
376
+ end
315
377
  end
316
378
 
317
379
  private
318
380
 
319
381
  def move_x(x)
320
- return if x < 0 && @collide[:left]
321
- return if x > 0 && @collide[:right]
382
+ return if x.negative? && @collide[:left]
383
+ return if x.positive? && @collide[:right]
322
384
 
323
385
  @x += x
324
386
  end
325
387
 
326
388
  def move_y(y)
327
- return if y < 0 && @collide[:up]
328
- return if y > 0 && @collide[:down]
389
+ return if y.negative? && @collide[:up]
390
+ return if y.positive? && @collide[:down]
329
391
 
330
392
  @y += y
331
393
  end
@@ -335,7 +397,8 @@ class Player
335
397
  def normalize_zero(x)
336
398
  return x if x.zero?
337
399
 
338
- return x - 1 if x > 0
400
+ return x - 1 if x.positive?
401
+
339
402
  x + 1
340
403
  end
341
404
  end
@@ -1,115 +1,114 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'console'
2
4
 
5
+ # Projectile class handles arrow logic
3
6
  class Projectile
4
- attr_accessor :x, :y, :dx, :dy, :r, :w, :h, :owner_id
7
+ attr_accessor :x, :y, :dx, :dy, :r, :w, :h, :owner_id
5
8
 
6
- def initialize
7
- @x = 0
8
- @y = 0
9
- @dx = 0
10
- @dy = 0
11
- @w = 16
12
- @h = 16
13
- @owner = nil
14
- @left_owner = false
15
- @flying = false
16
- @tick = 0
17
- end
9
+ def initialize
10
+ @x = 0
11
+ @y = 0
12
+ @dx = 0
13
+ @dy = 0
14
+ @w = 16
15
+ @h = 16
16
+ @owner = nil
17
+ @left_owner = false
18
+ @flying = false
19
+ @tick = 0
20
+ end
18
21
 
19
- def fire(x, y, dx, dy, owner)
20
- return if @flying
22
+ def fire(x, y, dx, dy, owner)
23
+ return if @flying
21
24
 
22
- @x = x
23
- @y = y
24
- @dx = dx
25
- @dy = dy
26
- calc_rotation()
27
- @owner = owner
28
- @left_owner = false
29
- @flying = true
30
- $console.dbg "Projectile(x=#{x}, y=#{y}, dx=#{dx}, dy=#{dy})"
31
- end
25
+ @x = x
26
+ @y = y
27
+ @dx = dx
28
+ @dy = dy
29
+ calc_rotation
30
+ @owner = owner
31
+ @left_owner = false
32
+ @flying = true
33
+ $console.dbg "Projectile(x=#{x}, y=#{y}, dx=#{dx}, dy=#{dy})"
34
+ end
32
35
 
33
- def hit
34
- @flying = false
35
- @x = 0
36
- @y = 0
37
- end
36
+ def hit
37
+ @flying = false
38
+ @x = 0
39
+ @y = 0
40
+ end
38
41
 
39
- def tick(players)
40
- return unless @flying
42
+ def tick(players)
43
+ return unless @flying
41
44
 
42
- @tick += 1
43
- @x = @x + @dx
44
- @y = @y + @dy
45
- @dy += 1 if @tick % 3 == 0
46
- calc_rotation()
47
- check_hit(players)
48
- hit if @y > WINDOW_SIZE_Y
49
- hit if @x > WINDOW_SIZE_X
50
- hit if @x < 0
51
- end
45
+ @tick += 1
46
+ @x += @dx
47
+ @y += @dy
48
+ @dy += 1 if (@tick % 3).zero?
49
+ calc_rotation
50
+ check_hit(players)
51
+ hit if @y > WINDOW_SIZE_Y
52
+ hit if @x > WINDOW_SIZE_X
53
+ hit if @x.negative?
54
+ end
52
55
 
53
- def check_hit(players)
54
- owner_hit = false
55
- players.each do |player|
56
- if player.x + player.w > @x && player.x < @x + @w
57
- if player.y + player.h > @y && player.y < @y + @h
58
- if @owner.id == player.id
59
- owner_hit = true
60
- if @left_owner
61
- player.damage(@owner)
62
- hit()
63
- end
64
- else
65
- player.damage(@owner)
66
- hit()
67
- end
68
- end
69
- end
70
- end
71
- if owner_hit == false
72
- @left_owner = true
56
+ def check_hit(players)
57
+ owner_hit = false
58
+ players.each do |player|
59
+ next unless player.x + player.w > @x && player.x < @x + @w
60
+
61
+ if player.y + player.h > @y && player.y < @y + @h
62
+ if @owner.id == player.id
63
+ owner_hit = true
64
+ if @left_owner
65
+ player.damage(@owner)
66
+ hit
67
+ end
68
+ else
69
+ player.damage(@owner)
70
+ hit
73
71
  end
72
+ end
74
73
  end
74
+ @left_owner = true if owner_hit == false
75
+ end
75
76
 
76
- # NETWORK ROTATION
77
- # 0 -> 4 <-
77
+ # NETWORK ROTATION
78
+ # 0 -> 4 <-
78
79
 
79
- # ^
80
- # 1 \ 5 \
81
- # v
80
+ # ^
81
+ # 1 \ 5 \
82
+ # v
82
83
 
83
- # ^
84
- # 2 | 6 |
85
- # v
84
+ # ^
85
+ # 2 | 6 |
86
+ # v
86
87
 
87
- # ^
88
- # 3 / 7 /
89
- # v
90
- def calc_rotation
91
- if @dy > -3 && @dy < 3
92
- if @dx < 0
93
- @r = 4
94
- else
95
- @r = 0
96
- end
97
- elsif @dy < 0
98
- if @dx > -3 && @dx < 3
99
- @r = 6
100
- elsif @dx < 0
101
- @r = 5
102
- else
103
- @r = 7
104
- end
105
- else
106
- if @dx > -3 && @dx < 3
107
- @r = 2
108
- elsif @dx < 0
109
- @r = 3
110
- else
111
- @r = 1
112
- end
113
- end
114
- end
88
+ # ^
89
+ # 3 / 7 /
90
+ # v
91
+ def calc_rotation
92
+ @r = if @dy > -3 && @dy < 3
93
+ if @dx.negative?
94
+ 4
95
+ else
96
+ 0
97
+ end
98
+ elsif @dy.negative?
99
+ if @dx > -3 && @dx < 3
100
+ 6
101
+ elsif @dx.negative?
102
+ 5
103
+ else
104
+ 7
105
+ end
106
+ elsif @dx > -3 && @dx < 3
107
+ 2
108
+ elsif @dx.negative?
109
+ 3
110
+ else
111
+ 1
112
+ end
113
+ end
115
114
  end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ # String color
4
+ class String
5
+ def colorize(color_code)
6
+ "\e[#{color_code}m#{self}\e[0m"
7
+ end
8
+
9
+ def red
10
+ colorize(31)
11
+ end
12
+
13
+ def green
14
+ colorize(32)
15
+ end
16
+
17
+ def yellow
18
+ colorize(33)
19
+ end
20
+
21
+ def pink
22
+ colorize(35)
23
+ end
24
+ end