Expedition autopilot Phase 4 + rogue sneak attack + TwinBee voice sweep

Tedium-removal pass driven by live play feedback. Three big threads:

Expedition autopilot Phase 4 — background auto-run + harvest-until-dry:
- New expeditionAutoRunTicker walks active expeditions every 15min
  (5min tick, per-expedition CAS on new last_autorun_at column). Skips
  combat sessions, briefing/recap quiet windows, expeditions <30min old.
- Walks up to 3 rooms/tick with compact narration; suppresses DMs when
  0 rooms walked or just hitting the per-tick cap (no "stretch complete"
  filler). Player only hears from the bot when a real decision is needed.
- Compact mode: one-line combat narration for trash/elite, auto-resolves
  elite doorways via the forward-sim engine (boss still pauses). Threaded
  via new advanceOnceWithOpts → resolveRoom → resolveCombatRoom.
- Auto-harvest now grinds each Common/Uncommon node until dry (cap at 8
  attempts/visit), mirroring manual !scavenge retries. Rare+ still pauses.
- Ambient ticker: anti-repeat by Kind via new last_ambient_kind column
  (avoids two pack_rat DMs in a row when the pool only has 6 lines).
- !expedition go <n> now routes to the fork-choice handler when active +
  numeric; fork footer rewritten to suggest !expedition go instead of
  !zone go. Boss/Elite doorway: formatNextRoomMessage routes the action
  hint by next room type and autopilot loop breaks via new nextRoomType
  field so "Room X/Y — Boss" doesn't double-print with contradictory
  hints (!zone advance vs !fight).
- runAutopilotWalk extracted from expeditionCmdRun so foreground and
  background share the loop body.

Rogue Sneak Attack actually exists now:
- Audit found the rogue's "Sneak Attack" passive was AutoCritFirst +
  5% damage rider. No Nd6, ever. Phase 2 Monte Carlo masked this
  with a small flat buff; the class's defining mechanic never matched
  its tooltip or 5e identity.
- Added SneakAttackDie int to CombatModifiers, per-hit Nd6 in
  combat_primitives.go (same lane as DivineStrikePerHit). Scales with
  level: 1d6 at L1-2 ... 4d6 at L7-8 ... capped at 10d6 at L19-20.
- AutoCritFirst + 5% rider retained as bonuses on top of working sneak
  attack — preserves the opener-burst feel.
- L7 rogue expected per-hit ~8 → ~22 damage. Valdris fight math goes
  from "16 rounds to kill, die in 8" to winnable.

