Drill the militia; drill sergeant replaces rallying
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
fd66b69401
commit
1c80bd80f7
6 changed files with 102 additions and 31 deletions
69
game.py
69
game.py
|
|
@ -371,6 +371,7 @@ 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("drill_militia", "combat", "Drill the militia", 1, 50, 10, special="drill"),
|
||||
_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], ["bones", 1, 1.0]]),
|
||||
_action("fight_scouts", "combat", "Bandit scouts", 10, 75, 30, difficulty=14, damage=6, gold=10,
|
||||
|
|
@ -1016,7 +1017,13 @@ def can_start(world: dict, player: dict, action: dict) -> str | None:
|
|||
blocked = skill_mods(world, action)["blocked"]
|
||||
if blocked:
|
||||
return blocked
|
||||
if action["skill"] == "combat" and player["hp"] < max_hp(player) * TOO_HURT and not edible(player):
|
||||
if action.get("special") == "drill":
|
||||
threat = upcoming_raid(world)
|
||||
if not threat:
|
||||
return "No raid to prepare for"
|
||||
if threat.get("drilled", 0) >= drill_cap(world):
|
||||
return "The militia is as drilled as it can be"
|
||||
if "difficulty" in action and player["hp"] < max_hp(player) * TOO_HURT and not edible(player):
|
||||
return "Too hurt to fight. Rest, or bring food"
|
||||
return None
|
||||
|
||||
|
|
@ -1047,7 +1054,7 @@ def _complete(world: dict, player: dict, action: dict, now: float, rng: random.R
|
|||
outputs[item] = int(exact) + (1 if rng.random() < exact - int(exact) else 0)
|
||||
xp *= mods["xp"]
|
||||
|
||||
if action["skill"] == "combat":
|
||||
if "difficulty" in action:
|
||||
blessed = blessing(player)
|
||||
if player["prayers"] and not blessed["cost"]:
|
||||
summary["prayer_out"] = True
|
||||
|
|
@ -1105,6 +1112,15 @@ def _complete(world: dict, player: dict, action: dict, now: float, rng: random.R
|
|||
event["recon"] += gain + boost
|
||||
if action.get("special") == "problem" and action["skill"] != "combat":
|
||||
_work_problem(world, player, action, now)
|
||||
threat = upcoming_raid(world) if action.get("special") == "drill" else None
|
||||
if threat:
|
||||
city = world["city"]
|
||||
gain = drill_per_action(player)
|
||||
# A drill sergeant doubles the drilling (and the XP) of whoever does it while the boost lasts.
|
||||
boost = min(city.get("drill_boost", 0), gain)
|
||||
city["drill_boost"] = city.get("drill_boost", 0) - boost
|
||||
xp = DRILL_XP * (gain + boost) * mods["xp"]
|
||||
threat["drilled"] = min(drill_cap(world), threat.get("drilled", 0) + gain + boost)
|
||||
if action.get("special") == "labour":
|
||||
city = world["city"]
|
||||
site = city["construction"]
|
||||
|
|
@ -1125,7 +1141,8 @@ def _complete(world: dict, player: dict, action: dict, now: float, rng: random.R
|
|||
if action.get("special") == "offer":
|
||||
city = world["city"]
|
||||
city["devotion"] = city.get("devotion", 0) + action["devotion"] * (1 + 0.25 * building_level(world, "temple")) * bonus
|
||||
_use_charge(player, action["skill"], summary)
|
||||
if action.get("special") != "drill":
|
||||
_use_charge(player, action["skill"], summary)
|
||||
|
||||
for item, qty in outputs.items():
|
||||
kept = bank_add(world, player, item, qty, now, summary)
|
||||
|
|
@ -1514,7 +1531,8 @@ def defence(world: dict) -> dict:
|
|||
"weapons": round(0.35 * best_kit(stock, "weapon", "power", armed), 1),
|
||||
"shields": round(0.5 * best_kit(stock, "shield", "armour", armed), 1),
|
||||
"arrows": min(100, stock.get("arrows", 0) // 10),
|
||||
"rallied": city.get("rally", 0),
|
||||
"drilled": round(drilled(world), 1),
|
||||
"rallied": city.get("rally", 0), # favour bought before drilling existed; gone after the next raid
|
||||
"ward": ward(world),
|
||||
"mercenaries": city.get("hired", 0),
|
||||
"garrison": 0,
|
||||
|
|
@ -2492,8 +2510,26 @@ def happenings_view(world: dict, now: float) -> dict:
|
|||
MASTER_BUILDER_LABOUR = 50 # boosted labour per hire: doubled labour and XP for whoever works it
|
||||
MASTER_BUILDER_FAVOUR = 150 # +50% for every further hire on the same site
|
||||
INFORMANT_FAVOUR = 250
|
||||
RALLY_FAVOUR = 150
|
||||
RALLY_DEFENCE = 10
|
||||
# Drilling: fighters train the townsfolk before a raid, a little defence per
|
||||
# session (more for a seasoned fighter), up to half the population.
|
||||
DRILL_PER_ACTION = 0.5 # plus as much again every 20 combat levels
|
||||
DRILL_XP = 20 # combat XP per point of defence drilled
|
||||
DRILL_SHARE = 0.5 # most drilled defence: this much per townsperson
|
||||
DRILL_SERGEANT_POINTS = 15 # boosted drilling per hire: doubled defence and XP
|
||||
DRILL_SERGEANT_FAVOUR = 150 # +50% for every further hire against the same threat
|
||||
|
||||
|
||||
def drill_per_action(player: dict) -> float:
|
||||
return DRILL_PER_ACTION * (1 + skill_level(player, "combat") // 20)
|
||||
|
||||
|
||||
def drill_cap(world: dict) -> float:
|
||||
return round(DRILL_SHARE * world["city"]["population"], 1)
|
||||
|
||||
|
||||
def drilled(world: dict) -> float:
|
||||
threat = upcoming_raid(world)
|
||||
return min(drill_cap(world), threat.get("drilled", 0)) if threat else 0
|
||||
|
||||
|
||||
def favours(world: dict, now: float) -> dict:
|
||||
|
|
@ -2517,10 +2553,13 @@ def favours(world: dict, now: float) -> dict:
|
|||
"recon": INFORMANT_RECON,
|
||||
"boost": round(city.get("recon_boost", 0)),
|
||||
},
|
||||
"militia": {
|
||||
"cost": RALLY_FAVOUR * (city.get("rallies", 0) + 1),
|
||||
"why": None if raid else "No raid to prepare for",
|
||||
"rallied": city.get("rally", 0),
|
||||
"sergeant": {
|
||||
"cost": round(DRILL_SERGEANT_FAVOUR * (1 + 0.5 * (raid or {}).get("sergeants", 0))),
|
||||
"why": "No raid to prepare for" if not raid else
|
||||
"The militia is as drilled as it can be" if drilled(world) >= drill_cap(world) else None,
|
||||
"drill": DRILL_SERGEANT_POINTS,
|
||||
"boost": round(city.get("drill_boost", 0), 1),
|
||||
"drilled": round(drilled(world), 1), "cap": drill_cap(world),
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -2550,9 +2589,11 @@ def spend_favour(world: dict, player: dict, kind: str, now: float) -> str | None
|
|||
log(world, now, "favour", f"{player['name']} paid an informant: the next {INFORMANT_RECON} "
|
||||
f"reconnaissance counts double", player["id"])
|
||||
else:
|
||||
city["rally"] = city.get("rally", 0) + RALLY_DEFENCE
|
||||
city["rallies"] = city.get("rallies", 0) + 1
|
||||
log(world, now, "favour", f"{player['name']} rallied the militia (+{RALLY_DEFENCE} defence for the coming raid)", player["id"])
|
||||
raid = upcoming_raid(world)
|
||||
raid["sergeants"] = raid.get("sergeants", 0) + 1
|
||||
city["drill_boost"] = city.get("drill_boost", 0) + DRILL_SERGEANT_POINTS
|
||||
log(world, now, "favour", f"{player['name']} hired a drill sergeant: the next {DRILL_SERGEANT_POINTS} "
|
||||
f"drilled defence counts double", player["id"])
|
||||
return None
|
||||
|
||||
|
||||
|
|
@ -2625,7 +2666,7 @@ ACTION_GLYPHS = {
|
|||
"fight_ruins": ("castle-ruins", "#a8a29e"), "fight_stronghold": ("castle", "#8a8f98"),
|
||||
"fight_green_dragon": ("dragon-head", "#5fae5a"), "fight_blue_dragon": ("dragon-head", "#4f86d6"),
|
||||
"fight_red_dragon": ("dragon-head", "#d0503c"), "fight_black_dragon": ("dragon-head", "#8a8f98"),
|
||||
"sabotage": ("fire-bomb", "#e0703a"), "recon_roads": ("spyglass", "#6f8fec"), "recon_camps": ("spyglass", "#8a7fd0"),
|
||||
"sabotage": ("fire-bomb", "#e0703a"), "drill_militia": ("sword-brandish", "#e0a050"), "recon_roads": ("spyglass", "#6f8fec"), "recon_camps": ("spyglass", "#8a7fd0"),
|
||||
"labour": ("hammer-nails", "#d1a46a"), "clear_wolves": ("wolf-head", "#9aa1ab"),
|
||||
"catch_goblins": ("goblin-head", "#8fbf6a"), "drive_bear": ("bear-head", "#8a5a32"), "cull_boars": ("boar", "#a06a4a"),
|
||||
"catch_rats": ("rat", "#9aa1ab"), "clear_rockslide": ("falling-rocks", "#a8a29e"),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue