"""M10: a campaign with a scene worth depicting, deliberately not a fantasy one. The M10 brief asks for at least one non-fantasy representation, and the reason is a real risk rather than a preference: the media contract's own examples are fantasy-shaped — hair and eyes, timber framing, oil lamps — and a schema written while looking at them can acquire that shape without anyone deciding to give it one. So the fixture is four people in an office, and the same code has to hold it with no change. Bill the protagonist Alice a coworker, with a visual profile Roger a coworker, with no profile at all John a coworker who is not in the room the office a location, with a visual profile a badge an item Bill is carrying the server room a second location, for divergence The cast is the one from the post-M8 playtest finding, and that is deliberate too — but only as *shape*. M10 does not investigate that finding, and nothing here asserts anything about coreference; it is M11's, and §23 of the brief says so. What the shape buys here is a scene with three present characters and one absent, which is what makes "the packet describes who is in the room" a claim with a wrong answer available. Roger having no profile is load-bearing: it is how the tests tell "no profile" from "an empty profile", which a future provider has to be able to distinguish. """ from __future__ import annotations from fakes import ScriptedProvider, state_block #: A narrator-only secret, used by the hidden-information tests. It is imported #: as an M7 hidden knowledge source — the product's real mechanism for #: narrator-only material — rather than as an invented marker, so the test #: exercises the boundary that actually exists. SECRET_SENTINEL = "ZARQUON-CONCEALED-OBSERVER-7731" SECRET_MD = f"""# What nobody in the room knows There is a concealed observer behind the north wall of the office, watching the meeting through a gap in the panelling. Their code name is {SECRET_SENTINEL}. Nobody present is aware of this. """ #: A source that is *not* hidden, so a test can show the packet excludes #: imported knowledge as a class rather than only excluding secrets. HANDBOOK_MD = """# Office handbook The building was refurbished in the spring. The north wall panelling is new. """ def play(client, adv_id, text, events, prose="The meeting continues."): ScriptedProvider.replies = [f"{prose}\n" + state_block(events)] response = client.post( f"/api/adventures/{adv_id}/actions", json={"type": "do", "text": text} ) assert response.status_code == 200, response.text[:400] return response def entity(key, kind, name): return {"type": "create_entity", "entity": key, "entity_type": kind, "name": name} def build(client, adv_id) -> dict: """Plays the office campaign and returns what a test needs to check it. Leaves the campaign with a scene set at the active head, two visual profiles, one character deliberately unprofiled, and one character deliberately not present. """ play(client, adv_id, "arrive at the office", [ entity("bill", "character", "Bill"), entity("alice", "character", "Alice"), entity("roger", "character", "Roger"), entity("john", "character", "John"), entity("office", "location", "The office"), entity("server_room", "location", "The server room"), entity("badge", "item", "Security badge"), ]) play(client, adv_id, "start the meeting", [ {"type": "set_possession", "item": "badge", "owner": "bill"}, {"type": "set_scene", "summary": "Bill, Alice and Roger meet around the table.", "location": "office", "present": ["bill", "alice", "roger"]}, ]) profiles = { "alice": { "descriptors": {"build": "tall", "hair": "short black", "clothing": "grey blazer"}, "features": ["tortoiseshell glasses"], "style_notes": "photographic, natural light", }, "office": { "descriptors": {"architecture": "open-plan floor", "lighting": "flat fluorescent"}, "features": ["whiteboard covered in diagrams"], "style_notes": "", }, } for key, profile in profiles.items(): response = client.put( f"/api/adventures/{adv_id}/visual-profiles/{key}", json=profile ) assert response.status_code == 200, response.text[:300] return {"profiles": profiles} def upload_secret(client, adv_id) -> int: """Imports the narrator-only source the hidden-information tests use.""" return _upload(client, adv_id, "observer.md", SECRET_MD, "canon", visibility="hidden") def upload_handbook(client, adv_id) -> int: return _upload(client, adv_id, "handbook.md", HANDBOOK_MD, "reference") def _upload(client, adv_id, name, body, classification, **fields): data = {"classification": classification} data.update({k: str(v).lower() if isinstance(v, bool) else str(v) for k, v in fields.items()}) response = client.post( f"/api/adventures/{adv_id}/knowledge", files={"file": (name, body.encode("utf-8"), "text/markdown")}, data=data, ) assert response.status_code == 201, response.text[:400] return response.json()["id"]