Phase-H review fixes: camp, fork streak, pet proc, BG transit, legacy streak

- Camp: campLocationCheck rejects only on live combat; no-encounter and
  post-kill rooms are campable (kills the misleading "clear it first").
- Fork: markActedToday moved after the pending-fork early-return so spamming
  !zone advance at a fork no longer keeps the daily streak alive.
- Pet whiff/deflect: single proc spent on the first multiattack swing only
  (was applied to every swing); also dedups the per-swing event spam.
- Autopilot: background region crossing now runs an HP/SU preflight and
  pauses if low, instead of burning a transit day while the player is idle.
- Legacy streak: acted-branch restamps LastActionDate=today so a purely-legacy
  actor's streak no longer resets to 1 every night.
This commit is contained in:
prosolis
2026-05-22 01:15:23 -07:00
parent bcd4a873a5
commit ef8fbe5496
5 changed files with 43 additions and 19 deletions

View File

@@ -348,11 +348,12 @@ func (te *turnEngine) stepEnemyTurn() {
}
if !abilityDealtDamage {
// Pet defensive procs are a per-round concept (mirrors the auto-resolve
// engine): roll once for the whole enemy turn, then apply to every swing
// in a multiattack profile. Whiff makes the enemy's attack this round a
// guaranteed miss; deflect halves the incoming damage. The shared
// resolveEnemyAttack primitive consumes both flags.
// Pet defensive procs are a single proc per enemy turn: roll once, then
// spend it on the first swing only. Whiff makes that one swing a
// guaranteed miss; deflect halves its damage. Against a multiattack the
// remaining swings resolve normally — a single proc shouldn't nullify a
// boss's whole multiattack round. (This deliberately diverges from the
// auto-resolve engine's apply-to-all model.)
petWhiff := te.player.Mods.PetWhiffProc > 0 && te.st.randFloat() < te.player.Mods.PetWhiffProc
petDeflect := te.player.Mods.PetDeflectProc > 0 && te.st.randFloat() < te.player.Mods.PetDeflectProc
if petDeflect {
@@ -365,11 +366,14 @@ func (te *turnEngine) stepEnemyTurn() {
// resolveEnemyAttack returns true when the fight is decided — either the
// player went down without a death save, or a reflect consumable killed
// the enemy. Disambiguate by inspecting HP.
for _, atk := range enemyAttackProfile(te.sess.EnemyID, te.enemy.Stats) {
for i, atk := range enemyAttackProfile(te.sess.EnemyID, te.enemy.Stats) {
swing := *te.enemy
swing.Stats.Attack = atk.Damage
swing.Stats.AttackBonus = atk.AttackBonus
decided := resolveEnemyAttack(te.st, te.player, &swing, &turnCombatPhase, te.result, petWhiff, petDeflect, false)
// Spend the proc on the first swing only; later swings see false.
swingWhiff := petWhiff && i == 0
swingDeflect := petDeflect && i == 0
decided := resolveEnemyAttack(te.st, te.player, &swing, &turnCombatPhase, te.result, swingWhiff, swingDeflect, false)
if te.st.playerHP <= 0 {
te.finish(CombatStatusLost)
return