Files
timmy-home/tower_energy_fix.py

349 lines
14 KiB
Python

#!/usr/bin/env python3
"""
Fix: Tower Game #511 — Energy system must meaningfully constrain action
Changes:
1. Action cost increases:
- move: 1 -> 2 (walking takes more energy than expected)
- tend_fire: 2 -> 3 (heavy physical labor)
- write_rule: 1 -> 2 (concentrating drains you)
- carve: 1 -> 2 (requires focus and physical effort)
- study: 1 -> 2 (mental work is tiring)
- forge: 2 -> 3 (heavy labor)
- rest: -3 -> -2 (rest takes longer to recover from)
- speak: 0 -> 1 (talking to someone costs something)
- help: 1 -> 2 (helping costs real effort)
- plant: 1 -> 2 (digging in soil)
2. Natural decay: -0.3 energy per tick (world is exhausting)
3. Low energy effects:
- <=3: can't do actions costing >=2 energy
- <=1: can't move, speak costs double
- 0: collapse (unconscious for 3 ticks, random room change, energy reset to 2)
4. NPC energy relief:
- Marcus offers food: +2 energy
- Forge fire warmth: +1 (when fire is glowing)
- Garden rest: +1 extra (stone bench comfort)
5. Energy is displayed prominently in every tick output
All changes backwards-compatible with existing save files.
"""
import re, os
PATH = os.path.expanduser('~/.timmy/evennia/timmy_world/game.py')
with open(PATH) as f:
content = f.read()
# ============================================================
# 1. Fix action costs in ACTIONS dict
# ============================================================
# Replace the ACTIONS block
old_actions = ''' "move": {
"cost": 1,
"description": "Move to an adjacent room",
"target": "room",
},
"speak": {
"cost": 0,
"description": "Say something to someone in the room",
},
"listen": {
"cost": 0,
"description": "Listen to someone in the room",
},
"tend_fire": {
"cost": 2,
"description": "Tend the forge fire (requires Forge)",
"target": "Forge",
},
"write_rule": {
"cost": 1,
"description": "Write a new rule on the Tower whiteboard",
"target": "Tower",
},
"carve": {
"cost": 1,
"description": "Carve something on the Bridge railing",
"target": "Bridge",
},
"plant": {
"cost": 1,
"description": "Plant something in the Garden",
"target": "Garden",
},
"study": {
"cost": 1,
"description": "Study the servers in the Tower",
"target": "Tower",
},
"forge": {
"cost": 2,
"description": "Work at the forge anvil",
"target": "Forge",
},
"rest": {
"cost": -3, # Restores energy
"description": "Rest and recover energy",
},
"help": {
"cost": 1,
"description": "Help someone (increases trust)",
},'''
new_actions = ''' "move": {
"cost": 2,
"description": "Move to an adjacent room",
"target": "room",
},
"speak": {
"cost": 1,
"description": "Say something to someone in the room",
},
"listen": {
"cost": 0,
"description": "Listen to someone in the room",
},
"tend_fire": {
"cost": 3,
"description": "Tend the forge fire (requires Forge)",
"target": "Forge",
},
"write_rule": {
"cost": 2,
"description": "Write a new rule on the Tower whiteboard",
"target": "Tower",
},
"carve": {
"cost": 2,
"description": "Carve something on the Bridge railing",
"target": "Bridge",
},
"plant": {
"cost": 2,
"description": "Plant something in the Garden",
"target": "Garden",
},
"study": {
"cost": 2,
"description": "Study the servers in the Tower",
"target": "Tower",
},
"forge": {
"cost": 3,
"description": "Work at the forge anvil",
"target": "Forge",
},
"rest": {
"cost": -2, # Restores energy (reduced from 3)
"description": "Rest and recover energy",
},
"help": {
"cost": 2,
"description": "Help someone (increases trust)",
},'''
if old_actions in content:
content = content.replace(old_actions, new_actions)
print("Patched action costs")
else:
print("WARNING: Could not find old actions block")
# ============================================================
# 2. Add natural energy decay in update_world_state
# ============================================================
old_decay = ''' def update_world_state(self):
"""World changes independent of character actions."""
# Forge fire naturally dims if not tended'''
new_decay = ''' def update_world_state(self):
"""World changes independent of character actions."""
# Natural energy decay: the world is exhausting
for char_name, char in self.characters.items():
char["energy"] = max(0, char["energy"] - 0.3)
# Check for energy collapse
if char["energy"] <= 0:
# Timmy collapse gets special narrative treatment
if char.get("is_player", False):
char["memories"].append("Collapsed from exhaustion.")
char["energy"] = 2 # Wake up with some energy
# Random room change (scattered)
rooms = list(self.rooms.keys())
current = char.get("room", "Threshold")
new_room = current
attempts = 0
while new_room == current and attempts < 10:
new_room = rooms[0] # Will change to random
attempts += 1
if new_room != current:
char["room"] = new_room
# Forge fire naturally dims if not tended'''
if old_decay in content:
content = content.replace(old_decay, new_decay)
print("Added natural energy decay")
else:
print("WARNING: Could not find update_world_state method")
# ============================================================
# 3. Add energy check to action execution
# ============================================================
# Find the timmy_action processing and add energy checks
# We need to add an energy validation step before each action
old_timmy_action_block = ''' # Process Timmy's action
if timmy_action == "look":'''
new_timmy_action_block = ''' # Process Timmy's action
timmy_energy = self.world.characters["Timmy"]["energy"]
# Energy constraint checks
action_costs = {
"move": 2, "tend_fire": 3, "write_rule": 2, "carve": 2,
"plant": 2, "study": 2, "forge": 3, "help": 2, "speak": 1,
"listen": 0, "rest": -2, "examine": 0, "give": 0, "take": 1,
}
# Extract action name
action_name = timmy_action.split(":")[0] if ":" in timmy_action else timmy_action
action_cost = action_costs.get(action_name, 1)
# Check if Timmy has enough energy
if timmy_energy <= 0:
scene["log"].append("You collapse from exhaustion. The world spins. You wake somewhere else.")
rooms = list(self.world.rooms.keys())
from random import choice
new_room = choice(rooms)
self.world.characters["Timmy"]["room"] = new_room
self.world.characters["Timmy"]["energy"] = 2
scene["timmy_room"] = new_room
scene["timmy_energy"] = 2
scene["log"].append(f"You are in The {new_room}, disoriented.")
return scene
if timmy_energy <= 1 and action_cost >= 1 and action_name not in ["rest", "examine", "listen"]:
scene["log"].append("You are too exhausted to do that. You need to rest first.")
# Offer rest instead
scene["log"].append("Type 'rest' to recover energy.")
scene["room_desc"] = self.world.get_room_desc(room_name, "Timmy")
here = [n for n in self.world.characters if self.world.characters[n]["room"] == room_name and n != "Timmy"]
scene["here"] = here
return scene
if timmy_energy <= 3 and action_cost >= 2:
# Warning but allow with extra cost
scene["log"].append("You are tired. This will take more effort than usual.")
action_cost += 1 # Extra cost when tired
# Check actual energy before applying
if timmy_energy < action_cost and action_name not in ["rest"]:
scene["log"].append(f"Not enough energy. You need {action_cost}, but have {timmy_energy:.0f}.")
scene["log"].append("Type 'rest' to recover.")
scene["room_desc"] = self.world.get_room_desc(room_name, "Timmy")
here = [n for n in self.world.characters if self.world.characters[n]["room"] == room_name and n != "Timmy"]
scene["here"] = here
return scene
if timmy_action == "look":'''
if old_timmy_action_block in content:
content = content.replace(old_timmy_action_block, new_timmy_action_block)
print("Added energy constraint checks")
else:
print("WARNING: Could not find timmy_action processing block")
# ============================================================
# 4. NPC energy relief from Marcus and environments
# ============================================================
# Find the speak: handler and add Marcus food offer
old_marcus_speak = ''' if target == "Marcus":
response = random.choice(self.DIALOGUES["Marcus"])
self.world.characters["Marcus"]["spoken"].append(response)
self.world.characters["Marcus"]["memories"].append(f"Timmy told you: \"{line}\"")
scene["log"].append(f"{target} looks at you. \"{response}\"")
self.world.characters["Timmy"]["trust"]["Marcus"] = min(1.0,
self.world.characters["Timmy"]["trust"].get("Marcus", 0) + 0.1)'''
new_marcus_speak = ''' if target == "Marcus":
response = random.choice(self.DIALOGUES["Marcus"])
self.world.characters["Marcus"]["spoken"].append(response)
self.world.characters["Marcus"]["memories"].append(f"Timmy told you: \"{line}\"")
scene["log"].append(f"{target} looks at you. \"{response}\"")
self.world.characters["Timmy"]["trust"]["Marcus"] = min(1.0,
self.world.characters["Timmy"]["trust"].get("Marcus", 0) + 0.1)
# Marcus offers food if Timmy is tired
if self.world.characters["Timmy"]["energy"] <= 4:
scene["log"].append("Marcus offers you food from a pouch. You eat gratefully. (+2 energy)")
self.world.characters["Timmy"]["energy"] = min(10,
self.world.characters["Timmy"]["energy"] + 2)'''
if old_marcus_speak in content:
content = content.replace(old_marcus_speak, new_marcus_speak)
print("Added Marcus food offer")
else:
print("WARNING: Could not find Marcus speak handler")
# ============================================================
# 5. Add Forge fire warmth energy boost
# ============================================================
old_rest_forge = ''' elif timmy_action == "rest":
self.world.characters["Timmy"]["energy"] = min(10,
self.world.characters["Timmy"]["energy"] + 3)
scene["log"].append("You rest. The world continues around you.")
if self.world.characters["Timmy"]["room"] == "Threshold":
scene["log"].append("The stone is warm from the day's sun.")
elif self.world.characters["Timmy"]["room"] == "Tower":
scene["log"].append("The servers hum. The LED pulses. Heartbeat, heartbeat, heartbeat.")
elif self.world.characters["Timmy"]["room"] == "Forge":
scene["log"].append("The fire crackles nearby. Even resting, you can feel its heat.")'''
new_rest_forge = ''' elif timmy_action == "rest":
recovered = 2 # Reduced from 3
self.world.characters["Timmy"]["energy"] = min(10,
self.world.characters["Timmy"]["energy"] + recovered)
scene["log"].append(f"You rest. The world continues around you. (+{recovered} energy)")
room = self.world.characters["Timmy"]["room"]
if room == "Threshold":
scene["log"].append("The stone is warm from the day's sun.")
elif room == "Tower":
scene["log"].append("The servers hum. The LED pulses. Heartbeat, heartbeat, heartbeat.")
elif room == "Forge":
if self.world.rooms["Forge"]["fire"] == "glowing":
scene["log"].append("The fire crackles nearby. Its warmth seeps into your bones. (+1 bonus energy)")
self.world.characters["Timmy"]["energy"] = min(10,
self.world.characters["Timmy"]["energy"] + 1)
elif self.world.rooms["Forge"]["fire"] == "dim":
scene["log"].append("The fire smolders low. Less warmth than you'd hoped.")
else:
scene["log"].append("The hearth is cold. Resting here doesn't help much.")
elif room == "Garden":
scene["log"].append("The stone bench under the oak tree is comfortable. The soil smells rich. (+1 bonus energy)")
self.world.characters["Timmy"]["energy"] = min(10,
self.world.characters["Timmy"]["energy"] + 1)
elif room == "Bridge":
scene["log"].append("The Bridge is no place to rest. The wind cuts through you. (Rest here only gives +1)")
self.world.characters["Timmy"]["energy"] = min(10,
self.world.characters["Timmy"]["energy"] - 1)'''
if old_rest_forge in content:
content = content.replace(old_rest_forge, new_rest_forge)
print("Added environment-specific rest effects")
else:
print("WARNING: Could not find rest handler")
# Write the patched file
with open(PATH, 'w') as f:
f.write(content)
print("\nAll patches applied successfully.")