#!/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.")