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.
@@ -4,46 +4,82 @@ module Termfront
4
4
  module Sprite
5
5
  module_function
6
6
 
7
+ EXECUTOR_EYE = Color.rgb_to_256(180, 120, 255)
8
+ EXECUTOR_HEAD = Color.rgb_to_256(130, 80, 220)
9
+ EXECUTOR_NECK = Color.rgb_to_256(90, 50, 180)
10
+ EXECUTOR_BODY = Color.rgb_to_256(80, 40, 160)
11
+
12
+ CRAWLER_EYE = Color.rgb_to_256(255, 240, 100)
13
+ CRAWLER_BODY = Color.rgb_to_256(220, 140, 30)
14
+ CRAWLER_LEG = Color.rgb_to_256(160, 100, 20)
15
+
16
+ PLAYER_EYE = Color.rgb_to_256(140, 220, 255)
17
+ PLAYER_HEAD = Color.rgb_to_256(40, 130, 180)
18
+ PLAYER_NECK = Color.rgb_to_256(30, 100, 160)
19
+ PLAYER_BODY = Color.rgb_to_256(25, 80, 140)
20
+
21
+ PLAYER_ENEMY_EYE = Color.rgb_to_256(210, 170, 165)
22
+ PLAYER_ENEMY_HEAD = Color.rgb_to_256(110, 80, 90)
23
+ PLAYER_ENEMY_NECK = Color.rgb_to_256(100, 50, 70)
24
+ PLAYER_ENEMY_BODY = Color.rgb_to_256(95, 30, 50)
25
+
26
+ DUMMY_HEAD = Color.rgb_to_256(235, 80, 80)
27
+ DUMMY_TORSO = Color.rgb_to_256(210, 210, 210)
28
+ DUMMY_LOWER = Color.rgb_to_256(200, 200, 200)
29
+ DUMMY_LEG = Color.rgb_to_256(180, 180, 180)
30
+
7
31
  def executor(nx, ny)
8
- return "180;120;255" if ((nx - 0.43) / 0.045)**2 + ((ny - 0.11) / 0.045)**2 <= 1.0
9
- return "180;120;255" if ((nx - 0.57) / 0.045)**2 + ((ny - 0.11) / 0.045)**2 <= 1.0
10
- return "130;80;220" if ((nx - 0.5) / 0.18)**2 + ((ny - 0.12) / 0.12)**2 <= 1.0
11
- return "90;50;180" if ((nx - 0.5) / 0.38)**2 + ((ny - 0.30) / 0.08)**2 <= 1.0
12
- return "80;40;160" if ((nx - 0.5) / 0.25)**2 + ((ny - 0.50) / 0.22)**2 <= 1.0
13
- return "80;40;160" if ((nx - 0.38) / 0.10)**2 + ((ny - 0.85) / 0.15)**2 <= 1.0
14
- return "80;40;160" if ((nx - 0.62) / 0.10)**2 + ((ny - 0.85) / 0.15)**2 <= 1.0
32
+ return EXECUTOR_EYE if ((nx - 0.43) / 0.045)**2 + ((ny - 0.11) / 0.045)**2 <= 1.0
33
+ return EXECUTOR_EYE if ((nx - 0.57) / 0.045)**2 + ((ny - 0.11) / 0.045)**2 <= 1.0
34
+ return EXECUTOR_HEAD if ((nx - 0.5) / 0.18)**2 + ((ny - 0.12) / 0.12)**2 <= 1.0
35
+ return EXECUTOR_NECK if ((nx - 0.5) / 0.38)**2 + ((ny - 0.30) / 0.08)**2 <= 1.0
36
+ return EXECUTOR_BODY if ((nx - 0.5) / 0.25)**2 + ((ny - 0.50) / 0.22)**2 <= 1.0
37
+ return EXECUTOR_BODY if ((nx - 0.38) / 0.10)**2 + ((ny - 0.85) / 0.15)**2 <= 1.0
38
+ return EXECUTOR_BODY if ((nx - 0.62) / 0.10)**2 + ((ny - 0.85) / 0.15)**2 <= 1.0
15
39
 
