diff --git a/src/GameLogic/PlugIns/PeriodicTasks/CastleSiegeEventPlugIn.cs b/src/GameLogic/PlugIns/PeriodicTasks/CastleSiegeEventPlugIn.cs index 746fc8b..ca4b132 100644 --- a/src/GameLogic/PlugIns/PeriodicTasks/CastleSiegeEventPlugIn.cs +++ b/src/GameLogic/PlugIns/PeriodicTasks/CastleSiegeEventPlugIn.cs @@ -25,17 +25,23 @@ using MUnique.OpenMU.PlugIns; public sealed class CastleSiegeEventPlugIn : IPeriodicTaskPlugIn, ISupportCustomConfiguration, ISupportDefaultCustomConfiguration { private const ushort ValleyOfLorenMapNumber = 30; - private const short CastleGateNumber = 131; // reuse BloodCastle "Castle Gate" destructible definition - private const short GuardianStatueNumber = 132; // reuse BloodCastle "Statue of Saint" destructible definition + private const short CastleGateNumber = 277; // client MONSTER_CASTLE_GATE1 -> renders a real gate + blocks terrain until broken + private const short GateTemplateNumber = 131; // BloodCastle "Castle Gate" destructible (has HP) — HP template for 277 + private const short GuardianStatueNumber = 283; // "Guardian Statue" — inner castle defense + private const short StatueTemplateNumber = 132; // BloodCastle "Statue of Saint" destructible (has HP) — HP template for 283 private const int SwitchHoldRange = 3; // Castle defenses spawned at siege start — destructibles the attackers must break: (number, x, y). - // Positions are in the throne-room area (near the crown ~176,212 and the switches ~167-184,194-195), - // where players can actually reach and hit them (the earlier statue spot 170/182,206 was confirmed reachable). + // Gates are at the 6 REAL castle gate positions (client g_byGateLocation) so the client renders them + // closed and blocks the terrain until broken. Guardian statues stand inside near the throne. private static readonly (short Number, byte X, byte Y)[] DefenseSpawns = { - (CastleGateNumber, 168, 200), - (CastleGateNumber, 184, 200), + (CastleGateNumber, 67, 114), + (CastleGateNumber, 93, 114), + (CastleGateNumber, 119, 114), + (CastleGateNumber, 81, 161), + (CastleGateNumber, 107, 161), + (CastleGateNumber, 93, 204), (GuardianStatueNumber, 170, 208), (GuardianStatueNumber, 182, 208), }; @@ -127,6 +133,23 @@ public sealed class CastleSiegeEventPlugIn : IPeriodicTaskPlugIn, ISupportCustom => gameContext.ForEachPlayerAsync(player => player.InvokeViewPlugInAsync(p => p.ShowMessageAsync(message, MessageType.GoldenCenter)).AsTask()); + private static void EnsureDestructible(MonsterDefinition target, MonsterDefinition? template) + { + if (target.ObjectKind == NpcObjectKind.Destructible) + { + return; + } + + target.ObjectKind = NpcObjectKind.Destructible; + if (template is not null && target.Attributes.Count == 0) + { + foreach (var attribute in template.Attributes) + { + target.Attributes.Add(attribute); + } + } + } + private static async Task SpawnCastleDefensesAsync(IGameContext gameContext, CastleSiegeContext context) { try @@ -154,6 +177,12 @@ public sealed class CastleSiegeEventPlugIn : IPeriodicTaskPlugIn, ISupportCustom continue; } + // The stock CS gate (277) / statue (283) definitions have no HP and aren't destructible; + // make them breakable by borrowing the HP/attributes of a working destructible (131/132). + var template = gameContext.Configuration.Monsters.FirstOrDefault( + m => m.Number == (spawn.Number == CastleGateNumber ? GateTemplateNumber : StatueTemplateNumber)); + EnsureDestructible(definition, template); + var spawnArea = new MonsterSpawnArea { MonsterDefinition = definition,