fix(combat): synchronize auto-target presentation

Make the retained toolbar consume retail's payload-free selection notice from canonical live state so a reentrant Auto Target replacement cannot be erased by the outer clear. Restrict automatic acquisition to the requested hostile non-player monster policy and record the intentional PK edge divergence.

Co-Authored-By: Codex <codex@openai.com>
This commit is contained in:
Erik 2026-07-12 20:26:00 +02:00
parent 8be933fc94
commit 0f2d98c501
11 changed files with 225 additions and 7 deletions

View file

@ -0,0 +1,42 @@
using AcDream.Core.Items;
namespace AcDream.Core.Combat;
/// <summary>
/// Eligibility policy for automatic monster acquisition.
/// </summary>
public static class CombatTargetPolicy
{
/// <summary>
/// Returns true only for a live-data creature that is attackable, is not a
/// player, and is not a pet. Friendly NPCs and non-creatures are rejected.
/// </summary>
/// <remarks>
/// The attackability core is retail
/// <c>ClientCombatSystem::ObjectIsAttackable @ 0x0056A600</c>. Retail
/// <c>AutoTarget @ 0x0056BC80</c> falls back to
/// <c>SelectNext(SELECTION_TYPE_COMPASS_ITEM)</c>, whose combat filter also
/// uses ObjectIsAttackable. acdream deliberately narrows that set to
/// non-player monsters for its automatic acquisition policy; explicit
/// player-selection commands remain separate.
/// </remarks>
public static bool IsHostileMonster(
uint playerId,
ClientObject? player,
ClientObject? candidate)
{
if (candidate is null
|| candidate.ObjectId == playerId
|| (candidate.Type & ItemType.Creature) == 0
|| (candidate.PublicWeenieBitfield.GetValueOrDefault()
& SelectedObjectHealthPolicy.BfPlayer) != 0
|| candidate.PetOwnerId != 0)
return false;
return SelectedObjectHealthPolicy.ObjectIsAttackable(
playerId,
player,
candidate.ObjectId,
candidate);
}
}