// // Licensed under the MIT License. See LICENSE file in the project root for full license information. // namespace MUnique.OpenMU.Persistence.Initialization.Updates; using System.Runtime.InteropServices; using MUnique.OpenMU.DataModel.Configuration; using MUnique.OpenMU.Persistence.Initialization.VersionSeasonSix.Events; using MUnique.OpenMU.Persistence.Initialization.VersionSeasonSix.Maps; using MUnique.OpenMU.PlugIns; /// /// Adds the TvT Event team event data to an existing configuration: the imported LoL arena map /// (server number 92 / client World93) with its terrain, the statue (561) and guard (580) monster /// definitions, the red/blue base spawn gates, the event NPC (560) placed in Lorencia, and the /// mini-game definition. The event logic (context, packet handlers, chat commands, buffs) ships in /// the game-logic assemblies and needs no configuration update. /// [PlugIn] [Display(Name = PlugInName, Description = PlugInDescription)] [Guid("F2A7C4D9-3E1B-4A86-9C0D-5B7E2A1F8D42")] public class AddHeykelSavasiEventUpdateSeason6 : UpdatePlugInBase { internal const string PlugInName = "Add TvT Event event (map 92)"; internal const string PlugInDescription = "Adds the TvT Event team-vs-team event: the LoL arena map (server 92 / World93) with terrain, statue+guard monsters, red/blue base spawn gates, the event NPC (560) in Lorencia, and the mini-game definition."; /// public override UpdateVersion Version => UpdateVersion.AddHeykelSavasiEventSeason6; /// public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id; /// public override string Name => PlugInName; /// public override string Description => PlugInDescription; /// public override bool IsMandatory => true; /// public override DateTime CreatedAt => new(2026, 07, 21, 12, 0, 0, DateTimeKind.Utc); /// protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration) { // Idempotency guard: skip if the TvT Event map (92) already exists. if (gameConfiguration.Maps.Any(m => m.Number == HeykelSavasiMap.Number)) { return; } // 1. Map (No 92) + terrain (Terrain93.att) + statue (561) / guard (580) monster definitions. var mapInitializer = new HeykelSavasiMap(context, gameConfiguration); mapInitializer.Initialize(); mapInitializer.SetSafezoneMap(); var map = gameConfiguration.Maps.First(m => m.Number == HeykelSavasiMap.Number); // 2. Host the map on every game server configuration (fresh seeds do this automatically, // but existing databases need it added explicitly). var serverConfigs = await context.GetAsync().ConfigureAwait(false); foreach (var serverConfig in serverConfigs) { if (serverConfig.Maps.All(m => m.Number != HeykelSavasiMap.Number)) { serverConfig.Maps.Add(map); } } // 3. Red/Blue base spawn gates. The X1/Y1 anchors MUST match HeykelSavasiContext.GetTeamSpawnGate // (Red = 42,10 top tip of the arena diamond; Blue = 42,92 bottom tip). var redBase = context.CreateNew(); map.ExitGates.Add(redBase); redBase.Map = map; redBase.X1 = 42; redBase.Y1 = 10; redBase.X2 = 42; redBase.Y2 = 10; redBase.Direction = Direction.South; redBase.IsSpawnGate = true; var blueBase = context.CreateNew(); map.ExitGates.Add(blueBase); blueBase.Map = map; blueBase.X1 = 42; blueBase.Y1 = 92; blueBase.X2 = 42; blueBase.Y2 = 92; blueBase.Direction = Direction.North; blueBase.IsSpawnGate = true; // 4. Event NPC (560) placed in Lorencia. if (gameConfiguration.Monsters.All(m => m.Number != 560)) { var npc = context.CreateNew(); gameConfiguration.Monsters.Add(npc); npc.Number = 560; npc.Designation = "TvT Guard"; npc.NpcWindow = NpcWindow.Undefined; npc.ObjectKind = NpcObjectKind.PassiveNpc; npc.SetGuid(npc.Number); var lorencia = gameConfiguration.Maps.First(m => m.Number == Lorencia.Number); var spawn = context.CreateNew(); lorencia.MonsterSpawns.Add(spawn); spawn.GameMap = lorencia; spawn.MonsterDefinition = npc; spawn.Quantity = 1; spawn.X1 = 140; spawn.X2 = 140; spawn.Y1 = 120; spawn.Y2 = 120; spawn.Direction = Direction.SouthEast; spawn.SpawnTrigger = SpawnTrigger.Automatic; } // 5. Mini-game definition (its Entrance references a spawn gate on the map -> after step 3). new HeykelSavasiInitializer(context, gameConfiguration).Initialize(); } }