Push
This commit is contained in:
121
scripts/convert_to_template_format.py
Normal file
121
scripts/convert_to_template_format.py
Normal file
@@ -0,0 +1,121 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Convert locations.json to use template-based interactables format.
|
||||
This script converts the new format (full instance data) to old format (template_id + outcomes).
|
||||
"""
|
||||
|
||||
import json
|
||||
import sys
|
||||
|
||||
def convert_interactables_to_template_format(interactables_dict):
|
||||
"""Convert interactables from full format to template format."""
|
||||
if not interactables_dict:
|
||||
return {}
|
||||
|
||||
converted = {}
|
||||
|
||||
for instance_id, instance_data in interactables_dict.items():
|
||||
# Check if already in template format
|
||||
if 'template_id' in instance_data:
|
||||
# Already in template format, keep as is
|
||||
converted[instance_id] = instance_data
|
||||
continue
|
||||
|
||||
# Check if in new format with 'id' and 'actions'
|
||||
if 'id' in instance_data and 'actions' in instance_data:
|
||||
# Convert to template format
|
||||
template_id = instance_data['id']
|
||||
|
||||
# Build outcomes from actions
|
||||
outcomes = {}
|
||||
for action_id, action_data in instance_data['actions'].items():
|
||||
outcome = {
|
||||
'success_rate': 0.5, # Default success rate
|
||||
'stamina_cost': action_data.get('stamina_cost', 2),
|
||||
'crit_success_chance': 0.1,
|
||||
'crit_failure_chance': 0.1,
|
||||
'text': {
|
||||
'success': '',
|
||||
'failure': '',
|
||||
'crit_success': '',
|
||||
'crit_failure': ''
|
||||
},
|
||||
'rewards': {
|
||||
'items': [],
|
||||
'damage': 0,
|
||||
'crit_items': [],
|
||||
'crit_damage': 0
|
||||
}
|
||||
}
|
||||
|
||||
# Extract text from outcomes if available
|
||||
if 'outcomes' in action_data:
|
||||
if 'success' in action_data['outcomes']:
|
||||
outcome['text']['success'] = action_data['outcomes']['success'].get('text', '')
|
||||
# Convert items_reward to items list
|
||||
items_reward = action_data['outcomes']['success'].get('items_reward', {})
|
||||
for item_id, quantity in items_reward.items():
|
||||
outcome['rewards']['items'].append({
|
||||
'item_id': item_id,
|
||||
'quantity': quantity,
|
||||
'chance': 1.0
|
||||
})
|
||||
outcome['rewards']['damage'] = action_data['outcomes']['success'].get('damage_taken', 0)
|
||||
|
||||
if 'failure' in action_data['outcomes']:
|
||||
outcome['text']['failure'] = action_data['outcomes']['failure'].get('text', '')
|
||||
|
||||
if 'critical_failure' in action_data['outcomes']:
|
||||
outcome['text']['crit_failure'] = action_data['outcomes']['critical_failure'].get('text', '')
|
||||
outcome['rewards']['crit_damage'] = action_data['outcomes']['critical_failure'].get('damage_taken', 0)
|
||||
|
||||
outcomes[action_id] = outcome
|
||||
|
||||
converted[instance_id] = {
|
||||
'template_id': template_id,
|
||||
'outcomes': outcomes
|
||||
}
|
||||
else:
|
||||
# Unknown format, keep as is
|
||||
converted[instance_id] = instance_data
|
||||
|
||||
return converted
|
||||
|
||||
def main():
|
||||
# Load locations.json
|
||||
locations_file = 'gamedata/locations.json'
|
||||
|
||||
try:
|
||||
with open(locations_file, 'r', encoding='utf-8') as f:
|
||||
data = json.load(f)
|
||||
except Exception as e:
|
||||
print(f"Error loading {locations_file}: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
# Convert all location interactables
|
||||
locations_converted = 0
|
||||
interactables_converted = 0
|
||||
|
||||
for location in data.get('locations', []):
|
||||
if 'interactables' in location and location['interactables']:
|
||||
original_count = len(location['interactables'])
|
||||
location['interactables'] = convert_interactables_to_template_format(location['interactables'])
|
||||
|
||||
if original_count > 0:
|
||||
locations_converted += 1
|
||||
interactables_converted += original_count
|
||||
|
||||
# Save back to file
|
||||
try:
|
||||
with open(locations_file, 'w', encoding='utf-8') as f:
|
||||
json.dump(data, f, indent=2, ensure_ascii=False)
|
||||
|
||||
print(f"✅ Conversion complete!")
|
||||
print(f" Processed {locations_converted} locations")
|
||||
print(f" Converted {interactables_converted} interactables to template format")
|
||||
except Exception as e:
|
||||
print(f"❌ Error saving {locations_file}: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
165
scripts/export_to_json.py
Normal file
165
scripts/export_to_json.py
Normal file
@@ -0,0 +1,165 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Extract current game data to JSON files
|
||||
This script reads the current Python-based game data and exports it to JSON format
|
||||
"""
|
||||
import sys
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
# Add parent directory to path
|
||||
sys.path.insert(0, str(Path(__file__).parent))
|
||||
|
||||
from data.world_loader import game_world, export_map_data
|
||||
from data.npcs import NPCS, LOCATION_DANGER, LOCATION_SPAWNS
|
||||
from data.items import ITEMS
|
||||
|
||||
def export_to_json():
|
||||
"""Export all game data to JSON files"""
|
||||
gamedata_dir = Path(__file__).parent / 'gamedata'
|
||||
gamedata_dir.mkdir(exist_ok=True)
|
||||
|
||||
print("🔄 Exporting game data to JSON...")
|
||||
|
||||
# 1. Export locations and world data
|
||||
print(" 📍 Exporting locations...")
|
||||
locations_data = {
|
||||
"locations": [],
|
||||
"connections": []
|
||||
}
|
||||
|
||||
for location in game_world.locations.values():
|
||||
loc_data = {
|
||||
"id": location.id,
|
||||
"name": location.name,
|
||||
"description": location.description,
|
||||
"x": location.x,
|
||||
"y": location.y,
|
||||
"image_path": location.image_path,
|
||||
"interactables": {}
|
||||
}
|
||||
|
||||
# Add interactables
|
||||
for instance_id, interactable in location.interactables.items():
|
||||
inter_data = {
|
||||
"id": interactable.id,
|
||||
"name": interactable.name,
|
||||
"image_path": interactable.image_path,
|
||||
"actions": {}
|
||||
}
|
||||
|
||||
# Add actions
|
||||
for action_id, action in interactable.actions.items():
|
||||
action_data = {
|
||||
"id": action.id,
|
||||
"label": action.label,
|
||||
"stamina_cost": action.stamina_cost,
|
||||
"outcomes": {}
|
||||
}
|
||||
|
||||
# Add outcomes
|
||||
for outcome_name, outcome in action.outcomes.items():
|
||||
outcome_data = {
|
||||
"text": outcome.text,
|
||||
"items_reward": outcome.items_reward,
|
||||
"damage_taken": outcome.damage_taken
|
||||
}
|
||||
action_data["outcomes"][outcome_name] = outcome_data
|
||||
|
||||
inter_data["actions"][action_id] = action_data
|
||||
|
||||
loc_data["interactables"][instance_id] = inter_data
|
||||
|
||||
locations_data["locations"].append(loc_data)
|
||||
|
||||
# Add connections with distance-based stamina cost
|
||||
for direction, dest_id in location.exits.items():
|
||||
dest_loc = game_world.get_location(dest_id)
|
||||
if dest_loc:
|
||||
from data.travel_helpers import calculate_base_stamina_cost
|
||||
stamina_cost = calculate_base_stamina_cost(location, dest_loc)
|
||||
else:
|
||||
stamina_cost = 5 # Fallback
|
||||
|
||||
locations_data["connections"].append({
|
||||
"from": location.id,
|
||||
"to": dest_id,
|
||||
"direction": direction,
|
||||
"stamina_cost": stamina_cost
|
||||
})
|
||||
|
||||
with open(gamedata_dir / 'locations.json', 'w') as f:
|
||||
json.dump(locations_data, f, indent=2)
|
||||
print(f" ✅ Exported {len(locations_data['locations'])} locations and {len(locations_data['connections'])} connections")
|
||||
|
||||
# 2. Export NPCs and spawns
|
||||
print(" 👹 Exporting NPCs...")
|
||||
npcs_data = {
|
||||
"npcs": {},
|
||||
"danger_levels": {},
|
||||
"spawn_tables": {}
|
||||
}
|
||||
|
||||
for npc_id, npc in NPCS.items():
|
||||
# Convert loot tables to serializable format
|
||||
loot_table = [
|
||||
{"item_id": loot.item_id, "quantity_min": loot.quantity_min, "quantity_max": loot.quantity_max, "drop_chance": loot.drop_chance}
|
||||
for loot in npc.loot_table
|
||||
]
|
||||
corpse_loot = [
|
||||
{"item_id": loot.item_id, "quantity_min": loot.quantity_min, "quantity_max": loot.quantity_max, "required_tool": loot.required_tool}
|
||||
for loot in npc.corpse_loot
|
||||
]
|
||||
|
||||
npcs_data["npcs"][npc_id] = {
|
||||
"npc_id": npc.npc_id,
|
||||
"name": npc.name,
|
||||
"description": npc.description,
|
||||
"emoji": npc.emoji,
|
||||
"hp_min": npc.hp_min,
|
||||
"hp_max": npc.hp_max,
|
||||
"damage_min": npc.damage_min,
|
||||
"damage_max": npc.damage_max,
|
||||
"defense": npc.defense,
|
||||
"xp_reward": npc.xp_reward,
|
||||
"loot_table": loot_table,
|
||||
"corpse_loot": corpse_loot,
|
||||
"flee_chance": npc.flee_chance,
|
||||
"status_inflict_chance": npc.status_inflict_chance,
|
||||
"image_url": npc.image_url,
|
||||
"death_message": npc.death_message
|
||||
}
|
||||
|
||||
for location_id, (danger, encounter_rate, wandering_chance) in LOCATION_DANGER.items():
|
||||
npcs_data["danger_levels"][location_id] = {
|
||||
"danger_level": danger,
|
||||
"encounter_rate": encounter_rate,
|
||||
"wandering_chance": wandering_chance
|
||||
}
|
||||
|
||||
for location_id, spawns in LOCATION_SPAWNS.items():
|
||||
npcs_data["spawn_tables"][location_id] = [
|
||||
{"npc_id": npc_id, "weight": weight}
|
||||
for npc_id, weight in spawns
|
||||
]
|
||||
|
||||
with open(gamedata_dir / 'npcs.json', 'w') as f:
|
||||
json.dump(npcs_data, f, indent=2)
|
||||
print(f" ✅ Exported {len(npcs_data['npcs'])} NPCs, {len(npcs_data['danger_levels'])} danger configs, {len(npcs_data['spawn_tables'])} spawn tables")
|
||||
|
||||
# 3. Export items
|
||||
print(" 🎒 Exporting items...")
|
||||
items_data = {
|
||||
"items": ITEMS # ITEMS is already a dict with the right structure
|
||||
}
|
||||
|
||||
with open(gamedata_dir / 'items.json', 'w') as f:
|
||||
json.dump(items_data, f, indent=2)
|
||||
print(f" ✅ Exported {len(items_data['items'])} items")
|
||||
|
||||
print("\n✅ Export complete! JSON files created in gamedata/")
|
||||
print(f" 📁 {gamedata_dir.absolute()}")
|
||||
|
||||
if __name__ == '__main__':
|
||||
export_to_json()
|
||||
79
scripts/migrate_add_wandering_flag.py
Normal file
79
scripts/migrate_add_wandering_flag.py
Normal file
@@ -0,0 +1,79 @@
|
||||
"""
|
||||
Migration script to add from_wandering_enemy column to active_combats table.
|
||||
Run this once to update the database schema.
|
||||
"""
|
||||
import asyncio
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
from sqlalchemy.ext.asyncio import create_async_engine
|
||||
from sqlalchemy import text
|
||||
|
||||
load_dotenv()
|
||||
|
||||
DB_USER = os.getenv("POSTGRES_USER")
|
||||
DB_PASS = os.getenv("POSTGRES_PASSWORD")
|
||||
DB_NAME = os.getenv("POSTGRES_DB")
|
||||
DB_HOST = os.getenv("POSTGRES_HOST")
|
||||
DB_PORT = os.getenv("POSTGRES_PORT")
|
||||
|
||||
DATABASE_URL = f"postgresql+psycopg://{DB_USER}:{DB_PASS}@{DB_HOST}:{DB_PORT}/{DB_NAME}"
|
||||
|
||||
async def migrate():
|
||||
"""Add from_wandering_enemy column to active_combats table."""
|
||||
engine = create_async_engine(DATABASE_URL)
|
||||
|
||||
async with engine.begin() as conn:
|
||||
# Check if column already exists
|
||||
result = await conn.execute(text("""
|
||||
SELECT column_name
|
||||
FROM information_schema.columns
|
||||
WHERE table_name='active_combats'
|
||||
AND column_name='from_wandering_enemy'
|
||||
"""))
|
||||
|
||||
exists = result.fetchone()
|
||||
|
||||
if exists:
|
||||
print("✅ Column 'from_wandering_enemy' already exists. No migration needed.")
|
||||
else:
|
||||
print("🔧 Adding 'from_wandering_enemy' column to active_combats table...")
|
||||
|
||||
# Add the column with default value False
|
||||
await conn.execute(text("""
|
||||
ALTER TABLE active_combats
|
||||
ADD COLUMN from_wandering_enemy BOOLEAN DEFAULT FALSE
|
||||
"""))
|
||||
|
||||
print("✅ Column added successfully!")
|
||||
|
||||
# Also check and create wandering_enemies table if it doesn't exist
|
||||
result = await conn.execute(text("""
|
||||
SELECT table_name
|
||||
FROM information_schema.tables
|
||||
WHERE table_name='wandering_enemies'
|
||||
"""))
|
||||
|
||||
table_exists = result.fetchone()
|
||||
|
||||
if table_exists:
|
||||
print("✅ Table 'wandering_enemies' already exists.")
|
||||
else:
|
||||
print("🔧 Creating 'wandering_enemies' table...")
|
||||
|
||||
await conn.execute(text("""
|
||||
CREATE TABLE wandering_enemies (
|
||||
id SERIAL PRIMARY KEY,
|
||||
npc_id VARCHAR NOT NULL,
|
||||
location_id VARCHAR NOT NULL,
|
||||
spawn_timestamp FLOAT NOT NULL,
|
||||
despawn_timestamp FLOAT NOT NULL
|
||||
)
|
||||
"""))
|
||||
|
||||
print("✅ Table 'wandering_enemies' created successfully!")
|
||||
|
||||
await engine.dispose()
|
||||
print("🎉 Migration complete!")
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(migrate())
|
||||
117
scripts/migrate_combat.py
Normal file
117
scripts/migrate_combat.py
Normal file
@@ -0,0 +1,117 @@
|
||||
"""
|
||||
Migration script to add combat system columns to existing database.
|
||||
Run this once to upgrade the database schema.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import os
|
||||
from sqlalchemy.ext.asyncio import create_async_engine
|
||||
from sqlalchemy import text
|
||||
from dotenv import load_dotenv
|
||||
|
||||
async def migrate():
|
||||
load_dotenv()
|
||||
|
||||
DB_USER = os.getenv("POSTGRES_USER")
|
||||
DB_PASS = os.getenv("POSTGRES_PASSWORD")
|
||||
DB_NAME = os.getenv("POSTGRES_DB")
|
||||
DB_HOST = os.getenv("POSTGRES_HOST")
|
||||
DB_PORT = os.getenv("POSTGRES_PORT")
|
||||
|
||||
DATABASE_URL = f"postgresql+psycopg://{DB_USER}:{DB_PASS}@{DB_HOST}:{DB_PORT}/{DB_NAME}"
|
||||
engine = create_async_engine(DATABASE_URL)
|
||||
|
||||
print("Starting database migration...")
|
||||
|
||||
async with engine.begin() as conn:
|
||||
# Add level column if it doesn't exist
|
||||
try:
|
||||
await conn.execute(text(
|
||||
"ALTER TABLE players ADD COLUMN level INTEGER DEFAULT 1"
|
||||
))
|
||||
print("✅ Added 'level' column")
|
||||
except Exception as e:
|
||||
if "already exists" in str(e):
|
||||
print("⚠️ 'level' column already exists, skipping")
|
||||
else:
|
||||
print(f"❌ Error adding 'level': {e}")
|
||||
|
||||
# Add xp column if it doesn't exist
|
||||
try:
|
||||
await conn.execute(text(
|
||||
"ALTER TABLE players ADD COLUMN xp INTEGER DEFAULT 0"
|
||||
))
|
||||
print("✅ Added 'xp' column")
|
||||
except Exception as e:
|
||||
if "already exists" in str(e):
|
||||
print("⚠️ 'xp' column already exists, skipping")
|
||||
else:
|
||||
print(f"❌ Error adding 'xp': {e}")
|
||||
|
||||
# Add unspent_points column if it doesn't exist
|
||||
try:
|
||||
await conn.execute(text(
|
||||
"ALTER TABLE players ADD COLUMN unspent_points INTEGER DEFAULT 0"
|
||||
))
|
||||
print("✅ Added 'unspent_points' column")
|
||||
except Exception as e:
|
||||
if "already exists" in str(e):
|
||||
print("⚠️ 'unspent_points' column already exists, skipping")
|
||||
else:
|
||||
print(f"❌ Error adding 'unspent_points': {e}")
|
||||
|
||||
# Create active_combats table if it doesn't exist
|
||||
try:
|
||||
await conn.execute(text("""
|
||||
CREATE TABLE IF NOT EXISTS active_combats (
|
||||
id SERIAL PRIMARY KEY,
|
||||
player_id INTEGER UNIQUE REFERENCES players(telegram_id) ON DELETE CASCADE,
|
||||
npc_id VARCHAR NOT NULL,
|
||||
npc_hp INTEGER NOT NULL,
|
||||
npc_max_hp INTEGER NOT NULL,
|
||||
turn VARCHAR NOT NULL,
|
||||
turn_started_at FLOAT NOT NULL,
|
||||
player_status_effects VARCHAR DEFAULT '[]',
|
||||
npc_status_effects VARCHAR DEFAULT '[]',
|
||||
location_id VARCHAR NOT NULL
|
||||
)
|
||||
"""))
|
||||
print("✅ Created 'active_combats' table")
|
||||
except Exception as e:
|
||||
print(f"⚠️ 'active_combats' table: {e}")
|
||||
|
||||
# Create player_corpses table if it doesn't exist
|
||||
try:
|
||||
await conn.execute(text("""
|
||||
CREATE TABLE IF NOT EXISTS player_corpses (
|
||||
id SERIAL PRIMARY KEY,
|
||||
player_name VARCHAR NOT NULL,
|
||||
location_id VARCHAR NOT NULL,
|
||||
items VARCHAR NOT NULL,
|
||||
death_timestamp FLOAT NOT NULL
|
||||
)
|
||||
"""))
|
||||
print("✅ Created 'player_corpses' table")
|
||||
except Exception as e:
|
||||
print(f"⚠️ 'player_corpses' table: {e}")
|
||||
|
||||
# Create npc_corpses table if it doesn't exist
|
||||
try:
|
||||
await conn.execute(text("""
|
||||
CREATE TABLE IF NOT EXISTS npc_corpses (
|
||||
id SERIAL PRIMARY KEY,
|
||||
npc_id VARCHAR NOT NULL,
|
||||
location_id VARCHAR NOT NULL,
|
||||
loot_remaining VARCHAR NOT NULL,
|
||||
death_timestamp FLOAT NOT NULL
|
||||
)
|
||||
"""))
|
||||
print("✅ Created 'npc_corpses' table")
|
||||
except Exception as e:
|
||||
print(f"⚠️ 'npc_corpses' table: {e}")
|
||||
|
||||
await engine.dispose()
|
||||
print("\n✅ Migration complete!")
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(migrate())
|
||||
281
scripts/update_locations.py
Normal file
281
scripts/update_locations.py
Normal file
@@ -0,0 +1,281 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Update locations.json with missing interactables from world_loader_old.py
|
||||
and spawn configurations from npcs_old.py
|
||||
"""
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
# Load current locations.json
|
||||
locations_file = Path("gamedata/locations.json")
|
||||
with open(locations_file, 'r') as f:
|
||||
data = json.load(f)
|
||||
|
||||
# Helper to find location by ID
|
||||
def find_location(loc_id):
|
||||
for loc in data['locations']:
|
||||
if loc['id'] == loc_id:
|
||||
return loc
|
||||
return None
|
||||
|
||||
# Add missing interactables to start_point
|
||||
start_point = find_location('start_point')
|
||||
if start_point and 'start_point_sedan' not in start_point.get('interactables', {}):
|
||||
if 'interactables' not in start_point:
|
||||
start_point['interactables'] = {}
|
||||
|
||||
start_point['interactables']['start_point_sedan'] = {
|
||||
"id": "sedan",
|
||||
"name": "🚗 Rusty Sedan",
|
||||
"image_path": "images/interactables/sedan.png",
|
||||
"actions": {
|
||||
"search_glovebox": {
|
||||
"id": "search_glovebox",
|
||||
"label": "🔎 Search Glovebox",
|
||||
"stamina_cost": 1,
|
||||
"outcomes": {
|
||||
"success": {"text": "You find a half-eaten [Stale Chocolate Bar].", "items_reward": {"stale_chocolate_bar": 1}, "damage_taken": 0},
|
||||
"failure": {"text": "The glovebox is empty except for dust and old receipts.", "items_reward": {}, "damage_taken": 0}
|
||||
}
|
||||
},
|
||||
"pop_trunk": {
|
||||
"id": "pop_trunk",
|
||||
"label": "🔧 Pop the Trunk",
|
||||
"stamina_cost": 3,
|
||||
"outcomes": {
|
||||
"success": {"text": "With a great heave, you pry the trunk open and find a [Tire Iron]!", "items_reward": {"tire_iron": 1}, "damage_taken": 0},
|
||||
"failure": {"text": "The trunk is rusted shut. You can't get it open.", "items_reward": {}, "damage_taken": 0}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
start_point['interactables']['start_point_dumpster'] = {
|
||||
"id": "dumpster",
|
||||
"name": "🗑️ Dumpster",
|
||||
"image_path": "images/interactables/dumpster.png",
|
||||
"actions": {
|
||||
"search_dumpster": {
|
||||
"id": "search_dumpster",
|
||||
"label": "🔎 Dig Through Trash",
|
||||
"stamina_cost": 2,
|
||||
"outcomes": {
|
||||
"success": {"text": "Despite the smell, you find some [Plastic Bottles] and [Cloth Scraps].", "items_reward": {"plastic_bottles": 3, "cloth_scraps": 2}, "damage_taken": 0},
|
||||
"failure": {"text": "Just rotting garbage. Nothing useful.", "items_reward": {}, "damage_taken": 0},
|
||||
"critical_failure": {"text": "You disturb a nest of rats! They bite you! (-8 HP)", "items_reward": {}, "damage_taken": 8}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Add missing interactables to subway_tunnels
|
||||
subway_tunnels = find_location('subway_tunnels')
|
||||
if subway_tunnels:
|
||||
if 'interactables' not in subway_tunnels:
|
||||
subway_tunnels['interactables'] = {}
|
||||
|
||||
subway_tunnels['interactables']['subway_train_sedan'] = {
|
||||
"id": "sedan",
|
||||
"name": "🚗 Rusty Sedan",
|
||||
"image_path": "images/interactables/sedan.png",
|
||||
"actions": {
|
||||
"search_glovebox": {
|
||||
"id": "search_glovebox",
|
||||
"label": "🔎 Search Glovebox",
|
||||
"stamina_cost": 1,
|
||||
"outcomes": {
|
||||
"success": {"text": "You find a half-eaten [Stale Chocolate Bar].", "items_reward": {"stale_chocolate_bar": 1}, "damage_taken": 0},
|
||||
"failure": {"text": "The glovebox is empty except for dust and old receipts.", "items_reward": {}, "damage_taken": 0}
|
||||
}
|
||||
},
|
||||
"pop_trunk": {
|
||||
"id": "pop_trunk",
|
||||
"label": "🔧 Pop the Trunk",
|
||||
"stamina_cost": 3,
|
||||
"outcomes": {
|
||||
"success": {"text": "With a great heave, you pry the trunk open and find a [Tire Iron]!", "items_reward": {"tire_iron": 1}, "damage_taken": 0},
|
||||
"failure": {"text": "The trunk is rusted shut. You can't get it open.", "items_reward": {}, "damage_taken": 0}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
subway_tunnels['interactables']['subway_medkit'] = {
|
||||
"id": "medkit",
|
||||
"name": "🏥 Medical Supply Cabinet",
|
||||
"image_path": "images/interactables/medkit.png",
|
||||
"actions": {
|
||||
"search_medkit": {
|
||||
"id": "search_medkit",
|
||||
"label": "🔎 Search Cabinet",
|
||||
"stamina_cost": 2,
|
||||
"outcomes": {
|
||||
"success": {"text": "Jackpot! You find a [First Aid Kit] and some [Bandages]!", "items_reward": {"first_aid_kit": 1, "bandage": 2}, "damage_taken": 0},
|
||||
"failure": {"text": "The cabinet is empty. Someone got here first.", "items_reward": {}, "damage_taken": 0}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
subway_tunnels['interactables']['subway_rubble'] = {
|
||||
"id": "rubble",
|
||||
"name": "Pile of Rubble",
|
||||
"image_path": "images/interactables/rubble.png",
|
||||
"actions": {
|
||||
"search": {
|
||||
"id": "search",
|
||||
"label": "🔎 Search Rubble",
|
||||
"stamina_cost": 2,
|
||||
"outcomes": {
|
||||
"success": {"text": "You dig through the debris and find some [Scrap Metal].", "items_reward": {"scrap_metal": 2}, "damage_taken": 0},
|
||||
"failure": {"text": "The pile seems to have been picked clean already.", "items_reward": {}, "damage_taken": 0},
|
||||
"critical_failure": {"text": "You cut your hand on a sharp piece of glass! (-5 HP)", "items_reward": {}, "damage_taken": 5}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Add missing interactables to office_interior
|
||||
office_interior = find_location('office_interior')
|
||||
if office_interior:
|
||||
if 'interactables' not in office_interior:
|
||||
office_interior['interactables'] = {}
|
||||
|
||||
office_interior['interactables']['office_desk1'] = {
|
||||
"id": "rubble",
|
||||
"name": "Pile of Rubble",
|
||||
"image_path": "images/interactables/rubble.png",
|
||||
"actions": {
|
||||
"search": {
|
||||
"id": "search",
|
||||
"label": "🔎 Search Rubble",
|
||||
"stamina_cost": 2,
|
||||
"outcomes": {
|
||||
"success": {"text": "You dig through the debris and find some [Scrap Metal].", "items_reward": {"scrap_metal": 2}, "damage_taken": 0},
|
||||
"failure": {"text": "The pile seems to have been picked clean already.", "items_reward": {}, "damage_taken": 0},
|
||||
"critical_failure": {"text": "You cut your hand on a sharp piece of glass! (-5 HP)", "items_reward": {}, "damage_taken": 5}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
office_interior['interactables']['office_desk2'] = {
|
||||
"id": "rubble",
|
||||
"name": "Pile of Rubble",
|
||||
"image_path": "images/interactables/rubble.png",
|
||||
"actions": {
|
||||
"search": {
|
||||
"id": "search",
|
||||
"label": "🔎 Search Rubble",
|
||||
"stamina_cost": 2,
|
||||
"outcomes": {
|
||||
"success": {"text": "You dig through the debris and find some [Scrap Metal].", "items_reward": {"scrap_metal": 2}, "damage_taken": 0},
|
||||
"failure": {"text": "The pile seems to have been picked clean already.", "items_reward": {}, "damage_taken": 0},
|
||||
"critical_failure": {"text": "You cut your hand on a sharp piece of glass! (-5 HP)", "items_reward": {}, "damage_taken": 5}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
office_interior['interactables']['office_corner'] = {
|
||||
"id": "house",
|
||||
"name": "🏚️ Abandoned House",
|
||||
"image_path": "images/interactables/house.png",
|
||||
"actions": {
|
||||
"search_house": {
|
||||
"id": "search_house",
|
||||
"label": "🔎 Search House",
|
||||
"stamina_cost": 3,
|
||||
"outcomes": {
|
||||
"success": {"text": "You find some useful supplies: [Canned Beans], [Bottled Water], and [Cloth Scraps]!", "items_reward": {"canned_beans": 1, "bottled_water": 1, "cloth_scraps": 3}, "damage_taken": 0},
|
||||
"failure": {"text": "The house has already been thoroughly looted. Nothing remains.", "items_reward": {}, "damage_taken": 0},
|
||||
"critical_failure": {"text": "The floor collapses beneath you! (-10 HP)", "items_reward": {}, "damage_taken": 10}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Update danger_config from npcs_old.py
|
||||
data['danger_config'] = {
|
||||
"start_point": {"danger_level": 0, "encounter_rate": 0.0, "wandering_chance": 0.0},
|
||||
"gas_station": {"danger_level": 0, "encounter_rate": 0.0, "wandering_chance": 0.0},
|
||||
"residential": {"danger_level": 1, "encounter_rate": 0.10, "wandering_chance": 0.20},
|
||||
"park": {"danger_level": 1, "encounter_rate": 0.10, "wandering_chance": 0.20},
|
||||
"clinic": {"danger_level": 2, "encounter_rate": 0.20, "wandering_chance": 0.35},
|
||||
"plaza": {"danger_level": 2, "encounter_rate": 0.15, "wandering_chance": 0.30},
|
||||
"warehouse": {"danger_level": 2, "encounter_rate": 0.18, "wandering_chance": 0.32},
|
||||
"warehouse_interior": {"danger_level": 2, "encounter_rate": 0.22, "wandering_chance": 0.40},
|
||||
"overpass": {"danger_level": 3, "encounter_rate": 0.30, "wandering_chance": 0.45},
|
||||
"office_building": {"danger_level": 3, "encounter_rate": 0.25, "wandering_chance": 0.40},
|
||||
"office_interior": {"danger_level": 3, "encounter_rate": 0.35, "wandering_chance": 0.50},
|
||||
"subway": {"danger_level": 4, "encounter_rate": 0.35, "wandering_chance": 0.50},
|
||||
"subway_tunnels": {"danger_level": 4, "encounter_rate": 0.45, "wandering_chance": 0.65}
|
||||
}
|
||||
|
||||
# Update spawn_config from npcs_old.py
|
||||
data['spawn_config'] = {
|
||||
"start_point": [],
|
||||
"gas_station": [],
|
||||
"residential": [
|
||||
{"npc_id": "feral_dog", "weight": 60},
|
||||
{"npc_id": "mutant_rat", "weight": 40}
|
||||
],
|
||||
"park": [
|
||||
{"npc_id": "feral_dog", "weight": 50},
|
||||
{"npc_id": "mutant_rat", "weight": 50}
|
||||
],
|
||||
"clinic": [
|
||||
{"npc_id": "infected_human", "weight": 40},
|
||||
{"npc_id": "mutant_rat", "weight": 30},
|
||||
{"npc_id": "scavenger", "weight": 30}
|
||||
],
|
||||
"plaza": [
|
||||
{"npc_id": "raider_scout", "weight": 40},
|
||||
{"npc_id": "scavenger", "weight": 35},
|
||||
{"npc_id": "feral_dog", "weight": 25}
|
||||
],
|
||||
"warehouse": [
|
||||
{"npc_id": "raider_scout", "weight": 45},
|
||||
{"npc_id": "scavenger", "weight": 35},
|
||||
{"npc_id": "mutant_rat", "weight": 20}
|
||||
],
|
||||
"warehouse_interior": [
|
||||
{"npc_id": "raider_scout", "weight": 50},
|
||||
{"npc_id": "scavenger", "weight": 30},
|
||||
{"npc_id": "mutant_rat", "weight": 20}
|
||||
],
|
||||
"overpass": [
|
||||
{"npc_id": "raider_scout", "weight": 50},
|
||||
{"npc_id": "infected_human", "weight": 30},
|
||||
{"npc_id": "scavenger", "weight": 20}
|
||||
],
|
||||
"office_building": [
|
||||
{"npc_id": "raider_scout", "weight": 45},
|
||||
{"npc_id": "infected_human", "weight": 35},
|
||||
{"npc_id": "scavenger", "weight": 20}
|
||||
],
|
||||
"office_interior": [
|
||||
{"npc_id": "infected_human", "weight": 50},
|
||||
{"npc_id": "raider_scout", "weight": 30},
|
||||
{"npc_id": "scavenger", "weight": 20}
|
||||
],
|
||||
"subway": [
|
||||
{"npc_id": "infected_human", "weight": 50},
|
||||
{"npc_id": "raider_scout", "weight": 30},
|
||||
{"npc_id": "mutant_rat", "weight": 20}
|
||||
],
|
||||
"subway_tunnels": [
|
||||
{"npc_id": "infected_human", "weight": 60},
|
||||
{"npc_id": "raider_scout", "weight": 25},
|
||||
{"npc_id": "mutant_rat", "weight": 15}
|
||||
]
|
||||
}
|
||||
|
||||
# Save updated file
|
||||
with open(locations_file, 'w') as f:
|
||||
json.dump(data, f, indent=2)
|
||||
|
||||
print("✅ Updated locations.json with:")
|
||||
print(" - Missing interactables for start_point, subway_tunnels, office_interior")
|
||||
print(" - Complete danger_config from npcs_old.py")
|
||||
print(" - Complete spawn_config from npcs_old.py")
|
||||
Reference in New Issue
Block a user