mirror of
https://github.com/prosolis/gogobee.git
synced 2026-07-15 00:32:40 +00:00
Lift caster trailers: concentration re-tick + Josie caster-aid bootstraps
Diagnosed a cleric "death loop" (L14 dying at T2/T3 bosses while over-levelled): the boss isn't overtuned — caster sustained DPS is under-delivered, compounded by a fragile healer build. Engine fix — concentration AOE re-tick: - Concentration damage spells (spirit_guardians, heat_metal, spike_growth, call_lightning, flaming_sphere) now tick the enemy every round at round_end instead of resolving as a one-shot, via a new CombatStatuses.ConcentrationDmg armed on cast and round-tripped through the turn engine. Closes the long-tracked turn-engine concentration gap; the burst still lands the casting round, then the aura lingers. - Sim picker skips re-casting an already-active aura (models competent play and prevents a burst+aura double-dip). Re-baseline (n=30 sweep + n=100 confirm): bard +47pp T3 (heat_metal), druid +3-7, cleric/mage flat, fighter unchanged — no regressions. Player-data bootstraps (idempotent, run once on Init): - bootstrapCasterSpellBackfill: ensureSpellsForCharacter only seeds an empty book, so defaults added after a character's roll never reach it. Backfill missing defaults into known+prepared for existing casters (gives the affected cleric inflict_wounds + a working healing_word, since her healing_word_spell is a dead alias). - bootstrapGrantStarterPet: one-off L10 pet for an endgame player who never got the morning arrival roll; adds per-round proc damage + deflect. - TestScenario_JosieCasterAid verifies both against a copy of the live DB, incl. idempotency. Also fix a pre-existing wall-clock flake in TestFireBriefings_EventAnchoredActivePlayerDelivers (start_date defaulted to real now, filtering the row out when the suite runs after 06:00 UTC).
This commit is contained in:
@@ -919,7 +919,12 @@ func (p *AdventurePlugin) pickAutoCombatAction(uid id.UserID, sess *CombatSessio
|
||||
if id := simPickSpiritualWeapon(c, uid, sess); id != "" {
|
||||
return "cast", id
|
||||
}
|
||||
if id := simPickSpell(c, uid); id != "" {
|
||||
// Once a concentration aura is up, a competent caster maintains it and
|
||||
// attacks (or casts a non-concentration spell) rather than burning a
|
||||
// slot to re-arm the same aura — so the picker excludes concentration
|
||||
// spells while one is active.
|
||||
auraActive := sess.Statuses.ConcentrationDmg > 0
|
||||
if id := simPickSpell(c, uid, auraActive); id != "" {
|
||||
return "cast", id
|
||||
}
|
||||
}
|
||||
@@ -1008,7 +1013,7 @@ func simPickSpiritualWeapon(c *DnDCharacter, uid id.UserID, sess *CombatSession)
|
||||
// - Among feasible candidates, prefer higher slot level (preserves
|
||||
// high-slot supremacy and burns the big slots first); tie-break on
|
||||
// expected damage from the dice string.
|
||||
func simPickSpell(c *DnDCharacter, uid id.UserID) string {
|
||||
func simPickSpell(c *DnDCharacter, uid id.UserID, auraActive bool) string {
|
||||
known, err := listKnownSpells(uid)
|
||||
if err != nil || len(known) == 0 {
|
||||
return ""
|
||||
@@ -1037,6 +1042,11 @@ func simPickSpell(c *DnDCharacter, uid id.UserID) string {
|
||||
if sp.CastTime == CastReaction {
|
||||
continue
|
||||
}
|
||||
// An aura is already ticking — don't re-arm it; prefer attacks or a
|
||||
// non-concentration spell this turn.
|
||||
if auraActive && sp.Concentration {
|
||||
continue
|
||||
}
|
||||
onList := false
|
||||
for _, cl := range sp.Classes {
|
||||
if cl == c.Class {
|
||||
|
||||
Reference in New Issue
Block a user