"""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(str(evidence['kind']))} INTEGRITY // DEGRADED

    {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"""
    CASE OBJECTIVES{completed}/{len(objectives)}
    {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}
    """