Commit graph

4 commits

Author SHA1 Message Date
Kamron Batman
346228fa69
fix(ai): pet order/home refactor — stop & post-combat behavior (#2459)
## Summary

Fixes two related pet-behavior bugs and the underlying design flaw behind both:

1. **Post-combat erratic** — after a pet killed its `all kill` target it milled around erratically at the kill site (or failed to return to the master) until the player issued `all follow`/`all stop`.
2. **`all stop` returns home** — a pet with a non-zero `Home` walked back toward that location on `all stop` (on ML; on non-ML the old `DoOrderStop` was a no-op, so the sighting there came from a residual `Stay`).

### Root cause

`ControlOrder` and the wild-creature `Home` field were overloaded to express several distinct ideas, mutated/read inconsistently across order transitions:

- `Home` doubled as the controlled-pet "stay anchor" (`HandleStayOrder` set `Home = Location`) but nothing cleared it when the pet left the staying state; `HandleStopOrder` was the only handler that never touched it.
- `DoOrderStop` had dropped RunUO's `Home = Location` re-anchor, so on ML it walked to a stale anchor.
- The post-combat fallback was a fragile `_lastPetOrder` hack in `DoOrderNone` that re-anchored a resumed `Stay` at the corpse.
- Controlled idle-wander bypassed the `CheckIdle()` rest gate that every non-controlled creature uses, so idling pets jittered every AI tick.

## Approach

Separate three concepts that were tangled together:

- **`ControlOrder`** — the active order (may be transient: Come/Attack/Drop).
- **Persistent command** (`PersistentOrder` ∈ `{None, Stay, Follow, Guard}`) — the standing directive a pet falls back to when a transient order completes. Runtime-only (not serialized; reset to `None` on load) and **derived from master proximity on login** (near → Follow, far → Stay).
- **Anchor** (`Home`) — a pure function of the persistent command, set only when that command changes (never on transient transitions or fallback-resume), so it can't go stale.

### Behavior

- **Stop** is resolved immediately from what the pet was doing: Attack/Come → resume the persistent command; Follow/Guard → cancel to idle where it stands; Stay → stay put.
- **Stay** holds its post (returns only if displaced, e.g. after a fight) — no shuffle.
- **Idle** (`None`) is a gentle wander routed through `CheckMove/CanMoveNow/CheckIdle`, so idling pets take the same 15–25s rest periods as other creatures, on both ML and non-ML.
- **Post-combat** the pet resumes its persistent command (a staying pet returns to its original post, not the corpse).
- **Release** without a spawner anchors where the pet stands instead of pathing to a stale anchor.

This restores the RunUO-intended behavior (verified against the RunUO reference) while fixing the ModernUO regressions.

## Tests

New `PetOrderTests` (13 deterministic xUnit tests) cover: anchor lifecycle, the full Stop truth table, report 1 (post-combat return to post), report 2 (no stale-anchor walk-home), frozen-Stay/gated-idle wiring, release fix, derive-on-login, and a non-ML spot-check. The subjective wander *feel* is covered by a manual-QA checklist in the implementation plan.

## Notes

- Engine project (`Projects/Server`) untouched; the one `BaseCreature.cs` change is the `ControlOrder` setter passing the previous order to `OnCurrentOrderChanged`.
- `DoOrderCome` keeps auto-converting to `Stay` on arrival, which under the new model cleanly means "come and hold near me."
- Commits in this PR are temporarily **unsigned** (the signing agent's passphrase cache expired mid-session); happy to re-sign / amend on request.
2026-06-07 01:22:43 -07:00
Kamron Batman
36e09bae13
fix: Pets stop permanently, simplifies pet movement speed (#2401)
## Summary
- Fixes pets falling behind mounted masters in AOS+ by setting `CurrentSpeed = 0.1` when following master
- Fixes AI timer permanently stopping when `Obey()`/`Think()` returns `false` for transient conditions
- Fixes controlled pets losing AI in inactive sectors (pet follows owner across sector boundary, sector deactivates, AI dies)
- Adds defense-in-depth: AI timer restarts on pet resurrection and order changes

## AI Timer Permanent Stop (Bug Fix)

`AITimer.OnTick()` called `Stop()` when `Obey()` or `Think()` returned `false`. By that point, `ShouldStop()` had already validated the creature is alive, on a valid map, and in an active sector — so any `false` return was a **transient** condition, not terminal. The timer stopped permanently with no mechanism to restart it.

**Scenarios that triggered permanent AI death:**
- Dead bonded pet with attack order (`DoOrderAttack` returned `false` for `IsDeadPet`)
- Failed pet transfer — loyalty refusal, combat, disconnected player, or pending trade (`DoOrderTransfer` returned `false` for 5 different transient conditions)
- Unknown `OrderType` or `ActionType` (defensive defaults)

**Fixes:**
- Removed `Stop()` from the `Obey()`/`Think()` failure path — timer skips the tick and fires again next interval
- Changed `DoOrderAttack()` and all five `DoOrderTransfer()` failure paths to return `true` (correct semantics: these are recoverable states, not "stop AI forever" signals)
- Added `Activate()` call in `ResurrectPet()` — ensures dead bonded pets have AI running after resurrection
- Added `Activate()` call in `OnCurrentOrderChanged()` — self-heals timer if any voice command is issued to a pet with a stopped timer

## Controlled Pet Sector Deactivation (Bug Fix)

`ShouldStop()` stopped the AI timer for **all** `PlayerRangeSensitive` creatures in inactive sectors, including controlled pets. But `Deactivate()` intentionally exempted controlled pets. The exemption was dead code — `ShouldStop()` bypassed it.

This matters when a pet follows its owner across a sector boundary: the owner enters the next sector (active), the pet's old sector deactivates (no more players), and the pet's AI dies. The pet stops following and stands there until the player backtracks far enough to reactivate the sector.

**Fix:** Added `Controlled` check to `ShouldStop()` to match `Deactivate()`. Controlled pets now keep their AI running in inactive sectors. The overhead is negligible — controlled pets are bounded by follower slots.

## Movement Speed Simplification

- Simplifies `AITimer` to use `CurrentSpeed` directly as the tick interval (in seconds), removing the complex multiplier/floor logic in `GetBaseInterval`
- Refactors `DoMoveImpl` speed assignment into explicit if/else for clarity
- AOS+ pets following master use `CurrentSpeed = 0.1` (100ms), matching `RunMountDelay`

## Files Changed
- `AITimer.cs` — removed `Stop()` on Obey/Think failure, added `Controlled` exemption to `ShouldStop()`, simplified interval logic
- `BaseAI.cs` — renamed `_timer` to `AITimer` (public), simplified `Deactivate()`, fixed `ReturnToHome` to use `Activate()`
- `PetOrders.cs` — `DoOrderAttack` and `DoOrderTransfer` return `true` for transient failures
- `PetOrderHandlers.cs` — `OnCurrentOrderChanged()` calls `Activate()` to self-heal stopped timers
- `BaseCreature.cs` — `ResurrectPet()` calls `Activate()`, fixed `GoHome_Callback` PlayerRangeSensitive check
- `AIMovement.cs` — refactored speed assignment, AOS+ follow-master speed fix
2026-04-25 11:10:30 -07:00
Kamron Batman
e1e1a7c640
fix: Bumps deps. Updates copyrights (#2353) 2026-03-05 19:36:54 -08:00
Kamron Batman
aab731ed96
feat: Overhauls AI (Speech/Movement) (#2232)
### Summary

* Refactors AI so it is easier to read and maintain
* Fixes NPC speed issues
* Fixes pet sector AI issue that was causing stuttering
* Fixes direction snapping for Melee/Mage AI
* Refactors pet orders
* Refactors speech commands
* Removes scale speed by dex for HS+ (it was a stupid feature anyways)
2025-07-18 22:39:57 -07:00