Dungeons: run on while anyone watches
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
13371ec269
commit
25ecb885f5
2 changed files with 29 additions and 12 deletions
20
game.py
20
game.py
|
|
@ -1008,7 +1008,7 @@ COMBAT_ACTIONS = [
|
|||
# cast what has to be interrupted, hit what has to be braced for, call in help, and lose patience.
|
||||
DUNGEON_SIZE = 4 # the most in a run
|
||||
DUNGEON_WIPES = 3 # on one pull, and the run is over
|
||||
DUNGEON_AWAY = 90 # real seconds without looking before a run waits for you
|
||||
DUNGEON_AWAY = 90 # real seconds without looking before you count as away (a run waits if everyone is)
|
||||
TRASH = (1.5, 1.5) # a pull's HP (and XP) and hits against the open world's
|
||||
MINI_BOSS = 5.0 # a mini-boss's HP (and XP, and loot odds) against its race's elite
|
||||
FINAL_BOSS = (2.5, 1.3) # the area boss's (and its adds') HP and hits against the open-world one
|
||||
|
|
@ -3476,9 +3476,9 @@ def process_party(world: dict, pid: str, now: float, rng: random.Random) -> None
|
|||
break
|
||||
members = [world["players"][uid] for uid in party["members"]]
|
||||
if action.get("dungeon"):
|
||||
# A run waits for its leader to start it, then for anyone who's looked away: it's played together.
|
||||
away = [p for p in members if p["last_active"] < party["start"] - DUNGEON_AWAY]
|
||||
if "run" not in party or away:
|
||||
# A run waits for its leader to start it, then for someone to be watching. Whoever's
|
||||
# looked away fights on auto.
|
||||
if "run" not in party or run_away(world, party):
|
||||
party["start"] = max(party["start"], now)
|
||||
for player in members:
|
||||
player["action"]["start"] = party["start"]
|
||||
|
|
@ -3593,6 +3593,12 @@ def _clear_bonus(world: dict, player: dict, spec: dict, now: float, rng: random.
|
|||
summary["items"][item] = summary["items"].get(item, 0) + kept
|
||||
|
||||
|
||||
def run_away(world: dict, party: dict) -> list:
|
||||
"""Everyone in the run, if no one's been looking: it waits for one of them to come back."""
|
||||
members = [world["players"][uid] for uid in party["members"]]
|
||||
return members if all(p["last_active"] < party["start"] - DUNGEON_AWAY for p in members) else []
|
||||
|
||||
|
||||
def run_of(world: dict, player: dict) -> dict | None:
|
||||
"""Where your group's dungeon run stands, if you're in one."""
|
||||
party = party_of(world, player)
|
||||
|
|
@ -3600,10 +3606,9 @@ def run_of(world: dict, player: dict) -> dict | None:
|
|||
return None
|
||||
spec = DUNGEONS[ACTION_BY_ID[party["action"]]["dungeon"]]
|
||||
run = party.get("run", {"pull": 0, "wipes": 0})
|
||||
away = [world["players"][uid]["name"] for uid in party["members"]
|
||||
if world["players"][uid]["last_active"] < party["start"] - DUNGEON_AWAY]
|
||||
return {"dungeon": spec["name"], "pull": run["pull"] + 1, "of": len(spec["pulls"]), "wipes": run["wipes"],
|
||||
"next": spec["pulls"][min(run["pull"], len(spec["pulls"]) - 1)][0], "away": away,
|
||||
"next": spec["pulls"][min(run["pull"], len(spec["pulls"]) - 1)][0],
|
||||
"away": [p["name"] for p in run_away(world, party)],
|
||||
"leader": party["members"][0], "size": len(party["members"]), "started": "run" in party}
|
||||
|
||||
|
||||
|
|
@ -6275,6 +6280,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", "Dungeons: runs go on while anyone is watching. Members who look away fight on auto."),
|
||||
("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."),
|
||||
|
|
|
|||
|
|
@ -1083,15 +1083,26 @@ class DungeonTest(unittest.TestCase):
|
|||
game.start_action(self.world, self.wil, "dungeon_watchtower", T0)
|
||||
self.assertEqual(game.start_run(self.world, self.wil, T0), "Not in a dungeon")
|
||||
|
||||
def test_a_run_waits_for_whoever_looked_away(self):
|
||||
def test_a_run_goes_on_while_someone_watches(self):
|
||||
game.start_action(self.world, self.wil, "dungeon_watchtower", T0)
|
||||
game.join_fight(self.world, self.kay, "wil", T0)
|
||||
game.start_run(self.world, self.wil, T0)
|
||||
self.online(T0 + 300, [self.wil]) # kay went to make tea
|
||||
self.online(T0 + 1200, [self.wil])
|
||||
now = T0
|
||||
while self.party()["run"]["pull"] < 2: # kay went to make tea: on auto, wil plays on
|
||||
now += 2
|
||||
self.online(now, [self.wil])
|
||||
self.assertLess(now, T0 + 3600)
|
||||
self.assertEqual(game.run_of(self.world, self.wil)["away"], [])
|
||||
self.assertIn("kay", [m["id"] for m in self.party()["fight"]["members"]])
|
||||
|
||||
def test_a_run_waits_when_no_one_watches(self):
|
||||
game.start_action(self.world, self.wil, "dungeon_watchtower", T0)
|
||||
game.join_fight(self.world, self.kay, "wil", T0)
|
||||
game.start_run(self.world, self.wil, T0)
|
||||
game.catch_up(self.world, T0 + 1200, random.Random(1)) # both went to make tea
|
||||
self.assertIsNone(self.party()["fight"])
|
||||
self.assertEqual(game.run_of(self.world, self.wil)["away"], ["Kay"])
|
||||
self.online(T0 + 1210)
|
||||
self.assertEqual(game.run_of(self.world, self.wil)["away"], ["Wil", "Kay"])
|
||||
self.online(T0 + 1210, [self.kay])
|
||||
self.assertIsNotNone(self.party()["fight"])
|
||||
|
||||
def test_three_wipes_on_a_pull_end_the_run(self):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue