import json
import random
import sys
from textwrap import dedent
# =========================
# Utility and data classes
# =========================
class Player:
def __init__(self, name="Hero"):
[Link] = name
[Link] = 100
self.max_health = 100
[Link] = 25
[Link] = {"Potion": 2}
[Link] = {"Weapon": None, "Armor": None}
[Link] = []
def is_alive(self):
return [Link] > 0
def heal(self, amount):
[Link] = min(self.max_health, [Link] + amount)
def take_damage(self, amount):
[Link] = max(0, [Link] - amount)
def add_item(self, item, qty=1):
[Link][item] = [Link](item, 0) + qty
def remove_item(self, item, qty=1):
if [Link](item, 0) >= qty:
[Link][item] -= qty
if [Link][item] == 0:
del [Link][item]
return True
return False
def equip(self, slot, item, stats):
[Link][slot] = item
# Apply simple stat effects
if slot == "Armor":
self.max_health = 100 + [Link]("hp_bonus", 0)
[Link] = min([Link], self.max_health)
def damage_output(self):
base = 8
weapon_bonus = 0
if [Link]["Weapon"] == "Iron Sword":
weapon_bonus = 6
elif [Link]["Weapon"] == "Steel Blade":
weapon_bonus = 10
elif [Link]["Weapon"] == "Mystic Dagger":
weapon_bonus = 4
return base + weapon_bonus + (2 if "Battle Focus" in [Link] else 0)
def defense(self):
base = 2
armor_bonus = 0
if [Link]["Armor"] == "Leather Vest":
armor_bonus = 2
elif [Link]["Armor"] == "Iron Mail":
armor_bonus = 5
elif [Link]["Armor"] == "Dragon Scale":
armor_bonus = 9
return base + armor_bonus
class Enemy:
def __init__(self, name, health, damage, loot=None):
[Link] = name
[Link] = health
[Link] = damage
[Link] = loot or {}
def is_alive(self):
return [Link] > 0
# =========================
# World and rooms
# =========================
class Room:
def __init__(self, key, name, desc, neighbors, items=None, enemy=None,
merchant=None, puzzle=None, locked=False):
[Link] = key
[Link] = name
[Link] = desc
[Link] = neighbors # dict: direction -> room_key
[Link] = items or []
[Link] = enemy
[Link] = merchant
[Link] = puzzle
[Link] = locked
class Merchant:
def __init__(self, name, stock):
[Link] = name
[Link] = stock # dict item -> {"price": int, "desc": str}
class Puzzle:
def __init__(self, prompt, answer, reward):
[Link] = prompt
[Link] = answer
[Link] = False
[Link] = reward # dict item -> qty
# =========================
# Game core
# =========================
class Game:
def __init__(self):
[Link] = Player()
[Link] = {}
self.current_room = "village_square"
self.win_condition_met = False
self.quest_flags = {"FoundKey": False, "SlainSorcerer": False,
"OpenedVault": False}
self._build_world()
def _build_world(self):
# Merchants
armorer = Merchant("Marin the Armorer", {
"Leather Vest": {"price": 15, "desc": "Basic protection. +2 defense."},
"Iron Mail": {"price": 40, "desc": "Sturdy armor. +5 defense, +20 max
HP."},
"Dragon Scale": {"price": 120, "desc": "Legendary armor. +9 defense,
+40 max HP."}
})
smith = Merchant("Daro the Smith", {
"Iron Sword": {"price": 25, "desc": "Reliable blade. +6 damage."},
"Steel Blade": {"price": 60, "desc": "Sharper, deadlier. +10 damage."},
"Mystic Dagger": {"price": 40, "desc": "Quick strikes. +4 damage; pairs
well with focus."}
})
apothecary = Merchant("Sena the Apothecary", {
"Potion": {"price": 10, "desc": "Restore 30 HP."},
"Elixir": {"price": 35, "desc": "Restore to full HP."},
"Focus Tonic": {"price": 25, "desc": "Grants Battle Focus for stronger
attacks for one fight."}
})
# Puzzles
library_riddle = Puzzle(
prompt="Riddle: I speak without a mouth and hear without ears. I have
no body, but I come alive with wind. What am I?",
answer="echo",
reward={"Ancient Key": 1}
)
vault_lock = Puzzle(
prompt="Vault Sigils: Enter the three-letter code of the element
binding flame to stone.",
answer="mag",
reward={"Vault Treasure": 1}
)
# Enemies
goblin = Enemy("Goblin Raider", 35, 6, loot={"Gold": 12})
skeleton = Enemy("Restless Skeleton", 45, 8, loot={"Bone Charm": 1})
cave_drake = Enemy("Cave Drake", 80, 12, loot={"Drake Fang": 1})
dark_sorcerer = Enemy("Dark Sorcerer", 120, 14, loot={"Sorcerer Seal": 1})
# Rooms
self._add_room(Room(
"village_square",
"Village Square",
"A bustling square with cobblestones warmed by the morning sun.
Merchants call out, and a quest board creaks.",
neighbors={"north": "blacksmith", "east": "armory", "south":
"apothecary", "west": "old_library", "down": "catacombs_entrance"},
items=["Map"],
merchant=None
))
self._add_room(Room(
"blacksmith",
"Blacksmith",
"Sparks fly as Daro hammers steel on the anvil. Racks of blades line
the wall.",
neighbors={"south": "village_square"},
merchant=smith
))
self._add_room(Room(
"armory",
"Armory",
"Marin polishes a gleaming breastplate. The scent of oiled leather
fills the air.",
neighbors={"west": "village_square"},
merchant=armorer
))
self._add_room(Room(
"apothecary",
"Apothecary",
"Glass vials clink; herbs hang drying overhead. Sena smiles warmly.",
neighbors={"north": "village_square"},
merchant=apothecary
))
self._add_room(Room(
"old_library",
"Old Library",
"Dusty shelves and faded tomes. A whisper seems to drift between the
stacks.",
neighbors={"east": "village_square", "north": "watchtower"},
puzzle=library_riddle
))
self._add_room(Room(
"watchtower",
"Watchtower",
"A wooden stair climbs to a lookout over fields and forest. A chest
rests near the wall.",
neighbors={"south": "old_library", "east": "forest_path"},
items=["Iron Sword"]
))
self._add_room(Room(
"forest_path",
"Forest Path",
"Sunlight filters through leaves. A goblin lurks near the bend.",
neighbors={"west": "watchtower", "east": "ruined_shrine"},
enemy=goblin
))
self._add_room(Room(
"ruined_shrine",
"Ruined Shrine",
"Broken pillars circle a mossy altar. Bones scrape as a skeleton
rises.",
neighbors={"west": "forest_path", "north": "cave_entrance"},
enemy=skeleton,
items=["Leather Vest"]
))
self._add_room(Room(
"cave_entrance",
"Cave Entrance",
"Chill air breathes from the depths. Scratch marks gouge the stone.",
neighbors={"south": "ruined_shrine", "in": "crystal_cavern"}
))
self._add_room(Room(
"crystal_cavern",
"Crystal Cavern",
"Blue crystals hum; the Cave Drake watches from a ledge, eyes like
embers.",
neighbors={"out": "cave_entrance", "east": "ancient_vault"},
enemy=cave_drake
))
self._add_room(Room(
"ancient_vault",
"Ancient Vault",
"A sealed door inscribed with sigils. A faint pulse of magic awaits the
right code.",
neighbors={"west": "crystal_cavern"},
puzzle=vault_lock,
locked=True
))
self._add_room(Room(
"catacombs_entrance",
"Catacombs Entrance",
"A stone stair descends. Torches gutter as if breathing. A door bears
three grooves for seals.",
neighbors={"up": "village_square", "down": "catacombs_depths"},
locked=True
))
self._add_room(Room(
"catacombs_depths",
"Catacombs Depths",
"Carved skulls watch from alcoves. A hush gathers around a final
chamber.",
neighbors={"up": "catacombs_entrance", "west": "sorcerer_sanctum"}
))
self._add_room(Room(
"sorcerer_sanctum",
"Sorcerer’s Sanctum",
"Candles float in the air. The Dark Sorcerer smiles, fingers weaving
shadow.",
neighbors={"east": "catacombs_depths"},
enemy=dark_sorcerer
))
def _add_room(self, room):
[Link][[Link]] = room
# =========================
# Save and load
# =========================
def save(self, filename="save_game.json"):
data = {
"player": {
"name": [Link],
"health": [Link],
"max_health": [Link].max_health,
"gold": [Link],
"inventory": [Link],
"equipped": [Link],
"effects": [Link],
},
"current_room": self.current_room,
"quest_flags": self.quest_flags,
"rooms": {k: {
"items": [Link],
"locked": [Link],
"puzzle": {"solved": [Link]} if [Link] else None,
"enemy": {"alive": [Link].is_alive()} if [Link] else None
} for k, v in [Link]()}
}
with open(filename, "w", encoding="utf-8") as f:
[Link](data, f, indent=2)
print("Game saved.")
def load(self, filename="save_game.json"):
try:
with open(filename, "r", encoding="utf-8") as f:
data = [Link](f)
except Exception as e:
print(f"Could not load save: {e}")
return
p = data["player"]
[Link] = p["name"]
[Link] = p["health"]
[Link].max_health = p["max_health"]
[Link] = p["gold"]
[Link] = p["inventory"]
[Link] = p["equipped"]
[Link] = p["effects"]
self.current_room = data["current_room"]
self.quest_flags = data["quest_flags"]
# Restore room states
for key, meta in data["rooms"].items():
room = [Link](key)
if not room:
continue
[Link] = [Link]("items", [Link])
[Link] = [Link]("locked", [Link])
if [Link] and [Link]("puzzle"):
[Link] = meta["puzzle"]["solved"]
if [Link] and [Link]("enemy"):
if not meta["enemy"]["alive"]:
[Link] = 0
print("Game loaded.")
# =========================
# Game loop and actions
# =========================
def intro(self):
print(dedent(f"""
Welcome, {[Link]}, to Embervale!
- Explore rooms, collect items, solve puzzles, and defeat foes.
- Commands: go <dir>, look, take <item>, use <item>, equip <item>, talk,
fight, inventory, stats, buy <item>, sell <item>, save, load,
help, quit
Your journey begins at the Village Square.
"""))
def current(self):
return [Link][self.current_room]
def help(self):
print(dedent("""
Commands:
- go <direction>: Move to a connected room (north, south, east, west, up,
down, in, out).
- look: Inspect the current room and see notable features.
- take <item>: Pick up an item if present.
- use <item>: Use an item from your inventory (Potion, Elixir, Focus
Tonic).
- equip <item>: Equip weapons or armor you own.
- talk: Speak to a merchant if present.
- buy <item>, sell <item>: Trade with a merchant.
- fight: Engage an enemy in the room.
- inventory: View items and gold.
- stats: View health, equipment, and effects.
- save, load: Save or load your adventure.
- quit: Exit the game.
"""))
def look(self):
room = [Link]()
print(f"\n{[Link]}: {[Link]}")
if [Link]:
print("Items here:", ", ".join([Link]))
if [Link] and [Link].is_alive():
print(f"An enemy prowls: {[Link]}")
if [Link]:
print(f"A merchant is here: {[Link]}")
if [Link] and not [Link]:
print("A puzzle or riddle invites your attention. Use: solve")
print("Exits:", ", ".join([Link]()))
def go(self, direction):
room = [Link]()
if direction not in [Link]:
print("You can't go that way.")
return
next_key = [Link][direction]
next_room = [Link][next_key]
# Check locks
if next_room.locked:
if next_key == "ancient_vault":
if any(i for i in [Link] if i == "Ancient Key"):
print("Your Ancient Key resonates, but the sigils still demand
the code. Try 'solve'.")
else:
print("The vault resists you. A special key and code may be
required.")
return
if next_key == "catacombs_entrance":
if any(i for i in [Link] if i == "Sorcerer Seal"):
print("The seals glow and the door grinds open.")
next_room.locked = False
else:
print("Three grooves demand seals. You need the Sorcerer Seal
to proceed.")
return
self.current_room = next_key
[Link]()
def take(self, item):
room = [Link]()
if item in [Link]:
[Link].add_item(item)
[Link](item)
print(f"You pick up: {item}")
if item == "Ancient Key":
self.quest_flags["FoundKey"] = True
else:
print("No such item here.")
def use_item(self, item):
if item not in [Link]:
print("You don't have that.")
return
if item == "Potion":
if [Link].remove_item("Potion"):
[Link](30)
print("You drink a Potion and restore 30 HP.")
elif item == "Elixir":
if [Link].remove_item("Elixir"):
[Link] = [Link].max_health
print("Elixir restores you to full health.")
elif item == "Focus Tonic":
if [Link].remove_item("Focus Tonic"):
[Link]("Battle Focus")
print("Your senses sharpen. Battle Focus gained for the next
fight.")
else:
print("You can't use that right now.")
def equip_item(self, item):
# Simple mapping for demo
armor_stats = {
"Leather Vest": {"hp_bonus": 0},
"Iron Mail": {"hp_bonus": 20},
"Dragon Scale": {"hp_bonus": 40}
}
if item in ["Iron Sword", "Steel Blade", "Mystic Dagger"]:
if item in [Link]:
[Link]["Weapon"] = item
print(f"You equip: {item}")
else:
print("You don't own that.")
elif item in armor_stats:
if item in [Link]:
[Link]("Armor", item, armor_stats[item])
print(f"You don the {item}. Max HP may change with armor.")
else:
print("You don't own that armor.")
else:
print("Not equippable.")
def talk(self):
room = [Link]()
if not [Link]:
print("No one to trade with here.")
return
m = [Link]
print(f"{[Link]} offers:")
for item, meta in [Link]():
print(f"- {item}: {meta['desc']} ({meta['price']} gold)")
def buy(self, item):
room = [Link]()
if not [Link]:
print("No merchant here.")
return
m = [Link]
if item not in [Link]:
print("They don't sell that.")
return
price = [Link][item]["price"]
if [Link] < price:
print("Not enough gold.")
return
[Link] -= price
[Link].add_item(item)
print(f"You bought {item} for {price} gold.")
def sell(self, item):
room = [Link]()
if not [Link]:
print("No merchant here.")
return
if item not in [Link]:
print("You don't have that to sell.")
return
sell_price = max(1, int(self._lookup_price(item) * 0.5))
[Link].remove_item(item)
[Link] += sell_price
print(f"You sold {item} for {sell_price} gold.")
def _lookup_price(self, item):
# Find the item in any merchant for a price reference
for r in [Link]():
if [Link] and item in [Link]:
return [Link][item]["price"]
# Default price
return 10
def fight(self):
room = [Link]()
if not [Link] or not [Link].is_alive():
print("No enemy to fight.")
return
enemy = [Link]
print(f"You engage the {[Link]}!")
# One-fight use of Battle Focus
focus_used = ("Battle Focus" in [Link])
while [Link].is_alive() and enemy.is_alive():
# Player turn
dmg = max(0, [Link].damage_output() - [Link](0, 2))
[Link] -= dmg
print(f"You strike for {dmg} damage. {[Link]} HP: {max(0,
[Link])}")
if not enemy.is_alive():
break
# Enemy turn
incoming = max(0, [Link] - [Link]() +
[Link](0, 2))
[Link].take_damage(incoming)
print(f"{[Link]} hits you for {incoming}. Your HP:
{[Link]}")
if not [Link].is_alive():
break
# Cleanup
if focus_used and "Battle Focus" in [Link]:
[Link]("Battle Focus")
if enemy.is_alive():
print("You were defeated...")
return
print(f"You defeated the {[Link]}!")
# Loot
for k, v in [Link]():
if k == "Gold":
[Link] += v
print(f"You gain {v} gold.")
else:
[Link].add_item(k, v)
print(f"You obtain: {k}")
# Flags
if [Link] == "Dark Sorcerer":
self.quest_flags["SlainSorcerer"] = True
print("The Sorcerer’s Seal falls to the floor, humming with broken
magic.")
# Mark enemy dead
[Link] = 0
# Check win
self._check_win()
def solve(self):
room = [Link]()
if not [Link]:
print("No puzzle here.")
return
if [Link]:
print("This puzzle is already solved.")
return
print([Link])
attempt = input("Your answer: ").strip().lower()
if attempt == [Link]:
[Link] = True
for item, qty in [Link]():
[Link].add_item(item, qty)
print("Puzzle solved! You receive:", ", ".join([f"{k} x{v}" for k, v in
[Link]()]))
if [Link] == "ancient_vault":
self.quest_flags["OpenedVault"] = True
print("The vault door slides open with a resonant chord.")
[Link] = False
else:
print("That isn’t correct. Clues may be hidden in the world.")
def inventory(self):
print("Inventory:")
for item, qty in [Link]():
print(f"- {item} x{qty}")
print(f"Gold: {[Link]}")
def stats(self):
print(f"{[Link]} | HP:
{[Link]}/{[Link].max_health} | Gold: {[Link]}")
print(f"Weapon: {[Link]['Weapon'] or 'None'} | Armor:
{[Link]['Armor'] or 'None'}")
if [Link]:
print("Effects:", ", ".join([Link]))
else:
print("Effects: None")
def _check_win(self):
if self.quest_flags["SlainSorcerer"] and self.quest_flags["OpenedVault"]:
self.win_condition_met = True
print("With the Sorcerer defeated and the Vault’s secret claimed,
Embervale breathes easy. You are victorious!")
def run(self):
[Link]()
[Link]()
while True:
if not [Link].is_alive():
print("You collapse. The tale ends here.")
break
if self.win_condition_met:
print("Thank you for playing!")
break
cmd = input("\n> ").strip()
if not cmd:
continue
parts = [Link]()
action = parts[0].lower()
arg = " ".join(parts[1:]) if len(parts) > 1 else ""
if action == "help":
[Link]()
elif action == "look":
[Link]()
elif action == "go":
[Link]([Link]())
elif action == "take":
[Link](arg)
elif action == "use":
self.use_item(arg)
elif action == "equip":
self.equip_item(arg)
elif action == "talk":
[Link]()
elif action == "buy":
[Link](arg)
elif action == "sell":
[Link](arg)
elif action == "fight":
[Link]()
elif action == "solve":
[Link]()
elif action == "inventory":
[Link]()
elif action == "stats":
[Link]()
elif action == "save":
[Link]()
elif action == "load":
[Link]()
elif action == "quit":
print("Farewell, traveler.")
break
else:
print("Unknown command. Type 'help' for guidance.")
if __name__ == "__main__":
name = input("Enter your hero's name (or press Enter for 'Hero'): ").strip() or
"Hero"
game = Game()
[Link] = name
[Link]()