16
40
  nil
17
41
  end
18
42
 
19
43
  def crawler(nx, ny)
20
- return "255;240;100" if ((nx - 0.36) / 0.063)**2 + ((ny - 0.28) / 0.063)**2 <= 1.0
21
- return "255;240;100" if ((nx - 0.64) / 0.063)**2 + ((ny - 0.28) / 0.063)**2 <= 1.0
22
- return "220;140;30" if ((nx - 0.5) / 0.40)**2 + ((ny - 0.40) / 0.40)**2 <= 1.0
23
- return "160;100;20" if ((nx - 0.35) / 0.12)**2 + ((ny - 0.90) / 0.10)**2 <= 1.0
24
- return "160;100;20" if ((nx - 0.65) / 0.12)**2 + ((ny - 0.90) / 0.10)**2 <= 1.0
44
+ return CRAWLER_EYE if ((nx - 0.36) / 0.063)**2 + ((ny - 0.28) / 0.063)**2 <= 1.0
45
+ return CRAWLER_EYE if ((nx - 0.64) / 0.063)**2 + ((ny - 0.28) / 0.063)**2 <= 1.0
46
+ return CRAWLER_BODY if ((nx - 0.5) / 0.40)**2 + ((ny - 0.40) / 0.40)**2 <= 1.0
47
+ return CRAWLER_LEG if ((nx - 0.35) / 0.12)**2 + ((ny - 0.90) / 0.10)**2 <= 1.0
48
+ return CRAWLER_LEG if ((nx - 0.65) / 0.12)**2 + ((ny - 0.90) / 0.10)**2 <= 1.0
25
49
 
26
50
  nil
27
51
  end
28
52
 
29
53
  def player(nx, ny)
30
- return "140;220;255" if ((nx - 0.43) / 0.045)**2 + ((ny - 0.11) / 0.045)**2 <= 1.0
31
- return "140;220;255" if ((nx - 0.57) / 0.045)**2 + ((ny - 0.11) / 0.045)**2 <= 1.0
32
- return "40;130;180" if ((nx - 0.5) / 0.18)**2 + ((ny - 0.12) / 0.12)**2 <= 1.0
33
- return "30;100;160" if ((nx - 0.5) / 0.38)**2 + ((ny - 0.30) / 0.08)**2 <= 1.0
34
- return "25;80;140" if ((nx - 0.5) / 0.25)**2 + ((ny - 0.50) / 0.22)**2 <= 1.0
35
- return "25;80;140" if ((nx - 0.38) / 0.10)**2 + ((ny - 0.85) / 0.15)**2 <= 1.0
36
- return "25;80;140" if ((nx - 0.62) / 0.10)**2 + ((ny - 0.85) / 0.15)**2 <= 1.0
54
+ return PLAYER_EYE if ((nx - 0.43) / 0.045)**2 + ((ny - 0.11) / 0.045)**2 <= 1.0
55
+ return PLAYER_EYE if ((nx - 0.57) / 0.045)**2 + ((ny - 0.11) / 0.045)**2 <= 1.0
56
+ return PLAYER_HEAD if ((nx - 0.5) / 0.18)**2 + ((ny - 0.12) / 0.12)**2 <= 1.0
57
+ return PLAYER_NECK if ((nx - 0.5) / 0.38)**2 + ((ny - 0.30) / 0.08)**2 <= 1.0
58
+ return PLAYER_BODY if ((nx - 0.5) / 0.25)**2 + ((ny - 0.50) / 0.22)**2 <= 1.0
59
+ return PLAYER_BODY if ((nx - 0.38) / 0.10)**2 + ((ny - 0.85) / 0.15)**2 <= 1.0
60
+ return PLAYER_BODY if ((nx - 0.62) / 0.10)**2 + ((ny - 0.85) / 0.15)**2 <= 1.0
61
+
62
+ nil
63
+ end
64
+
65
+ def player_enemy(nx, ny)
66
+ return PLAYER_ENEMY_EYE if ((nx - 0.43) / 0.045)**2 + ((ny - 0.11) / 0.045)**2 <= 1.0
67
+ return PLAYER_ENEMY_EYE if ((nx - 0.57) / 0.045)**2 + ((ny - 0.11) / 0.045)**2 <= 1.0
68
+ return PLAYER_ENEMY_HEAD if ((nx - 0.5) / 0.18)**2 + ((ny - 0.12) / 0.12)**2 <= 1.0
69
+ return PLAYER_ENEMY_NECK if ((nx - 0.5) / 0.38)**2 + ((ny - 0.30) / 0.08)**2 <= 1.0
70
+ return PLAYER_ENEMY_BODY if ((nx - 0.5) / 0.25)**2 + ((ny - 0.50) / 0.22)**2 <= 1.0
71
+ return PLAYER_ENEMY_BODY if ((nx - 0.38) / 0.10)**2 + ((ny - 0.85) / 0.15)**2 <= 1.0
72
+ return PLAYER_ENEMY_BODY if ((nx - 0.62) / 0.10)**2 + ((ny - 0.85) / 0.15)**2 <= 1.0
37
73
 
38
74
  nil
39
75
  end
40
76
 
41
77
  def training_dummy(nx, ny)
42
- return "235;80;80" if ((nx - 0.5) / 0.18)**2 + ((ny - 0.18) / 0.14)**2 <= 1.0
43
- return "210;210;210" if ((nx - 0.5) / 0.08)**2 + ((ny - 0.42) / 0.14)**2 <= 1.0
44
- return "200;200;200" if ((nx - 0.5) / 0.22)**2 + ((ny - 0.66) / 0.12)**2 <= 1.0
45
- return "180;180;180" if ((nx - 0.38) / 0.08)**2 + ((ny - 0.90) / 0.12)**2 <= 1.0
46
- return "180;180;180" if ((nx - 0.62) / 0.08)**2 + ((ny - 0.90) / 0.12)**2 <= 1.0
78
+ return DUMMY_HEAD if ((nx - 0.5) / 0.18)**2 + ((ny - 0.18) / 0.14)**2 <= 1.0
79
+ return DUMMY_TORSO if ((nx - 0.5) / 0.08)**2 + ((ny - 0.42) / 0.14)**2 <= 1.0
80
+ return DUMMY_LOWER if ((nx - 0.5) / 0.22)**2 + ((ny - 0.66) / 0.12)**2 <= 1.0
81
+ return DUMMY_LEG if ((nx - 0.38) / 0.08)**2 + ((ny - 0.90) / 0.12)**2 <= 1.0
82
+ return DUMMY_LEG if ((nx - 0.62) / 0.08)**2 + ((ny - 0.90) / 0.12)**2 <= 1.0
47
83
 
48
84
  nil
49
85
  end
@@ -3,7 +3,6 @@
3
3
  module Termfront
4
4
  module TerminalOutput
5
5
  module_function
6
- ANSI_PATTERN = /\e\[[0-9;]*[A-Za-z]/.freeze
7
6
 
8
7
  def begin_frame(home: false, clear: false)
9
8
  buf = +""
@@ -17,25 +16,41 @@ module Termfront
17
16
  end
18
17
 
19
18
  def fit_ansi(text, width)
20
- visible = 0
21
19
  out = +""
22
- index = 0
20
+ visible = 0
21
+ any_ansi = false
22
+ i = 0
23
+ len = text.bytesize
24
+ segment_start = 0
23
25
 
24
- while index < text.length && visible < width
25
- if (match = ANSI_PATTERN.match(text, index)) && match.begin(0) == index
26
- out << match[0]
27
- index = match.end(0)
28
- next
29
- end
26
+ while i < len
27
+ byte = text.getbyte(i)
28
+ if byte == 0x1B
29
+ out << text.byteslice(segment_start, i - segment_start) if i > segment_start
30
30
 
31
- char = text[index]
32
- out << char
33
- visible += 1
34
- index += 1
31
+ esc_start = i
32
+ i += 1
33
+ while i < len
34
+ b = text.getbyte(i)
35
+ i += 1
36
+ break if (b >= 65 && b <= 90) || (b >= 97 && b <= 122)
37
+ end
38
+ out << text.byteslice(esc_start, i - esc_start)
39
+ any_ansi = true
40
+ segment_start = i
41
+ else
42
+ if (byte & 0xC0) != 0x80
43
+ break if visible >= width
44
+
45
+ visible += 1
46
+ end
47
+ i += 1
48
+ end
35
49
  end
36
50
 
37
- out << "\e[0m" if out.include?("\e[")
38
- out << (" " * (width - visible)) if visible < width
51
+ out << text.byteslice(segment_start, i - segment_start) if i > segment_start
52
+ out << "\e[0m" if any_ansi
53
+ (width - visible).times { out << " " } if visible < width
39
54
  out
40
55
  end
41
56
 
@@ -47,7 +62,7 @@ module Termfront
47
62
  begin
48
63
  written = io.syswrite(data.byteslice(total, bytes - total))
49
64
  total += written
50
- rescue IO::WaitWritable
65
+ rescue IO::WaitWritable, Errno::EAGAIN
51
66
  IO.select(nil, [io])
52
67
  end
53
68
  end
@@ -7,12 +7,35 @@ module Termfront
7
7
  [13.0, 8.0], [10.0, 8.5], [5.0, 8.5], [2.5, 5.0]
8
8
  ].freeze
9
9
 
10
+ TITLE_TEXT = "T E R M F R O N T"
11
+ SUB_TEXT = "Terminal FPS"
12
+ MENU_ITEMS = ["[P] PvP", "[F] Wavesfight", "[C] Campaign", "[S] Training", "[Q] Quit"].freeze
13
+
14
+ TITLE_CEIL_C = Color.rgb_to_256(0, 0, 95)
15
+ TITLE_FLOOR_C = Color.rgb_to_256(28, 28, 28)
16
+ TITLE_EXECUTOR_FALLBACK = Color.rgb_to_256(100, 60, 200)
17
+ TITLE_CRAWLER_FALLBACK = Color.rgb_to_256(220, 140, 30)
18
+
10
19
  def initialize(stdout)
11
20
  @stdout = stdout
12
21
  @title_spin = 0.0
13
22
  @demo_wp_idx = 0
14
23
  @demo_wp_t = 0.0
15
24
  @demo_fire = 0
25
+
26
+ mission_class = Mission::Base.campaign.first
27
+ if mission_class
28
+ m = mission_class.new
29
+ @demo_map = m.map_data.map { |row| row.is_a?(Array) ? row : row.chars }.freeze
30
+ @demo_map_h = @demo_map.size
31
+ @demo_map_w = @demo_map[0].size
32
+ @demo_enemies = m.enemy_defs.freeze
33
+ end
34
+
35
+ @static_lines_cols = 0
36
+ @static_title_line = nil
37
+ @static_sub_line = nil
38
+ @static_menu_lines = nil
16
39
  end
17
40
 
18
41
  def show
@@ -24,11 +47,15 @@ module Termfront
24
47
  TerminalOutput.write_all(@stdout, TerminalOutput.begin_frame(home: true, clear: true) + TerminalOutput.end_frame)
25
48
 
26
49
  STDIN.raw do |stdin|
50
+ last_render = clock - Config::RENDER_DT
27
51
  loop do
28
52
  now = clock
29
53
  @title_spin += 0.015
30
54
 
31
- render
55
+ if now - last_render >= Config::RENDER_DT
56
+ render
57
+ last_render = now
58
+ end
32
59
 
33
60
  while IO.select([stdin], nil, nil, 0)
34
61
  begin
@@ -60,6 +87,20 @@ module Termfront
60
87
  Process.clock_gettime(Process::CLOCK_MONOTONIC)
61
88
  end
62
89
 
