termfront 0.1.4 → 0.1.6

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,44 +3,54 @@
3
3
  module Termfront
4
4
  module TerminalOutput
5
5
  module_function
6
- ANSI_PATTERN = /\e\[[0-9;]*[A-Za-z]/.freeze
7
-
8
- def sync_updates?
9
- ENV.fetch("TERMFRONT_SYNC_UPDATES", "1") == "1"
10
- end
11
6
 
12
7
  def begin_frame(home: false, clear: false)
13
8
  buf = +""
14
- buf << "\e[?2026h" if sync_updates?
15
9
  buf << "\e[H" if home
16
10
  buf << "\e[2J" if clear
17
11
  buf
18
12
  end
19
13
 
20
14
  def end_frame
21
- sync_updates? ? "\e[?2026l" : ""
15
+ ""
22
16
  end
23
17
 
24
18
  def fit_ansi(text, width)
25
- visible = 0
26
19
  out = +""
27
- index = 0
20
+ visible = 0
21
+ any_ansi = false
22
+ i = 0
23
+ len = text.bytesize
24
+ segment_start = 0
28
25
 
29
- while index < text.length && visible < width
30
- if (match = ANSI_PATTERN.match(text, index)) && match.begin(0) == index
31
- out << match[0]
32
- index = match.end(0)
33
- next
34
- 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
+
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
35
44
 
36
- char = text[index]
37
- out << char
38
- visible += 1
39
- index += 1
45
+ visible += 1
46
+ end
47
+ i += 1
48
+ end
40
49
  end
41
50
 
42
- out << "\e[0m" if out.include?("\e[")
43
- 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
44
54
  out
45
55
  end
46
56
 
@@ -7,12 +7,30 @@ 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
+
10
14
  def initialize(stdout)
11
15
  @stdout = stdout
12
16
  @title_spin = 0.0
13
17
  @demo_wp_idx = 0
14
18
  @demo_wp_t = 0.0
15
19
  @demo_fire = 0
20
+
21
+ mission_class = Mission::Base.campaign.first
22
+ if mission_class
23
+ m = mission_class.new
24
+ @demo_map = m.map_data.map { |row| row.is_a?(Array) ? row : row.chars }.freeze
25
+ @demo_map_h = @demo_map.size
26
+ @demo_map_w = @demo_map[0].size
27
+ @demo_enemies = m.enemy_defs.freeze
28
+ end
29
+
30
+ @static_lines_cols = 0
31
+ @static_title_line = nil
32
+ @static_sub_line = nil
33
+ @static_menu_lines = nil
16
34
  end
17
35
 
18
36
  def show
@@ -24,11 +42,15 @@ module Termfront
24
42
  TerminalOutput.write_all(@stdout, TerminalOutput.begin_frame(home: true, clear: true) + TerminalOutput.end_frame)
25
43
 
26
44
  STDIN.raw do |stdin|
45
+ last_render = clock - Config::RENDER_DT
27
46
  loop do
28
47
  now = clock
29
48
  @title_spin += 0.015
30
49
 
31
- render
50
+ if now - last_render >= Config::RENDER_DT
51
+ render
52
+ last_render = now
53
+ end
32
54
 
33
55
  while IO.select([stdin], nil, nil, 0)
34
56
  begin
@@ -60,6 +82,20 @@ module Termfront
60
82
  Process.clock_gettime(Process::CLOCK_MONOTONIC)
61
83
  end
62
84
 
85
+ def ensure_static_lines(cols)
86
+ return if @static_lines_cols == cols
87
+
88
+ tc = [(cols - TITLE_TEXT.size) / 2 + 1, 1].max
89
+ sc = [(cols - SUB_TEXT.size) / 2 + 1, 1].max
90
+ @static_title_line = TerminalOutput.fit_ansi("#{" " * (tc - 1)}\e[1;38;2;120;140;255m#{TITLE_TEXT}\e[0m", cols)
91
+ @static_sub_line = TerminalOutput.fit_ansi("#{" " * (sc - 1)}\e[38;2;80;80;120m#{SUB_TEXT}\e[0m", cols)
92
+ @static_menu_lines = MENU_ITEMS.map do |item|
93
+ ic = [(cols - item.size) / 2 + 1, 1].max
94
+ TerminalOutput.fit_ansi("#{" " * (ic - 1)}\e[97m#{item}\e[0m", cols)
95
+ end
96
+ @static_lines_cols = cols
97
+ end
98
+
63
99
  def render
64
100
  rows, cols = @stdout.winsize
65
101
  rows = [rows, 10].max
@@ -76,13 +112,11 @@ module Termfront
76
112
  virt_h = th * 2
77
113
  color = Array.new(tw * virt_h, nil)
78
114
 
79
- mission = Mission::Base.campaign.first
80
- return unless mission
115
+ return unless @demo_map
81
116
 
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
117
+ demo_map = @demo_map
118
+ dm_h = @demo_map_h
119
+ dm_w = @demo_map_w
86
120
 
87
121
  @demo_wp_t += Config::DEMO_SPEED
88
122
  if @demo_wp_t >= 1.0
@@ -184,7 +218,7 @@ module Termfront
184
218
  end
185
219
 
186
220
  # Demo enemies
187
- demo_enemies = m.enemy_defs
221
+ demo_enemies = @demo_enemies
188
222
  ddx = Math.cos(cam_a)
189
223
  ddy = Math.sin(cam_a)
190
224
  ppx = -ddy * Math.tan(fov / 2.0)
@@ -269,22 +303,16 @@ module Termfront
269
303
  lines[r] = TerminalOutput.fit_ansi(line, cols)
270
304
  end
271
305
 
272
- # Title text
306
+ # Title text + menu items (memoized per cols)
307
+ ensure_static_lines(cols)
308
+
273
309
  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)
310
+ lines[title_row - 1] = @static_title_line
311
+ lines[title_row] = @static_sub_line
312
+
313
+ menu_row = [[title_row + 2, rows - MENU_ITEMS.size].min, 1].max
314
+ @static_menu_lines.each_with_index do |menu_line, i|
315
+ lines[menu_row + i - 1] = menu_line
288
316
  end
289
317
 
290
318
  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.4"
4
+ VERSION = "0.1.6"
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.4
4
+ version: 0.1.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - S-H-GAMELINKS