termfront 0.1.3 → 0.1.5
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 +59 -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/config.rb +2 -1
- data/lib/termfront/game.rb +2 -1
- data/lib/termfront/mission/final_push.rb +1 -1
- data/lib/termfront/network/client.rb +28 -55
- data/lib/termfront/network/connection.rb +8 -0
- data/lib/termfront/network/server.rb +489 -95
- data/lib/termfront/network/wavesfight_client.rb +104 -55
- data/lib/termfront/renderer.rb +128 -73
- data/lib/termfront/terminal_output.rb +1 -6
- 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/renderer.rb
CHANGED
|
@@ -2,12 +2,40 @@
|
|
|
2
2
|
|
|
3
3
|
module Termfront
|
|
4
4
|
class Renderer
|
|
5
|
+
RADAR_CRAWLER = "\e[91m*\e[0m"
|
|
6
|
+
RADAR_EXECUTOR = "\e[95m*\e[0m"
|
|
7
|
+
RADAR_ALLY = "\e[96m+\e[0m"
|
|
8
|
+
RADAR_TERMINAL = "\e[96mT\e[0m"
|
|
9
|
+
RADAR_PLAYER = "\e[92m^\e[0m"
|
|
10
|
+
RADAR_WALL = "\e[90m#\e[0m"
|
|
11
|
+
|
|
12
|
+
FG_256 = Array.new(256) { |i| "\e[38;5;#{i}m".freeze }.freeze
|
|
13
|
+
BG_256 = Array.new(256) { |i| "\e[48;5;#{i}m".freeze }.freeze
|
|
14
|
+
|
|
5
15
|
def initialize(stdout)
|
|
6
16
|
@stdout = stdout
|
|
17
|
+
@buf_view_w = 0
|
|
18
|
+
@buf_virt_h = 0
|
|
19
|
+
@radar_grid_template = build_radar_grid_template
|
|
20
|
+
@hrule_cache = Hash.new { |h, c| h[c] = ("\xE2\x94\x80" * c)[0, c * 3].freeze }
|
|
21
|
+
@radar_drop_glyphs = {}
|
|
22
|
+
@fg_truecolor_cache = {}
|
|
23
|
+
@bg_truecolor_cache = {}
|
|
24
|
+
@enemy_sprites = []
|
|
25
|
+
@proj_sprites = []
|
|
26
|
+
@drop_sprites = []
|
|
27
|
+
@ally_sprites = []
|
|
28
|
+
@radar_line_buf = +""
|
|
29
|
+
@size_cache = nil
|
|
30
|
+
@size_cache_at = -Float::INFINITY
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def invalidate_size_cache!
|
|
34
|
+
@size_cache = nil
|
|
7
35
|
end
|
|
8
36
|
|
|
9
37
|
def render(player:, map:, enemies:, projectiles:, drops:, terminals: [], status_line: nil, allies: [])
|
|
10
|
-
rows, cols =
|
|
38
|
+
rows, cols = current_size
|
|
11
39
|
rows = [rows, 6].max
|
|
12
40
|
cols = [cols, 20].max
|
|
13
41
|
|
|
@@ -16,38 +44,35 @@ module Termfront
|
|
|
16
44
|
view_w = cols
|
|
17
45
|
virt_h = view_h * 2
|
|
18
46
|
|
|
47
|
+
prepare_frame_buffers(view_w, virt_h)
|
|
48
|
+
|
|
19
49
|
dx = Math.cos(player.angle)
|
|
20
50
|
dy = Math.sin(player.angle)
|
|
21
51
|
plane_x = -dy * Math.tan(Config::FOV / 2.0)
|
|
22
52
|
plane_y = dx * Math.tan(Config::FOV / 2.0)
|
|
23
53
|
|
|
24
|
-
dists = Array.new(view_w)
|
|
25
|
-
sides = Array.new(view_w)
|
|
26
54
|
view_w.times do |c|
|
|
27
55
|
cam = 2.0 * c / view_w - 1.0
|
|
28
|
-
dists[c], sides[c] = cast_ray(map, player.x, player.y, dx + plane_x * cam, dy + plane_y * cam)
|
|
56
|
+
@dists[c], @sides[c] = cast_ray(map, player.x, player.y, dx + plane_x * cam, dy + plane_y * cam)
|
|
29
57
|
end
|
|
30
58
|
|
|
31
59
|
vmid = virt_h / 2.0
|
|
32
|
-
wtop = Array.new(view_w)
|
|
33
|
-
wbot = Array.new(view_w)
|
|
34
|
-
wcol = Array.new(view_w)
|
|
35
60
|
view_w.times do |c|
|
|
36
|
-
d = dists[c]
|
|
61
|
+
d = @dists[c]
|
|
37
62
|
lh = d > 0.01 ? (virt_h / d).to_i : virt_h
|
|
38
|
-
wtop[c] = [(vmid - lh / 2.0).to_i, 0].max
|
|
39
|
-
wbot[c] = [(vmid + lh / 2.0).to_i, virt_h].min
|
|
40
|
-
wcol[c] = Sprite.wall_brightness(d, sides[c])
|
|
63
|
+
@wtop[c] = [(vmid - lh / 2.0).to_i, 0].max
|
|
64
|
+
@wbot[c] = [(vmid + lh / 2.0).to_i, virt_h].min
|
|
65
|
+
@wcol[c] = Sprite.wall_brightness(d, @sides[c])
|
|
41
66
|
end
|
|
42
|
-
|
|
43
|
-
overlay_enemies_3d(pixels, view_h, view_w, dists, player, enemies, projectiles, drops)
|
|
44
|
-
overlay_allies_3d(pixels, view_h, view_w, dists, player, allies)
|
|
45
|
-
overlay_damage_flash(pixels, view_h, view_w, player)
|
|
67
|
+
build_view_pixels(virt_h, view_w, @wtop, @wbot, @wcol)
|
|
68
|
+
overlay_enemies_3d(@pixels, view_h, view_w, @dists, player, enemies, projectiles, drops)
|
|
69
|
+
overlay_allies_3d(@pixels, view_h, view_w, @dists, player, allies)
|
|
70
|
+
overlay_damage_flash(@pixels, view_h, view_w, player)
|
|
46
71
|
|
|
47
72
|
buf = TerminalOutput.begin_frame(home: true)
|
|
48
73
|
|
|
49
74
|
render_hud(buf, cols, player, drops, terminals, status_line)
|
|
50
|
-
render_view(buf, view_h, view_w, pixels)
|
|
75
|
+
render_view(buf, view_h, view_w, @pixels)
|
|
51
76
|
buf << "\e[#{3 + view_h};1H"
|
|
52
77
|
render_radar(buf, cols, radar_h, player, enemies, drops, terminals, allies)
|
|
53
78
|
render_crosshair(buf, view_h, view_w, cols, player)
|
|
@@ -132,6 +157,59 @@ module Termfront
|
|
|
132
157
|
|
|
133
158
|
private
|
|
134
159
|
|
|
160
|
+
def current_size
|
|
161
|
+
now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
162
|
+
if @size_cache.nil? || now - @size_cache_at >= 0.25
|
|
163
|
+
@size_cache = @stdout.winsize
|
|
164
|
+
@size_cache_at = now
|
|
165
|
+
end
|
|
166
|
+
@size_cache
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def build_radar_grid_template
|
|
170
|
+
r = Config::RADAR_RADIUS
|
|
171
|
+
diam = r * 2 + 1
|
|
172
|
+
Array.new(diam) do |ry|
|
|
173
|
+
Array.new(diam) do |rx|
|
|
174
|
+
if ry == r && rx == r
|
|
175
|
+
"^"
|
|
176
|
+
else
|
|
177
|
+
dx = rx - r
|
|
178
|
+
dy = ry - r
|
|
179
|
+
d2 = dx * dx + dy * dy
|
|
180
|
+
if d2 <= r * r
|
|
181
|
+
"."
|
|
182
|
+
elsif d2 <= (r + 1) * (r + 1)
|
|
183
|
+
"#"
|
|
184
|
+
else
|
|
185
|
+
" "
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
end.freeze
|
|
189
|
+
end.freeze
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def radar_drop_glyph(drop)
|
|
193
|
+
@radar_drop_glyphs[drop.type] ||= begin
|
|
194
|
+
dc = drop.type.to_s.start_with?("shock") ? "\e[96m" : "\e[93m"
|
|
195
|
+
dl = Weapon::Base.registry[drop.type].new.name[0]
|
|
196
|
+
"#{dc}#{dl}\e[0m".freeze
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def prepare_frame_buffers(view_w, virt_h)
|
|
201
|
+
if @buf_view_w != view_w || @buf_virt_h != virt_h
|
|
202
|
+
@buf_view_w = view_w
|
|
203
|
+
@buf_virt_h = virt_h
|
|
204
|
+
@dists = Array.new(view_w)
|
|
205
|
+
@sides = Array.new(view_w)
|
|
206
|
+
@wtop = Array.new(view_w)
|
|
207
|
+
@wbot = Array.new(view_w)
|
|
208
|
+
@wcol = Array.new(view_w)
|
|
209
|
+
@pixels = Array.new(virt_h) { Array.new(view_w) }
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
|
|
135
213
|
def render_hud(buf, cols, player, drops, terminals, status_line)
|
|
136
214
|
bar_w = [cols - 20, 10].max
|
|
137
215
|
pct = player.shield / Config::SHIELD_MAX.to_f
|
|
@@ -180,9 +258,8 @@ module Termfront
|
|
|
180
258
|
end
|
|
181
259
|
|
|
182
260
|
def build_view_pixels(virt_h, view_w, wtop, wbot, wcol)
|
|
183
|
-
pixels = Array.new(virt_h) { Array.new(view_w) }
|
|
184
261
|
virt_h.times do |vr|
|
|
185
|
-
row = pixels[vr]
|
|
262
|
+
row = @pixels[vr]
|
|
186
263
|
view_w.times do |c|
|
|
187
264
|
row[c] = if vr < wtop[c]
|
|
188
265
|
Config::CEIL_C
|
|
@@ -193,7 +270,6 @@ module Termfront
|
|
|
193
270
|
end
|
|
194
271
|
end
|
|
195
272
|
end
|
|
196
|
-
pixels
|
|
197
273
|
end
|
|
198
274
|
|
|
199
275
|
def render_view(buf, view_h, view_w, pixels)
|
|
@@ -239,25 +315,11 @@ module Termfront
|
|
|
239
315
|
end
|
|
240
316
|
|
|
241
317
|
def render_radar(buf, cols, radar_h, player, enemies, drops, terminals, allies = [])
|
|
242
|
-
buf <<
|
|
318
|
+
buf << @hrule_cache[cols] << "\r\n"
|
|
243
319
|
|
|
244
320
|
r = Config::RADAR_RADIUS
|
|
245
321
|
diam = r * 2 + 1
|
|
246
|
-
|
|
247
|
-
grid = Array.new(diam) { Array.new(diam, " ") }
|
|
248
|
-
diam.times do |ry|
|
|
249
|
-
diam.times do |rx|
|
|
250
|
-
dx = rx - r
|
|
251
|
-
dy = ry - r
|
|
252
|
-
d2 = dx * dx + dy * dy
|
|
253
|
-
if d2 <= r * r
|
|
254
|
-
grid[ry][rx] = "."
|
|
255
|
-
elsif d2 <= (r + 1) * (r + 1)
|
|
256
|
-
grid[ry][rx] = "#"
|
|
257
|
-
end
|
|
258
|
-
end
|
|
259
|
-
end
|
|
260
|
-
grid[r][r] = "^"
|
|
322
|
+
grid = @radar_grid_template
|
|
261
323
|
|
|
262
324
|
cos_a = Math.cos(-player.angle + Math::PI / 2)
|
|
263
325
|
sin_a = Math.sin(-player.angle + Math::PI / 2)
|
|
@@ -267,8 +329,7 @@ module Termfront
|
|
|
267
329
|
|
|
268
330
|
ex = e.x - player.x
|
|
269
331
|
ey = e.y - player.y
|
|
270
|
-
|
|
271
|
-
next if dist > Config::RADAR_RANGE
|
|
332
|
+
next if ex * ex + ey * ey > Config::RADAR_RANGE_SQ
|
|
272
333
|
|
|
273
334
|
rx = -(ex * cos_a - ey * sin_a)
|
|
274
335
|
ry = -(ex * sin_a + ey * cos_a)
|
|
@@ -286,8 +347,7 @@ module Termfront
|
|
|
286
347
|
drops.each do |d|
|
|
287
348
|
ex = d.x - player.x
|
|
288
349
|
ey = d.y - player.y
|
|
289
|
-
|
|
290
|
-
next if dist > Config::RADAR_RANGE
|
|
350
|
+
next if ex * ex + ey * ey > Config::RADAR_RANGE_SQ
|
|
291
351
|
|
|
292
352
|
rx = -(ex * cos_a - ey * sin_a)
|
|
293
353
|
ry = -(ex * sin_a + ey * cos_a)
|
|
@@ -305,8 +365,7 @@ module Termfront
|
|
|
305
365
|
terminals.each do |terminal|
|
|
306
366
|
ex = terminal[:x] - player.x
|
|
307
367
|
ey = terminal[:y] - player.y
|
|
308
|
-
|
|
309
|
-
next if dist > Config::RADAR_RANGE
|
|
368
|
+
next if ex * ex + ey * ey > Config::RADAR_RANGE_SQ
|
|
310
369
|
|
|
311
370
|
rx = -(ex * cos_a - ey * sin_a)
|
|
312
371
|
ry = -(ex * sin_a + ey * cos_a)
|
|
@@ -324,8 +383,7 @@ module Termfront
|
|
|
324
383
|
allies.each do |ally|
|
|
325
384
|
ex = ally.x - player.x
|
|
326
385
|
ey = ally.y - player.y
|
|
327
|
-
|
|
328
|
-
next if dist > Config::RADAR_RANGE
|
|
386
|
+
next if ex * ex + ey * ey > Config::RADAR_RANGE_SQ
|
|
329
387
|
|
|
330
388
|
rx = -(ex * cos_a - ey * sin_a)
|
|
331
389
|
ry = -(ex * sin_a + ey * cos_a)
|
|
@@ -348,25 +406,22 @@ module Termfront
|
|
|
348
406
|
]
|
|
349
407
|
|
|
350
408
|
radar_h.times do |row|
|
|
351
|
-
line =
|
|
409
|
+
line = @radar_line_buf.clear
|
|
352
410
|
if row < diam
|
|
353
411
|
line << " "
|
|
354
412
|
diam.times do |cx|
|
|
355
413
|
if (etype = enemy_cells[[row, cx]])
|
|
356
|
-
|
|
357
|
-
line << "#{ec}*\e[0m"
|
|
414
|
+
line << (etype == :executor ? RADAR_EXECUTOR : RADAR_CRAWLER)
|
|
358
415
|
elsif ally_cells[[row, cx]]
|
|
359
|
-
line <<
|
|
416
|
+
line << RADAR_ALLY
|
|
360
417
|
elsif (drop = drop_cells[[row, cx]])
|
|
361
|
-
|
|
362
|
-
dl = Weapon::Base.registry[drop.type].new.name[0]
|
|
363
|
-
line << "#{dc}#{dl}\e[0m"
|
|
418
|
+
line << radar_drop_glyph(drop)
|
|
364
419
|
elsif terminal_cells[[row, cx]]
|
|
365
|
-
line <<
|
|
420
|
+
line << RADAR_TERMINAL
|
|
366
421
|
elsif row == r && cx == r
|
|
367
|
-
line <<
|
|
422
|
+
line << RADAR_PLAYER
|
|
368
423
|
elsif grid[row][cx] == "#"
|
|
369
|
-
line <<
|
|
424
|
+
line << RADAR_WALL
|
|
370
425
|
else
|
|
371
426
|
line << grid[row][cx]
|
|
372
427
|
end
|
|
@@ -386,7 +441,7 @@ module Termfront
|
|
|
386
441
|
virt_h = view_h * 2
|
|
387
442
|
inv = 1.0 / (px * dy - py * dx)
|
|
388
443
|
|
|
389
|
-
|
|
444
|
+
@enemy_sprites.clear
|
|
390
445
|
enemies.each do |e|
|
|
391
446
|
next unless e.alive
|
|
392
447
|
|
|
@@ -396,10 +451,10 @@ module Termfront
|
|
|
396
451
|
tz = inv * (-py * ex + px * ey)
|
|
397
452
|
next if tz < 0.2
|
|
398
453
|
|
|
399
|
-
|
|
454
|
+
@enemy_sprites << [tz, tx, e]
|
|
400
455
|
end
|
|
401
456
|
|
|
402
|
-
proj_sprites
|
|
457
|
+
@proj_sprites.clear
|
|
403
458
|
projectiles.each do |p|
|
|
404
459
|
ex = p.x - player.x
|
|
405
460
|
ey = p.y - player.y
|
|
@@ -407,12 +462,12 @@ module Termfront
|
|
|
407
462
|
tz = inv * (-py * ex + px * ey)
|
|
408
463
|
next if tz < 0.2
|
|
409
464
|
|
|
410
|
-
proj_sprites << [tz, tx, p]
|
|
465
|
+
@proj_sprites << [tz, tx, p]
|
|
411
466
|
end
|
|
412
467
|
|
|
413
|
-
|
|
468
|
+
@enemy_sprites.sort! { |a, b| b[0] <=> a[0] }
|
|
414
469
|
|
|
415
|
-
|
|
470
|
+
@enemy_sprites.each do |tz, tx, e|
|
|
416
471
|
sx = ((view_w / 2.0) * (1 + tx / tz)).to_i
|
|
417
472
|
sprite_h = (virt_h / tz).to_i
|
|
418
473
|
draw_top = [(virt_h / 2 - sprite_h / 2), 0].max
|
|
@@ -481,7 +536,7 @@ module Termfront
|
|
|
481
536
|
end
|
|
482
537
|
|
|
483
538
|
# Render weapon drops
|
|
484
|
-
drop_sprites
|
|
539
|
+
@drop_sprites.clear
|
|
485
540
|
drops.each do |d|
|
|
486
541
|
ex = d.x - player.x
|
|
487
542
|
ey = d.y - player.y
|
|
@@ -489,11 +544,11 @@ module Termfront
|
|
|
489
544
|
tz = inv * (-py * ex + px * ey)
|
|
490
545
|
next if tz < 0.2
|
|
491
546
|
|
|
492
|
-
drop_sprites << [tz, tx, d]
|
|
547
|
+
@drop_sprites << [tz, tx, d]
|
|
493
548
|
end
|
|
494
|
-
drop_sprites.
|
|
549
|
+
@drop_sprites.sort! { |a, b| b[0] <=> a[0] }
|
|
495
550
|
|
|
496
|
-
drop_sprites.each do |tz, tx, d|
|
|
551
|
+
@drop_sprites.each do |tz, tx, d|
|
|
497
552
|
sx = ((view_w / 2.0) * (1 + tx / tz)).to_i
|
|
498
553
|
sprite_h = (virt_h / tz * 0.3).to_i.clamp(2, virt_h / 2)
|
|
499
554
|
ground = (virt_h / 2 + virt_h / tz * 0.35).to_i
|
|
@@ -527,8 +582,8 @@ module Termfront
|
|
|
527
582
|
end
|
|
528
583
|
|
|
529
584
|
# Render projectiles
|
|
530
|
-
proj_sprites.
|
|
531
|
-
proj_sprites.each do |tz, tx, p|
|
|
585
|
+
@proj_sprites.sort! { |a, b| b[0] <=> a[0] }
|
|
586
|
+
@proj_sprites.each do |tz, tx, p|
|
|
532
587
|
sx = ((view_w / 2.0) * (1 + tx / tz)).to_i
|
|
533
588
|
pw = (4.0 / tz).ceil.clamp(1, 5)
|
|
534
589
|
ph = (virt_h / tz * 0.15).ceil.clamp(2, 6)
|
|
@@ -570,7 +625,7 @@ module Termfront
|
|
|
570
625
|
virt_h = view_h * 2
|
|
571
626
|
inv = 1.0 / (px * dy - py * dx)
|
|
572
627
|
|
|
573
|
-
|
|
628
|
+
@ally_sprites.clear
|
|
574
629
|
allies.each do |ally|
|
|
575
630
|
ex = ally.x - player.x
|
|
576
631
|
ey = ally.y - player.y
|
|
@@ -578,11 +633,11 @@ module Termfront
|
|
|
578
633
|
tz = inv * (-py * ex + px * ey)
|
|
579
634
|
next if tz < 0.2
|
|
580
635
|
|
|
581
|
-
|
|
636
|
+
@ally_sprites << [tz, tx, ally]
|
|
582
637
|
end
|
|
583
|
-
|
|
638
|
+
@ally_sprites.sort! { |a, b| b[0] <=> a[0] }
|
|
584
639
|
|
|
585
|
-
|
|
640
|
+
@ally_sprites.each do |tz, tx, ally|
|
|
586
641
|
sx = ((view_w / 2.0) * (1 + tx / tz)).to_i
|
|
587
642
|
sprite_h = (virt_h / tz).to_i
|
|
588
643
|
draw_top = [(virt_h / 2 - sprite_h / 2), 0].max
|
|
@@ -690,17 +745,17 @@ module Termfront
|
|
|
690
745
|
|
|
691
746
|
def ansi_fg(color)
|
|
692
747
|
if color.is_a?(Integer)
|
|
693
|
-
|
|
748
|
+
FG_256[color]
|
|
694
749
|
else
|
|
695
|
-
"\e[38;2;#{color}m"
|
|
750
|
+
@fg_truecolor_cache[color] ||= "\e[38;2;#{color}m".freeze
|
|
696
751
|
end
|
|
697
752
|
end
|
|
698
753
|
|
|
699
754
|
def ansi_bg(color)
|
|
700
755
|
if color.is_a?(Integer)
|
|
701
|
-
|
|
756
|
+
BG_256[color]
|
|
702
757
|
else
|
|
703
|
-
"\e[48;2;#{color}m"
|
|
758
|
+
@bg_truecolor_cache[color] ||= "\e[48;2;#{color}m".freeze
|
|
704
759
|
end
|
|
705
760
|
end
|
|
706
761
|
end
|
|
@@ -5,20 +5,15 @@ module Termfront
|
|
|
5
5
|
module_function
|
|
6
6
|
ANSI_PATTERN = /\e\[[0-9;]*[A-Za-z]/.freeze
|
|
7
7
|
|
|
8
|
-
def sync_updates?
|
|
9
|
-
ENV.fetch("TERMFRONT_SYNC_UPDATES", "1") == "1"
|
|
10
|
-
end
|
|
11
|
-
|
|
12
8
|
def begin_frame(home: false, clear: false)
|
|
13
9
|
buf = +""
|
|
14
|
-
buf << "\e[?2026h" if sync_updates?
|
|
15
10
|
buf << "\e[H" if home
|
|
16
11
|
buf << "\e[2J" if clear
|
|
17
12
|
buf
|
|
18
13
|
end
|
|
19
14
|
|
|
20
15
|
def end_frame
|
|
21
|
-
|
|
16
|
+
""
|
|
22
17
|
end
|
|
23
18
|
|
|
24
19
|
def fit_ansi(text, width)
|
data/lib/termfront/version.rb
CHANGED