90
+ def ensure_static_lines(cols)
91
+ return if @static_lines_cols == cols
92
+
93
+ tc = [(cols - TITLE_TEXT.size) / 2 + 1, 1].max
94
+ sc = [(cols - SUB_TEXT.size) / 2 + 1, 1].max
95
+ @static_title_line = TerminalOutput.fit_ansi("#{" " * (tc - 1)}\e[1;38;2;120;140;255m#{TITLE_TEXT}\e[0m", cols)
96
+ @static_sub_line = TerminalOutput.fit_ansi("#{" " * (sc - 1)}\e[38;2;80;80;120m#{SUB_TEXT}\e[0m", cols)
97
+ @static_menu_lines = MENU_ITEMS.map do |item|
98
+ ic = [(cols - item.size) / 2 + 1, 1].max
99
+ TerminalOutput.fit_ansi("#{" " * (ic - 1)}\e[97m#{item}\e[0m", cols)
100
+ end
101
+ @static_lines_cols = cols
102
+ end
103
+
63
104
  def render
64
105
  rows, cols = @stdout.winsize
65
106
  rows = [rows, 10].max
@@ -76,13 +117,11 @@ module Termfront
76
117
  virt_h = th * 2
77
118
  color = Array.new(tw * virt_h, nil)
78
119
 
79
- mission = Mission::Base.campaign.first
80
- return unless mission
120
+ return unless @demo_map
81
121
 
82
- m = mission.new
83
- demo_map = m.map_data.map { |r| r.is_a?(Array) ? r : r.chars }
84
- dm_h = demo_map.size
85
- dm_w = demo_map[0].size
122
+ demo_map = @demo_map
123
+ dm_h = @demo_map_h
124
+ dm_w = @demo_map_w
86
125
 
87
126
  @demo_wp_t += Config::DEMO_SPEED
88
127
  if @demo_wp_t >= 1.0
@@ -109,8 +148,8 @@ module Termfront
109
148
  dists = Array.new(tw, 100.0)
110
149
  horizon = (virt_h / 2 + bob).to_i
111
150
 
112
- ceil_c = "0;0;95"
113
- floor_c = "28;28;28"
151
+ ceil_c = TITLE_CEIL_C
152
+ floor_c = TITLE_FLOOR_C
114
153
 
115
154
  tw.times do |col|
116
155
  ray_a = cam_a - half_fov + fov * col.to_f / tw
@@ -167,9 +206,9 @@ module Termfront
167
206
  flash = @demo_fire / 4.0 * (1.0 - dist / 4.0)
168
207
  rr = (grey + flash * 160).to_i.clamp(0, 255)
169
208
  gg = (grey + flash * 60).to_i.clamp(0, 255)
170
- wall_c = "#{rr};#{gg};#{grey}"
209
+ wall_c = Color.rgb_to_256(rr, gg, grey)
171
210
  else
172
- wall_c = "#{grey};#{grey};#{grey}"
211
+ wall_c = Color.rgb_to_256(grey, grey, grey)
173
212
  end
174
213
 
175
214
  virt_h.times do |vr|
@@ -184,7 +223,7 @@ module Termfront
184
223
  end
185
224
 
186
225
  # Demo enemies
187
- demo_enemies = m.enemy_defs
226
+ demo_enemies = @demo_enemies
188
227
  ddx = Math.cos(cam_a)
189
228
  ddy = Math.sin(cam_a)
190
229
  ppx = -ddy * Math.tan(fov / 2.0)
@@ -237,54 +276,158 @@ module Termfront
237
276
  sc = Sprite.for(type, nx, ny)
238
277
  next unless sc
239
278
  else
240
- sc = type == :executor ? "100;60;200" : "220;140;30"
279
+ sc = type == :executor ? TITLE_EXECUTOR_FALLBACK : TITLE_CRAWLER_FALLBACK
241
280
  end
242
281
  color[vr * tw + c] = sc
243
282
  end
244
283
  end
245
284
  end
246
285
 
