using System; using System.Linq; using System.Threading.Tasks; using PhoenixLib.MultiLanguage; using WingsEmu.Game._enum; using WingsEmu.Game._i18n; using WingsEmu.Game.Battle; using WingsEmu.Game.Characters; using WingsEmu.Game.Entities; using WingsEmu.Game.Entities.Event; using WingsEmu.Game.Items; using WingsEmu.Game.Mates; using WingsEmu.Game.Monster; using WingsEmu.Game.Networking; using WingsEmu.Game.Npcs; using WingsEmu.Packets.Enums; using WingsEmu.Packets.Enums.Battle; namespace WingsEmu.Game.Extensions; public static class NpcMonsterExtension { public static string GenerateEInfo(this IMonsterData npcMonster, IGameLanguageService gameLanguage, RegionLanguageType languageType) { string name = gameLanguage.GetLanguage(GameDataType.NpcMonster, npcMonster.Name, languageType); return "e_info 10 " + $"{npcMonster.MonsterVNum} " + $"{npcMonster.BaseLevel} " + $"{npcMonster.BaseElement} " + $"{(byte)npcMonster.AttackType} " + $"{npcMonster.BaseElementRate} " + $"{npcMonster.AttackUpgrade} " + $"{npcMonster.BaseDamageMinimum} " + $"{npcMonster.BaseDamageMaximum} " + $"{npcMonster.BaseConcentrate} " + $"{npcMonster.BaseCriticalChance} " + $"{npcMonster.BaseCriticalRate} " + $"{npcMonster.DefenceUpgrade} " + $"{npcMonster.BaseCloseDefence} " + $"{npcMonster.DefenceDodge} " + $"{npcMonster.DistanceDefence} " + $"{npcMonster.DistanceDefenceDodge} " + $"{npcMonster.MagicDefence} " + $"{npcMonster.BaseFireResistance} " + $"{npcMonster.BaseWaterResistance} " + $"{npcMonster.BaseLightResistance} " + $"{npcMonster.BaseDarkResistance} " + $"{npcMonster.BaseMaxHp} " + $"{npcMonster.BaseMaxMp} " + "-1 " + $"{name.Replace(' ', '^')}"; } public static void SendNpcInfo(this IClientSession session, IMonsterData npcMonster, IGameLanguageService gameLanguage) => session.SendPacket(npcMonster.GenerateEInfo(gameLanguage, session.UserLanguage)); public static int GetModifier(this MonsterData monster) { return monster.AttackType switch { AttackType.Melee => monster.MeleeHpFactor, AttackType.Ranged => monster.RangeDodgeFactor, AttackType.Magical => monster.MagicMpFactor }; } public static bool CanHit(this IMonsterData npcMonster, IBattleEntity entity) { switch (entity) { case IPlayerEntity player when player.CheatComponent.IsInvisible: return false; case IMateEntity mateEntity: if (mateEntity.Owner == null) { break; } if (mateEntity.Owner.IsInvisible()) { return false; } if (mateEntity.Owner.Invisible) { return false; } if (mateEntity.Owner.CheatComponent.IsInvisible) { return false; } if (mateEntity.Owner.IsOnVehicle) { return false; } break; } return !(npcMonster.MonsterRaceType == MonsterRaceType.People && npcMonster.MonsterRaceSubType == 3 && entity.Faction == FactionType.Angel | npcMonster.MonsterRaceType == MonsterRaceType.Angel && entity.Faction == FactionType.Demon); } public static bool FindPhantomAmulet(this IMonsterData npcMonster, IBattleEntity entity) { if (npcMonster.RawHostility != (int)HostilityType.ATTACK_NOT_WEARING_PHANTOM_AMULET) { return false; } switch (entity) { case IPlayerEntity character: GameItemInstance amulet = character.Amulet; if (amulet == null) { return false; } return amulet.GameItem.Id == (short)ItemVnums.PHANTOM_AMULET; case IMateEntity mateEntity: IPlayerEntity owner = mateEntity.Owner; GameItemInstance ownerAmulet = owner.Amulet; if (ownerAmulet == null) { return false; } return ownerAmulet.GameItem.Id == (short)ItemVnums.PHANTOM_AMULET; } return true; } public static MonsterRaceType GetMonsterRaceType(this IBattleEntity entity) { return entity switch { IPlayerEntity => MonsterRaceType.People, IMateEntity mateEntity => mateEntity.MonsterRaceType, INpcEntity mapNpc => mapNpc.MonsterRaceType, IMonsterEntity monster => monster.MonsterRaceType, _ => MonsterRaceType.People }; } public static Enum GetMonsterRaceSubType(this IMonsterData monster) { MonsterRaceType monsterRaceType = monster.MonsterRaceType; byte subType = monster.MonsterRaceSubType; Enum raceSubType; switch (monsterRaceType) { case MonsterRaceType.LowLevel: raceSubType = (MonsterSubRace.LowLevel)subType; break; case MonsterRaceType.HighLevel: raceSubType = (MonsterSubRace.HighLevel)subType; break; case MonsterRaceType.Furry: raceSubType = (MonsterSubRace.Furry)subType; break; case MonsterRaceType.People: raceSubType = (MonsterSubRace.People)subType; break; case MonsterRaceType.Angel: raceSubType = (MonsterSubRace.Angels)subType; break; case MonsterRaceType.Undead: raceSubType = (MonsterSubRace.Undead)subType; break; case MonsterRaceType.Spirit: raceSubType = (MonsterSubRace.Spirits)subType; break; case MonsterRaceType.Other: raceSubType = (MonsterSubRace.Other)subType; break; case MonsterRaceType.Fixed: raceSubType = (MonsterSubRace.Fixed)subType; break; default: return null; } return raceSubType; } public static string GenerateCMode(this IMonsterEntity monsterEntity, short? value = null) => $"c_mode 3 {monsterEntity.Id} {value ?? monsterEntity.Morph} 0 0"; public static void BroadcastMonsterMorph(this IMonsterEntity monsterEntity, short? value = null) => monsterEntity.MapInstance.Broadcast(monsterEntity.GenerateCMode(value)); public static bool IsMonsterSpawningMonstersForQuest(this IMonsterEntity monsterEntity) => monsterEntity.RawHostility > 20000; public static string GenerateEnhancedGuri(this IMonsterEntity monsterEntity) => $"guri 21 3 {monsterEntity.Id}"; public static void BroadcastEnhancedGuri(this IMonsterEntity monsterEntity) => monsterEntity.MapInstance.Broadcast(monsterEntity.GenerateEnhancedGuri()); public static bool IsMandra(this IBattleEntity battleEntity) { if (battleEntity is not IMonsterEntity monsterEntity) { return false; } return monsterEntity.MonsterVNum is >= (short)MonsterVnum.HAPPY_MANDRA and <= (short)MonsterVnum.STRONG_GRASSMANDRA or (short)MonsterVnum.GIANT_MANDRA or (short)MonsterVnum.DASHING_MANDRA; } public static bool IsPhantom(this IMonsterEntity monsterEntity) => (MonsterVnum)monsterEntity.MonsterVNum is MonsterVnum.RUBY_PHANTOM or MonsterVnum.EMERALD_PHANTOM or MonsterVnum.SAPPHIRE_PHANTOM; public static bool IsPhantom(this INpcEntity npcEntity) => (MonsterVnum)npcEntity.MonsterVNum is MonsterVnum.RUBY_SHADOW_PHANTOM or MonsterVnum.EMERALD_SHADOW_PHANTOM or MonsterVnum.SAPPHIRE_SHADOW_PHANTOM; public static async Task TryDespawnTimeSpacePortal(this IClientSession session) { INpcEntity timeSpacePortal = session.CurrentMapInstance.GetPassiveNpcs().FirstOrDefault(x => x.TimeSpaceOwnerId.HasValue && x.TimeSpaceOwnerId.Value == session.PlayerEntity.Id); if (timeSpacePortal == null) { return; } string firstEffectPacket = timeSpacePortal.GenerateEffectGround(EffectType.BlueTimeSpace, timeSpacePortal.PositionX, timeSpacePortal.PositionY, true); string secondEffectPacket = timeSpacePortal.GenerateEffectGround(EffectType.BlueRemoveTimeSpace, timeSpacePortal.PositionX, timeSpacePortal.PositionY, false); timeSpacePortal.MapInstance.Broadcast(_ => firstEffectPacket); timeSpacePortal.MapInstance.Broadcast(_ => secondEffectPacket); await timeSpacePortal.EmitEventAsync(new MapLeaveNpcEntityEvent(timeSpacePortal)); } }