Combat HP, food and retreat; armour, leatherworking, fishing, tools
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
3716df7493
commit
d2991ebf7a
6 changed files with 381 additions and 44 deletions
218
game.py
218
game.py
|
|
@ -20,6 +20,9 @@ CLASS_SPEED = 0.8
|
|||
LUCKY_CHANCE = 0.1
|
||||
RECON_PER_INTEL = 8
|
||||
MAX_LOG = 200
|
||||
REGEN_PER_HOUR = 0.25 # share of max HP recovered per game hour
|
||||
EAT_BELOW = 0.5 # auto-eat when HP drops under this share of max
|
||||
TOO_HURT = 0.3 # won't start a fight below this share without food
|
||||
|
||||
|
||||
def _xp_table(max_level: int = 99) -> list[int]:
|
||||
|
|
@ -63,26 +66,62 @@ ITEMS = {
|
|||
"bricks": {"name": "Bricks", "icon": "🧱", "value": 3},
|
||||
"cut_stone": {"name": "Cut stone", "icon": "🔷", "value": 5},
|
||||
"beam": {"name": "Reinforced beam", "plural": "Reinforced beams", "icon": "🪜", "value": 25},
|
||||
"bread": {"name": "Bread", "icon": "🍞", "value": 4, "food": 1},
|
||||
"cooked_meat": {"name": "Cooked meat", "icon": "🍖", "value": 5, "food": 1},
|
||||
"stew": {"name": "Stew", "icon": "🍲", "value": 10, "food": 2},
|
||||
# `food` feeds that many townsfolk for two days; `heal` restores HP in a fight.
|
||||
"bread": {"name": "Bread", "icon": "🍞", "value": 4, "food": 1, "heal": 8},
|
||||
"cooked_meat": {"name": "Cooked meat", "icon": "🍖", "value": 5, "food": 1, "heal": 12},
|
||||
"stew": {"name": "Stew", "icon": "🍲", "value": 10, "food": 2, "heal": 20},
|
||||
"raw_shrimp": {"name": "Raw shrimp", "icon": "🦐", "value": 1},
|
||||
"raw_trout": {"name": "Raw trout", "icon": "🐟", "value": 3},
|
||||
"raw_salmon": {"name": "Raw salmon", "icon": "🐠", "value": 6},
|
||||
"raw_eel": {"name": "Raw eel", "icon": "🐍", "value": 10},
|
||||
"cooked_shrimp": {"name": "Cooked shrimp", "icon": "🍤", "value": 4, "food": 1, "heal": 10},
|
||||
"cooked_trout": {"name": "Grilled trout", "icon": "🍢", "value": 8, "food": 1, "heal": 18},
|
||||
"cooked_salmon": {"name": "Baked salmon", "icon": "🍣", "value": 14, "food": 2, "heal": 28},
|
||||
"smoked_eel": {"name": "Smoked eel", "icon": "🥓", "value": 22, "food": 2, "heal": 40},
|
||||
"leather": {"name": "Leather", "icon": "🪢", "value": 5},
|
||||
# `armour` lowers the damage a fight deals; `power` adds to fighting strength.
|
||||
"leather_cap": {"name": "Leather cap", "icon": "🧢", "value": 12, "slot": "helmet", "armour": 1},
|
||||
"leather_legs": {"name": "Leather trousers", "plural": "Leather trousers", "icon": "👖", "value": 18, "slot": "legs", "armour": 2},
|
||||
"leather_body": {"name": "Leather jerkin", "icon": "🦺", "value": 24, "slot": "body", "armour": 3},
|
||||
"studded_body": {"name": "Studded jerkin", "icon": "🧥", "value": 40, "slot": "body", "armour": 6},
|
||||
"copper_helmet": {"name": "Copper helmet", "icon": "⛑️", "value": 12, "slot": "helmet", "armour": 2},
|
||||
"copper_legs": {"name": "Copper greaves", "plural": "Copper greaves", "icon": "🦵", "value": 22, "slot": "legs", "armour": 3},
|
||||
"copper_body": {"name": "Copper cuirass", "icon": "🥋", "value": 28, "slot": "body", "armour": 4},
|
||||
"iron_helmet": {"name": "Iron helmet", "icon": "🪖", "value": 25, "slot": "helmet", "armour": 4},
|
||||
"iron_legs": {"name": "Iron greaves", "plural": "Iron greaves", "icon": "🦿", "value": 45, "slot": "legs", "armour": 6},
|
||||
"iron_body": {"name": "Iron cuirass", "icon": "🦾", "value": 55, "slot": "body", "armour": 8},
|
||||
}
|
||||
FOOD_ORDER = ("bread", "cooked_meat", "stew")
|
||||
# Gathering tools: one slot per skill, `speed` cuts the action time.
|
||||
TOOLS = {"mining": ("pickaxe", "⛏️"), "woodcutting": ("axe", "🪓"), "farming": ("hoe", ""),
|
||||
"fishing": ("fishing rod", "🎣"), "hunting": ("hunting knife", "🔪")}
|
||||
for _skill, (_tool, _icon) in TOOLS.items():
|
||||
for _metal, _value, _speed in (("copper", 14, 0.15), ("iron", 30, 0.3)):
|
||||
ITEMS[f"{_metal}_{_tool.replace(' ', '_')}"] = {
|
||||
"name": f"{_metal.title()} {_tool}", "icon": _icon, "value": _value,
|
||||
"slot": f"tool_{_skill}", "tool": _skill, "speed": _speed,
|
||||
}
|
||||
ITEMS["iron_shield"]["armour"] = 4
|
||||
# What the city eats first: plain food before the good stuff.
|
||||
FOOD_ORDER = ("bread", "cooked_meat", "cooked_shrimp", "cooked_trout", "stew", "cooked_salmon", "smoked_eel")
|
||||
EQUIP_SLOTS = ["weapon", "shield", "helmet", "body", "legs"] + [f"tool_{skill}" for skill in TOOLS]
|
||||
|
||||
SKILLS = [
|
||||
{"id": "mining", "name": "Mining", "icon": "⛏️", "group": "Gathering"},
|
||||
{"id": "woodcutting", "name": "Woodcutting", "icon": "🪓", "group": "Gathering"},
|
||||
{"id": "farming", "name": "Farming", "icon": "🌱", "group": "Gathering"},
|
||||
{"id": "hunting", "name": "Hunting", "icon": "🦌", "group": "Gathering"},
|
||||
{"id": "fishing", "name": "Fishing", "icon": "🎣", "group": "Gathering"},
|
||||
{"id": "smithing", "name": "Smithing", "icon": "🔨", "group": "Crafting"},
|
||||
{"id": "masonry", "name": "Masonry", "icon": "🧱", "group": "Crafting"},
|
||||
{"id": "cooking", "name": "Cooking", "icon": "🍳", "group": "Crafting"},
|
||||
{"id": "leatherworking", "name": "Leatherworking", "icon": "🪡", "group": "Crafting"},
|
||||
{"id": "combat", "name": "Combat", "icon": "⚔️", "group": "Combat"},
|
||||
{"id": "reconnaissance", "name": "Reconnaissance", "icon": "🔭", "group": "Signature", "class": "scout"},
|
||||
]
|
||||
SKILL_IDS = [skill["id"] for skill in SKILLS]
|
||||
GATHERING = {"mining", "woodcutting", "farming", "hunting"}
|
||||
CRAFTING = {"smithing", "masonry", "cooking"}
|
||||
GATHERING = {"mining", "woodcutting", "farming", "hunting", "fishing"}
|
||||
CRAFTING = {"smithing", "masonry", "cooking", "leatherworking"}
|
||||
SURVEYED = {"mining", "woodcutting", "farming"} # what the pioneer's double yield applies to
|
||||
|
||||
CLASSES = {
|
||||
"warrior": {
|
||||
|
|
@ -91,13 +130,13 @@ CLASSES = {
|
|||
"signature": "Command: counts for half again as much in the garrison",
|
||||
},
|
||||
"scout": {
|
||||
"name": "Scout", "icon": "🏹", "fast": ["hunting", "reconnaissance"],
|
||||
"bonus": "Hunting 20% faster",
|
||||
"name": "Scout", "icon": "🏹", "fast": ["hunting", "fishing", "reconnaissance"],
|
||||
"bonus": "Hunting and fishing 20% faster",
|
||||
"signature": "Reconnaissance: scout the roads to sharpen intel on what's coming",
|
||||
},
|
||||
"artisan": {
|
||||
"name": "Artisan", "icon": "⚒️", "fast": ["smithing", "masonry", "cooking"],
|
||||
"bonus": "Smithing, masonry and cooking 20% faster",
|
||||
"name": "Artisan", "icon": "⚒️", "fast": ["smithing", "masonry", "cooking", "leatherworking"],
|
||||
"bonus": "Smithing, masonry, cooking and leatherworking 20% faster",
|
||||
"signature": "Masterwork: 1 in 10 crafts makes double",
|
||||
},
|
||||
"pioneer": {
|
||||
|
|
@ -123,18 +162,45 @@ ACTIONS = [
|
|||
_action("farm_veg", "farming", "Grow vegetables", 12, 35, 13, outputs={"vegetables": 2}),
|
||||
_action("hunt_birds", "hunting", "Snare birds", 1, 25, 6, outputs={"feathers": 3}),
|
||||
_action("hunt_deer", "hunting", "Hunt deer", 8, 40, 13, outputs={"raw_meat": 2, "hide": 1}),
|
||||
_action("fish_shrimp", "fishing", "Net shrimp", 1, 25, 6, outputs={"raw_shrimp": 1}),
|
||||
_action("fish_trout", "fishing", "Fly-fish trout", 15, 35, 15, outputs={"raw_trout": 1}),
|
||||
_action("fish_salmon", "fishing", "Catch salmon", 30, 45, 28, outputs={"raw_salmon": 1}),
|
||||
_action("fish_eel", "fishing", "Trap eels", 45, 55, 45, outputs={"raw_eel": 1}),
|
||||
_action("smith_copper_bar", "smithing", "Copper bar", 1, 20, 6,
|
||||
{"copper_ore": 1, "logs": 1}, {"copper_bar": 1}),
|
||||
_action("smith_arrows", "smithing", "Arrows", 5, 30, 10,
|
||||
{"copper_bar": 1, "logs": 1, "feathers": 2}, {"arrows": 15}),
|
||||
_action("smith_copper_sword", "smithing", "Copper sword", 8, 40, 18,
|
||||
{"copper_bar": 3}, {"copper_sword": 1}),
|
||||
{"copper_ore": 1, "logs": 1}, {"copper_bar": 1}, group="Bars"),
|
||||
_action("smith_iron_bar", "smithing", "Iron bar", 15, 30, 14,
|
||||
{"iron_ore": 1, "logs": 1}, {"iron_bar": 1}),
|
||||
{"iron_ore": 1, "logs": 1}, {"iron_bar": 1}, group="Bars"),
|
||||
_action("smith_copper_sword", "smithing", "Copper sword", 8, 40, 18,
|
||||
{"copper_bar": 3}, {"copper_sword": 1}, group="Weapons"),
|
||||
_action("smith_iron_sword", "smithing", "Iron sword", 20, 45, 32,
|
||||
{"iron_bar": 2}, {"iron_sword": 1}, city={"smithy": 1}),
|
||||
{"iron_bar": 2}, {"iron_sword": 1}, city={"smithy": 1}, group="Weapons"),
|
||||
_action("smith_iron_shield", "smithing", "Iron shield", 25, 50, 40,
|
||||
{"iron_bar": 3}, {"iron_shield": 1}, city={"smithy": 2}),
|
||||
{"iron_bar": 3}, {"iron_shield": 1}, city={"smithy": 2}, group="Weapons"),
|
||||
_action("smith_copper_helmet", "smithing", "Copper helmet", 6, 35, 14,
|
||||
{"copper_bar": 2}, {"copper_helmet": 1}, group="Armour"),
|
||||
_action("smith_copper_legs", "smithing", "Copper greaves", 10, 40, 20,
|
||||
{"copper_bar": 4}, {"copper_legs": 1}, group="Armour"),
|
||||
_action("smith_copper_body", "smithing", "Copper cuirass", 12, 45, 26,
|
||||
{"copper_bar": 5}, {"copper_body": 1}, group="Armour"),
|
||||
_action("smith_iron_helmet", "smithing", "Iron helmet", 22, 40, 34,
|
||||
{"iron_bar": 2}, {"iron_helmet": 1}, city={"smithy": 1}, group="Armour"),
|
||||
_action("smith_iron_legs", "smithing", "Iron greaves", 27, 45, 46,
|
||||
{"iron_bar": 4}, {"iron_legs": 1}, city={"smithy": 1}, group="Armour"),
|
||||
_action("smith_iron_body", "smithing", "Iron cuirass", 30, 50, 56,
|
||||
{"iron_bar": 5}, {"iron_body": 1}, city={"smithy": 2}, group="Armour"),
|
||||
*[_action(f"smith_{metal}_{tool.replace(' ', '_')}", "smithing", f"{metal.title()} {tool}", level, time, xp,
|
||||
{f"{metal}_bar": 2, wood: 1}, {f"{metal}_{tool.replace(' ', '_')}": 1}, group="Tools",
|
||||
**({"city": {"smithy": 1}} if metal == "iron" else {}))
|
||||
for metal, level, time, xp, wood in (("copper", 4, 30, 12, "logs"), ("iron", 20, 40, 30, "oak_logs"))
|
||||
for tool, _icon in TOOLS.values()],
|
||||
_action("smith_arrows", "smithing", "Arrows", 5, 30, 10,
|
||||
{"copper_bar": 1, "logs": 1, "feathers": 2}, {"arrows": 15}, group="Supplies"),
|
||||
_action("tan_leather", "leatherworking", "Tan leather", 1, 25, 6, {"hide": 1}, {"leather": 1}),
|
||||
_action("lw_cap", "leatherworking", "Leather cap", 3, 30, 10, {"leather": 2}, {"leather_cap": 1}),
|
||||
_action("lw_legs", "leatherworking", "Leather trousers", 8, 35, 16, {"leather": 3}, {"leather_legs": 1}),
|
||||
_action("lw_body", "leatherworking", "Leather jerkin", 12, 40, 22, {"leather": 4}, {"leather_body": 1}),
|
||||
_action("lw_studded", "leatherworking", "Studded jerkin", 25, 50, 40,
|
||||
{"leather": 4, "iron_bar": 1}, {"studded_body": 1}),
|
||||
_action("mason_bricks", "masonry", "Bricks", 1, 25, 6, {"stone": 2, "logs": 1}, {"bricks": 2}),
|
||||
_action("mason_cut_stone", "masonry", "Cut stone", 10, 30, 12, {"stone": 3}, {"cut_stone": 1}),
|
||||
_action("mason_beam", "masonry", "Reinforced beam", 20, 45, 30,
|
||||
|
|
@ -143,15 +209,19 @@ ACTIONS = [
|
|||
_action("cook_meat", "cooking", "Cooked meat", 5, 20, 7, {"raw_meat": 1, "logs": 1}, {"cooked_meat": 1}),
|
||||
_action("cook_stew", "cooking", "Stew", 15, 35, 16,
|
||||
{"raw_meat": 1, "vegetables": 2, "logs": 1}, {"stew": 2}),
|
||||
_action("fight_wolves", "combat", "Wolves in the woods", 1, 60, 14, difficulty=2, gold=2,
|
||||
_action("cook_shrimp", "cooking", "Cooked shrimp", 1, 20, 6, {"raw_shrimp": 1, "logs": 1}, {"cooked_shrimp": 1}),
|
||||
_action("cook_trout", "cooking", "Grilled trout", 15, 30, 15, {"raw_trout": 1, "logs": 1}, {"cooked_trout": 1}),
|
||||
_action("cook_salmon", "cooking", "Baked salmon", 30, 35, 27, {"raw_salmon": 1, "logs": 1}, {"cooked_salmon": 1}),
|
||||
_action("cook_eel", "cooking", "Smoked eel", 45, 40, 42, {"raw_eel": 1, "logs": 1}, {"smoked_eel": 1}),
|
||||
_action("fight_wolves", "combat", "Wolves in the woods", 1, 60, 14, difficulty=2, damage=2, gold=2,
|
||||
drops=[["raw_meat", 1, 0.6], ["hide", 1, 0.5]]),
|
||||
_action("fight_scouts", "combat", "Bandit scouts", 10, 75, 30, difficulty=14, gold=10,
|
||||
_action("fight_scouts", "combat", "Bandit scouts", 10, 75, 30, difficulty=14, damage=6, gold=10,
|
||||
drops=[["copper_bar", 1, 0.3], ["arrows", 5, 0.4]]),
|
||||
_action("fight_ruins", "combat", "Watchtower ruins", 25, 90, 60, difficulty=30, gold=25,
|
||||
_action("fight_ruins", "combat", "Watchtower ruins", 25, 90, 60, difficulty=30, damage=14, gold=25,
|
||||
drops=[["iron_bar", 1, 0.4], ["iron_sword", 1, 0.03]]),
|
||||
_action("fight_stronghold", "combat", "Bandit stronghold", 40, 105, 95, difficulty=50, gold=60,
|
||||
_action("fight_stronghold", "combat", "Bandit stronghold", 40, 105, 95, difficulty=50, damage=25, gold=60,
|
||||
drops=[["iron_bar", 2, 0.5], ["iron_shield", 1, 0.04]]),
|
||||
_action("sabotage", "combat", "Sabotage the bandit camp", 10, 120, 35, difficulty=12,
|
||||
_action("sabotage", "combat", "Sabotage the bandit camp", 10, 120, 35, difficulty=12, damage=10,
|
||||
special="sabotage"),
|
||||
_action("recon_roads", "reconnaissance", "Scout the roads", 1, 60, 12, special="recon", recon=1),
|
||||
_action("recon_camps", "reconnaissance", "Shadow the war bands", 15, 90, 28, special="recon", recon=2),
|
||||
|
|
@ -214,7 +284,7 @@ RAID_TRAITS = ["lightly armoured, heavy on archers", "mounted and fast, few bows
|
|||
|
||||
# --- world ---------------------------------------------------------------
|
||||
|
||||
WORLD_VERSION = 2
|
||||
WORLD_VERSION = 3
|
||||
|
||||
|
||||
def new_world(now: float, speed: float = 1.0) -> dict:
|
||||
|
|
@ -253,6 +323,17 @@ def migrate(world: dict) -> dict:
|
|||
city.setdefault("queue", [])
|
||||
city.setdefault("tier", 0)
|
||||
world["version"] = 2
|
||||
if world["version"] < 3:
|
||||
now = world["city"]["last_tick"]
|
||||
for player in world["players"].values():
|
||||
for skill in SKILL_IDS:
|
||||
player["xp"].setdefault(skill, 0)
|
||||
for slot in EQUIP_SLOTS:
|
||||
player["equip"].setdefault(slot, None)
|
||||
player.setdefault("food", None)
|
||||
player.setdefault("hp", max_hp(player))
|
||||
player.setdefault("hp_at", now)
|
||||
world["version"] = 3
|
||||
return world
|
||||
|
||||
|
||||
|
|
@ -329,7 +410,9 @@ def new_player(uid: str, name: str, cls: str, now: float) -> dict:
|
|||
"id": uid, "name": name, "cls": cls, "joined": now,
|
||||
"xp": {skill: 0 for skill in SKILL_IDS},
|
||||
"bank": {}, "gold": 0, "favour": 0,
|
||||
"equip": {"weapon": None, "shield": None},
|
||||
"equip": {slot: None for slot in EQUIP_SLOTS},
|
||||
"food": None, # preferred food in fights; None eats the cheapest first
|
||||
"hp": 44, "hp_at": now,
|
||||
"action": None, "stopped": None,
|
||||
"last_active": now, "overflow": "city",
|
||||
"perks": {"bank_slot": 0},
|
||||
|
|
@ -383,19 +466,69 @@ def bank_take(player: dict, item: str, qty: int) -> None:
|
|||
del player["bank"][item]
|
||||
|
||||
|
||||
def armour(player: dict) -> int:
|
||||
return sum(ITEMS[item].get("armour", 0) for item in player["equip"].values() if item)
|
||||
|
||||
|
||||
def damage_reduction(player: dict) -> float:
|
||||
points = armour(player)
|
||||
return points / (points + 25)
|
||||
|
||||
|
||||
def combat_power(player: dict) -> int:
|
||||
power = skill_level(player, "combat")
|
||||
for slot in ("weapon", "shield"):
|
||||
item = player["equip"][slot]
|
||||
power = skill_level(player, "combat") + armour(player) // 2
|
||||
for item in player["equip"].values():
|
||||
if item:
|
||||
power += ITEMS[item]["power"]
|
||||
power += ITEMS[item].get("power", 0)
|
||||
return power
|
||||
|
||||
|
||||
def max_hp(player: dict) -> int:
|
||||
return 40 + 4 * skill_level(player, "combat")
|
||||
|
||||
|
||||
def regen(world: dict, player: dict, now: float) -> None:
|
||||
"""Heal for the time since HP was last settled."""
|
||||
if now > player["hp_at"]:
|
||||
top = max_hp(player)
|
||||
per_second = top * REGEN_PER_HOUR * speed(world) / HOUR
|
||||
player["hp"] = min(top, player["hp"] + (now - player["hp_at"]) * per_second)
|
||||
player["hp_at"] = now
|
||||
|
||||
|
||||
def fight_damage(player: dict, action: dict) -> float:
|
||||
"""Average damage one fight deals: less when out-levelling the enemy or armoured."""
|
||||
gap = combat_power(player) - action["difficulty"]
|
||||
return action["damage"] * min(1.5, max(0.3, 1 - gap * 0.03)) * (1 - damage_reduction(player))
|
||||
|
||||
|
||||
def edible(player: dict) -> list[str]:
|
||||
"""Food in the bank, in the order it gets eaten."""
|
||||
foods = sorted((item for item in player["bank"] if "heal" in ITEMS[item]), key=lambda i: ITEMS[i]["heal"])
|
||||
if player["food"] in foods:
|
||||
foods.remove(player["food"])
|
||||
foods.insert(0, player["food"])
|
||||
return foods
|
||||
|
||||
|
||||
def auto_eat(player: dict, summary: dict) -> None:
|
||||
top = max_hp(player)
|
||||
while player["hp"] < top * EAT_BELOW:
|
||||
foods = edible(player)
|
||||
if not foods:
|
||||
return
|
||||
bank_take(player, foods[0], 1)
|
||||
player["hp"] = min(top, player["hp"] + ITEMS[foods[0]]["heal"])
|
||||
summary["eaten"][foods[0]] = summary["eaten"].get(foods[0], 0) + 1
|
||||
|
||||
|
||||
def action_time(world: dict, player: dict, action: dict) -> float:
|
||||
seconds = action["time"]
|
||||
if action["skill"] in CLASSES[player["cls"]]["fast"]:
|
||||
seconds *= CLASS_SPEED
|
||||
tool = player["equip"].get(f"tool_{action['skill']}")
|
||||
if tool:
|
||||
seconds *= 1 - ITEMS[tool]["speed"]
|
||||
if action["skill"] == "smithing":
|
||||
seconds *= 1 - 0.08 * building_level(world, "smithy")
|
||||
return seconds / speed(world)
|
||||
|
|
@ -433,6 +566,8 @@ def can_start(world: dict, player: dict, action: dict) -> str | None:
|
|||
return "No bandit camp has been located"
|
||||
if raid["sabotage"] >= 0.3:
|
||||
return "The camp is already in ruins"
|
||||
if action["skill"] == "combat" and player["hp"] < max_hp(player) * TOO_HURT and not edible(player):
|
||||
return "Too hurt to fight. Rest, or bring food"
|
||||
return None
|
||||
|
||||
|
||||
|
|
@ -442,15 +577,31 @@ def _complete(world: dict, player: dict, action: dict, now: float, rng: random.R
|
|||
xp = action["xp"]
|
||||
lucky = (
|
||||
(player["cls"] == "artisan" and action["skill"] in CRAFTING)
|
||||
or (player["cls"] == "pioneer" and action["skill"] in GATHERING - {"hunting"})
|
||||
or (player["cls"] == "pioneer" and action["skill"] in SURVEYED)
|
||||
) and rng.random() < LUCKY_CHANCE
|
||||
outputs = {item: qty * (2 if lucky else 1) for item, qty in action["outputs"].items()}
|
||||
if lucky:
|
||||
summary["lucky"] += 1
|
||||
|
||||
if action["skill"] == "combat":
|
||||
regen(world, player, now)
|
||||
auto_eat(player, summary)
|
||||
chance = 0.5 + (combat_power(player) - action["difficulty"]) * 0.04
|
||||
if rng.random() < min(0.97, max(0.15, chance)):
|
||||
won = rng.random() < min(0.97, max(0.15, chance))
|
||||
hurt = fight_damage(player, action) * rng.uniform(0.6, 1.4) * (1 if won else 1.3)
|
||||
player["hp"] -= hurt
|
||||
summary["damage"] += hurt
|
||||
auto_eat(player, summary)
|
||||
if player["hp"] <= 0:
|
||||
# Out of food and out of luck: fall back with nothing but your life.
|
||||
player["hp"] = 1
|
||||
summary["losses"] += 1
|
||||
reason = "Retreated: ran out of food" if not edible(player) else "Retreated"
|
||||
stop(player, reason)
|
||||
summary["stopped"] = reason
|
||||
gain_xp(world, player, "combat", xp // 3, now, summary)
|
||||
return
|
||||
if won:
|
||||
summary["wins"] += 1
|
||||
player["gold"] += action.get("gold", 0)
|
||||
summary["gold"] += action.get("gold", 0)
|
||||
|
|
@ -490,7 +641,7 @@ def gain_xp(world: dict, player: dict, skill: str, xp: float, now: float, summar
|
|||
|
||||
|
||||
def empty_summary() -> dict:
|
||||
return {"done": 0, "items": {}, "overflow": {}, "xp": {}, "levels": [],
|
||||
return {"done": 0, "items": {}, "overflow": {}, "xp": {}, "levels": [], "eaten": {}, "damage": 0,
|
||||
"gold": 0, "wins": 0, "losses": 0, "lucky": 0, "stopped": None}
|
||||
|
||||
|
||||
|
|
@ -508,6 +659,8 @@ def process_player(world: dict, player: dict, now: float, rng: random.Random | N
|
|||
if current["start"] + duration > limit:
|
||||
break
|
||||
reason = can_start(world, player, action)
|
||||
if reason and reason.startswith("Too hurt"):
|
||||
reason = "Retreated: hurt and out of food"
|
||||
if reason:
|
||||
stop(player, reason)
|
||||
summary["stopped"] = reason
|
||||
|
|
@ -1058,4 +1211,5 @@ def content() -> dict:
|
|||
return {
|
||||
"items": ITEMS, "skills": SKILLS, "classes": CLASSES, "actions": ACTIONS,
|
||||
"buildings": buildings, "xp_table": XP_TABLE, "offline_cap": OFFLINE_CAP, "tiers": TIERS,
|
||||
"equip_slots": EQUIP_SLOTS, "eat_below": EAT_BELOW, "regen_per_hour": REGEN_PER_HOUR,
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue