| from __future__ import annotations |
|
|
| from typing import Any, cast |
|
|
| from world_simulator.domain import Npc, WorldLogEvent, WorldState |
| from world_simulator.observability import append_record |
| from world_simulator.simulation.mechanics import is_alive |
|
|
| DEFAULT_DIRECTIVE_TTL_TICKS = 24 |
|
|
|
|
| def set_active_directive( |
| npc: Npc, |
| directive: str, |
| *, |
| source: str, |
| tick: int, |
| ttl_ticks: int = DEFAULT_DIRECTIVE_TTL_TICKS, |
| ) -> None: |
| npc.god_directive = directive |
| npc.directive_source = source |
| npc.directive_issued_tick = tick |
| npc.directive_ttl_ticks = ttl_ticks |
|
|
|
|
| def clear_active_directive(npc: Npc) -> None: |
| npc.god_directive = None |
| npc.directive_source = None |
| npc.directive_issued_tick = None |
| npc.directive_ttl_ticks = None |
|
|
|
|
| def directive_remaining_ttl(npc: Npc, *, tick: int) -> int | None: |
| if npc.god_directive is None: |
| return None |
| if npc.directive_issued_tick is None or npc.directive_ttl_ticks is None: |
| return DEFAULT_DIRECTIVE_TTL_TICKS |
| return max(0, npc.directive_issued_tick + npc.directive_ttl_ticks - tick) |
|
|
|
|
| def active_directive_payload(npc: Npc, *, tick: int) -> dict[str, Any] | None: |
| if npc.god_directive is None: |
| return None |
| return { |
| "text": npc.god_directive, |
| "source": npc.directive_source or "unknown", |
| "issued_tick": npc.directive_issued_tick, |
| "ttl_ticks": npc.directive_ttl_ticks or DEFAULT_DIRECTIVE_TTL_TICKS, |
| "remaining_ttl": directive_remaining_ttl(npc, tick=tick), |
| } |
|
|
|
|
| def expire_directives(world: WorldState, *, tick: int) -> list[dict[str, Any]]: |
| expired: list[dict[str, Any]] = [] |
| for npc in world.living_npcs(): |
| remaining = directive_remaining_ttl(npc, tick=tick) |
| if remaining is None or remaining > 0: |
| continue |
| record = { |
| "npc_id": npc.id, |
| "directive": npc.god_directive, |
| "source": npc.directive_source, |
| "issued_tick": npc.directive_issued_tick, |
| "ttl_ticks": npc.directive_ttl_ticks, |
| } |
| clear_active_directive(npc) |
| world.event_log.append( |
| WorldLogEvent( |
| tick=tick, |
| type="directive_expired", |
| actor_id="engine", |
| target_id=npc.id, |
| object_id=None, |
| summary=f"Directive expired for {npc.name}", |
| severity=cast(Any, "neutral"), |
| ) |
| ) |
| append_record({"tick": tick, "phase": "directive_expired", **record}) |
| expired.append(record) |
|
|
| for npc in world.npcs: |
| if not is_alive(npc): |
| clear_active_directive(npc) |
| return expired |
|
|