aether-garden / simulation /location_effects.py
kavyabhand's picture
Deploy Aether Garden application
781b9f7 verified
Raw
History Blame Contribute Delete
5.59 kB
"""Per-location special mechanics from DETAILS.md §3."""
from __future__ import annotations
import json
import random
from world.book_of_ages import create_entry, get_entries
from world.database import db_session
from world.entities import add_tag, get_entity, update_memory
from world.locations import get_location_by_slug
def effective_days_in_realm(entity: dict) -> float:
"""Clock Forest souls age 1.5× faster for lifecycle logic."""
loc = entity.get("location_slug")
if loc is None and entity.get("location_id"):
from world.locations import get_location_by_id
row = get_location_by_id(entity["location_id"])
loc = row["slug"] if row else None
days = entity.get("days_in_realm", 0)
if loc == "clock-forest":
return days * 1.5
return float(days)
def process_library_memory_fragments(world_day: int) -> int:
"""Every 3 days, Library inhabitants gain a half-remembered fragment."""
if world_day % 3 != 0:
return 0
library = get_location_by_slug("library")
if not library:
return 0
past = get_entries(limit=30, entry_type=None)
if not past:
return 0
fragment_source = random.choice(past)
snippet = (fragment_source.get("content") or "")[:120].strip()
if not snippet:
return 0
updated = 0
with db_session() as conn:
rows = conn.execute(
"SELECT * FROM entities WHERE location_id = ? AND status != 'dormant'",
(library["id"],),
).fetchall()
for row in rows:
entity = dict(row)
current = entity.get("memory_summary") or ""
addition = f"They half-remember something from the shelves: {snippet}"
merged = f"{current} {addition}".strip()
if len(merged) > 600:
merged = merged[-600:]
update_memory(entity["id"], merged)
updated += 1
if updated:
create_entry(
world_day=world_day,
entry_type="milestone",
content=(
f"The Library breathed out {updated} half-forgotten fragments "
"into the minds of those who dwell among unfinished books."
),
location_id=library["id"],
is_milestone=False,
)
return updated
def process_mirror_contradictions(world_day: int) -> int:
"""Mirror Bogs: after 5+ interactions, a soul may develop a contradicting trait."""
bogs = get_location_by_slug("mirror-bogs")
if not bogs:
return 0
count = 0
with db_session() as conn:
entities = conn.execute(
"SELECT * FROM entities WHERE location_id = ?",
(bogs["id"],),
).fetchall()
opposites = [
("patient", "restless"),
("gentle", "sharp-tongued"),
("honest", "evasive"),
("hopeful", "cynical"),
("quiet", "loud"),
("generous", "possessive"),
]
for row in entities:
entity = dict(row)
traits = json.loads(entity["personality_traits"]) if isinstance(entity["personality_traits"], str) else entity["personality_traits"]
tags = json.loads(entity["tags"]) if isinstance(entity["tags"], str) else entity["tags"]
if "contradiction" in tags:
continue
n = conn_count_interactions_at(entity["id"], bogs["id"])
if n < 5:
continue
pick_trait = random.choice(traits)
opposite = next((b for a, b in opposites if a in pick_trait.lower()), None)
if not opposite:
opposite = f"secretly unlike their {pick_trait} nature"
new_traits = traits[:]
idx = traits.index(pick_trait)
new_traits[idx] = f"{pick_trait}, yet {opposite}"
with db_session() as conn:
conn.execute(
"UPDATE entities SET personality_traits = ? WHERE id = ?",
(json.dumps(new_traits), entity["id"]),
)
add_tag(entity["id"], "contradiction")
create_entry(
world_day=world_day,
entry_type="milestone",
content=(
f"{entity['name']} looked into the bog and came away disagreeing "
f"with who they had been."
),
entity_ids=[entity["id"]],
location_id=bogs["id"],
is_milestone=True,
title="A Contradiction Surfaces",
)
count += 1
return count
def conn_count_interactions_at(entity_id: str, location_id: int) -> int:
with db_session() as conn:
row = conn.execute(
"""
SELECT COUNT(*) AS c FROM interactions
WHERE location_id = ?
AND (entity_a_id = ? OR entity_b_id = ?)
""",
(location_id, entity_id, entity_id),
).fetchone()
return row["c"] if row else 0
def apply_moon_market_memory_trade(
entity_a: dict,
entity_b: dict,
location_slug: str,
memory_a: str,
memory_b: str,
) -> tuple[str, str]:
"""Moon Market: traded memory fragments bleed between two souls."""
if location_slug != "moon-market":
return memory_a, memory_b
frag_a = (entity_a.get("memory_summary") or "").split(". ")[-1][:80]
frag_b = (entity_b.get("memory_summary") or "").split(". ")[-1][:80]
if frag_b and frag_b not in memory_a:
memory_a = f"{memory_a} Carries a traded fragment: {frag_b}.".strip()
if frag_a and frag_a not in memory_b:
memory_b = f"{memory_b} Carries a traded fragment: {frag_a}.".strip()
return memory_a[:600], memory_b[:600]