247
- # Half-block rendering
248
- th.times do |r|
249
- vp0 = r * 2
250
- vp1 = r * 2 + 1
286
+ # Half-block rendering - build each line directly to skip fit_ansi
287
+ fg_256 = Renderer::FG_256
288
+ bg_256 = Renderer::BG_256
289
+ cap_w = tw < cols ? tw : cols
290
+
291
+ r = 0
292
+ while r < th
293
+ vp0_offset = (r * 2) * tw
294
+ vp1_offset = vp0_offset + tw
295
+
296
+ first_tc = color[vp0_offset]
297
+ first_bc = color[vp1_offset]
298
+
299
+ if first_tc && first_tc == first_bc
300
+ uniform = true
301
+ cu = 1
302
+ while cu < cap_w
303
+ if color[vp0_offset + cu] != first_tc || color[vp1_offset + cu] != first_bc
304
+ uniform = false
305
+ break
306
+ end
307
+ cu += 1
308
+ end
309
+ if uniform
310
+ lines[r] = +bg_256[first_tc] << "\e[K\e[0m"
311
+ r += 1
312
+ next
313
+ end
314
+ elsif first_tc.nil? && first_bc.nil?
315
+ all_nil = true
316
+ cu = 1
317
+ while cu < cap_w
318
+ if !color[vp0_offset + cu].nil? || !color[vp1_offset + cu].nil?
319
+ all_nil = false
320
+ break
321
+ end
322
+ cu += 1
323
+ end
324
+ if all_nil
325
+ lines[r] = " " * cols
326
+ r += 1
327
+ next
328
+ end
329
+ end
330
+
251
331
  line = +""
252
- tw.times do |c|
253
- tc = color[vp0 * tw + c]
254
- bc = color[vp1 * tw + c]
255
- line << if tc && bc
256
- if tc == bc
257
- "\e[38;2;#{tc}m\xE2\x96\x88\e[0m"
258
- else
259
- "\e[38;2;#{tc};48;2;#{bc}m\xE2\x96\x80\e[0m"
260
- end
261
- elsif tc
262
- "\e[38;2;#{tc}m\xE2\x96\x80\e[0m"
263
- elsif bc
264
- "\e[38;2;#{bc}m\xE2\x96\x84\e[0m"
265
- else
266
- " "
267
- end
332
+ pfg = nil
333
+ pbg = nil
334
+ visible = 0
335
+
336
+ c = 0
337
+ while c < tw && visible < cap_w
338
+ tc = color[vp0_offset + c]
339
+ bc = color[vp1_offset + c]
340
+
341
+ if tc == bc
342
+ run_end = c + 1
343
+ while run_end < tw &&
344
+ color[vp0_offset + run_end] == tc &&
345
+ color[vp1_offset + run_end] == bc
346
+ run_end += 1
347
+ end
348
+ n = run_end - c
349
+ n = cap_w - visible if visible + n > cap_w
350
+
351
+ if tc.nil?
352
+ if pfg || pbg
353
+ line << "\e[0m"
354
+ pfg = nil
355
+ pbg = nil
356
+ end
357
+ line << (n == 1 ? " " : " " * n)
358
+ else
359
+ if pfg != tc || pbg
360
+ line << fg_256[tc]
361
+ line << "\e[49m" if pbg
362
+ pfg = tc
363
+ pbg = nil
364
+ end
365
+ line << (n == 1 ? "\xE2\x96\x88" : "\xE2\x96\x88" * n)
366
+ end
367
+ visible += n
368
+ c += n
369
+ else
370
+ if tc && bc
371
+ if pfg != tc || pbg != bc
372
+ line << fg_256[tc]
373
+ line << bg_256[bc]
374
+ pfg = tc
375
+ pbg = bc
376
+ end
377
+ line << "\xE2\x96\x80"
378
+ elsif tc
379
+ if pfg != tc || pbg
380
+ line << fg_256[tc]
381
+ line << "\e[49m" if pbg
382
+ pfg = tc
383
+ pbg = nil
384
+ end
385
+ line << "\xE2\x96\x80"
386
+ elsif bc
387
+ if pfg != bc || pbg
388
+ line << fg_256[bc]
389
+ line << "\e[49m" if pbg
390
+ pfg = bc
391
+ pbg = nil
392
+ end
393
+ line << "\xE2\x96\x84"
394
+ else
395
+ if pfg || pbg
396
+ line << "\e[0m"
397
+ pfg = nil
398
+ pbg = nil
399
+ end
400
+ line << " "
401
+ end
402
+ visible += 1
403
+ c += 1
404
+ end
405
+ end
406
+
407
+ if visible < cols
408
+ if pfg || pbg
409
+ line << "\e[0m"
410
+ pfg = nil
411
+ pbg = nil
412
+ end
413
+ line << (" " * (cols - visible))
268
414
  end
