termfront 0.1.3 → 0.1.4

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.
@@ -3,6 +3,9 @@
3
3
  module Termfront
4
4
  module Network
5
5
  class WavesfightClient
6
+ ALLOWED_WEAPONS = %w[pistol ar shock_pistol shock_rifle].freeze
7
+ ALLOWED_ENEMY_TYPES = %w[crawler executor].freeze
8
+
6
9
  def initialize(stdout)
7
10
  @stdout = stdout
8
11
  @conn = Connection.new
@@ -14,13 +17,14 @@ module Termfront
14
17
  def run(mission_id:, difficulty:)
15
18
  @queue_mission_id = mission_id
16
19
  @queue_difficulty = difficulty
17
- addr = prompt_address
18
- return unless addr
19
20
 
20
- host, port = addr.include?(":") ? addr.split(":", 2).then { |h, p| [h, p.to_i] } : [addr, Config::PVP_PORT]
21
+ host, port = Config::PVP_DEFAULT_ADDRESS.split(":", 2).then { |h, p| [h, p.to_i] }
22
+ queue_msg = { t: "queue", mode: "wavesfight", mission_id: mission_id, difficulty: difficulty }
23
+ token = ENV["TERMFRONT_PVP_TOKEN"]
24
+ queue_msg[:token] = token if token && !token.empty?
21
25
  begin
22
- @conn.connect(host, port)
23
- @conn.send_msg({ t: "queue", mode: "wavesfight", mission_id: mission_id, difficulty: difficulty })
26
+ @conn.connect(host, port, ca_file: ENV["TERMFRONT_TLS_CA_FILE"])
27
+ @conn.send_msg(queue_msg)
24
28
  rescue StandardError => e
25
29
  show_error("Connection failed: #{e.message}")
26
30
  return
@@ -42,51 +46,6 @@ module Termfront
42
46
 
43
47
  private
44
48
 
45
- def prompt_address
46
- input = Config::PVP_DEFAULT_ADDRESS
47
-
48
- STDIN.raw do |stdin|
49
- loop do
50
- rows, cols = @stdout.winsize
51
- buf = TerminalOutput.begin_frame(home: true, clear: true)
52
- lines = Array.new(rows) { " " * cols }
53
-
54
- title = "Wavesfight Co-op - Enter Server Address"
55
- tc = [(cols - title.size) / 2 + 1, 1].max
56
- lines[rows / 2 - 3] = TerminalOutput.fit_ansi("#{" " * (tc - 1)}\e[1;96m#{title}\e[0m", cols)
57
-
58
- pc = [(cols - input.size - 3) / 2 + 1, 1].max
59
- lines[rows / 2 - 1] = TerminalOutput.fit_ansi("#{" " * (pc - 1)}\e[97m> #{input}\e[5m_\e[0m", cols)
60
-
61
- hint = "(Enter to continue, ESC to cancel)"
62
- hc = [(cols - hint.size) / 2 + 1, 1].max
63
- lines[rows / 2 + 1] = TerminalOutput.fit_ansi("#{" " * (hc - 1)}\e[90m#{hint}\e[0m", cols)
64
-
65
- lines.each_with_index do |line, index|
66
- buf << line
67
- buf << "\r\n" if index < rows - 1
68
- end
69
- buf << TerminalOutput.end_frame
70
- TerminalOutput.write_all(@stdout, buf)
71
-
72
- next unless IO.select([stdin], nil, nil, Config::FRAME_DT)
73
-
74
- begin
75
- data = stdin.read_nonblock(64)
76
- data.each_byte do |b|
77
- case b
78
- when 27 then return nil
79
- when 13, 10 then return input.empty? ? Config::PVP_DEFAULT_ADDRESS : input
80
- when 127, 8 then input = input[0...-1] unless input.empty?
81
- when 32..126 then input << b.chr
82
- end
83
- end
84
- rescue IO::WaitReadable
85
- end
86
- end
87
- end
88
- end
89
-
90
49
  def wait_for_start
91
50
  STDIN.raw do |stdin|
92
51
  loop do
@@ -148,7 +107,10 @@ module Termfront
148
107
  end
149
108
  @enemies = []
150
109
  @projectiles = []
110
+ @drops = []
111
+ @server_drops = []
151
112
  @match_end = nil
113
+ @regen_active = false
152
114
  end
153
115
 
154
116
  def run_game_loop
@@ -192,6 +154,7 @@ module Termfront
192
154
 
193
155
  def handle_player_actions(keys)
194
156
  @player.swap_weapon if keys.include?(:t)
157
+ send_pickup_request if keys.include?(:e)
195
158
 
196
159
  return unless keys.include?(:space)
197
160
 
@@ -206,6 +169,28 @@ module Termfront
206
169
  @conn.send_msg({ t: "fire" })
207
170
  end
208
171
 
172
+ def send_pickup_request
173
+ nearest = nearest_drop_in_range
174
+ return unless nearest
175
+
176
+ @conn.send_msg({ t: "pickup", id: nearest[:id] })
177
+ end
178
+
179
+ def nearest_drop_in_range
180
+ best = nil
181
+ best_d2 = Config::PICKUP_RADIUS**2
182
+ @server_drops.each do |drop|
183
+ dx = drop[:x] - @player.x
184
+ dy = drop[:y] - @player.y
185
+ d2 = dx * dx + dy * dy
186
+ if d2 < best_d2
187
+ best = drop
188
+ best_d2 = d2
189
+ end
190
+ end
191
+ best
192
+ end
193
+
209
194
  def update_local(dt)
210
195
  @player.game_time += dt
211
196
 
@@ -278,9 +263,12 @@ module Termfront
278
263
 
279
264
  msg[:players].each do |entry|
280
265
  if entry[:id] == @player_id
266
+ prev_shield = @player.shield
281
267
  @player.shield = entry[:s]
282
268
  @player.health = entry[:h]
283
269
  @player.dead = !entry[:alive]
270
+ sync_own_weapon(entry)
271
+ update_regen_audio(prev_shield)
284
272
  else
285
273
  remote = @remote_players[entry[:id]]
286
274
  next unless remote
@@ -290,7 +278,8 @@ module Termfront
290
278
  remote.angle = entry[:a]
291
279
  remote.shield = entry[:s]
292
280
  remote.health = entry[:h]
293
- remote.weapon = entry[:w]&.to_sym
281
+ weapon = safe_weapon(entry[:w])
282
+ remote.weapon = weapon if weapon
294
283
  remote.ammo = entry[:am]
295
284
  remote.fire_flash = entry[:ff] || 0
296
285
  end
@@ -299,19 +288,79 @@ module Termfront
299
288
  @enemies = msg[:enemies].filter_map do |enemy|
300
289
  next unless enemy[:alive]
301
290
 
291
+ sprite = safe_enemy_type(enemy[:type])
292
+ next unless sprite
293
+
302
294
  RemoteEnemy.new(
303
295
  id: enemy[:id],
304
296
  x: enemy[:x],
305
297
  y: enemy[:y],
306
- sprite_id: enemy[:type].to_sym,
298
+ sprite_id: sprite,
307
299
  hp: enemy[:hp],
308
300
  max_hp: enemy[:max_hp],
309
301
  alive: true
310
302
  )
311
303
  end
312
304
 
313
- @projectiles = msg[:projectiles].map do |projectile|
314
- Projectile.new(x: projectile[:x], y: projectile[:y], vx: 0.0, vy: 0.0, type: projectile[:type].to_sym)
305
+ @projectiles = msg[:projectiles].filter_map do |projectile|
306
+ type = safe_enemy_type(projectile[:type])
307
+ next unless type
308
+
309
+ Projectile.new(x: projectile[:x], y: projectile[:y], vx: 0.0, vy: 0.0, type: type)
310
+ end
311
+
312
+ @server_drops = (msg[:drops] || []).filter_map do |raw|
313
+ drop_type = safe_weapon(raw[:type])
314
+ next unless drop_type
315
+ next unless raw[:id].is_a?(Numeric)
316
+
317
+ { id: raw[:id], x: raw[:x].to_f, y: raw[:y].to_f, type: drop_type, ammo: raw[:am].to_i }
318
+ end
319
+ @drops = @server_drops.map do |drop|
320
+ DropItem::Weapon.new(x: drop[:x], y: drop[:y], type: drop[:type], ammo: drop[:ammo])
321
+ end
322
+ end
323
+
324
+ def sync_own_weapon(entry)
325
+ weapon_sym = safe_weapon(entry[:w])
326
+ return unless weapon_sym
327
+
328
+ current = @player.current_weapon
329
+ if current.type_id != weapon_sym
330
+ @player.weapons[@player.weapon_idx] = Weapon::Base.build(weapon_sym, entry[:am])
331
+ elsif entry.key?(:am) && current.respond_to?(:ammo=)
332
+ current.ammo = entry[:am]
333
+ end
334
+ end
335
+
336
+ def safe_weapon(value)
337
+ return nil unless value.is_a?(String) || value.is_a?(Symbol)
338
+
339
+ name = value.to_s
340
+ return nil unless ALLOWED_WEAPONS.include?(name)
341
+
342
+ name.to_sym
343
+ end
344
+
345
+ def safe_enemy_type(value)
346
+ return nil unless value.is_a?(String) || value.is_a?(Symbol)
347
+
348
+ name = value.to_s
349
+ return nil unless ALLOWED_ENEMY_TYPES.include?(name)
350
+
351
+ name.to_sym
352
+ end
353
+
354
+ def update_regen_audio(prev_shield)
355
+ regen_now = !@player.dead && @player.shield > prev_shield && @player.shield < Config::SHIELD_MAX
356
+ if regen_now
357
+ unless @regen_active
358
+ @audio.play_loop_se(:shield_regen)
359
+ @regen_active = true
360
+ end
361
+ elsif @regen_active
362
+ @audio.stop_loop_se(:shield_regen)
363
+ @regen_active = false
315
364
  end
316
365
  end
317
366
 
@@ -323,7 +372,7 @@ module Termfront
323
372
  map: @map,
324
373
  enemies: @enemies,
325
374
  projectiles: @projectiles,
326
- drops: [],
375
+ drops: @drops,
327
376
  terminals: [],
328
377
  status_line: status,
329
378
  allies: allies
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Termfront
4
- VERSION = "0.1.3"
4
+ VERSION = "0.1.4"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: termfront
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - S-H-GAMELINKS