Instant targeting, damage on bars, interrupted casts, no arrows

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
rope 2026-10-24 14:21:08 +01:00
parent bc99a1e0c5
commit 049e2c771a
6 changed files with 85 additions and 41 deletions

View file

@ -99,9 +99,9 @@ ABILITIES = {
"barbed_arrow": {"name": "Barbed Arrow", "target": "enemy", "cd": 12,
"effects": [_dmg(0.4), {"t": "dot", "c": 0.25, "every": 2, "dur": 12}], "auto": {"r": "dot"},
"tip": "Hit, then bleeds 6× 0.25 over 12s."},
"volley": {"name": "Volley", "target": "aoe", "cd": 10, "cost": 5,
"volley": {"name": "Volley", "target": "aoe", "cd": 10,
"effects": [{"t": "aoe", "c": 0.9}], "auto": {"r": "aoe", "n": 2},
"tip": "Hits every enemy. 5 arrows."},
"tip": "Hits every enemy."},
# Trades: one each, whatever the weapon.
"find_the_seam": {"name": "Find the Seam", "target": "enemy", "cd": 30, "offgcd": True,
"effects": [_aura("seam", 8, "target")], "auto": {"r": "debuff", "pct": 0.6},
@ -147,16 +147,13 @@ def known_abilities(style: str, cls: str, level: int, shield: bool) -> list[str]
def modded(ab: dict, mod: dict | None) -> dict:
"""An ability as something you wear changes it: `cd` and `cast` multiply, `c` multiplies every
effect's size, `dur` adds seconds to every effect that lasts, `cost` replaces the arrows it takes,
and `extra` adds effects."""
effect's size, `dur` adds seconds to every effect that lasts, and `extra` adds effects."""
if not mod:
return ab
ab = {**ab, "effects": [dict(eff) for eff in ab["effects"]]}
for key in ("cd", "cast"):
if key in mod and ab.get(key):
ab[key] = ab[key] * mod[key]
if "cost" in mod:
ab["cost"] = mod["cost"]
for eff in ab["effects"]:
if "c" in eff and "c" in mod:
eff["c"] *= mod["c"]
@ -267,7 +264,7 @@ def _member_unit(member: dict) -> dict:
"kit": {a: modded(ABILITIES[a], me.get("mods", {}).get(a)) for a in me["abilities"]},
"next": member["at"] + (0.5 if me["first"] else me["interval"]), "stun": 0.0, "target": None,
"mend": member["mend"], "wind": member["wind"], "wind_used": False,
"potion": member.get("potion") or 0, "drank": False, "arrows": member.get("arrows", 0), "shot": 0,
"potion": member.get("potion") or 0, "drank": False,
"dealt": 0, "hurt": 0, "down": False, "manual": set(member.get("manual", [])),
"watch": member.get("watch", -1.0), "inputs": sorted(member.get("inputs", []), key=lambda i: i[0]),
"seen": 0, "queued": None, "picked": False, "ftarget": None}
@ -309,7 +306,7 @@ def run(fight: dict) -> dict:
unit = units[member["id"]]
won = result == "won" and unit["out"] in (None, "down") and unit["in"]
member.update(hp=max(0, unit["hp"]), damage=unit["hurt"], dealt=unit["dealt"], wind_used=unit["wind_used"],
drank=unit["drank"], shot=unit["shot"], down=unit["down"],
drank=unit["drank"], down=unit["down"],
result="won" if won else unit["out"] or ("left" if not unit["in"] else result))
fight.update(events=state["events"], kills=state["kills"], result=result,
end=_real(state, t), duration=(fight["rest"] + t) / fight["pace"])
@ -453,8 +450,6 @@ def _ready(unit: dict, ab: dict, t: float, manual: bool) -> str | None:
return "cooldown"
if not ab.get("offgcd") and unit["busy"] + (0 if manual else AUTO_DELAY) > t + 1e-9:
return "gcd"
if ab.get("cost", 0) > unit["arrows"]:
return "arrows"
return None
@ -477,7 +472,7 @@ def _think(state: dict, unit: dict, t: float, rng: random.Random) -> None:
_use(state, unit, ab, target, t, rng, manual=True)
if unit["cast"] or not ab.get("offgcd"):
return
elif why == "arrows" or t - queued["at"] > QUEUE:
elif t - queued["at"] > QUEUE:
unit["queued"] = None
for ab_id in unit["abilities"]:
ab = unit["kit"][ab_id]
@ -516,9 +511,6 @@ def _manual_target(state: dict, unit: dict, ab: dict, given: str | None, t: floa
def _use(state: dict, unit: dict, ab: dict, target, t: float, rng: random.Random, manual: bool = False) -> None:
unit["cds"][ab["id"]] = t + ab.get("cd", 0) * (1 if manual else AUTO_CD)
if ab.get("cost"):
unit["arrows"] -= ab["cost"]
unit["shot"] += ab["cost"]
if not ab.get("offgcd"):
unit["busy"] = t + max(GCD, ab.get("cast", 0))
if ab.get("cast"):