using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using PhoenixLib.Events; using WingsEmu.Core.Extensions; using WingsEmu.Game; using WingsEmu.Game._enum; using WingsEmu.Game.Battle.Managers; using WingsEmu.Game.Characters; using WingsEmu.Game.Entities; using WingsEmu.Game.Entities.Event; using WingsEmu.Game.Helpers.Damages; using WingsEmu.Game.Managers.StaticData; using WingsEmu.Game.Maps; using WingsEmu.Game.Monster.Event; using WingsEmu.Game.Npcs.Event; using WingsEmu.Game.Triggers; namespace WingsEmu.Plugins.BasicImplementations.Event.Monster; public class MonsterSummonEventHandler : IAsyncEventProcessor { private readonly IMonsterEntityFactory _monsterEntityFactory; private readonly INpcMonsterManager _npcMonsterManager; private readonly IPhantomPositionManager _phantomPositionManager; private readonly IRandomGenerator _randomGenerator; public MonsterSummonEventHandler(IRandomGenerator randomGenerator, INpcMonsterManager npcMonsterManager, IMonsterEntityFactory monsterEntityFactory, IPhantomPositionManager phantomPositionManager) { _randomGenerator = randomGenerator; _npcMonsterManager = npcMonsterManager; _monsterEntityFactory = monsterEntityFactory; _phantomPositionManager = phantomPositionManager; } public async Task HandleAsync(MonsterSummonEvent e, CancellationToken cancellation) { List toSummon = new(); short? scaledAmount = e.ScaledWithPlayerAmount; if (scaledAmount.HasValue) { for (int i = 0; i < scaledAmount.Value; i++) { toSummon.AddRange(e.Monsters); } } else { toSummon = e.Monsters.ToList(); } foreach (ToSummon summon in toSummon) { IMonsterData npcMonster = _npcMonsterManager.GetNpc(summon.VNum); if (npcMonster == null || _randomGenerator.RandomNumber() > summon.SummonChance) { continue; } if (e.Map.IsSummonLimitReached(e.Summoner?.Id, summon.SummonType)) { continue; } IMonsterEntity mapMonster = summon.MonsterEntity ?? _monsterEntityFactory.CreateMonster(npcMonster, e.Map, new MonsterEntityBuilder { IsMateTrainer = summon.IsMateTrainer, IsBonus = summon.IsBonusOrProtected, IsBoss = summon.IsBossOrMate, IsTarget = summon.IsTarget, IsHostile = summon.IsHostile, IsWalkingAround = summon.IsMoving, IsVesselMonster = summon.IsVesselMonster, SummonType = summon.SummonType, SummonerId = e.Summoner?.Id, SummonerType = e.Summoner?.Type, FactionType = summon.FactionType ?? e.Summoner?.Faction, HpMultiplier = summon.HpMultiplier, MpMultiplier = summon.MpMultiplier, SetHitChance = summon.SetHitChance != 0 && !summon.IsBossOrMate ? summon.SetHitChance : npcMonster.BasicHitChance, Direction = summon.Direction, GoToBossPosition = summon.GoToBossPosition, IsInstantBattle = summon.IsInstantBattle, Level = summon.Level }); mapMonster.Target = summon.Target; mapMonster.Waypoints = summon.Waypoints; if (summon.TriggerEvents != null) { foreach ((string key, IAsyncEvent asyncEvent, bool removeOnUse) in summon.TriggerEvents) { mapMonster.AddEvent(key, asyncEvent, removeOnUse); } } if (e.Summoner != null) { if (e.GetSummonerLevel) { mapMonster.Level = e.Summoner.Level; } if (e.Summoner is IPlayerEntity player && summon.VNum == (short)MonsterVnum.BOMB) { player.SkillComponent.BombEntityId = mapMonster.Id; } } Position? phantomPosition = e.NpcId.HasValue ? _phantomPositionManager.GetPosition(e.NpcId.Value) : null; Position spawnCell = phantomPosition ?? summon.SpawnCell ?? e.Map.GetRandomPosition(); if (summon.AtAroundMobId.HasValue && summon.AtAroundMobRange.HasValue) { IMonsterEntity originalEntity = e.Map.GetMonsterByUniqueId(summon.AtAroundMobId.Value); byte range = summon.AtAroundMobRange.Value; if (originalEntity is not null) { spawnCell = new Position((short)(originalEntity.PositionX + _randomGenerator.RandomNumber(-range, range)), (short)(originalEntity.PositionY + _randomGenerator.RandomNumber(-range, range))); if (originalEntity.MapInstance.IsBlockedZone(spawnCell.X, spawnCell.Y)) { spawnCell = originalEntity.Position; } } } await mapMonster.EmitEventAsync(new MapJoinMonsterEntityEvent(mapMonster, spawnCell.X, spawnCell.Y, e.ShowEffect)); CheckBeriosPhantom(mapMonster); DateTime now = DateTime.UtcNow; mapMonster.AttentionTime = now + TimeSpan.FromSeconds(10); if (!summon.RemoveTick) { continue; } mapMonster.NextTick = DateTime.UtcNow.AddSeconds(-1000); mapMonster.NextAttackReady = DateTime.UtcNow.AddSeconds(-1000); } } private void CheckBeriosPhantom(IMonsterEntity mapMonster) { switch ((MonsterVnum)mapMonster.MonsterVNum) { case MonsterVnum.EMERALD_PHANTOM: mapMonster.AddEvent(BattleTriggers.OnDeath, new NpcSummonEvent { Map = mapMonster.MapInstance, MonsterId = mapMonster.UniqueId, Npcs = Lists.Create(new ToSummon { VNum = (short)MonsterVnum.EMERALD_SHADOW_PHANTOM, SpawnCell = mapMonster.Position, IsMoving = true, IsHostile = true }) }, true); break; case MonsterVnum.SAPPHIRE_PHANTOM: mapMonster.AddEvent(BattleTriggers.OnDeath, new NpcSummonEvent { Map = mapMonster.MapInstance, MonsterId = mapMonster.UniqueId, Npcs = Lists.Create(new ToSummon { VNum = (short)MonsterVnum.SAPPHIRE_SHADOW_PHANTOM, SpawnCell = mapMonster.Position, IsMoving = true, IsHostile = true }) }, true); break; case MonsterVnum.RUBY_PHANTOM: mapMonster.AddEvent(BattleTriggers.OnDeath, new NpcSummonEvent { Map = mapMonster.MapInstance, MonsterId = mapMonster.UniqueId, Npcs = Lists.Create(new ToSummon { VNum = (short)MonsterVnum.RUBY_SHADOW_PHANTOM, SpawnCell = mapMonster.Position, IsMoving = true, IsHostile = true }) }, true); break; default: return; } } }