Add persistent AFK reserves for both engines (Refs #12)

This commit is contained in:
2026-10-17 16:56:06 -04:00
parent 7b95f604b4
commit 6781a4ed5a
22 changed files with 3840 additions and 248 deletions
+6 -43
View File
@@ -1,4 +1,5 @@
// Execute selected core HUD/AFK functions with engine-value shims.
// Execute selected core HUD functions with engine-value shims.
// Persistent AFK ownership/countdown behavior is covered by check_afk.mjs.
// These checks are not a GSC VM, compiler, linker, or live layout test.
// Common visibility helpers are stubbed here and executed directly by check_hud.
import assert from 'node:assert/strict';
@@ -99,8 +100,6 @@ function engine(game, source, afk, hidden = false) {
int: Math.trunc, max: Math.max });
install(context, source, ['hud_connect', 'hud_text', 'hud_start', 'hud_toggle', 'hud_timer_row',
'hud_health_display', 'hud_zombie_counter', 'freeze_timers_on_game_over']);
install(context, afk, ['afk_spawn', 'afk_enter', 'afk_exit',
...(game === 't6' ? ['afk_release', 'afk_lock_controls', 'afk_restore_controls'] : [])]);
return { context, self, level, queued, elems, actions, common,
setTime(value) { now = value; },
spawnAfk() { const watcher = context.afk_spawn(); watcher?.next(); return watcher; },
@@ -126,9 +125,8 @@ for (const game of ['t5', 't6']) {
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_core_overlay_text'));
assert(afk.includes('level.hud_overlay_afk_y'));
assert(!/\.alpha\s*=/.test(afk), 'AFK visibility must use the common mask');
assert(afk.includes('NewClientHudElem(self)'), 'protection countdown is an independent critical HUD');
assert(!afk.includes('set_core_alpha'), 'hiding the optional HUD must not hide the protection deadline');
});
check(`${game}: connection places 10 box-free core elements at the screen edges without seeding clocks`, () => {
const e = make(); e.context.hud_connect();
@@ -266,41 +264,6 @@ for (const game of ['t5', 't6']) {
assert.equal(JSON.stringify([match.writes, round.writes, e.queued.map(item => item[0])]), before);
assert.equal(e.level.match_start_time, 123456); assert.equal(e.level.round_start_time, 123456);
});
check(`${game}: inactive AFK stays hidden and protection survives HUD toggles`, () => {
const e = make(); e.context.hud_connect(); e.spawnAfk();
const indicator = e.self.hud_afk_elem;
assert.equal(indicator.alpha, 0); assert.equal(indicator.tws_core_alpha, 0);
assert.deepEqual(indicator.args.slice(1), [320, 421, 'center', 1, 'yellow']);
e.context.hud_toggle(); e.context.hud_toggle(); assert.equal(indicator.alpha, 0);
e.context.hud_toggle(); e.context.afk_enter();
assert.equal(e.self.hud_afk_on, true); assert.equal(e.self.ignoreme, true);
assert.equal(indicator.alpha, 0); assert.equal(indicator.tws_core_alpha, 1);
assert(e.actions.some(([name]) => name === 'enableInvulnerability'));
assert(e.actions.some(([name, enabled]) => name === 'freezeControls' && enabled));
const actions = e.actions.length;
e.context.hud_toggle(); assert.equal(indicator.alpha, 1); assert.equal(e.actions.length, actions);
e.context.hud_toggle(); const exit = e.context.afk_exit();
assert.equal(exit.next().value, 2);
assert.equal(indicator.alpha, 0); assert.equal(indicator.tws_core_alpha, 0);
assert.equal(e.self.ignoreme, true, 'the existing protection grace tail remains active');
e.context.hud_toggle(); assert.equal(indicator.alpha, 0);
exit.next(); assert.equal(e.self.ignoreme, false);
assert(e.actions.some(([name]) => name === 'disableInvulnerability'));
assert.equal(e.elems.length, 11);
});
if (game === 't6') check('t6: AFK release while hidden respects stock protection ownership', () => {
const e = make(); e.context.hud_connect(); const watcher = e.spawnAfk();
e.context.afk_enter(); e.context.hud_toggle(); e.self.active = false;
watcher.next();
assert.equal(e.self.hud_afk_on, false); assert.equal(e.self.tws_afk_protected, false);
assert.equal(e.self.hud_afk_elem.tws_core_alpha, 0);
assert.equal(e.self.ignoreme, true);
assert(!e.actions.some(([name]) => name === 'disableInvulnerability'));
e.context.hud_toggle(); assert.equal(e.self.hud_afk_elem.alpha, 0);
e.self.active = true; watcher.next();
assert.equal(e.self.tws_afk_controls_locked, false);
assert(e.actions.some(([name]) => name === 'enableWeapons'));
assert(e.actions.some(([name]) => name === 'enableOffhandWeapons'));
});
}
console.log(`${checks} core HUD/AFK fixture checks passed (transpiled subset, not a GSC VM).`);
console.log(`${checks} core HUD fixture checks passed (transpiled subset, not a GSC VM).`);