termfront 0.1.5 → 0.1.7
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 +40 -0
- data/exe/termfront +2 -0
- data/lib/termfront/async_writer.rb +45 -0
- data/lib/termfront/audio_manager.rb +10 -2
- data/lib/termfront/color.rb +19 -0
- data/lib/termfront/config.rb +2 -0
- data/lib/termfront/drop_item/weapon.rb +4 -1
- data/lib/termfront/game.rb +78 -50
- data/lib/termfront/network/client.rb +32 -31
- data/lib/termfront/network/wavesfight_client.rb +2 -1
- data/lib/termfront/renderer.rb +369 -239
- data/lib/termfront/sprite.rb +60 -24
- data/lib/termfront/terminal_output.rb +31 -16
- data/lib/termfront/title_screen.rb +192 -49
- data/lib/termfront/version.rb +1 -1
- data/lib/termfront.rb +2 -0
- metadata +3 -1
data/lib/termfront/renderer.rb
CHANGED
|
@@ -12,6 +12,17 @@ module Termfront
|
|
|
12
12
|
FG_256 = Array.new(256) { |i| "\e[38;5;#{i}m".freeze }.freeze
|
|
13
13
|
BG_256 = Array.new(256) { |i| "\e[48;5;#{i}m".freeze }.freeze
|
|
14
14
|
|
|
15
|
+
EXECUTOR_FALLBACK = Color.rgb_to_256(100, 60, 200)
|
|
16
|
+
CRAWLER_FALLBACK = Color.rgb_to_256(220, 140, 30)
|
|
17
|
+
ENEMY_BAR_FILL = Color.rgb_to_256(0, 200, 0)
|
|
18
|
+
ENEMY_BAR_EMPTY = Color.rgb_to_256(200, 0, 0)
|
|
19
|
+
PROJ_EXECUTOR = Color.rgb_to_256(94, 94, 255)
|
|
20
|
+
PROJ_DEFAULT = Color.rgb_to_256(255, 210, 80)
|
|
21
|
+
ALLY_FALLBACK = Color.rgb_to_256(70, 210, 255)
|
|
22
|
+
ALLY_BAR_FILL = Color.rgb_to_256(0, 180, 255)
|
|
23
|
+
ALLY_BAR_EMPTY = Color.rgb_to_256(80, 20, 20)
|
|
24
|
+
DAMAGE_FLASH_RAMP = Array.new(256) { |i| Color.rgb_to_256(i, 0, 0) }.freeze
|
|
25
|
+
|
|
15
26
|
def initialize(stdout)
|
|
16
27
|
@stdout = stdout
|
|
17
28
|
@buf_view_w = 0
|
|
@@ -19,8 +30,10 @@ module Termfront
|
|
|
19
30
|
@radar_grid_template = build_radar_grid_template
|
|
20
31
|
@hrule_cache = Hash.new { |h, c| h[c] = ("\xE2\x94\x80" * c)[0, c * 3].freeze }
|
|
21
32
|
@radar_drop_glyphs = {}
|
|
22
|
-
@
|
|
23
|
-
@
|
|
33
|
+
@radar_enemy_cells = {}
|
|
34
|
+
@radar_drop_cells = {}
|
|
35
|
+
@radar_terminal_cells = {}
|
|
36
|
+
@radar_ally_cells = {}
|
|
24
37
|
@enemy_sprites = []
|
|
25
38
|
@proj_sprites = []
|
|
26
39
|
@drop_sprites = []
|
|
@@ -28,6 +41,10 @@ module Termfront
|
|
|
28
41
|
@radar_line_buf = +""
|
|
29
42
|
@size_cache = nil
|
|
30
43
|
@size_cache_at = -Float::INFINITY
|
|
44
|
+
@cached_hud_shield_key = nil
|
|
45
|
+
@cached_hud_shield_line = nil
|
|
46
|
+
@cached_hud_ammo_key = nil
|
|
47
|
+
@cached_hud_ammo_line = nil
|
|
31
48
|
end
|
|
32
49
|
|
|
33
50
|
def invalidate_size_cache!
|
|
@@ -46,23 +63,27 @@ module Termfront
|
|
|
46
63
|
|
|
47
64
|
prepare_frame_buffers(view_w, virt_h)
|
|
48
65
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
66
|
+
cast_state = [player.x, player.y, player.angle, map.object_id, view_w, virt_h]
|
|
67
|
+
if cast_state != @last_cast_state
|
|
68
|
+
dx = Math.cos(player.angle)
|
|
69
|
+
dy = Math.sin(player.angle)
|
|
70
|
+
plane_x = -dy * Math.tan(Config::FOV / 2.0)
|
|
71
|
+
plane_y = dx * Math.tan(Config::FOV / 2.0)
|
|
53
72
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
73
|
+
view_w.times do |c|
|
|
74
|
+
cam = 2.0 * c / view_w - 1.0
|
|
75
|
+
@dists[c], @sides[c] = cast_ray(map, player.x, player.y, dx + plane_x * cam, dy + plane_y * cam)
|
|
76
|
+
end
|
|
58
77
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
78
|
+
vmid = virt_h / 2.0
|
|
79
|
+
view_w.times do |c|
|
|
80
|
+
d = @dists[c]
|
|
81
|
+
lh = d > 0.01 ? (virt_h / d).to_i : virt_h
|
|
82
|
+
@wtop[c] = [(vmid - lh / 2.0).to_i, 0].max
|
|
83
|
+
@wbot[c] = [(vmid + lh / 2.0).to_i, virt_h].min
|
|
84
|
+
@wcol[c] = Sprite.wall_brightness(d, @sides[c])
|
|
85
|
+
end
|
|
86
|
+
@last_cast_state = cast_state
|
|
66
87
|
end
|
|
67
88
|
build_view_pixels(virt_h, view_w, @wtop, @wbot, @wcol)
|
|
68
89
|
overlay_enemies_3d(@pixels, view_h, view_w, @dists, player, enemies, projectiles, drops)
|
|
@@ -211,6 +232,14 @@ module Termfront
|
|
|
211
232
|
end
|
|
212
233
|
|
|
213
234
|
def render_hud(buf, cols, player, drops, terminals, status_line)
|
|
235
|
+
buf << hud_shield_line(cols, player, status_line) << "\r\n"
|
|
236
|
+
buf << hud_ammo_line(cols, player, drops, terminals) << "\r\n"
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
def hud_shield_line(cols, player, status_line)
|
|
240
|
+
key = [player.shield.to_i, status_line, cols]
|
|
241
|
+
return @cached_hud_shield_line if @cached_hud_shield_key == key
|
|
242
|
+
|
|
214
243
|
bar_w = [cols - 20, 10].max
|
|
215
244
|
pct = player.shield / Config::SHIELD_MAX.to_f
|
|
216
245
|
filled = (pct * bar_w).to_i
|
|
@@ -226,11 +255,23 @@ module Termfront
|
|
|
226
255
|
shield_str = "SHIELD #{color}#{"█" * filled}#{"░" * empty}\e[0m #{pct_s}"
|
|
227
256
|
shield_str = "#{shield_str}\e[90m#{status_line}\e[0m" if status_line
|
|
228
257
|
pad = [(cols - bar_w - 15) / 2, 0].max
|
|
229
|
-
|
|
258
|
+
line = TerminalOutput.fit_ansi("#{" " * pad}#{shield_str}", cols)
|
|
230
259
|
|
|
260
|
+
@cached_hud_shield_key = key
|
|
261
|
+
@cached_hud_shield_line = line
|
|
262
|
+
line
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
def hud_ammo_line(cols, player, drops, terminals)
|
|
231
266
|
weapon = player.current_weapon
|
|
232
|
-
|
|
267
|
+
can_pickup = drops.any? { |d| d.in_range?(player.x, player.y) }
|
|
268
|
+
can_use_terminal = terminals.any? do |terminal|
|
|
269
|
+
(terminal[:x] - player.x)**2 + (terminal[:y] - player.y)**2 < Config::TERMINAL_USE_RADIUS**2
|
|
270
|
+
end
|
|
271
|
+
key = [weapon.type_id, weapon.ammo, can_pickup, can_use_terminal, cols]
|
|
272
|
+
return @cached_hud_ammo_line if @cached_hud_ammo_key == key
|
|
233
273
|
|
|
274
|
+
wcolor = weapon.type_id.to_s.start_with?("shock") ? "\e[96m" : "\e[97m"
|
|
234
275
|
if weapon.max_ammo
|
|
235
276
|
ammo_bar_w = 12
|
|
236
277
|
ammo_pct = weapon.ammo.to_f / weapon.max_ammo
|
|
@@ -241,10 +282,6 @@ module Termfront
|
|
|
241
282
|
ammo_str = "#{wcolor}#{weapon.name}\e[0m [\xe2\x88\x9e]"
|
|
242
283
|
end
|
|
243
284
|
|
|
244
|
-
can_pickup = drops.any? { |d| d.in_range?(player.x, player.y) }
|
|
245
|
-
can_use_terminal = terminals.any? do |terminal|
|
|
246
|
-
(terminal[:x] - player.x)**2 + (terminal[:y] - player.y)**2 < Config::TERMINAL_USE_RADIUS**2
|
|
247
|
-
end
|
|
248
285
|
interact_str = if can_use_terminal
|
|
249
286
|
"\e[1;96m[E]Use Terminal\e[0m"
|
|
250
287
|
elsif can_pickup
|
|
@@ -253,64 +290,118 @@ module Termfront
|
|
|
253
290
|
"E:interact"
|
|
254
291
|
end
|
|
255
292
|
|
|
256
|
-
line = "#{ammo_str} T:swap #{interact_str} Space:fire"
|
|
257
|
-
|
|
293
|
+
line = TerminalOutput.fit_ansi("#{ammo_str} T:swap #{interact_str} Space:fire", cols)
|
|
294
|
+
|
|
295
|
+
@cached_hud_ammo_key = key
|
|
296
|
+
@cached_hud_ammo_line = line
|
|
297
|
+
line
|
|
258
298
|
end
|
|
259
299
|
|
|
260
300
|
def build_view_pixels(virt_h, view_w, wtop, wbot, wcol)
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
301
|
+
ceil_c = Config::CEIL_C
|
|
302
|
+
floor_c = Config::FLOOR_C
|
|
303
|
+
pixels = @pixels
|
|
304
|
+
|
|
305
|
+
min_wt = wtop.min
|
|
306
|
+
max_wb = wbot.max
|
|
307
|
+
upper_done = min_wt < virt_h ? min_wt : virt_h
|
|
308
|
+
lower_start = max_wb > upper_done ? max_wb : upper_done
|
|
309
|
+
lower_start = virt_h if lower_start > virt_h
|
|
310
|
+
|
|
311
|
+
vr = 0
|
|
312
|
+
while vr < upper_done
|
|
313
|
+
pixels[vr].fill(ceil_c, 0, view_w)
|
|
314
|
+
vr += 1
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
vr_bot = virt_h - 1
|
|
318
|
+
while vr_bot >= lower_start
|
|
319
|
+
pixels[vr_bot].fill(floor_c, 0, view_w)
|
|
320
|
+
vr_bot -= 1
|
|
321
|
+
end
|
|
322
|
+
middle_end = vr_bot + 1
|
|
323
|
+
|
|
324
|
+
c = 0
|
|
325
|
+
while c < view_w
|
|
326
|
+
wt = wtop[c]
|
|
327
|
+
wb = wbot[c]
|
|
328
|
+
wc = wcol[c]
|
|
329
|
+
|
|
330
|
+
vr = upper_done
|
|
331
|
+
while vr < wt && vr < middle_end
|
|
332
|
+
pixels[vr][c] = ceil_c
|
|
333
|
+
vr += 1
|
|
334
|
+
end
|
|
335
|
+
while vr < wb && vr < middle_end
|
|
336
|
+
pixels[vr][c] = wc
|
|
337
|
+
vr += 1
|
|
338
|
+
end
|
|
339
|
+
while vr < middle_end
|
|
340
|
+
pixels[vr][c] = floor_c
|
|
341
|
+
vr += 1
|
|
271
342
|
end
|
|
343
|
+
|
|
344
|
+
c += 1
|
|
272
345
|
end
|
|
273
346
|
end
|
|
274
347
|
|
|
275
348
|
def render_view(buf, view_h, view_w, pixels)
|
|
276
|
-
|
|
349
|
+
fg_256 = FG_256
|
|
350
|
+
bg_256 = BG_256
|
|
351
|
+
|
|
352
|
+
r = 0
|
|
353
|
+
while r < view_h
|
|
277
354
|
vp0 = r * 2
|
|
278
|
-
vp1 =
|
|
279
|
-
pfg = nil
|
|
280
|
-
pbg = nil
|
|
355
|
+
vp1 = vp0 + 1
|
|
281
356
|
top_row = pixels[vp0]
|
|
282
357
|
bot_row = pixels[vp1]
|
|
283
358
|
|
|
284
|
-
|
|
359
|
+
first = top_row[0]
|
|
360
|
+
if bot_row[0] == first
|
|
361
|
+
uniform = true
|
|
362
|
+
cu = 1
|
|
363
|
+
while cu < view_w
|
|
364
|
+
if top_row[cu] != first || bot_row[cu] != first
|
|
365
|
+
uniform = false
|
|
366
|
+
break
|
|
367
|
+
end
|
|
368
|
+
cu += 1
|
|
369
|
+
end
|
|
370
|
+
if uniform
|
|
371
|
+
buf << bg_256[first] << "\e[K\e[0m\r\n"
|
|
372
|
+
r += 1
|
|
373
|
+
next
|
|
374
|
+
end
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
pbg = nil
|
|
378
|
+
|
|
379
|
+
c = 0
|
|
380
|
+
while c < view_w
|
|
285
381
|
tc = top_row[c]
|
|
286
382
|
bc = bot_row[c]
|
|
287
383
|
|
|
288
384
|
if tc == bc
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
pbg = tc
|
|
293
|
-
pfg = nil
|
|
294
|
-
end
|
|
295
|
-
buf << " "
|
|
296
|
-
else
|
|
297
|
-
if tc != pfg || pbg
|
|
298
|
-
buf << ansi_fg(tc)
|
|
299
|
-
pfg = tc
|
|
300
|
-
pbg = nil
|
|
301
|
-
end
|
|
302
|
-
buf << "\xE2\x96\x88"
|
|
385
|
+
run_end = c + 1
|
|
386
|
+
while run_end < view_w && top_row[run_end] == tc && bot_row[run_end] == bc
|
|
387
|
+
run_end += 1
|
|
303
388
|
end
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
pbg =
|
|
389
|
+
n = run_end - c
|
|
390
|
+
|
|
391
|
+
if tc != pbg
|
|
392
|
+
buf << bg_256[tc]
|
|
393
|
+
pbg = tc
|
|
309
394
|
end
|
|
310
|
-
buf << "
|
|
395
|
+
buf << (n == 1 ? " " : " " * n)
|
|
396
|
+
c = run_end
|
|
397
|
+
else
|
|
398
|
+
buf << fg_256[tc] << bg_256[bc] << "\xE2\x96\x80"
|
|
399
|
+
pbg = bc
|
|
400
|
+
c += 1
|
|
311
401
|
end
|
|
312
402
|
end
|
|
313
403
|
buf << "\e[0m\r\n"
|
|
404
|
+
r += 1
|
|
314
405
|
end
|
|
315
406
|
end
|
|
316
407
|
|
|
@@ -319,82 +410,97 @@ module Termfront
|
|
|
319
410
|
|
|
320
411
|
r = Config::RADAR_RADIUS
|
|
321
412
|
diam = r * 2 + 1
|
|
413
|
+
range = Config::RADAR_RANGE
|
|
414
|
+
range_sq = Config::RADAR_RANGE_SQ
|
|
415
|
+
r_sq = r * r
|
|
322
416
|
grid = @radar_grid_template
|
|
323
417
|
|
|
324
418
|
cos_a = Math.cos(-player.angle + Math::PI / 2)
|
|
325
419
|
sin_a = Math.sin(-player.angle + Math::PI / 2)
|
|
326
|
-
|
|
420
|
+
px = player.x
|
|
421
|
+
py = player.y
|
|
422
|
+
|
|
423
|
+
enemy_cells = @radar_enemy_cells
|
|
424
|
+
drop_cells = @radar_drop_cells
|
|
425
|
+
terminal_cells = @radar_terminal_cells
|
|
426
|
+
ally_cells = @radar_ally_cells
|
|
427
|
+
enemy_cells.clear
|
|
428
|
+
drop_cells.clear
|
|
429
|
+
terminal_cells.clear
|
|
430
|
+
ally_cells.clear
|
|
431
|
+
|
|
327
432
|
enemies.each do |e|
|
|
328
433
|
next unless e.alive
|
|
329
434
|
|
|
330
|
-
ex = e.x -
|
|
331
|
-
ey = e.y -
|
|
332
|
-
next if ex * ex + ey * ey >
|
|
435
|
+
ex = e.x - px
|
|
436
|
+
ey = e.y - py
|
|
437
|
+
next if ex * ex + ey * ey > range_sq
|
|
333
438
|
|
|
334
439
|
rx = -(ex * cos_a - ey * sin_a)
|
|
335
440
|
ry = -(ex * sin_a + ey * cos_a)
|
|
336
|
-
sx = r + (rx /
|
|
337
|
-
sy = r + (ry /
|
|
338
|
-
next
|
|
441
|
+
sx = r + (rx / range * r).round
|
|
442
|
+
sy = r + (ry / range * r).round
|
|
443
|
+
next if sx < 0 || sx >= diam || sy < 0 || sy >= diam
|
|
339
444
|
|
|
340
|
-
|
|
341
|
-
|
|
445
|
+
dxr = sx - r
|
|
446
|
+
dyr = sy - r
|
|
447
|
+
next if dxr * dxr + dyr * dyr > r_sq
|
|
342
448
|
|
|
343
|
-
enemy_cells[
|
|
449
|
+
enemy_cells[sy * diam + sx] = e.sprite_id
|
|
344
450
|
end
|
|
345
451
|
|
|
346
|
-
drop_cells = {}
|
|
347
452
|
drops.each do |d|
|
|
348
|
-
ex = d.x -
|
|
349
|
-
ey = d.y -
|
|
350
|
-
next if ex * ex + ey * ey >
|
|
453
|
+
ex = d.x - px
|
|
454
|
+
ey = d.y - py
|
|
455
|
+
next if ex * ex + ey * ey > range_sq
|
|
351
456
|
|
|
352
457
|
rx = -(ex * cos_a - ey * sin_a)
|
|
353
458
|
ry = -(ex * sin_a + ey * cos_a)
|
|
354
|
-
sx = r + (rx /
|
|
355
|
-
sy = r + (ry /
|
|
356
|
-
next
|
|
459
|
+
sx = r + (rx / range * r).round
|
|
460
|
+
sy = r + (ry / range * r).round
|
|
461
|
+
next if sx < 0 || sx >= diam || sy < 0 || sy >= diam
|
|
357
462
|
|
|
358
|
-
|
|
359
|
-
|
|
463
|
+
dxr = sx - r
|
|
464
|
+
dyr = sy - r
|
|
465
|
+
next if dxr * dxr + dyr * dyr > r_sq
|
|
360
466
|
|
|
361
|
-
drop_cells[
|
|
467
|
+
drop_cells[sy * diam + sx] = d
|
|
362
468
|
end
|
|
363
469
|
|
|
364
|
-
terminal_cells = {}
|
|
365
470
|
terminals.each do |terminal|
|
|
366
|
-
ex = terminal[:x] -
|
|
367
|
-
ey = terminal[:y] -
|
|
368
|
-
next if ex * ex + ey * ey >
|
|
471
|
+
ex = terminal[:x] - px
|
|
472
|
+
ey = terminal[:y] - py
|
|
473
|
+
next if ex * ex + ey * ey > range_sq
|
|
369
474
|
|
|
370
475
|
rx = -(ex * cos_a - ey * sin_a)
|
|
371
476
|
ry = -(ex * sin_a + ey * cos_a)
|
|
372
|
-
sx = r + (rx /
|
|
373
|
-
sy = r + (ry /
|
|
374
|
-
next
|
|
477
|
+
sx = r + (rx / range * r).round
|
|
478
|
+
sy = r + (ry / range * r).round
|
|
479
|
+
next if sx < 0 || sx >= diam || sy < 0 || sy >= diam
|
|
375
480
|
|
|
376
|
-
|
|
377
|
-
|
|
481
|
+
dxr = sx - r
|
|
482
|
+
dyr = sy - r
|
|
483
|
+
next if dxr * dxr + dyr * dyr > r_sq
|
|
378
484
|
|
|
379
|
-
terminal_cells[
|
|
485
|
+
terminal_cells[sy * diam + sx] = terminal
|
|
380
486
|
end
|
|
381
487
|
|
|
382
|
-
ally_cells = {}
|
|
383
488
|
allies.each do |ally|
|
|
384
|
-
ex = ally.x -
|
|
385
|
-
ey = ally.y -
|
|
386
|
-
next if ex * ex + ey * ey >
|
|
489
|
+
ex = ally.x - px
|
|
490
|
+
ey = ally.y - py
|
|
491
|
+
next if ex * ex + ey * ey > range_sq
|
|
387
492
|
|
|
388
493
|
rx = -(ex * cos_a - ey * sin_a)
|
|
389
494
|
ry = -(ex * sin_a + ey * cos_a)
|
|
390
|
-
sx = r + (rx /
|
|
391
|
-
sy = r + (ry /
|
|
392
|
-
next
|
|
495
|
+
sx = r + (rx / range * r).round
|
|
496
|
+
sy = r + (ry / range * r).round
|
|
497
|
+
next if sx < 0 || sx >= diam || sy < 0 || sy >= diam
|
|
393
498
|
|
|
394
|
-
|
|
395
|
-
|
|
499
|
+
dxr = sx - r
|
|
500
|
+
dyr = sy - r
|
|
501
|
+
next if dxr * dxr + dyr * dyr > r_sq
|
|
396
502
|
|
|
397
|
-
ally_cells[
|
|
503
|
+
ally_cells[sy * diam + sx] = true
|
|
398
504
|
end
|
|
399
505
|
|
|
400
506
|
alive_count = enemies.count(&:alive)
|
|
@@ -402,21 +508,25 @@ module Termfront
|
|
|
402
508
|
info_lines = [
|
|
403
509
|
"Enemies: #{alive_count}/#{total_count}",
|
|
404
510
|
"Heading: #{format("%.0f", (player.angle % (Math::PI * 2)) * 180 / Math::PI)}\xC2\xB0",
|
|
405
|
-
"Pos: (#{"%.1f" %
|
|
511
|
+
"Pos: (#{"%.1f" % px}, #{"%.1f" % py}) T:terminal"
|
|
406
512
|
]
|
|
407
513
|
|
|
408
|
-
|
|
514
|
+
row = 0
|
|
515
|
+
while row < radar_h
|
|
409
516
|
line = @radar_line_buf.clear
|
|
410
517
|
if row < diam
|
|
411
518
|
line << " "
|
|
412
|
-
|
|
413
|
-
|
|
519
|
+
cx = 0
|
|
520
|
+
base = row * diam
|
|
521
|
+
while cx < diam
|
|
522
|
+
key = base + cx
|
|
523
|
+
if (etype = enemy_cells[key])
|
|
414
524
|
line << (etype == :executor ? RADAR_EXECUTOR : RADAR_CRAWLER)
|
|
415
|
-
elsif ally_cells[
|
|
525
|
+
elsif ally_cells[key]
|
|
416
526
|
line << RADAR_ALLY
|
|
417
|
-
elsif (drop = drop_cells[
|
|
527
|
+
elsif (drop = drop_cells[key])
|
|
418
528
|
line << radar_drop_glyph(drop)
|
|
419
|
-
elsif terminal_cells[
|
|
529
|
+
elsif terminal_cells[key]
|
|
420
530
|
line << RADAR_TERMINAL
|
|
421
531
|
elsif row == r && cx == r
|
|
422
532
|
line << RADAR_PLAYER
|
|
@@ -425,28 +535,36 @@ module Termfront
|
|
|
425
535
|
else
|
|
426
536
|
line << grid[row][cx]
|
|
427
537
|
end
|
|
538
|
+
cx += 1
|
|
428
539
|
end
|
|
429
540
|
line << (row < info_lines.size ? " #{info_lines[row]}" : "")
|
|
430
541
|
end
|
|
431
542
|
buf << TerminalOutput.fit_ansi(line, cols)
|
|
432
543
|
buf << "\r\n" if row < radar_h - 1
|
|
544
|
+
row += 1
|
|
433
545
|
end
|
|
434
546
|
end
|
|
435
547
|
|
|
436
548
|
def overlay_enemies_3d(pixels, view_h, view_w, dists, player, enemies, projectiles, drops)
|
|
437
549
|
dx = Math.cos(player.angle)
|
|
438
550
|
dy = Math.sin(player.angle)
|
|
439
|
-
|
|
440
|
-
|
|
551
|
+
tan_half_fov = Math.tan(Config::FOV / 2.0)
|
|
552
|
+
px = -dy * tan_half_fov
|
|
553
|
+
py = dx * tan_half_fov
|
|
441
554
|
virt_h = view_h * 2
|
|
555
|
+
half_virt_h = virt_h / 2
|
|
556
|
+
half_view_w = view_w / 2.0
|
|
557
|
+
view_w_last = view_w - 1
|
|
442
558
|
inv = 1.0 / (px * dy - py * dx)
|
|
559
|
+
player_x = player.x
|
|
560
|
+
player_y = player.y
|
|
443
561
|
|
|
444
562
|
@enemy_sprites.clear
|
|
445
563
|
enemies.each do |e|
|
|
446
564
|
next unless e.alive
|
|
447
565
|
|
|
448
|
-
ex = e.x -
|
|
449
|
-
ey = e.y -
|
|
566
|
+
ex = e.x - player_x
|
|
567
|
+
ey = e.y - player_y
|
|
450
568
|
tx = inv * (dy * ex - dx * ey)
|
|
451
569
|
tz = inv * (-py * ex + px * ey)
|
|
452
570
|
next if tz < 0.2
|
|
@@ -456,8 +574,8 @@ module Termfront
|
|
|
456
574
|
|
|
457
575
|
@proj_sprites.clear
|
|
458
576
|
projectiles.each do |p|
|
|
459
|
-
ex = p.x -
|
|
460
|
-
ey = p.y -
|
|
577
|
+
ex = p.x - player_x
|
|
578
|
+
ey = p.y - player_y
|
|
461
579
|
tx = inv * (dy * ex - dx * ey)
|
|
462
580
|
tz = inv * (-py * ex + px * ey)
|
|
463
581
|
next if tz < 0.2
|
|
@@ -468,78 +586,95 @@ module Termfront
|
|
|
468
586
|
@enemy_sprites.sort! { |a, b| b[0] <=> a[0] }
|
|
469
587
|
|
|
470
588
|
@enemy_sprites.each do |tz, tx, e|
|
|
471
|
-
sx = (
|
|
589
|
+
sx = (half_view_w * (1 + tx / tz)).to_i
|
|
472
590
|
sprite_h = (virt_h / tz).to_i
|
|
473
|
-
draw_top =
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
591
|
+
draw_top = half_virt_h - sprite_h / 2
|
|
592
|
+
draw_top = 0 if draw_top < 0
|
|
593
|
+
draw_bot = half_virt_h + sprite_h / 2
|
|
594
|
+
draw_bot = virt_h if draw_bot > virt_h
|
|
595
|
+
sprite_w = sprite_h / 2
|
|
596
|
+
start_x = sx - sprite_w / 2
|
|
597
|
+
start_x = 0 if start_x < 0
|
|
598
|
+
end_x = sx + sprite_w / 2
|
|
599
|
+
end_x = view_w_last if end_x > view_w_last
|
|
478
600
|
|
|
479
601
|
actual_h = draw_bot - draw_top
|
|
480
602
|
actual_w = end_x - start_x + 1
|
|
481
603
|
next if actual_h < 1 || actual_w < 1
|
|
482
604
|
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
605
|
+
sprite_id = e.sprite_id
|
|
606
|
+
sprite_fn = Sprite::REGISTRY[sprite_id]
|
|
607
|
+
fallback_color = sprite_id == :executor ? EXECUTOR_FALLBACK : CRAWLER_FALLBACK
|
|
608
|
+
use_shape = actual_h >= 6 && sprite_fn
|
|
609
|
+
r_top = (draw_top + 1) >> 1
|
|
610
|
+
r_bot = draw_bot >> 1
|
|
611
|
+
actual_h_f = actual_h.to_f
|
|
612
|
+
actual_w_f = actual_w.to_f
|
|
613
|
+
|
|
614
|
+
c = start_x
|
|
615
|
+
while c <= end_x
|
|
616
|
+
if c >= 0 && c < view_w && dists[c] >= tz
|
|
617
|
+
nx = (c - start_x) / actual_w_f
|
|
618
|
+
|
|
619
|
+
r = r_top
|
|
620
|
+
while r < r_bot
|
|
621
|
+
vp0 = r << 1
|
|
622
|
+
vp1 = vp0 + 1
|
|
623
|
+
top_in = vp0 >= draw_top && vp0 < draw_bot
|
|
624
|
+
bot_in = vp1 >= draw_top && vp1 < draw_bot
|
|
625
|
+
|
|
626
|
+
if top_in || bot_in
|
|
627
|
+
if use_shape
|
|
628
|
+
ny0 = top_in ? (vp0 - draw_top) / actual_h_f : nil
|
|
629
|
+
ny1 = bot_in ? (vp1 - draw_top) / actual_h_f : nil
|
|
630
|
+
top_color = ny0 ? sprite_fn.call(nx, ny0) : nil
|
|
631
|
+
bot_color = ny1 ? sprite_fn.call(nx, ny1) : nil
|
|
632
|
+
if top_color || bot_color
|
|
633
|
+
pixels[vp0][c] = top_color if top_color
|
|
634
|
+
pixels[vp1][c] = bot_color if bot_color
|
|
635
|
+
end
|
|
636
|
+
else
|
|
637
|
+
pixels[vp0][c] = fallback_color if top_in
|
|
638
|
+
pixels[vp1][c] = fallback_color if bot_in
|
|
639
|
+
end
|
|
640
|
+
end
|
|
641
|
+
r += 1
|
|
513
642
|
end
|
|
514
643
|
end
|
|
644
|
+
c += 1
|
|
515
645
|
end
|
|
516
646
|
|
|
517
647
|
next unless e.max_hp > 1
|
|
518
648
|
|
|
519
|
-
bar_row =
|
|
649
|
+
bar_row = r_top - 1
|
|
520
650
|
next unless bar_row >= 0 && bar_row < view_h
|
|
521
651
|
|
|
522
|
-
bar_w =
|
|
523
|
-
bar_sx =
|
|
524
|
-
|
|
652
|
+
bar_w = actual_w > 2 ? actual_w : 2
|
|
653
|
+
bar_sx = sx - bar_w / 2
|
|
654
|
+
bar_sx = 0 if bar_sx < 0
|
|
655
|
+
bar_ex = bar_sx + bar_w - 1
|
|
656
|
+
bar_ex = view_w_last if bar_ex > view_w_last
|
|
525
657
|
hp_pct = e.hp.to_f / e.max_hp
|
|
526
658
|
filled = (hp_pct * (bar_ex - bar_sx + 1)).ceil
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
659
|
+
bar_vp0 = bar_row << 1
|
|
660
|
+
bar_vp1 = bar_vp0 + 1
|
|
661
|
+
c = bar_sx
|
|
662
|
+
while c <= bar_ex
|
|
663
|
+
if c >= 0 && c < view_w && dists[c] >= tz
|
|
664
|
+
ci = c - bar_sx
|
|
665
|
+
color = ci < filled ? ENEMY_BAR_FILL : ENEMY_BAR_EMPTY
|
|
666
|
+
pixels[bar_vp0][c] = color
|
|
667
|
+
pixels[bar_vp1][c] = color
|
|
668
|
+
end
|
|
669
|
+
c += 1
|
|
535
670
|
end
|
|
536
671
|
end
|
|
537
672
|
|
|
538
673
|
# Render weapon drops
|
|
539
674
|
@drop_sprites.clear
|
|
540
675
|
drops.each do |d|
|
|
541
|
-
ex = d.x -
|
|
542
|
-
ey = d.y -
|
|
676
|
+
ex = d.x - player_x
|
|
677
|
+
ey = d.y - player_y
|
|
543
678
|
tx = inv * (dy * ex - dx * ey)
|
|
544
679
|
tz = inv * (-py * ex + px * ey)
|
|
545
680
|
next if tz < 0.2
|
|
@@ -549,70 +684,83 @@ module Termfront
|
|
|
549
684
|
@drop_sprites.sort! { |a, b| b[0] <=> a[0] }
|
|
550
685
|
|
|
551
686
|
@drop_sprites.each do |tz, tx, d|
|
|
552
|
-
sx = (
|
|
553
|
-
sprite_h = (virt_h / tz * 0.3).to_i.clamp(2,
|
|
554
|
-
ground = (
|
|
555
|
-
draw_bot =
|
|
556
|
-
draw_top =
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
687
|
+
sx = (half_view_w * (1 + tx / tz)).to_i
|
|
688
|
+
sprite_h = (virt_h / tz * 0.3).to_i.clamp(2, half_virt_h)
|
|
689
|
+
ground = (half_virt_h + virt_h / tz * 0.35).to_i
|
|
690
|
+
draw_bot = ground < virt_h ? ground : virt_h
|
|
691
|
+
draw_top = draw_bot - sprite_h
|
|
692
|
+
draw_top = 0 if draw_top < 0
|
|
693
|
+
sprite_w = (sprite_h / 2).clamp(1, 6)
|
|
694
|
+
start_x = sx - sprite_w / 2
|
|
695
|
+
start_x = 0 if start_x < 0
|
|
696
|
+
end_x = sx + sprite_w / 2
|
|
697
|
+
end_x = view_w_last if end_x > view_w_last
|
|
560
698
|
|
|
561
699
|
color = d.sprite_color
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
700
|
+
r_top = (draw_top + 1) >> 1
|
|
701
|
+
r_bot = draw_bot >> 1
|
|
702
|
+
|
|
703
|
+
c = start_x
|
|
704
|
+
while c <= end_x
|
|
705
|
+
if c >= 0 && c < view_w && dists[c] >= tz
|
|
706
|
+
r = r_top
|
|
707
|
+
while r < r_bot
|
|
708
|
+
if r >= 0 && r < view_h
|
|
709
|
+
vp0 = r << 1
|
|
710
|
+
vp1 = vp0 + 1
|
|
711
|
+
top_in = vp0 >= draw_top && vp0 < draw_bot
|
|
712
|
+
bot_in = vp1 >= draw_top && vp1 < draw_bot
|
|
713
|
+
if top_in || bot_in
|
|
714
|
+
pixels[vp0][c] = color if top_in
|
|
715
|
+
pixels[vp1][c] = color if bot_in
|
|
716
|
+
end
|
|
717
|
+
end
|
|
718
|
+
r += 1
|
|
719
|
+
end
|
|
580
720
|
end
|
|
721
|
+
c += 1
|
|
581
722
|
end
|
|
582
723
|
end
|
|
583
724
|
|
|
584
725
|
# Render projectiles
|
|
585
726
|
@proj_sprites.sort! { |a, b| b[0] <=> a[0] }
|
|
586
727
|
@proj_sprites.each do |tz, tx, p|
|
|
587
|
-
sx = (
|
|
728
|
+
sx = (half_view_w * (1 + tx / tz)).to_i
|
|
588
729
|
pw = (4.0 / tz).ceil.clamp(1, 5)
|
|
589
730
|
ph = (virt_h / tz * 0.15).ceil.clamp(2, 6)
|
|
590
|
-
|
|
591
|
-
draw_top =
|
|
592
|
-
draw_bot =
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
731
|
+
draw_top = half_virt_h - ph / 2
|
|
732
|
+
draw_top = 0 if draw_top < 0
|
|
733
|
+
draw_bot = half_virt_h + ph / 2
|
|
734
|
+
draw_bot = draw_top + 2 if draw_bot < draw_top + 2
|
|
735
|
+
draw_bot = virt_h if draw_bot > virt_h
|
|
736
|
+
start_x = sx - pw / 2
|
|
737
|
+
start_x = 0 if start_x < 0
|
|
738
|
+
end_x = sx + pw / 2
|
|
739
|
+
end_x = view_w_last if end_x > view_w_last
|
|
740
|
+
proj_color = p.type == :executor ? PROJ_EXECUTOR : PROJ_DEFAULT
|
|
741
|
+
r_top = (draw_top + 1) >> 1
|
|
742
|
+
r_bot = draw_bot >> 1
|
|
743
|
+
r_bot = r_top + 1 if r_bot < r_top + 1
|
|
744
|
+
|
|
745
|
+
c = start_x
|
|
746
|
+
while c <= end_x
|
|
747
|
+
if c >= 0 && c < view_w && dists[c] >= tz
|
|
748
|
+
r = r_top
|
|
749
|
+
while r < r_bot
|
|
750
|
+
if r >= 0 && r < view_h
|
|
751
|
+
vp0 = r << 1
|
|
752
|
+
vp1 = vp0 + 1
|
|
753
|
+
top_in = vp0 >= draw_top && vp0 < draw_bot
|
|
754
|
+
bot_in = vp1 >= draw_top && vp1 < draw_bot
|
|
755
|
+
if top_in || bot_in
|
|
756
|
+
pixels[vp0][c] = proj_color if top_in
|
|
757
|
+
pixels[vp1][c] = proj_color if bot_in
|
|
758
|
+
end
|
|
759
|
+
end
|
|
760
|
+
r += 1
|
|
761
|
+
end
|
|
615
762
|
end
|
|
763
|
+
c += 1
|
|
616
764
|
end
|
|
617
765
|
end
|
|
618
766
|
end
|
|
@@ -677,8 +825,8 @@ module Termfront
|
|
|
677
825
|
pixels[vp0][c] = top_color if top_color
|
|
678
826
|
pixels[vp1][c] = bot_color if bot_color
|
|
679
827
|
else
|
|
680
|
-
pixels[vp0][c] =
|
|
681
|
-
pixels[vp1][c] =
|
|
828
|
+
pixels[vp0][c] = ALLY_FALLBACK if top_in
|
|
829
|
+
pixels[vp1][c] = ALLY_FALLBACK if bot_in
|
|
682
830
|
end
|
|
683
831
|
end
|
|
684
832
|
end
|
|
@@ -698,7 +846,7 @@ module Termfront
|
|
|
698
846
|
next if dists[c] < tz
|
|
699
847
|
|
|
700
848
|
ci = c - bar_sx
|
|
701
|
-
color = ci < filled ?
|
|
849
|
+
color = ci < filled ? ALLY_BAR_FILL : ALLY_BAR_EMPTY
|
|
702
850
|
pixels[bar_row * 2][c] = color
|
|
703
851
|
pixels[bar_row * 2 + 1][c] = color
|
|
704
852
|
end
|
|
@@ -708,9 +856,10 @@ module Termfront
|
|
|
708
856
|
def overlay_damage_flash(pixels, view_h, view_w, player)
|
|
709
857
|
return unless player.damage_flash > 0
|
|
710
858
|
|
|
711
|
-
intensity = player.damage_flash * 60
|
|
859
|
+
intensity = (player.damage_flash * 60).to_i
|
|
860
|
+
intensity = 255 if intensity > 255
|
|
712
861
|
flash_w = 2
|
|
713
|
-
color =
|
|
862
|
+
color = DAMAGE_FLASH_RAMP[intensity]
|
|
714
863
|
|
|
715
864
|
view_h.times do |r|
|
|
716
865
|
vp0 = r * 2
|
|
@@ -739,24 +888,5 @@ module Termfront
|
|
|
739
888
|
buf << "\e[#{cr};#{fs}H\e[93m#{"*" * (fe - fs + 1)}\e[0m"
|
|
740
889
|
end
|
|
741
890
|
|
|
742
|
-
def bg_only?(color)
|
|
743
|
-
color.is_a?(Integer)
|
|
744
|
-
end
|
|
745
|
-
|
|
746
|
-
def ansi_fg(color)
|
|
747
|
-
if color.is_a?(Integer)
|
|
748
|
-
FG_256[color]
|
|
749
|
-
else
|
|
750
|
-
@fg_truecolor_cache[color] ||= "\e[38;2;#{color}m".freeze
|
|
751
|
-
end
|
|
752
|
-
end
|
|
753
|
-
|
|
754
|
-
def ansi_bg(color)
|
|
755
|
-
if color.is_a?(Integer)
|
|
756
|
-
BG_256[color]
|
|
757
|
-
else
|
|
758
|
-
@bg_truecolor_cache[color] ||= "\e[48;2;#{color}m".freeze
|
|
759
|
-
end
|
|
760
|
-
end
|
|
761
891
|
end
|
|
762
892
|
end
|