using Theriapolis.Core.Data; using Theriapolis.Core.Entities; using Theriapolis.Core.Items; using Theriapolis.Core.Rules.Reputation; using Theriapolis.Core.Time; using Theriapolis.Core.World; using Theriapolis.Core.World.Settlements; namespace Theriapolis.Core.Rules.Quests; /// /// Phase 6 M4 — read/write window the QuestEngine uses to evaluate /// conditions and apply effects. Deliberately holds references rather /// than copies so engine ticks see live state. /// public sealed class QuestContext { public ContentResolver Content { get; } public ActorManager Actors { get; } public PlayerReputation Reputation { get; } public Dictionary Flags { get; } public AnchorRegistry Anchors { get; } public WorldClock Clock { get; } public WorldState World { get; } public Rules.Character.Character? PlayerCharacter { get; set; } /// Most recent dialogue node id reached, surfaced by InteractionScreen for the dialogue_choice condition. public string LastDialogueNodeReached { get; set; } = ""; public QuestContext(ContentResolver content, ActorManager actors, PlayerReputation rep, Dictionary flags, AnchorRegistry anchors, WorldClock clock, WorldState world) { Content = content; Actors = actors; Reputation = rep; Flags = flags; Anchors = anchors; Clock = clock; World = world; } public bool HasItem(string itemId) { if (PlayerCharacter is null) return false; foreach (var inst in PlayerCharacter.Inventory.Items) if (string.Equals(inst.Def.Id, itemId, System.StringComparison.OrdinalIgnoreCase)) return true; return false; } /// /// Position of the live player actor in tactical-tile (= world-pixel) /// space, or null if the actor isn't yet spawned. /// public (int x, int y)? PlayerTacticalPos() { if (Actors.Player is null) return null; return ((int)Actors.Player.Position.X, (int)Actors.Player.Position.Y); } /// /// World-tile Chebyshev distance from the player to the settlement /// with the given id, or null if the player or settlement isn't /// resolvable. /// public int? PlayerDistanceToSettlement(int settlementId) { if (Actors.Player is null) return null; Settlement? s = null; foreach (var sx in World.Settlements) if (sx.Id == settlementId) { s = sx; break; } if (s is null) return null; int playerWX = (int)Actors.Player.Position.X / C.WORLD_TILE_PIXELS; int playerWY = (int)Actors.Player.Position.Y / C.WORLD_TILE_PIXELS; int dx = System.Math.Abs(playerWX - s.TileX); int dy = System.Math.Abs(playerWY - s.TileY); return System.Math.Max(dx, dy); } }