"""Rendering and small game helpers."""
from __future__ import annotations
import html
from .game_data import CLUE_LABELS, EVIDENCE, GAME_OBJECTS
from .game_state import GameState
from .scoring import ScoreResult
def _meter(value: int, css_class: str) -> str:
return (
f'
'
)
def render_status(state: GameState) -> str:
clues = len(state.discovered_clues)
status = "READY" if state.readiness >= 70 else "BUILDING" if state.readiness >= 35 else "COLD"
return f"""
CASEACTIVE
CLUES{clues:02d}
TRUST // MIRROR{state.trust}%
{_meter(state.trust, "trust-fill")}
MODEL CORRUPTION{state.corruption}%
{_meter(state.corruption, "corruption-fill")}
ACCUSATION // {status}{state.readiness}%
{_meter(state.readiness, "readiness-fill")}
"""
def render_clues(state: GameState) -> str:
if not state.discovered_clues:
return 'NO VERIFIED CLUES // interrogate the artifacts
'
items = "".join(
f'{index:02d}{html.escape(CLUE_LABELS.get(clue, clue))}'
for index, clue in enumerate(state.discovered_clues, 1)
)
return f''
def render_feed(state: GameState) -> str:
lines = []
for item in reversed(state.feed[-10:]):
prefix, separator, body = item.partition(" // ")
if separator:
lines.append(
f'{html.escape(prefix)}'
f'{html.escape(body)}
'
)
else:
lines.append(f'{html.escape(item)}
')
return '' + "".join(lines) + "
"
def render_evidence(name: str) -> str:
evidence = EVIDENCE[name]
return f"""
{html.escape(name)}
{html.escape(str(evidence['summary']))}
{html.escape(str(evidence['content']))}
"""
def render_terminal(state: GameState, latest: str = "") -> str:
history = state.terminal_history[-8:]
blocks = ['CODEX NOIR SHELL v0.13 // evidence sandbox
']
for entry in history:
command, _, response = entry.partition("\n")
blocks.append(f'trace@metrogrid:~$ {html.escape(command)}
')
blocks.append(f'{html.escape(response)}
')
if not history and not latest:
blocks.append('Type `help` to list recovered commands.
')
return '' + "".join(blocks) + "
"
def render_theory(state: GameState) -> str:
if not state.theory_notes:
return "No pinned theory fragments."
return "\n".join(f"- {note}" for note in state.theory_notes[-6:])
def _notebook_entries(
items: list[dict[str, object]],
text_key: str,
empty: str,
badge_key: str,
) -> str:
if not items:
return f'{html.escape(empty)}
'
rendered = []
for item in items[-4:]:
badge = str(item.get(badge_key, "info"))
text = str(item.get(text_key, item.get("text", "Recorded.")))
rendered.append(
''
f'
'
f'{html.escape(badge)}'
f"
{html.escape(text)}
"
)
return "".join(rendered)
def render_notebook(state: GameState) -> str:
theories = [
{
"claim": item.get("claim", ""),
"status": item.get("status", "untested"),
}
for item in state.pinned_theories
]
traces = [
{
"summary": f"[{item.get('tool', 'tool')}] {item.get('summary', '')}",
"severity": item.get("severity", "info"),
}
for item in state.tool_trace
]
secret = (
'SUPPRESSED MEMORY DETECTED
'
if state.secret_unlocked
else 'MIRROR MEMORY AUDIT // LOCKED
'
)
return f"""
ACCUSATION READINESS{state.readiness}%
{_meter(state.readiness, "readiness-fill")}
{secret}
KNOWN FACTS
{_notebook_entries(state.known_facts, "text", "No deterministic facts indexed.", "strength")}
MIRROR CLAIMS
{_notebook_entries(state.mirror_claims, "text", "No MIRROR claims recorded.", "strength")}
KNOWN CONTRADICTIONS
{_notebook_entries(state.known_contradictions, "text", "No contradictions proven.", "severity")}
PINNED THEORIES
{_notebook_entries(theories, "claim", "No theories pinned.", "status")}
LATEST TOOL TRACE
{_notebook_entries(traces, "summary", "No tools executed.", "severity")}
"""
def render_game_hud(state: GameState) -> str:
obj = GAME_OBJECTS.get(state.selected_3d_object, GAME_OBJECTS["mirror_core"])
vault_steps = [
(state.challenged_mirror_count, 1, "MIRROR CHALLENGE"),
(state.contradiction_scans, 1, "CONTRADICTION SCAN"),
(1 if "duplicate_token" in state.discovered_clues else 0, 1, "J-17 DUPLICATION"),
]
vault_progress = sum(min(current, required) for current, required, _ in vault_steps)
vault_total = sum(required for _, required, _ in vault_steps)
progress = round(vault_progress / vault_total * 100)
progress_rows = "".join(
(
''
f"{html.escape(label)}"
f"{min(current, required)}/{required}"
"
"
)
for current, required, label in vault_steps
)
vault_status = (
"BREACHED"
if state.secret_unlocked
else "ACCESS READY"
if state.memory_vault_unlocked
else "ENCRYPTED"
)
return f"""
SELECTED // {html.escape(str(obj['type'])).upper()}
{html.escape(str(obj['label']))}
{html.escape(str(obj.get('description', 'No description recovered.')))}
TRUST{state.trust}%{_meter(state.trust, "trust-fill")}
CORRUPTION{state.corruption}%{_meter(state.corruption, "corruption-fill")}
MEMORY VAULT // {vault_status}{progress}%
{_meter(progress, "readiness-fill")}
{progress_rows}
"""
def render_objectives(state: GameState) -> str:
objectives = [
("Scan sector log", "Corrupted Sector Log" in state.analyzed_evidence),
("Find duplicate token", "duplicate_token" in state.discovered_clues),
("Inspect commit", "janitor_commit" in state.discovered_clues),
("Decode memory filter", "thirteen_minute_filter" in state.discovered_clues),
("Challenge MIRROR", state.challenged_mirror_count >= 1),
("Audit MIRROR memory", state.secret_unlocked),
("Submit accusation", state.accusation_submitted),
]
completed = sum(done for _, done in objectives)
rows = "".join(
(
f''
f'{"OK" if done else ".."}'
f"{html.escape(label)}
"
)
for label, done in objectives
)
return f"""
{rows}
DEMO PATH
Select and scan the Sector Log. Run a forensic scan. Trace J-17. Challenge MIRROR once,
breach the vault, then build the accusation.
"""
def render_score(result: ScoreResult, verdict: str) -> str:
breakdown = "".join(
f"| {html.escape(label)} | {points} |
"
for label, points in result.breakdown.items()
)
feedback = "".join(f"{html.escape(item)}" for item in result.feedback)
ending_class = f"ending-{result.ending}"
return f"""
CASE RESOLUTION // SCORE {result.total}/100
{html.escape(result.title)}
{verdict}
{breakdown}| TOTAL | {result.total} |
"""