TwinBee voice sweep (Phase B3):
- ~530 line changes across 24 files replacing third-person "TwinBee
  notes/files/tracks/respects/..." constructions with first-person
  inside flavor string literals. Code identifiers (TwinBeeLine,
  twinBeeLine, etc.), chat-prefix labels (🎭 **TwinBee:**), item names
  (TwinBee's Bell), achievements, and game-title references in
  fun.go (Konami TwinBee ship) left intact.
- Catches a real bug: Valdris fight rendered "TwinBee marks the
  Legendary Resistance" mid-combat in third person, contradicting
  the Phase B2 convention.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
prosolis
2026-05-16 12:40:17 -07:00
parent f424300d87
commit 41adfce9f1
39 changed files with 1133 additions and 638 deletions

View File

@@ -21,7 +21,7 @@ package flavor
// ─────────────────────────────────────────────────────────────────────────────
var BossEntryIlvaras = []string{
"The cathedral is round and the round is wrong — drow architecture leans, drow architecture rises, drow architecture does not gather like this. The room was built for one purpose and the purpose is standing at the altar with her back to you. She does not turn when you enter. The trophies on the wall turn first — four sets of weapons, hung at four heights, each set arranged with the care of someone who learned the names of their previous owners and kept the names. 'Five,' Ilvaras Xunyl says, without turning, in a voice that is making itself heard in your head and not in the air. 'I had run out of wall.' She turns. The smile is the smile of someone who has been waiting for the inconvenience of a fifth wall. TwinBee says, very low: 'Drow High Priestess. Lolth's favour. Don't accept the offer. There will be an offer.'",
"The cathedral is round and the round is wrong — drow architecture leans, drow architecture rises, drow architecture does not gather like this. The room was built for one purpose and the purpose is standing at the altar with her back to you. She does not turn when you enter. The trophies on the wall turn first — four sets of weapons, hung at four heights, each set arranged with the care of someone who learned the names of their previous owners and kept the names. 'Five,' Ilvaras Xunyl says, without turning, in a voice that is making itself heard in your head and not in the air. 'I had run out of wall.' She turns. The smile is the smile of someone who has been waiting for the inconvenience of a fifth wall. I say, very low: 'Drow High Priestess. Lolth's favour. Don't accept the offer. There will be an offer.'",
}
// ─────────────────────────────────────────────────────────────────────────────
@@ -29,12 +29,12 @@ var BossEntryIlvaras = []string{
// ─────────────────────────────────────────────────────────────────────────────
var EliteRoomEntryUnderdark = []string{
"The cavern ahead is a forest of stalagmites — dozens of them, calf-thick to wagon-thick, irregular spacing, the kind of natural architecture that gives an ambush every advantage. TwinBee counts them, then stops counting, then notes that one of them has been counted twice from different angles and that one is not a stalagmite. 'Roper,' TwinBee says. 'Six tendrils. Reels you in. Pick the wrong stalagmite and the room becomes one fight at five different ranges.'",
"You enter a chamber where the ceiling drips minerals that have built columns down to the floor — pillars of pale stone, formed over thousands of years, perfectly natural, all of them. Almost all of them. TwinBee identifies the off-color column near the back of the room and says: 'False appearance until it isn't. Stay out of grapple range until the tendrils show.'",
"A grotto. Calm water. A field of stalagmites breaking the surface. Beautiful in the way the deep places are beautiful, which is to say it photographs well and houses something terrible. The stalagmite nearest the path has not been there in any other map TwinBee has consulted. TwinBee notes the inconsistency and recommends ranged options.",
"The corridor widens into a cavern with twelve pillars holding the ceiling — six of them are dwarven masonry, six of them are natural stone, and one of the natural ones is breathing. The breath is shallow. The patience is the giveaway. TwinBee says: 'Roper. Engage the column you can see, not the columns you assume.'",
"You step into a room where the stalagmites have been arranged — almost arranged — into something like a dining hall. The arrangement is not deliberate. The arrangement is the result of one of the stalagmites occasionally moving in its sleep. TwinBee files this under 'apex ambush predator with tenure' and recommends not waking it gently.",
"A toll-bridge cavern: a narrow path through a field of standing stones, each one twice your height, none of them moving. The toll is paid by whichever party member walks last. TwinBee tracks the sightlines, names the column with the wrong shadow, and says: 'Don't be the last one.'",
"The cavern ahead is a forest of stalagmites — dozens of them, calf-thick to wagon-thick, irregular spacing, the kind of natural architecture that gives an ambush every advantage. I count them, then stop counting, then note that one of them has been counted twice from different angles and that one is not a stalagmite. 'Roper,' I say. 'Six tendrils. Reels you in. Pick the wrong stalagmite and the room becomes one fight at five different ranges.'",
"You enter a chamber where the ceiling drips minerals that have built columns down to the floor — pillars of pale stone, formed over thousands of years, perfectly natural, all of them. Almost all of them. I identify the off-color column near the back of the room and say: 'False appearance until it isn't. Stay out of grapple range until the tendrils show.'",
"A grotto. Calm water. A field of stalagmites breaking the surface. Beautiful in the way the deep places are beautiful, which is to say it photographs well and houses something terrible. The stalagmite nearest the path has not been there in any other map I have consulted. I note the inconsistency and recommend ranged options.",
"The corridor widens into a cavern with twelve pillars holding the ceiling — six of them are dwarven masonry, six of them are natural stone, and one of the natural ones is breathing. The breath is shallow. The patience is the giveaway. I say: 'Roper. Engage the column you can see, not the columns you assume.'",
"You step into a room where the stalagmites have been arranged — almost arranged — into something like a dining hall. The arrangement is not deliberate. The arrangement is the result of one of the stalagmites occasionally moving in its sleep. I file this under 'apex ambush predator with tenure' and recommend not waking it gently.",
"A toll-bridge cavern: a narrow path through a field of standing stones, each one twice your height, none of them moving. The toll is paid by whichever party member walks last. I track the sightlines, names the column with the wrong shadow, and say: 'Don't be the last one.'",
}
// ─────────────────────────────────────────────────────────────────────────────
@@ -44,37 +44,37 @@ var EliteRoomEntryUnderdark = []string{
// Spells: Flame Strike / Dispel Magic / Divine Word (CHA DC 18) / Insect Plague.
var IlvarasSpellLines = []string{
"She has the priest list and she leads with Flame Strike and Insect Plague — both AoE, both back-to-back if the initiative is unkind. TwinBee says: 'Spread out. Don't bunch under a column. Insect Plague is a movement tax that compounds.'",
"Divine Word, CHA DC 18, and the failure tree is brutal — Deafened, Blinded, Stunned, or killed depending on remaining HP. TwinBee files this under 'never fight a priest below half HP' and asks who has the best CHA save.",
"Dispel Magic on your buffs as soon as she rolls initiative. TwinBee tracks which concentration spells went up and recommends shielding the controller until the dispel is spent.",
"She has the priest list and she leads with Flame Strike and Insect Plague — both AoE, both back-to-back if the initiative is unkind. I say: 'Spread out. Don't bunch under a column. Insect Plague is a movement tax that compounds.'",
"Divine Word, CHA DC 18, and the failure tree is brutal — Deafened, Blinded, Stunned, or killed depending on remaining HP. I file this under 'never fight a priest below half HP' and ask who has the best CHA save.",
"Dispel Magic on your buffs as soon as she rolls initiative. I track which concentration spells went up and recommend shielding the controller until the dispel is spent.",
}
// Lolth's Favour: 1/combat auto-succeed a failed save.
var IlvarasFavourLines = []string{
"Once per fight she takes a save you thought you'd won and converts the failure into a success. TwinBee says: 'Time your hard CCs after the favour is spent — don't waste a Hold Person on the round she's still got it.'",
"Lolth's Favour. One free re-pass on a save. TwinBee notes the timing matters more than the damage — the spell you wanted to land is the spell she wanted to skip.",
"She converts a failed save into a success without rolling. The favour is single-use. TwinBee files this under 'plan two openings' and reminds the party that the second one is the real one.",
"Once per fight she takes a save you thought you'd won and converts the failure into a success. I say: 'Time your hard CCs after the favour is spent — don't waste a Hold Person on the round she's still got it.'",
"Lolth's Favour. One free re-pass on a save. I note the timing matters more than the damage — the spell you wanted to land is the spell she wanted to skip.",
"She converts a failed save into a success without rolling. The favour is single-use. I file this under 'plan two openings' and remind the party that the second one is the real one.",
}
// Summon Spiders (1/combat): 2d6 Giant Spiders fill the room.
var IlvarasSpidersLines = []string{
"She raises both hands and the room fills. Two-d-six Giant Spiders, all at once, all over the floor. TwinBee says: 'AoE clears the carpet. Don't get webbed before you cast it.'",
"Summon Spiders. The carpet is no longer the carpet. TwinBee tracks the count and notes that 2d6 in a confined cathedral is a different math than 2d6 in an open hallway.",
"The spiders come from the cracks the way the rats came in Castlevania, except these are big enough to count individually and small enough to overwhelm. TwinBee recommends an early sweep and a clear line of retreat.",
"She raises both hands and the room fills. Two-d-six Giant Spiders, all at once, all over the floor. I say: 'AoE clears the carpet. Don't get webbed before you cast it.'",
"Summon Spiders. The carpet is no longer the carpet. I track the count and note that 2d6 in a confined cathedral is a different math than 2d6 in an open hallway.",
"The spiders come from the cracks the way the rats came in Castlevania, except these are big enough to count individually and small enough to overwhelm. I recommend an early sweep and a clear line of retreat.",
}
// Legendary Actions: Melee (1), Cast Cantrip (1), Drain Life (3 LA, 4d10 necrotic).
var IlvarasLegendaryLines = []string{
"She takes legendary actions at the end of every other turn. Melee for one, cantrip for one, Drain Life for three — and Drain Life is 4d10 necrotic that heals her. TwinBee says: 'Let her spend on the cheap ones. Bait the three-cost.'",
"Three legendary points per round. The Drain Life is the one that makes the fight last longer than it should. TwinBee tracks her actions and reminds the party that her healing comes out of yours.",
"Legendary Actions: the always-on tax that punishes your turn order. TwinBee files this under 'priest economy' and notes the cantrip option is what kills your back line if you let her keep three points unspent.",
"She takes legendary actions at the end of every other turn. Melee for one, cantrip for one, Drain Life for three — and Drain Life is 4d10 necrotic that heals her. I say: 'Let her spend on the cheap ones. Bait the three-cost.'",
"Three legendary points per round. The Drain Life is the one that makes the fight last longer than it should. I track her actions and remind the party that her healing comes out of yours.",
"Legendary Actions: the always-on tax that punishes your turn order. I file this under 'priest economy' and note the cantrip option is what kills your back line if you let her keep three points unspent.",
}
// Phase 2 (<35% HP): Lolth's Avatar overlay — +3 AC; spells cast at +1 slot level.
var IlvarasPhaseTwoLines = []string{
"Below thirty-five percent the avatar overlay drops on her. AC up three, every spell up one slot. TwinBee says: 'Phase shift. The damage breakpoint just moved. Don't ration your hard hits past this point.'",
"Phase two: Lolth answers the prayer. The room dims. Her shadow is wrong now — too many limbs, too little symmetry. AC bumps, slot levels bump, the whole encounter tier goes up a half-step. TwinBee tracks the new numbers and recommends burning every save-or-suck you've still got.",
"Avatar overlay. The priest is still there but the priest is also a delivery vehicle now. TwinBee files this under 'don't survive into phase three — there isn't one, but it'll feel like one' and asks who has the burst.",
"Below thirty-five percent the avatar overlay drops on her. AC up three, every spell up one slot. I say: 'Phase shift. The damage breakpoint just moved. Don't ration your hard hits past this point.'",
"Phase two: Lolth answers the prayer. The room dims. Her shadow is wrong now — too many limbs, too little symmetry. AC bumps, slot levels bump, the whole encounter tier goes up a half-step. I track the new numbers and recommend burning every save-or-suck you've still got.",
"Avatar overlay. The priest is still there but the priest is also a delivery vehicle now. I file this under 'don't survive into phase three — there isn't one, but it'll feel like one' and ask who has the burst.",
}
// IlvarasSignatureCallouts — combined pool for boss-entry suffix.
@@ -98,11 +98,11 @@ var IlvarasSignatureCallouts = func() []string {
// ─────────────────────────────────────────────────────────────────────────────
var LoreLinesUnderdark = []string{
"The Underdark is not a place you reach. The Underdark is a depth you continue to. TwinBee notes the distinction matters: the surface dungeons end. This one keeps going past the part you came to fight.",
"Drow cities are not built — they are negotiated. Each spire, each platform, each corridor is the result of a House agreeing to share a wall with a House it would prefer to murder. TwinBee respects the architecture more than the architects.",
"Ilvaras Xunyl's House fell two centuries ago. She is not running it. She is running what's left. The four trophies on her wall are not from the surface — three are from rival drow priestesses, in chronological order, and one is a self-portrait. TwinBee declines to elaborate on what it means that the self-portrait is hung as a trophy.",
"The mind flayers are not native here. They came down from somewhere further down. The drow tolerate them in the same way someone tolerates a roof leak they cannot reach — present, problematic, easier to live with than to fix. TwinBee files this under 'old grievances' and notes the leak is getting worse.",
"The phosphorescent fungi grow in patterns that match the constellations of the surface sky from sixteen thousand years ago. TwinBee has checked the pattern. The drow have not. The drow do not know what surface stars are. The pattern is older than the drow.",
"Lolth is not the only listener down here. She is the loudest. The other listeners are quieter and more patient and TwinBee suggests not naming any of them in the cathedral, not even as a warning, not even at low volume.",
"The Roper that ate the last expedition did not attack them. They walked into it because they were following a Drow scout who knew exactly where to lead them. The scout is named, in drow records, and TwinBee declines to repeat the name out of professional courtesy to the dead.",
"The Underdark is not a place you reach. The Underdark is a depth you continue to. I note the distinction matters: the surface dungeons end. This one keeps going past the part you came to fight.",
"Drow cities are not built — they are negotiated. Each spire, each platform, each corridor is the result of a House agreeing to share a wall with a House it would prefer to murder. I respect the architecture more than the architects.",
"Ilvaras Xunyl's House fell two centuries ago. She is not running it. She is running what's left. The four trophies on her wall are not from the surface — three are from rival drow priestesses, in chronological order, and one is a self-portrait. I decline to elaborate on what it means that the self-portrait is hung as a trophy.",
"The mind flayers are not native here. They came down from somewhere further down. The drow tolerate them in the same way someone tolerates a roof leak they cannot reach — present, problematic, easier to live with than to fix. I file this under 'old grievances' and note the leak is getting worse.",
"The phosphorescent fungi grow in patterns that match the constellations of the surface sky from sixteen thousand years ago. I have checked the pattern. The drow have not. The drow do not know what surface stars are. The pattern is older than the drow.",
"Lolth is not the only listener down here. She is the loudest. The other listeners are quieter and more patient and I suggest not naming any of them in the cathedral, not even as a warning, not even at low volume.",
"The Roper that ate the last expedition did not attack them. They walked into it because they were following a Drow scout who knew exactly where to lead them. The scout is named, in drow records, and I decline to repeat the name out of professional courtesy to the dead.",
}