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:
|
def empty_summary() -> dict:
|
||||||
return {"done": 0, "items": {}, "overflow": {}, "xp": {}, "levels": [], "eaten": {}, "used": {}, "damage": 0,
|
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,
|
"sold": {}, "sold_gold": 0, "pp_spent": 0, "pp_gained": 0, "prayer_out": False, "second_wind": 0,
|
||||||
"kept": 0, "potions_drunk": {}, "potion_out": [], "labour": 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
|
return
|
||||||
reason = f"Wiped {DUNGEON_WIPES} times at {spec['pulls'][run['pull']][0]}"
|
reason = f"Wiped {DUNGEON_WIPES} times at {spec['pulls'][run['pull']][0]}"
|
||||||
log(world, now, "party", f"{names}: {reason.lower()} in {spec['name']}")
|
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:
|
for player in members:
|
||||||
summary = player.setdefault("away", empty_summary())
|
player.setdefault("away", empty_summary())["run_over"] = reason
|
||||||
if not finish_once(world, player, now, reason, summary):
|
|
||||||
summary["stopped"] = reason
|
|
||||||
|
|
||||||
|
|
||||||
def _clear_bonus(world: dict, player: dict, spec: dict, now: float, rng: random.Random) -> None:
|
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.
|
# line here (new / balance / fix), and a new release a new entry on top.
|
||||||
PATCH_NOTES = [
|
PATCH_NOTES = [
|
||||||
{"date": "2026-10-27", "title": "Prayer", "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", "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", "Firekeeping: Tend all fires. Each log goes on the lowest fire with room in your pile."),
|
||||||
("new", "Added resource dumps for Leatherworking: Guard Jerkins"),
|
("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`);
|
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");
|
for (const [skill, level] of sum.levels) toast(`🎉 ${skillOf(skill).name} level ${level}!`, "level");
|
||||||
if (sum.stopped) toast(`Stopped: ${sum.stopped}`, "bad");
|
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");
|
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 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()}`);
|
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>` : ""}
|
${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.skipped?.length ? `<p class="tag bad">⏭️ Skipped: ${esc(skippedNames(sum))}</p>` : ""}
|
||||||
${sum.stopped ? `<p class="tag bad">Stopped: ${esc(sum.stopped)}</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>`);
|
<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))
|
self.assertIsNone(game.start_run(self.world, self.wil, T0 + 600))
|
||||||
names = []
|
names = []
|
||||||
now = T0 + 600
|
now = T0 + 600
|
||||||
while game.party_of(self.world, self.wil):
|
while "run" in self.party():
|
||||||
fight = self.party()["fight"] or {}
|
fight = self.party()["fight"] or {}
|
||||||
if fight.get("stage"):
|
if fight.get("stage"):
|
||||||
names.append(fight["enemy"])
|
names.append(fight["enemy"])
|
||||||
|
|
@ -1046,10 +1046,16 @@ class DungeonTest(unittest.TestCase):
|
||||||
pulls = game.ACTION_BY_ID["dungeon_watchtower"]["pulls"]
|
pulls = game.ACTION_BY_ID["dungeon_watchtower"]["pulls"]
|
||||||
self.assertEqual(list(dict.fromkeys(names)), pulls)
|
self.assertEqual(list(dict.fromkeys(names)), pulls)
|
||||||
for p in (self.wil, self.kay):
|
for p in (self.wil, self.kay):
|
||||||
self.assertIsNone(p["action"])
|
self.assertEqual(p["away"]["run_over"], "Cleared Watchtower Depths")
|
||||||
self.assertEqual(p["stopped"], "Cleared Watchtower Depths")
|
|
||||||
self.assertGreaterEqual(p["gold"], game.DUNGEONS["dungeon_watchtower"]["bonus_gold"])
|
self.assertGreaterEqual(p["gold"], game.DUNGEONS["dungeon_watchtower"]["bonus_gold"])
|
||||||
self.assertIn("cleared Watchtower Depths", self.world["ledger"][-1]["text"])
|
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):
|
def test_a_group_waits_for_its_leader_and_holds_four(self):
|
||||||
game.start_action(self.world, self.wil, "dungeon_watchtower", T0)
|
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]
|
p["xp"]["combat"] = game.XP_TABLE[20]
|
||||||
self.party()["run"] = {"pull": 7, "wipes": 0} # the ogre, naked
|
self.party()["run"] = {"pull": 7, "wipes": 0} # the ogre, naked
|
||||||
now = T0
|
now = T0
|
||||||
while game.party_of(self.world, self.wil):
|
while "run" in self.party():
|
||||||
now += 30
|
now += 30
|
||||||
self.online(now)
|
self.online(now)
|
||||||
self.assertLess(now, T0 + 3 * 3600)
|
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):
|
def test_the_leader_can_pull_early(self):
|
||||||
game.start_action(self.world, self.wil, "dungeon_watchtower", T0)
|
game.start_action(self.world, self.wil, "dungeon_watchtower", T0)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue