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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +34 -0
- data/README.md +14 -1
- data/data/events/final_push.json +0 -11
- data/lib/termfront/audio_manager.rb +6 -2
- data/lib/termfront/mission/final_push.rb +1 -1
- data/lib/termfront/network/client.rb +27 -54
- data/lib/termfront/network/connection.rb +8 -0
- data/lib/termfront/network/server.rb +459 -91
- data/lib/termfront/network/wavesfight_client.rb +104 -55
- data/lib/termfront/version.rb +1 -1
- metadata +1 -1
|
@@ -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 =
|
|
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(
|
|
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
|
-
|
|
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:
|
|
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].
|
|
314
|
-
|
|
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
|
data/lib/termfront/version.rb
CHANGED