Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 9 additions & 13 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,8 @@
<link rel="preload" as="image" href="/src/assets/main-menu-multiplayer-sprite.png" fetchpriority="high" />
<link rel="preload" as="image" href="/src/assets/main-menu-options-sprite.png" fetchpriority="high" />
<link rel="preload" as="image" href="/src/assets/main-menu-help-sprite.png" fetchpriority="high" />
<link rel="preload" as="image" href="/src/assets/main-menu-level-select-sprite.png" fetchpriority="high" />
<link rel="preload" as="image" href="/src/assets/main-menu-quit-sprite.png" fetchpriority="high" />
<link rel="preload" as="image" href="/q/conchars.png" />
<link rel="preload" as="image" href="/q/hud-numbers-damage.png" />
<script>
(function () {
var GA_ID = "G-XV72TXWTM5";
Expand Down Expand Up @@ -189,14 +187,6 @@
image-rendering: pixelated;
pointer-events: auto;
user-select: none;
cursor: wait;
}

body.quake-loading,
body.quake-loading *,
.quake-menu-panel[aria-busy="true"],
.quake-menu-panel[aria-busy="true"] * {
cursor: wait !important;
}

#quake-loading-overlay[hidden] {
Expand Down Expand Up @@ -842,7 +832,7 @@ <h1 id="quake-level-title" class="quake-menu-panel-title">
<div id="quake-about-card" class="quake-menu-card" role="document">
<header class="quake-menu-panel-header">
<h1 id="quake-about-title" class="quake-menu-panel-title">
<img src="/q/menu-title-help.png" width="75" height="20" alt="Help" />
<img src="/q/menu-title-help.png" width="96" height="24" alt="Help" />
</h1>
</header>
<div id="quake-help-sections" class="quake-menu-panel-sections">
Expand Down Expand Up @@ -918,7 +908,7 @@ <h2 class="quake-options-group-title quake-bm-label quake-bm-alt">Debug</h2>
<span class="quake-option-value quake-option-value-on quake-bm-label quake-bm-alt">on</span>
</label>
<label class="quake-option-toggle" tabindex="0">
<input id="quake-debug-show-fps" type="checkbox" />
<input id="quake-debug-show-fps" type="checkbox" checked />
<span class="quake-option-checkbox" aria-hidden="true"></span>
<span class="quake-option-text quake-bm-label">Show FPS panel</span>
<span class="quake-option-value quake-option-value-off quake-bm-label quake-bm-alt">off</span>
Expand Down Expand Up @@ -1121,7 +1111,13 @@ <h2 class="quake-options-group-title quake-bm-label quake-bm-alt">Gameplay</h2>
<h2 id="quake-debug-title" class="quake-bm-label quake-bm-alt">Debug</h2>
</header>
<dl id="quake-debug-stats" aria-label="Debug stats">
<div><dt class="quake-bm-label">Record</dt><dd data-qstat="recording">-</dd></div>
<div id="quake-debug-recording-row" hidden>
<dt class="quake-bm-label">Record</dt>
<dd class="quake-debug-recording-control">
<button id="quake-debug-recording-toggle" class="quake-debug-recording-button" type="button" aria-pressed="false">RECORD</button>
<span data-qstat="recording">-</span>
</dd>
</div>
<div><dt class="quake-bm-label">Capture</dt><dd data-qstat="capture">-</dd></div>
<div><dt class="quake-bm-label">Visible</dt><dd data-qstat="visible">-</dd></div>
<div><dt class="quake-bm-label">DOM</dt><dd data-qstat="dom">-</dd></div>
Expand Down
10 changes: 9 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,15 @@
"build": "vite build",
"build:full": "pnpm prepare:quake && vite build",
"preview": "vite preview",
"test": "node scripts/testContracts.mjs"
"test": "node scripts/testContracts.mjs",
"test:asset-state": "node test/assets/checkAssetState.mjs",
"test:assets": "node test/assets/runAssetIntegrity.mjs",
"test:browser:smoke": "node test/browser/runBrowserSmoke.mjs",
"test:browser": "node test/browser/runBrowserFixtures.mjs",
"test:harness": "node test/runHarnessPlan.mjs",
"test:perf": "node test/perf/runPerfPreflight.mjs",
"test:dev": "pnpm test && pnpm test:perf",
"test:all": "pnpm test && pnpm test:assets && pnpm test:browser:smoke && pnpm test:browser && pnpm test:perf"
},
"dependencies": {
"@layoutit/polycss": "^0.2.6",
Expand Down
21 changes: 16 additions & 5 deletions scripts/testContracts.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,10 @@ import { fileURLToPath } from "node:url";
const scriptDir = path.dirname(fileURLToPath(import.meta.url));
const projectRoot = path.resolve(scriptDir, "..");
const testDir = path.join(projectRoot, "test");
const testFiles = (await readdir(testDir))
.filter((entry) => entry.endsWith(".test.mjs"))
.sort()
.map((entry) => path.join(testDir, entry));
const testFiles = (await collectContractTestFiles(testDir)).sort();

if (!testFiles.length) {
throw new Error("No contract test files found in test/*.test.mjs.");
throw new Error("No contract test files found in test/**/*.test.mjs.");
}

const child = spawn(process.execPath, ["--test", ...testFiles], {
Expand All @@ -35,3 +32,17 @@ if (exitCode !== 0) {
}

console.log("\nContract tests passed.");

async function collectContractTestFiles(directory) {
const entries = await readdir(directory, { withFileTypes: true });
const testFiles = [];
for (const entry of entries) {
const resolved = path.join(directory, entry.name);
if (entry.isDirectory()) {
testFiles.push(...await collectContractTestFiles(resolved));
} else if (entry.isFile() && entry.name.endsWith(".test.mjs")) {
testFiles.push(resolved);
}
}
return testFiles;
}
51 changes: 47 additions & 4 deletions src/App.ts
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,8 @@ const {
debugFlyModeOption,
debugShowOutlinesOption,
debugShowLabelsOption,
debugRecordingRow,
debugRecordingButton,
debugStatElements,
loadingOverlay,
loadingStatus,
Expand Down Expand Up @@ -730,7 +732,9 @@ let quakeDamageDisabled = quakeUrlBoolean("disableDamage") || (disableDamageOpti
let quakeEnemiesFrozen = quakeUrlBoolean("freezeEnemies") || (debugFreezeEnemiesOption?.checked ?? false);
let quakeAttacksDisabled = quakeUrlBoolean("disableAttacks") || (debugDisableAttacksOption?.checked ?? false);
const quakeDebugPointerTraceConsole = quakeUrlBoolean("debugPointer");
const quakeDebugRecordingPanelEnabled = quakeUrlBoolean("debugRecording");
const quakeInitialDebugFlyMode = quakeUrlBoolean("debugFly") || (debugFlyModeOption?.checked ?? false);
if (debugRecordingRow) debugRecordingRow.hidden = !quakeDebugRecordingPanelEnabled;
const QUAKE_MULTIPLAYER_ROOM_TOKEN_LENGTH = 8;
const QUAKE_MULTIPLAYER_ROOM_TOKEN_ALPHABET = "bcdfghjkmnpqrstvwxyz23456789";
const QUAKE_MULTIPLAYER_ROOM_TOKEN_PATTERN = /^[bcdfghjkmnpqrstvwxyz23456789]{8}$/i;
Expand Down Expand Up @@ -1607,8 +1611,9 @@ const quakeDebugRecorder = createQuakeDebugRecorder({
appVersion: __CSSQUAKE_VERSION__,
currentMapName: () => currentMapName,
entityManifest: () => currentResult?.entityManifest ?? null,
onStateChange: quakeDebugRecordingPanelEnabled ? syncQuakeDebugRecordingButton : undefined,
snapshot: () => quakeDebugRecordingSnapshot.capture(),
statusElement: debugStatElements.get("recording") ?? null,
statusElement: quakeDebugRecordingPanelEnabled ? debugStatElements.get("recording") ?? null : null,
});
const quakePointerTracer = createQuakePointerTracer({
enabled: () => quakeDebugPanelFlow.isModeEnabled() || isQuakeDebugHooksEnabled(),
Expand Down Expand Up @@ -1981,6 +1986,7 @@ quakePointerGameplay = createQuakePointerGameplayFlow({
isDebugFlyModeActive: isQuakeDebugFlyModeActive,
isDeathUnlockControlsEndTraceSuppressed: isQuakeDeathUnlockControlsEndTraceSuppressed,
isDisposed: () => quakeAppDisposed,
isInteractiveOverlayTarget: (target) => target instanceof Node && debugPanel?.contains(target) === true,
isPlayerDead: () => quakePlayerDead,
mobileRoot: quakeApp,
onAvailabilityChange: () => quakeStatsOverlay.syncAvailability(),
Expand Down Expand Up @@ -2327,6 +2333,7 @@ quakePlayerLifecycle = createQuakePlayerLifecycleFlow({
viewmodel,
});
let quakeDebugCollisionBypassUntil = 0;
let quakeDebugGameplaySyncActive = false;
let quakeGamePaused = false;
let quakeGamePausedAt = 0;
let quakeMenuPauseActive = false;
Expand Down Expand Up @@ -2369,7 +2376,7 @@ function isQuakeKey(value: string): value is QuakeKey {
}

function isQuakeGamePaused(): boolean {
return quakeGamePaused;
return !quakeDebugGameplaySyncActive && quakeGamePaused;
}

function setQuakeMenuPauseState(paused: boolean): void {
Expand Down Expand Up @@ -2590,6 +2597,29 @@ function toggleQuakeOutlineTextureModeShortcut(): void {
showQuakeShortcutState("Outlines", quakeDebugPanelFlow.toggleOutlineTextureMode());
}

function syncQuakeDebugRecordingButton(recording: boolean): void {
if (!quakeDebugRecordingPanelEnabled || !debugRecordingButton) return;
debugRecordingButton.textContent = recording ? "STOP" : "RECORD";
debugRecordingButton.setAttribute("aria-pressed", String(recording));
debugRecordingButton.setAttribute("aria-label", recording ? "Stop debug recording" : "Start debug recording");
}

function handleQuakeDebugRecordingButtonClick(event: Event): void {
event.preventDefault();
event.stopPropagation();
if (!quakeDebugRecordingPanelEnabled) return;
if (quakeDebugRecorder.isRecording()) {
quakeDebugRecorder.stop("stop");
return;
}
if (quakeAppLoading || currentResult === null) {
const recordingStatus = debugStatElements.get("recording");
if (recordingStatus) recordingStatus.textContent = "load first";
return;
}
quakeDebugRecorder.start();
}

function syncQuakeInteractionPresentation(): void {
const menuSurfaceOpen = menu.isMainMenuOpen() || menu.isMenuPanelOpen();
const pointerUnlocked = document.pointerLockElement !== host;
Expand Down Expand Up @@ -2728,7 +2758,7 @@ function isQuakeLevelTransitionActive(): boolean {
}

function canUseQuakeGameplayInput(): boolean {
return quakePlayerLifecycle.canUseGameplayInput();
return !isQuakeGamePaused() && quakePlayerLifecycle.canUseGameplayInput();
}

function shouldResumeQuakeMainMenuOnEscape(): boolean {
Expand Down Expand Up @@ -4334,7 +4364,14 @@ function syncTouchedTriggers(origin: [number, number, number]): QuakeTouchedTrig
}

function syncQuakeDebugGameplay(origin: [number, number, number]): void {
quakeSceneMount.syncDebugGameplay(origin);
getPlayer().setDebugOrigin(origin);
const previousDebugGameplaySyncActive = quakeDebugGameplaySyncActive;
quakeDebugGameplaySyncActive = true;
try {
quakeSceneMount.syncDebugGameplay(origin);
} finally {
quakeDebugGameplaySyncActive = previousDebugGameplaySyncActive;
}
}

function applyQuakeUrlView(view: QuakeCssView): void {
Expand Down Expand Up @@ -4495,6 +4532,9 @@ function disposeQuakeApp(): void {
debugFlyModeOption?.removeEventListener("change", handleQuakeDebugFlyModeOptionChange);
debugShowOutlinesOption?.removeEventListener("change", quakeDebugPanelFlow.handleShowOutlinesOptionChange);
debugShowLabelsOption?.removeEventListener("change", quakeDebugPanelFlow.handleShowLabelsOptionChange);
if (quakeDebugRecordingPanelEnabled) {
debugRecordingButton?.removeEventListener("click", handleQuakeDebugRecordingButtonClick);
}
quakeDebugRecorder.dispose();
quakeDebugPanelFlow.stopStats();
controls.removeEventListener("change", handleQuakeControlsChange);
Expand Down Expand Up @@ -4678,6 +4718,9 @@ debugShowTexturesOption?.addEventListener("change", quakeDebugPanelFlow.handleSh
debugFlyModeOption?.addEventListener("change", handleQuakeDebugFlyModeOptionChange);
debugShowOutlinesOption?.addEventListener("change", quakeDebugPanelFlow.handleShowOutlinesOptionChange);
debugShowLabelsOption?.addEventListener("change", quakeDebugPanelFlow.handleShowLabelsOptionChange);
if (quakeDebugRecordingPanelEnabled) {
debugRecordingButton?.addEventListener("click", handleQuakeDebugRecordingButtonClick);
}
controls.addEventListener("change", handleQuakeControlsChange);
controls.addEventListener("end", quakeGameplayInput.clearCrouchInput);

Expand Down
Binary file added src/assets/menu-title-help-source.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
66 changes: 66 additions & 0 deletions src/generated/quakeProgramFacts.json
Original file line number Diff line number Diff line change
Expand Up @@ -14715,6 +14715,72 @@
}
}
],
"callbackFacts": {
"sigil_touch": {
"assignments": [
{
"field": "self.solid",
"expression": "SOLID_NOT",
"value": 0,
"sourceRef": {
"sourceFile": "qcc/v101qc/items.qc",
"functionName": "sigil_touch",
"line": 1038
}
},
{
"field": "self.model",
"expression": "string_null",
"sourceRef": {
"sourceFile": "qcc/v101qc/items.qc",
"functionName": "sigil_touch",
"line": 1039
}
},
{
"field": "serverflags",
"expression": "serverflags | (self.spawnflags & 15)",
"sourceRef": {
"sourceFile": "qcc/v101qc/items.qc",
"functionName": "sigil_touch",
"line": 1040
}
},
{
"field": "self.classname",
"expression": "\"\"",
"value": "",
"sourceRef": {
"sourceFile": "qcc/v101qc/items.qc",
"functionName": "sigil_touch",
"line": 1041
}
},
{
"field": "activator",
"expression": "other",
"sourceRef": {
"sourceFile": "qcc/v101qc/items.qc",
"functionName": "sigil_touch",
"line": 1043
}
}
],
"calls": [
"centerprint",
"sound",
"stuffcmd",
"SUB_UseTargets"
],
"sourceRefs": [
{
"sourceFile": "qcc/v101qc/items.qc",
"functionName": "sigil_touch",
"line": 1025
}
]
}
},
"callbacks": {
"touch": "sigil_touch"
},
Expand Down
66 changes: 66 additions & 0 deletions src/generated/quakeProgramFacts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16651,6 +16651,72 @@ export const QUAKE_PROGRAM_FACTS = {
}
}
],
"callbackFacts": {
"sigil_touch": {
"assignments": [
{
"field": "self.solid",
"expression": "SOLID_NOT",
"value": 0,
"sourceRef": {
"sourceFile": "qcc/v101qc/items.qc",
"functionName": "sigil_touch",
"line": 1038
}
},
{
"field": "self.model",
"expression": "string_null",
"sourceRef": {
"sourceFile": "qcc/v101qc/items.qc",
"functionName": "sigil_touch",
"line": 1039
}
},
{
"field": "serverflags",
"expression": "serverflags | (self.spawnflags & 15)",
"sourceRef": {
"sourceFile": "qcc/v101qc/items.qc",
"functionName": "sigil_touch",
"line": 1040
}
},
{
"field": "self.classname",
"expression": "\"\"",
"value": "",
"sourceRef": {
"sourceFile": "qcc/v101qc/items.qc",
"functionName": "sigil_touch",
"line": 1041
}
},
{
"field": "activator",
"expression": "other",
"sourceRef": {
"sourceFile": "qcc/v101qc/items.qc",
"functionName": "sigil_touch",
"line": 1043
}
}
],
"calls": [
"centerprint",
"sound",
"stuffcmd",
"SUB_UseTargets"
],
"sourceRefs": [
{
"sourceFile": "qcc/v101qc/items.qc",
"functionName": "sigil_touch",
"line": 1025
}
]
}
},
"callbacks": {
"touch": "sigil_touch"
},
Expand Down
Loading
Loading