DeltaZN
feat: rename god -> world
c58d3eb
Raw
History Blame Contribute Delete
4.74 kB
"""Deterministic Chaos tools (God Console quick-buttons).
Unlike the freeform LLM god console, these apply engine-validated effects
directly: beasts actually spawn, famine actually halves food nodes, and the
maniac directive flows through the existing hostile-directive pipeline.
Cooldowns (in ticks) follow GAME_DESIGN.md §11.
"""
from __future__ import annotations
import random
from world_simulator.domain import Beast, WorldState
from world_simulator.simulation.directives import set_active_directive
from world_simulator.simulation.mechanics import is_alive
from world_simulator.simulation.survival import (
_log_event,
_next_beast_index,
_random_map_edge_position,
)
CHAOS_COOLDOWNS: dict[str, int] = {
"spawn_beast": 20,
"beast_pack": 80,
"famine": 120,
"maniac": 100,
}
CHAOS_TOOL_LABELS: dict[str, str] = {
"spawn_beast": "Spawn Beast",
"beast_pack": "Beast Pack",
"famine": "Famine",
"maniac": "Maniac",
}
FAMINE_DURATION_TICKS = 180
MANIAC_DIRECTIVE = (
"Madness consumes you. Attack the nearest villager — ignore work, ignore friends."
)
CHAOS_BEAST_CAP = 6 # God Console may exceed MAX_BEASTS (3) up to this.
def apply_chaos_action(world: WorldState, action: str) -> list[str]:
"""Apply one chaos quick-button. Returns human-readable applied lines."""
if action == "spawn_beast":
applied = _spawn_chaos_beasts(world, count=1)
elif action == "beast_pack":
applied = _spawn_chaos_beasts(world, count=3)
elif action == "famine":
applied = _apply_famine(world)
elif action == "maniac":
applied = _apply_maniac(world)
else:
raise ValueError(f"Unknown chaos action: {action}")
if applied:
world.chaos_intervention_until = max(
world.chaos_intervention_until, world.tick + 100
)
return applied
def _spawn_chaos_beasts(world: WorldState, *, count: int) -> list[str]:
living = sum(1 for beast in world.beasts if beast.state != "dead")
allowed = max(0, min(count, CHAOS_BEAST_CAP - living))
if allowed == 0:
return []
rng = random.Random(f"{world.seed}:{world.tick}:chaos_beast")
applied: list[str] = []
for _ in range(allowed):
beast = Beast(
id=f"beast_{_next_beast_index(world)}",
position=_random_map_edge_position(world, rng),
health=120.0,
damage=15.0,
)
world.beasts.append(beast)
_log_event(
world,
"chaos_event",
f"Chaos unleashed {beast.id} at the edge of the map",
severity="danger",
actor_id=beast.id,
)
applied.append(f"{beast.id} unleashed at the map edge.")
return applied
def _apply_famine(world: WorldState) -> list[str]:
if world.famine_until >= world.tick:
return []
affected = 0
for node in world.resource_nodes:
if node.resource_type != "food":
continue
world.famine_saved_max[node.id] = node.max_amount
node.amount //= 2
node.max_amount = max(1, node.max_amount // 2)
affected += 1
if affected == 0:
return []
world.famine_until = world.tick + FAMINE_DURATION_TICKS
_log_event(
world,
"chaos_event",
f"Famine grips the land: {affected} food nodes halved for "
f"{FAMINE_DURATION_TICKS} ticks",
severity="danger",
)
return [f"Famine: {affected} food nodes halved for {FAMINE_DURATION_TICKS} ticks."]
def end_famine_if_due(world: WorldState) -> None:
"""Restore food node caps once the famine duration elapses."""
if world.famine_until < 0 or world.tick <= world.famine_until:
return
for node in world.resource_nodes:
saved = world.famine_saved_max.get(node.id)
if saved is not None:
node.max_amount = saved
world.famine_saved_max.clear()
world.famine_until = -1
_log_event(world, "chaos_event", "The famine has ended", severity="neutral")
def _apply_maniac(world: WorldState) -> list[str]:
candidates = [npc for npc in world.living_npcs() if is_alive(npc)]
if not candidates:
return []
rng = random.Random(f"{world.seed}:{world.tick}:chaos_maniac")
victim = rng.choice(candidates)
set_active_directive(
victim,
MANIAC_DIRECTIVE,
source="chaos:maniac",
tick=world.tick,
ttl_ticks=60,
)
victim.personality = "violent maniac"
_log_event(
world,
"chaos_event",
f"{victim.name} has gone mad and turned on the village!",
severity="danger",
actor_id=victim.id,
)
return [f"{victim.name} has gone mad and turned on the village."]