349 lines
14 KiB
Python
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.") |