Add fixed core HUD and shared service window for T5 and T6
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
// Execute selected core HUD functions with engine-value shims.
|
||||
// These checks are not a GSC VM, compiler, linker, or live layout test.
|
||||
import assert from 'node:assert/strict';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import vm from 'node:vm';
|
||||
const root = new URL('../', import.meta.url);
|
||||
let checks = 0;
|
||||
function check(name, fn) { fn(); checks++; console.log(name); }
|
||||
function body(source, name) {
|
||||
const match = new RegExp(`^${name}\\(([^\\n]*)\\)\\s*\\n\\{`, 'm').exec(source);
|
||||
assert(match, name);
|
||||
let at = match.index + match[0].length, end = at, depth = 1;
|
||||
while (depth) { const ch = source[end++]; if (ch === '{') depth++; if (ch === '}') depth--; }
|
||||
return [match[1], source.slice(at, end - 1)];
|
||||
}
|
||||
for (const game of ['t5', 't6']) {
|
||||
const source = readFileSync(new URL(`${game}/scripts/tws_${game}_hud.gsc`, root), 'utf8');
|
||||
const afk = readFileSync(new URL(`${game}/scripts/tws_${game}_afk.gsc`, root), 'utf8');
|
||||
check(`${game}: shared lifecycle, frozen clocks and engine-specific timer seeds`, () => {
|
||||
assert(source.includes('register_connect_handler(::hud_connect)'));
|
||||
assert(source.includes('register_spawn_handler(::hud_start)'));
|
||||
assert(!source.includes('waittill("connected"'));
|
||||
assert(!body(source, 'hud_connect')[1].includes('getTime()'));
|
||||
for (const name of ['round_watcher', 'reset_timer_each_round', 'match_timer_bind'])
|
||||
assert(body(source, name)[1].includes('level endon("hud_game_over")'));
|
||||
const bind = body(source, 'match_timer_bind')[1];
|
||||
if (game === 't5') assert(bind.includes('elem SetTimerUp(0)'));
|
||||
else assert(bind.includes('(getTime() - level.match_start_time) / 1000.0'));
|
||||
assert(afk.includes('make_panel_text'));
|
||||
assert(afk.includes('level.hud_row_afk'));
|
||||
});
|
||||
const queued = [], elems = [];
|
||||
const common = {
|
||||
make_panel_text: (...args) => element(args),
|
||||
make_panel_shader: (...args) => element(args),
|
||||
};
|
||||
function element(args) {
|
||||
const result = { args, alpha: 1, writes: [], SetText(value) { this.text = value; this.writes.push(['text', value]); },
|
||||
SetValue(value) { this.value = value; this.writes.push(['value', value]); },
|
||||
SetShader(...args) { this.shader = args; }, SetTimerUp(value) { this.timer = value; } };
|
||||
elems.push(result); return result;
|
||||
}
|
||||
const level = { round_number: 1, hud_panel_width: 220, hud_core_height: 100, hud_pad: 10,
|
||||
hud_row_header: 10, hud_row_timers: 34, hud_row_stats: 59, hud_row_bar: 80,
|
||||
hud_bar_width: 200, hud_bar_height: 4, hud_rate: .1,
|
||||
hud_font_small: 1.1, hud_font_mid: 1.25,
|
||||
hud_c_high: 'green', hud_c_mid: 'yellow', hud_c_low: 'red', notify() {} };
|
||||
const self = {};
|
||||
const context = vm.createContext({ self, level, common, queued, getTime: () => 123456,
|
||||
isDefined: value => value !== undefined && value !== null, is_true: value => !!value,
|
||||
int: Math.trunc, max: Math.max });
|
||||
for (const name of ['hud_connect', 'hud_text', 'hud_start', 'hud_timer_row', 'hud_health_display']) {
|
||||
const [args, original] = body(source, name);
|
||||
let code = original.replace(/\/\/[^\n]*/g, '');
|
||||
const locals = [...new Set([...code.matchAll(/(?<![\w.])(\w+)\s*=(?!=)/g)].map(match => match[1]))];
|
||||
code = code.replace(/(?:self|level) endon\([^;]+;/g, '')
|
||||
.replace(/scripts\\(?:sp|zm)\\tws_t[56]_common::/g, 'common.')
|
||||
.replace(/self thread ([\w.]+)\(([^;]*)\);/g, (_, name, args) => `queued.push(['${name}', ${args}]);`)
|
||||
.replace(/self (hud_\w+)\(/g, '$1(')
|
||||
.replace(/(\w+) (SetText|SetValue|SetTimerUp|SetShader)\(([^;]*)\);/g, '$1.$2($3);')
|
||||
.replace(/level notify\(([^;]*)\);/g, 'level.notify($1);')
|
||||
.replace(/\.size\b/g, '.length')
|
||||
.replace(/\((\d+\.\d+), (\d+\.\d+), (\d+\.\d+)\)/g, '[$1, $2, $3]')
|
||||
.replace(/wait \(([^;]*)\);/g, 'yield $1;');
|
||||
vm.runInContext(`function${name === 'hud_health_display' ? '*' : ''} ${name}(${args}) { var ${locals.join(', ') || 'unused'}; ${code} }`, context);
|
||||
}
|
||||
check(`${game}: connection constructs one fixed panel without seeding clocks`, () => {
|
||||
context.hud_connect();
|
||||
assert.equal(level.match_start_time, undefined); assert.equal(level.round_start_time, undefined);
|
||||
assert.equal(self.tws_hud_match.text, '--:--'); assert.equal(self.tws_hud_health.text, '--');
|
||||
const before = elems.length; context.hud_connect(); assert.equal(elems.length, before);
|
||||
assert.equal(queued.length, 1); assert.equal(queued[0][0], 'common.destroy_on_disconnect');
|
||||
assert.equal(queued[0][1].length, elems.length);
|
||||
});
|
||||
check(`${game}: repeated first-spawn binding reuses panel and launches workers once`, () => {
|
||||
const before = elems.length; context.hud_start(); context.hud_start();
|
||||
assert.equal(elems.length, before);
|
||||
assert.deepEqual(queued.slice(1).map(item => item[0]), ['hud_timer_row', 'hud_zombie_counter', 'hud_health_display']);
|
||||
context.hud_timer_row(); assert.equal(level.match_start_time, 123456); assert.equal(level.round_start_time, 123456);
|
||||
});
|
||||
check(`${game}: HP stays visible at full health and falls back without mutating maxhealth`, () => {
|
||||
self.health = 100; const run = context.hud_health_display(); run.next();
|
||||
assert.equal(self.maxhealth, undefined); assert.equal(self.tws_hud_health.value, 100);
|
||||
assert.equal(self.tws_hud_maxhealth.value, 100); assert.equal(self.tws_hud_health.alpha, 1);
|
||||
assert.equal(self.tws_hud_bar.alpha, 1); assert.equal(self.tws_hud_bar.shader[1], 200);
|
||||
const writes = self.tws_hud_health.writes.length; run.next(); assert.equal(self.tws_hud_health.writes.length, writes);
|
||||
self.maxhealth = 250; self.health = 50; run.next();
|
||||
assert.equal(self.tws_hud_maxhealth.value, 250); assert.equal(self.tws_hud_bar.shader[1], 40);
|
||||
assert.equal(self.tws_hud_bar.color, 'red');
|
||||
self.health = 0; run.next(); assert.equal(self.tws_hud_bar.alpha, 0); assert.equal(self.tws_hud_health.value, 0);
|
||||
self.health = 250; run.next(); assert.equal(self.tws_hud_bar.alpha, 1); assert.equal(self.tws_hud_bar.color, 'green');
|
||||
assert.equal(self.tws_hud_health.writes.filter(item => item[0] === 'text').length, 1);
|
||||
});
|
||||
}
|
||||
console.log(`${checks} core HUD fixture checks passed (transpiled subset, not a GSC VM).`);
|
||||
Reference in New Issue
Block a user