using Theriapolis.Core.Entities; using Theriapolis.Core.Util; namespace Theriapolis.Core.Rules.Combat; /// /// Per-tick check used by : /// "is there a hostile NPC within encounter trigger range that has line of /// sight?" Returns the closest qualifying actor (or null) so the caller can /// kick off an encounter. /// /// Friendly / Neutral proximity is the same shape but uses a tighter radius /// — see . /// public static class EncounterTrigger { /// /// Find the closest live hostile NPC within /// of the player that the /// predicate can see (no blocking tile /// between). Returns null if none found. /// public static NpcActor? FindHostileTrigger( ActorManager actors, System.Func? losBlocked = null) { var player = actors.Player; if (player is null) return null; var blocked = losBlocked ?? LineOfSight.AlwaysClear; NpcActor? best = null; int bestDistSq = C.ENCOUNTER_TRIGGER_TILES * C.ENCOUNTER_TRIGGER_TILES; foreach (var npc in actors.Npcs) { if (!npc.IsAlive) continue; if (npc.Allegiance != Rules.Character.Allegiance.Hostile) continue; int distSq = ChebyshevDistSq(player.Position, npc.Position); if (distSq > bestDistSq) continue; if (!LineOfSight.HasLine(player.Position, npc.Position, blocked)) continue; if (best is null || distSq < bestDistSq) { best = npc; bestDistSq = distSq; } } return best; } /// /// Friendly / Neutral NPCs within /// of the player. The HUD shows /// "[F] Talk to ..." for the closest match. /// public static NpcActor? FindInteractCandidate( ActorManager actors, System.Func? losBlocked = null) { var player = actors.Player; if (player is null) return null; var blocked = losBlocked ?? LineOfSight.AlwaysClear; NpcActor? best = null; int bestDistSq = C.INTERACT_PROMPT_TILES * C.INTERACT_PROMPT_TILES; foreach (var npc in actors.Npcs) { if (!npc.IsAlive) continue; if (npc.Allegiance != Rules.Character.Allegiance.Friendly && npc.Allegiance != Rules.Character.Allegiance.Neutral) continue; int distSq = ChebyshevDistSq(player.Position, npc.Position); if (distSq > bestDistSq) continue; if (!LineOfSight.HasLine(player.Position, npc.Position, blocked)) continue; if (best is null || distSq < bestDistSq) { best = npc; bestDistSq = distSq; } } return best; } private static int ChebyshevDistSq(Vec2 a, Vec2 b) { int dx = (int)System.Math.Abs(a.X - b.X); int dy = (int)System.Math.Abs(a.Y - b.Y); int d = System.Math.Max(dx, dy); return d * d; } }