269
- lines[r] = TerminalOutput.fit_ansi(line, cols)
415
+ line << "\e[0m" if pfg || pbg
416
+
417
+ lines[r] = line
418
+ r += 1
270
419
  end
271
420
 
272
- # Title text
421
+ # Title text + menu items (memoized per cols)
422
+ ensure_static_lines(cols)
423
+
273
424
  title_row = [[th + 1, rows - 4].min, 1].max
274
- title = "T E R M F R O N T"
275
- sub = "Terminal FPS"
276
- tc = [(cols - title.size) / 2 + 1, 1].max
277
- sc = [(cols - sub.size) / 2 + 1, 1].max
278
- lines[title_row - 1] = TerminalOutput.fit_ansi("#{" " * (tc - 1)}\e[1;38;2;120;140;255m#{title}\e[0m", cols)
279
- lines[title_row] = TerminalOutput.fit_ansi("#{" " * (sc - 1)}\e[38;2;80;80;120m#{sub}\e[0m", cols)
280
-
281
- # Menu items
282
- items = ["[P] PvP", "[F] Wavesfight", "[C] Campaign", "[S] Training", "[Q] Quit"]
283
- items_count_for_menu = items.size
284
- menu_row = [[title_row + 2, rows - items_count_for_menu].min, 1].max
285
- items.each_with_index do |item, i|
286
- ic = [(cols - item.size) / 2 + 1, 1].max
287
- lines[menu_row + i - 1] = TerminalOutput.fit_ansi("#{" " * (ic - 1)}\e[97m#{item}\e[0m", cols)
425
+ lines[title_row - 1] = @static_title_line
426
+ lines[title_row] = @static_sub_line
427
+
428
+ menu_row = [[title_row + 2, rows - MENU_ITEMS.size].min, 1].max
429
+ @static_menu_lines.each_with_index do |menu_line, i|
430
+ lines[menu_row + i - 1] = menu_line
288
431
  end
289
432
 
290
433
  lines.each_with_index do |line, index|
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Termfront
4
- VERSION = "0.1.5"
4
+ VERSION = "0.1.7"
5
5
  end
data/lib/termfront.rb CHANGED
@@ -4,6 +4,7 @@ require "io/console"
4
4
 
5
5
  require_relative "termfront/version"
6
6
  require_relative "termfront/config"
7
+ require_relative "termfront/color"
7
8
  require_relative "termfront/map"
8
9
  require_relative "termfront/weapon/base"
9
10
  require_relative "termfront/weapon/pistol"
@@ -32,6 +33,7 @@ require_relative "termfront/remote_enemy"
32
33
  require_relative "termfront/sprite"
33
34
  require_relative "termfront/input"
34
35
  require_relative "termfront/terminal_output"
36
+ require_relative "termfront/async_writer"
35
37
  require_relative "termfront/renderer"
36
38
  require_relative "termfront/demo_player"
37
39
  require_relative "termfront/scene_player"
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.5
4
+ version: 0.1.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - S-H-GAMELINKS
@@ -51,7 +51,9 @@ files:
51
51
  - exe/termfront
52
52
  - exe/termfront-server
53
53
  - lib/termfront.rb
54
+ - lib/termfront/async_writer.rb
54
55
  - lib/termfront/audio_manager.rb
56
+ - lib/termfront/color.rb
55
57
  - lib/termfront/config.rb
56
58
  - lib/termfront/demo_player.rb
57
59
  - lib/termfront/drop_item/base.rb