Dungeons: stay in lobby after a run
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
2597a1d7e7
commit
13371ec269
3 changed files with 19 additions and 10 deletions
10
game.py
10
game.py
|
|
@ -3097,7 +3097,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": [], "eaten": {}, "used": {}, "damage": 0,
|
||||
"gold": 0, "wins": 0, "losses": 0, "lucky": 0, "stopped": None, "resumed": None, "skipped": [],
|
||||
"gold": 0, "wins": 0, "losses": 0, "lucky": 0, "stopped": None, "resumed": None, "run_over": None, "skipped": [],
|
||||
"sold": {}, "sold_gold": 0, "pp_spent": 0, "pp_gained": 0, "prayer_out": False, "second_wind": 0,
|
||||
"kept": 0, "potions_drunk": {}, "potion_out": [], "labour": 0}
|
||||
|
||||
|
|
@ -3574,11 +3574,10 @@ def _advance_run(world: dict, party: dict, action: dict, fight: dict, members: l
|
|||
return
|
||||
reason = f"Wiped {DUNGEON_WIPES} times at {spec['pulls'][run['pull']][0]}"
|
||||
log(world, now, "party", f"{names}: {reason.lower()} in {spec['name']}")
|
||||
del world["parties"][party["id"]]
|
||||
# Back to the lobby, together: the leader can take the group in again, and others can join.
|
||||
del party["run"]
|
||||
for player in members:
|
||||
summary = player.setdefault("away", empty_summary())
|
||||
if not finish_once(world, player, now, reason, summary):
|
||||
summary["stopped"] = reason
|
||||
player.setdefault("away", empty_summary())["run_over"] = reason
|
||||
|
||||
|
||||
def _clear_bonus(world: dict, player: dict, spec: dict, now: float, rng: random.Random) -> None:
|
||||
|
|
@ -6276,6 +6275,7 @@ ORDER_RECIPES, ORDER_RAW = _order_sources()
|
|||
# line here (new / balance / fix), and a new release a new entry on top.
|
||||
PATCH_NOTES = [
|
||||
{"date": "2026-10-27", "title": "Prayer", "notes": [
|
||||
("new", "Group stays in lobby after finishing dungeon"),
|
||||
("new", "Added \"Drink all\" option to potions in the bank."),
|
||||
("new", "Firekeeping: Tend all fires. Each log goes on the lowest fire with room in your pile."),
|
||||
("new", "Added resource dumps for Leatherworking: Guard Jerkins"),
|
||||
|
|
|
|||
|
|
@ -190,6 +190,7 @@ function showSummary(sum) {
|
|||
if (sold.length) toast(`📦 Bank full: sold ${sold.map(([id, q]) => `${fmt(q)} ${item(id).name.toLowerCase()}`).join(", ")} for ${fmt(sum.sold_gold)} gold`);
|
||||
for (const [skill, level] of sum.levels) toast(`🎉 ${skillOf(skill).name} level ${level}!`, "level");
|
||||
if (sum.stopped) toast(`Stopped: ${sum.stopped}`, "bad");
|
||||
if (sum.run_over) toast(`🏁 ${sum.run_over}`, sum.run_over.startsWith("Cleared") ? "level" : "bad");
|
||||
if (sum.skipped?.length) toast(`⏭️ Skipped: ${skippedNames(sum)}`, "bad");
|
||||
for (const id of sum.potion_out || []) toast(`🧪 Your ${item(id).name.toLowerCase()} ran out`, "bad");
|
||||
for (const id of Object.keys(sum.potions_drunk || {})) toast(`🧪 Drank another ${item(id).name.toLowerCase()}`);
|
||||
|
|
@ -223,6 +224,7 @@ function awayModal(sum) {
|
|||
${spilled ? `<p class="muted small">Bank full, sent to the stockpile:</p><ul>${spilled}</ul>` : ""}
|
||||
${sum.skipped?.length ? `<p class="tag bad">⏭️ Skipped: ${esc(skippedNames(sum))}</p>` : ""}
|
||||
${sum.stopped ? `<p class="tag bad">Stopped: ${esc(sum.stopped)}</p>` : ""}
|
||||
${sum.run_over ? `<p class="tag">🏁 ${esc(sum.run_over)}</p>` : ""}
|
||||
<p><button class="btn primary" data-close>Back to work</button></p>`);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1036,7 +1036,7 @@ class DungeonTest(unittest.TestCase):
|
|||
self.assertIsNone(game.start_run(self.world, self.wil, T0 + 600))
|
||||
names = []
|
||||
now = T0 + 600
|
||||
while game.party_of(self.world, self.wil):
|
||||
while "run" in self.party():
|
||||
fight = self.party()["fight"] or {}
|
||||
if fight.get("stage"):
|
||||
names.append(fight["enemy"])
|
||||
|
|
@ -1046,10 +1046,16 @@ class DungeonTest(unittest.TestCase):
|
|||
pulls = game.ACTION_BY_ID["dungeon_watchtower"]["pulls"]
|
||||
self.assertEqual(list(dict.fromkeys(names)), pulls)
|
||||
for p in (self.wil, self.kay):
|
||||
self.assertIsNone(p["action"])
|
||||
self.assertEqual(p["stopped"], "Cleared Watchtower Depths")
|
||||
self.assertEqual(p["away"]["run_over"], "Cleared Watchtower Depths")
|
||||
self.assertGreaterEqual(p["gold"], game.DUNGEONS["dungeon_watchtower"]["bonus_gold"])
|
||||
self.assertIn("cleared Watchtower Depths", self.world["ledger"][-1]["text"])
|
||||
# Back in the lobby together: waiting on the leader, open to join, ready to go again.
|
||||
self.online(now + 600)
|
||||
self.assertIs(game.party_of(self.world, self.kay), self.party())
|
||||
self.assertIsNone(self.party()["fight"])
|
||||
self.assertFalse(game.run_of(self.world, self.wil)["started"])
|
||||
self.assertTrue(game.joinable(self.world, self.wil))
|
||||
self.assertIsNone(game.start_run(self.world, self.wil, now + 600))
|
||||
|
||||
def test_a_group_waits_for_its_leader_and_holds_four(self):
|
||||
game.start_action(self.world, self.wil, "dungeon_watchtower", T0)
|
||||
|
|
@ -1096,11 +1102,12 @@ class DungeonTest(unittest.TestCase):
|
|||
p["xp"]["combat"] = game.XP_TABLE[20]
|
||||
self.party()["run"] = {"pull": 7, "wipes": 0} # the ogre, naked
|
||||
now = T0
|
||||
while game.party_of(self.world, self.wil):
|
||||
while "run" in self.party():
|
||||
now += 30
|
||||
self.online(now)
|
||||
self.assertLess(now, T0 + 3 * 3600)
|
||||
self.assertEqual(self.wil["stopped"], "Wiped 3 times at Watchtower ogre")
|
||||
self.assertEqual(self.wil["away"]["run_over"], "Wiped 3 times at Watchtower ogre")
|
||||
self.assertEqual(self.party()["members"], ["wil", "kay"])
|
||||
|
||||
def test_the_leader_can_pull_early(self):
|
||||
game.start_action(self.world, self.wil, "dungeon_watchtower", T0)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue