From 33091ed942aa0d1b721795437e5a7c80d18d552f Mon Sep 17 00:00:00 2001 From: hondacrx Date: Fri, 13 Oct 2023 16:11:34 -0400 Subject: [PATCH] More updates to scripts (still wont build) --- Source/Game/Chat/Channels/Channel.cs | 2 +- Source/Game/Entities/Object/WorldObject.cs | 1 + Source/Game/Entities/Player/Player.cs | 2 +- Source/Game/Entities/Unit/Unit.cs | 1 + Source/Game/Groups/Group.cs | 2 +- Source/Game/Maps/GridNotifiers.cs | 6 +- Source/Game/Scripting/CoreScripts.cs | 2 +- Source/Game/Scripting/ScriptManager.cs | 6 +- Source/Scripts/Smart/SmartAI.cs | 4 +- Source/Scripts/Spells/Quest.cs | 2 +- .../World/{Achievements.cs => Achievement.cs} | 75 +- Source/Scripts/World/ActionIpLogger.cs | 296 +++ Source/Scripts/World/AreaTrigger.cs | 322 ++- Source/Scripts/World/BoostedXp.cs | 23 +- Source/Scripts/World/ChatLog.cs | 115 + Source/Scripts/World/Conversation.cs | 9 +- Source/Scripts/World/DuelReset.cs | 36 +- Source/Scripts/World/EmeraldDragons.cs | 125 +- Source/Scripts/World/GameObject.cs | 752 +++---- .../Scripts/World/{ItemScripts.cs => Item.cs} | 91 +- Source/Scripts/World/NpcGuard.cs | 91 +- Source/Scripts/World/NpcInnkeeper.cs | 106 +- Source/Scripts/World/NpcProfessions.cs | 1175 ++++++++++ Source/Scripts/World/NpcSpecial.cs | 1910 ---------------- Source/Scripts/World/NpcsSpecial.cs | 1921 +++++++++++++++++ .../World/{SceneScripts.cs => Scene.cs} | 14 +- 26 files changed, 4195 insertions(+), 2894 deletions(-) rename Source/Scripts/World/{Achievements.cs => Achievement.cs} (54%) create mode 100644 Source/Scripts/World/ActionIpLogger.cs create mode 100644 Source/Scripts/World/ChatLog.cs rename Source/Scripts/World/{ItemScripts.cs => Item.cs} (69%) create mode 100644 Source/Scripts/World/NpcProfessions.cs delete mode 100644 Source/Scripts/World/NpcSpecial.cs create mode 100644 Source/Scripts/World/NpcsSpecial.cs rename Source/Scripts/World/{SceneScripts.cs => Scene.cs} (74%) diff --git a/Source/Game/Chat/Channels/Channel.cs b/Source/Game/Chat/Channels/Channel.cs index baf98fb63..3183f8d35 100644 --- a/Source/Game/Chat/Channels/Channel.cs +++ b/Source/Game/Chat/Channels/Channel.cs @@ -847,7 +847,7 @@ namespace Game.Chat public uint GetNumPlayers() { return (uint)_playersStore.Count; } public ChannelFlags GetFlags() { return _channelFlags; } - bool HasFlag(ChannelFlags flag) { return _channelFlags.HasAnyFlag(flag); } + public bool HasFlag(ChannelFlags flag) { return _channelFlags.HasAnyFlag(flag); } public AreaTableRecord GetZoneEntry() { return _zoneEntry; } diff --git a/Source/Game/Entities/Object/WorldObject.cs b/Source/Game/Entities/Object/WorldObject.cs index 012577b58..2fda4988e 100644 --- a/Source/Game/Entities/Object/WorldObject.cs +++ b/Source/Game/Entities/Object/WorldObject.cs @@ -3126,6 +3126,7 @@ namespace Game.Entities public virtual uint GetFaction() { return 0; } public virtual void SetFaction(uint faction) { } + public virtual void SetFaction(FactionTemplates faction) { } //Position diff --git a/Source/Game/Entities/Player/Player.cs b/Source/Game/Entities/Player/Player.cs index b9992f4d6..2214de536 100644 --- a/Source/Game/Entities/Player/Player.cs +++ b/Source/Game/Entities/Player/Player.cs @@ -6669,7 +6669,7 @@ namespace Game.Entities uint level = GetLevel(); - ScriptMgr.OnGivePlayerXP(this, xp, victim); + ScriptMgr.OnGivePlayerXP(this, ref xp, victim); // XP to money conversion processed in Player.RewardQuest if (IsMaxLevel()) diff --git a/Source/Game/Entities/Unit/Unit.cs b/Source/Game/Entities/Unit/Unit.cs index f76475d88..b2c205259 100644 --- a/Source/Game/Entities/Unit/Unit.cs +++ b/Source/Game/Entities/Unit/Unit.cs @@ -2263,6 +2263,7 @@ namespace Game.Entities public override uint GetFaction() { return m_unitData.FactionTemplate; } public override void SetFaction(uint faction) { SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.FactionTemplate), faction); } + public override void SetFaction(FactionTemplates faction) { SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.FactionTemplate), (uint)faction); } public void RestoreFaction() { diff --git a/Source/Game/Groups/Group.cs b/Source/Game/Groups/Group.cs index 73a627f6b..b6553ac13 100644 --- a/Source/Game/Groups/Group.cs +++ b/Source/Game/Groups/Group.cs @@ -1620,7 +1620,7 @@ namespace Game.Groups return m_guid.GetCounter(); } - string GetLeaderName() + public string GetLeaderName() { return m_leaderName; } diff --git a/Source/Game/Maps/GridNotifiers.cs b/Source/Game/Maps/GridNotifiers.cs index b8533c2f3..8b208b7b0 100644 --- a/Source/Game/Maps/GridNotifiers.cs +++ b/Source/Game/Maps/GridNotifiers.cs @@ -2793,7 +2793,7 @@ namespace Game.Maps bool _reverse; } - public class UnitAuraCheck : ICheck where T : WorldObject + public class UnitAuraCheck : ICheck { public UnitAuraCheck(bool present, uint spellId, ObjectGuid casterGUID = default) { @@ -2802,12 +2802,12 @@ namespace Game.Maps _casterGUID = casterGUID; } - public bool Invoke(T obj) + public bool Invoke(WorldObject obj) { return obj.ToUnit() != null && obj.ToUnit().HasAura(_spellId, _casterGUID) == _present; } - public static implicit operator Predicate(UnitAuraCheck unit) + public static implicit operator Predicate(UnitAuraCheck unit) { return unit.Invoke; } diff --git a/Source/Game/Scripting/CoreScripts.cs b/Source/Game/Scripting/CoreScripts.cs index 5d67c2847..c2b578420 100644 --- a/Source/Game/Scripting/CoreScripts.cs +++ b/Source/Game/Scripting/CoreScripts.cs @@ -619,7 +619,7 @@ namespace Game.Scripting public virtual void OnMoneyChanged(Player player, long amount) { } // Called when a player gains XP (before anything is given) - public virtual void OnGiveXP(Player player, uint amount, Unit victim) { } + public virtual uint OnGiveXP(Player player, uint amount, Unit victim) { return 0; } // Called when a player's reputation changes (before it is actually changed) public virtual void OnReputationChange(Player player, uint factionId, int standing, bool incremental) { } diff --git a/Source/Game/Scripting/ScriptManager.cs b/Source/Game/Scripting/ScriptManager.cs index 8055c4985..017045ae6 100644 --- a/Source/Game/Scripting/ScriptManager.cs +++ b/Source/Game/Scripting/ScriptManager.cs @@ -801,9 +801,11 @@ namespace Game.Scripting { ForEach(p => p.OnMoneyChanged(player, amount)); } - public void OnGivePlayerXP(Player player, uint amount, Unit victim) + public void OnGivePlayerXP(Player player, ref uint amount, Unit victim) { - ForEach(p => p.OnGiveXP(player, amount, victim)); + uint tempAmount = amount; + ForEach(p => tempAmount = p.OnGiveXP(player, tempAmount, victim)); + amount = tempAmount; } public void OnPlayerReputationChange(Player player, uint factionID, int standing, bool incremental) { diff --git a/Source/Scripts/Smart/SmartAI.cs b/Source/Scripts/Smart/SmartAI.cs index 58e9af505..3564d020a 100644 --- a/Source/Scripts/Smart/SmartAI.cs +++ b/Source/Scripts/Smart/SmartAI.cs @@ -4,9 +4,9 @@ using Framework.Constants; using Game; using Game.AI; +using Game.DataStorage; using Game.Entities; using Game.Scripting; -using Game.DataStorage; namespace Scripts.Smart { @@ -126,7 +126,7 @@ namespace Scripts.Smart Log.outDebug(LogFilter.ScriptsAi, $"Event {eventId} is using SmartEventTrigger script"); SmartScript script = new(); // Set invoker as BaseObject if there isn't target for GameEvents::Trigger - script.OnInitialize(obj?? invoker, null, null, null, eventId); + script.OnInitialize(obj ?? invoker, null, null, null, eventId); script.ProcessEventsFor(SmartEvents.SendEventTrigger, invoker.ToUnit(), 0, 0, false, null, invoker.ToGameObject()); } } diff --git a/Source/Scripts/Spells/Quest.cs b/Source/Scripts/Spells/Quest.cs index caab61b85..063a6f62e 100644 --- a/Source/Scripts/Spells/Quest.cs +++ b/Source/Scripts/Spells/Quest.cs @@ -1390,7 +1390,7 @@ namespace Scripts.Spells.Azerite } } - // 13790 13793 13811 13814 - Among the ChaMathF.PIons + // 13790 13793 13811 13814 - Among the Chapions [Script] // 13665 13745 13750 13756 13761 13767 13772 13777 13782 13787 - The Grand Melee class spell_q13665_q13790_bested_trigger : SpellScript { diff --git a/Source/Scripts/World/Achievements.cs b/Source/Scripts/World/Achievement.cs similarity index 54% rename from Source/Scripts/World/Achievements.cs rename to Source/Scripts/World/Achievement.cs index fca7c838b..eba927035 100644 --- a/Source/Scripts/World/Achievements.cs +++ b/Source/Scripts/World/Achievement.cs @@ -2,47 +2,18 @@ // Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information. using Framework.Constants; -using Game.BattleGrounds; -using Game.BattleGrounds.Zones; +using Game.DataStorage; using Game.Entities; using Game.Scripting; -using Game.DataStorage; namespace Scripts.World.Achievements { - struct AreaIds - { - //Tilted - public const uint AreaArgentTournamentFields = 4658; - public const uint AreaRingOfAspirants = 4670; - public const uint AreaRingOfArgentValiants = 4671; - public const uint AreaRingOfAllianceValiants = 4672; - public const uint AreaRingOfHordeValiants = 4673; - public const uint AreaRingOfChampions = 4669; - } - - struct AuraIds - { - //Flirt With Disaster - public const uint AuraPerfumeForever = 70235; - public const uint AuraPerfumeEnchantress = 70234; - public const uint AuraPerfumeVictory = 70233; - } - - struct VehicleIds - { - //BgSA Artillery - public const uint AntiPersonnalCannon = 27894; - } - - [Script("achievement_arena_2v2_kills", ArenaTypes.Team2v2)] - [Script("achievement_arena_3v3_kills", ArenaTypes.Team3v3)] - [Script("achievement_arena_5v5_kills", ArenaTypes.Team5v5)] + [Script] class achievement_arena_kills : AchievementCriteriaScript { - ArenaTypes _arenaType; + byte _arenaType; - public achievement_arena_kills(string name, ArenaTypes arenaType) : base(name) + public achievement_arena_kills(string name, byte arenaType) : base(name) { _arenaType = arenaType; } @@ -53,26 +24,33 @@ namespace Scripts.World.Achievements if (!source.InArena()) return false; - return source.GetBattleground().GetArenaType() == _arenaType; + return source.GetBattleground().GetArenaType() == (ArenaTypes)_arenaType; } } [Script] class achievement_tilted : AchievementCriteriaScript { + const uint AreaArgentTournamentFields = 4658; + const uint AreaRingOfAspirants = 4670; + const uint AreaRingOfArgentValiants = 4671; + const uint AreaRingOfAllianceValiants = 4672; + const uint AreaRingOfHordeValiants = 4673; + const uint AreaRingOfChapions = 4669; + public achievement_tilted() : base("achievement_tilted") { } public override bool OnCheck(Player player, Unit target) { - if (!player) + if (player == null) return false; - bool checkArea = player.GetAreaId() == AreaIds.AreaArgentTournamentFields || - player.GetAreaId() == AreaIds.AreaRingOfAspirants || - player.GetAreaId() == AreaIds.AreaRingOfArgentValiants || - player.GetAreaId() == AreaIds.AreaRingOfAllianceValiants || - player.GetAreaId() == AreaIds.AreaRingOfHordeValiants || - player.GetAreaId() == AreaIds.AreaRingOfChampions; + bool checkArea = player.GetAreaId() == AreaArgentTournamentFields || + player.GetAreaId() == AreaRingOfAspirants || + player.GetAreaId() == AreaRingOfArgentValiants || + player.GetAreaId() == AreaRingOfAllianceValiants || + player.GetAreaId() == AreaRingOfHordeValiants || + player.GetAreaId() == AreaRingOfChapions; return checkArea && player.duel != null && player.duel.IsMounted; } @@ -81,14 +59,18 @@ namespace Scripts.World.Achievements [Script] class achievement_flirt_with_disaster_perf_check : AchievementCriteriaScript { + const uint AuraPerfumeForever = 70235; + const uint AuraPerfumeEnchantress = 70234; + const uint AuraPerfumeVictory = 70233; + public achievement_flirt_with_disaster_perf_check() : base("achievement_flirt_with_disaster_perf_check") { } public override bool OnCheck(Player player, Unit target) { - if (!player) + if (player == null) return false; - if (player.HasAura(AuraIds.AuraPerfumeForever) || player.HasAura(AuraIds.AuraPerfumeEnchantress) || player.HasAura(AuraIds.AuraPerfumeVictory)) + if (player.HasAura(AuraPerfumeForever) || player.HasAura(AuraPerfumeEnchantress) || player.HasAura(AuraPerfumeVictory)) return true; return false; @@ -102,7 +84,7 @@ namespace Scripts.World.Achievements public override bool OnCheck(Player player, Unit target) { - return target && player.IsHonorOrXPTarget(target); + return target != null && player.IsHonorOrXPTarget(target); } } @@ -114,7 +96,7 @@ namespace Scripts.World.Achievements public override void OnCompleted(Player player, AchievementRecord achievement) { player.GetSession().GetBattlePetMgr().UnlockSlot(BattlePetSlots.Slot1); - // TODO: Unlock trap + // Todo: Unlock trap } } @@ -128,4 +110,5 @@ namespace Scripts.World.Achievements player.GetSession().GetBattlePetMgr().UnlockSlot(BattlePetSlots.Slot2); } } -} \ No newline at end of file +} + diff --git a/Source/Scripts/World/ActionIpLogger.cs b/Source/Scripts/World/ActionIpLogger.cs new file mode 100644 index 000000000..7b8d16a9e --- /dev/null +++ b/Source/Scripts/World/ActionIpLogger.cs @@ -0,0 +1,296 @@ +// Copyright (c) CypherCore All rights reserved. +// Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information. + +using Framework.Configuration; +using Framework.Constants; +using Framework.Database; +using Framework.Networking; +using Game.Entities; +using System.Collections.Generic; +using Game.AI; +using Game.Scripting; +using Game.Spells; + +namespace Scripts.World.Achievements +{ + enum IPLoggingTypes + { + // AccountActionIpLogger(); + AccountLogin = 0, + AccountFailLogin = 1, + AccountChangePw = 2, + AccountChangePwFail = 3, // Only two types of account changes exist... + AccountChangeEmail = 4, + AccountChangeEmailFail = 5, // ...so we log them individually + // Obsolete - AccountLogout = 6, Can not be logged. We still keep the type however + // CharacterActionIpLogger(); + CharacterCreate = 7, + CharacterLogin = 8, + CharacterLogout = 9, + // CharacterDeleteActionIpLogger(); + CharacterDelete = 10, + CharacterFailedDelete = 11, + // AccountActionIpLogger(), CharacterActionIpLogger(), CharacterActionIpLogger(); + UnknownAction = 12 + } + + class AccountActionIpLogger : AccountScript + { + public AccountActionIpLogger() : base("AccountActionIpLogger") { } + + // We log last_ip instead of last_attempt_ip, as login was successful + // AccountLogin = 0 + void OnAccountLogin(uint accountId) + { + AccountIPLogAction(accountId, AccountLogin); + } + + // We log last_attempt_ip instead of last_ip, as failed login doesn't necessarily mean approperiate user + // AccountFailLogin = 1 + void OnFailedAccountLogin(uint accountId) + { + AccountIPLogAction(accountId, AccountFailLogin); + } + + // AccountChangePw = 2 + void OnPasswordChange(uint accountId) + { + AccountIPLogAction(accountId, AccountChangePw); + } + + // AccountChangePwFail = 3 + void OnFailedPasswordChange(uint accountId) + { + AccountIPLogAction(accountId, AccountChangePwFail); + } + + // Registration Email can Not be changed apart from Gm level users. Thus, we do not require to log them... + // AccountChangeEmail = 4 + void OnEmailChange(uint accountId) + { + AccountIPLogAction(accountId, AccountChangeEmail); // ... they get logged by gm command logger anyway + } + + // AccountChangeEmailFail = 5 + void OnFailedEmailChange(uint accountId) + { + AccountIPLogAction(accountId, AccountChangeEmailFail); + } + + // AccountLogout = 6 + void AccountIPLogAction(uint accountId, IPLoggingTypes aType) + { + // Action Ip Logger is only intialized if config is set up + // Else, this script isn't loaded in the first place: We require no config check. + + // We declare all the required variables + uint playerGuid = accountId; + uint realmId = realm.Id.Realm; + std.string systemNote = "Error"; // "Error" is a placeholder here. We change it later. + + // With this switch, we change systemNote so that we have a more accurate phraMath.Sing of what type it is. + // Avoids Magicnumbers in Sql table + switch (aType) + { + case AccountLogin: + systemNote = "Logged into WoW"; + break; + case AccountFailLogin: + systemNote = "Login to WoW Failed"; + break; + case AccountChangePw: + systemNote = "Password Reset Completed"; + break; + case AccountChangePwFail: + systemNote = "Password Reset Failed"; + break; + case AccountChangeEmail: + systemNote = "Email Change Completed"; + break; + case AccountChangeEmailFail: + systemNote = "Email Change Failed"; + break; + case AccountLogout: + systemNote = "Logged on AccountLogout"; //Can not be logged + break; + // Neither should happen. Ever. Period. If it does, call Ghostbusters and all your local software defences to investigate. + case UnknownAction: + default: + systemNote = "Error! Unknown action!"; + break; + } + + // Once we have done everything, we can Add the new log. + // Seeing as the time differences should be minimal, we do not get unixtime and the timestamp right now; + // Rather, we let it be added with the Sql query. + if (aType != AccountFailLogin) + { + // As we can assume most account actions are Not failed login, so this is the more accurate check. + // For those, we need last_ip... + + stmt.setUint(0, playerGuid); + stmt.setUInt64(1, 0); + stmt.setUint(2, realmId); + stmt.setUInt8(3, aType); + stmt.setUint(4, playerGuid); + stmt.setString(5, systemNote); + LoginDatabase.Execute(stmt); + } + else // ... but for failed login, we query last_attempt_ip from account table. Which we do with an unique query + { + + stmt.setUint(0, playerGuid); + stmt.setUInt64(1, 0); + stmt.setUint(2, realmId); + stmt.setUInt8(3, aType); + stmt.setUint(4, playerGuid); + stmt.setString(5, systemNote); + LoginDatabase.Execute(stmt); + } + return; + } + } + + class CharacterActionIpLogger : PlayerScript + { + public CharacterActionIpLogger() : base("CharacterActionIpLogger") { } + + // CharacterCreate = 7 + void OnCreate(Player player) + { + CharacterIPLogAction(player, CharacterCreate); + } + + // CharacterLogin = 8 + void OnLogin(Player player, bool firstLogin) + { + CharacterIPLogAction(player, CharacterLogin); + } + + // CharacterLogout = 9 + void OnLogout(Player player) + { + CharacterIPLogAction(player, CharacterLogout); + } + + // CharacterDelete = 10 + // CharacterFailedDelete = 11 + // We don't log either here - they require a guid + + // UnknownAction = 12 + // There is no real hook we could use for that. + // Shouldn't happen anyway, should it ? Nothing to see here. + + /// Logs a number of actions done by players with an Ip + void CharacterIPLogAction(Player player, IPLoggingTypes aType) + { + // Action Ip Logger is only intialized if config is set up + // Else, this script isn't loaded in the first place: We require no config check. + + // We declare all the required variables + uint playerGuid = player.GetSession().GetAccountId(); + uint realmId = realm.Id.Realm; + std.string currentIp = player.GetSession().GetRemoteAddress(); + std.string systemNote = "Error"; // "Error" is a placeholder here. We change it... + + // ... with this switch, so that we have a more accurate phraMath.Sing of what type it is + switch (aType) + { + case CharacterCreate: + systemNote = "Character Created"; + break; + case CharacterLogin: + systemNote = "Logged onto Character"; + break; + case CharacterLogout: + systemNote = "Logged out of Character"; + break; + case CharacterDelete: + systemNote = "Character Deleted"; + break; + case CharacterFailedDelete: + systemNote = "Character Deletion Failed"; + break; + // Neither should happen. Ever. Period. If it does, call Mythbusters. + case UnknownAction: + default: + systemNote = "Error! Unknown action!"; + break; + } + + // Once we have done everything, we can Add the new log. + + stmt.setUint(0, playerGuid); + stmt.setUInt64(1, player.GetGUID().GetCounter()); + stmt.setUint(2, realmId); + stmt.setUInt8(3, aType); + stmt.setString(4, currentIp); // We query the ip here. + stmt.setString(5, systemNote); + // Seeing as the time differences should be minimal, we do not get unixtime and the timestamp right now; + // Rather, we let it be added with the Sql query. + + LoginDatabase.Execute(stmt); + return; + } + } + + class CharacterDeleteActionIpLogger : PlayerScript + { + public CharacterDeleteActionIpLogger() : base("CharacterDeleteActionIpLogger") { } + + // CharacterDelete = 10 + void OnDelete(ObjectGuid guid, uint accountId) + { + DeleteIPLogAction(guid, accountId, CharacterDelete); + } + + // CharacterFailedDelete = 11 + void OnFailedDelete(ObjectGuid guid, uint accountId) + { + DeleteIPLogAction(guid, accountId, CharacterFailedDelete); + } + + void DeleteIPLogAction(ObjectGuid guid, uint playerGuid, IPLoggingTypes aType) + { + // Action Ip Logger is only intialized if config is set up + // Else, this script isn't loaded in the first place: We require no config check. + + uint realmId = realm.Id.Realm; + // Query playerGuid/accountId, as we only have characterGuid + std.string systemNote = "Error"; // "Error" is a placeholder here. We change it later. + + // With this switch, we change systemNote so that we have a more accurate phraMath.Sing of what type it is. + // Avoids Magicnumbers in Sql table + switch (aType) + { + case CharacterDelete: + systemNote = "Character Deleted"; + break; + case CharacterFailedDelete: + systemNote = "Character Deletion Failed"; + break; + // Neither should happen. Ever. Period. If it does, call to whatever god you have for mercy and guidance. + case UnknownAction: + default: + systemNote = "Error! Unknown action!"; + break; + } + + // Once we have done everything, we can Add the new log. + + stmt.setUint(0, playerGuid); + stmt.setUInt64(1, guid.GetCounter()); + stmt.setUint(2, realmId); + stmt.setUInt8(3, aType); + stmt.setUint(4, playerGuid); + stmt.setString(5, systemNote); + + // Seeing as the time differences should be minimal, we do not get unixtime and the timestamp right now; + // Rather, we let it be added with the Sql query. + + LoginDatabase.Execute(stmt); + return; + } + } +} + diff --git a/Source/Scripts/World/AreaTrigger.cs b/Source/Scripts/World/AreaTrigger.cs index 04704e743..d54df09e6 100644 --- a/Source/Scripts/World/AreaTrigger.cs +++ b/Source/Scripts/World/AreaTrigger.cs @@ -12,128 +12,17 @@ using System.Collections.Generic; namespace Scripts.World.Areatriggers { - struct TextIds - { - //Brewfest - public const uint SayWelcome = 4; - } - - struct SpellIds - { - //Legion Teleporter - public const uint TeleATo = 37387; - public const uint TeleHTo = 37389; - - //Sholazar Waygate - public const uint SholazarToUngoroTeleport = 52056; - public const uint UngoroToSholazarTeleport = 52057; - - //Nats Landing - public const uint FishPaste = 42644; - - //Area 52 - public const uint A52Neuralyzer = 34400; - - //Stormwind teleport - public const uint DustInTheStormwind = 312593; - } - - struct QuestIds - { - //Legion Teleporter - public const uint GainingAccessA = 10589; - public const uint GainingAccessH = 10604; - - //Scent Larkorwi - public const uint ScentOfLarkorwi = 4291; - - //Last Rites - public const uint LastRites = 12019; - public const uint BreakingThrough = 11898; - - //Sholazar Waygate - public const uint TheMakersOverlook = 12613; - public const uint TheMakersPerch = 12559; - public const uint MeetingAGreatOne = 13956; - - //Nats Landing - public const uint NatsBargain = 11209; - - //Frostgrips Hollow - public const uint TheLonesomeWatcher = 12877; - } - - struct CreatureIds - { - //Scent Larkorwi - public const uint LarkorwiMate = 9683; - - //Nats Landing - public const uint LurkingShark = 23928; - - //Brewfest - public const uint TapperSwindlekeg = 24711; - public const uint IpfelkoferIronkeg = 24710; - - //Area 52 - public const uint Spotlight = 19913; - - //Frostgrips Hollow - public const uint StormforgedMonitor = 29862; - public const uint StormforgedEradictor = 29861; - - //Stormwind Teleport - public const uint KillCreditTeleportStormwind = 160561; - } - - struct GameObjectIds - { - //Coilfang Waterfall - public const uint CoilfangWaterfall = 184212; - } - - struct AreaTriggerIds - { - //Sholazar Waygate - public const uint Sholazar = 5046; - public const uint Ungoro = 5047; - - //Brewfest - public const uint BrewfestDurotar = 4829; - public const uint BrewfestDunMorogh = 4820; - - //Area 52 - public const uint Area52South = 4472; - public const uint Area52North = 4466; - public const uint Area52West = 4471; - public const uint Area52East = 4422; - } - - struct Misc - { - //Brewfest - public const uint AreatriggerTalkCooldown = 5; // In Seconds - - //Area 52 - public const uint SummonCooldown = 5; - - //Frostgrips Hollow - public const uint TypeWaypoint = 0; - public const uint DataStart = 0; - - public static Position StormforgedMonitorPosition = new(6963.95f, 45.65f, 818.71f, 4.948f); - public static Position StormforgedEradictorPosition = new(6983.18f, 7.15f, 806.33f, 2.228f); - } - [Script] class AreaTrigger_at_coilfang_waterfall : AreaTriggerScript { + const uint GoCoilfangWaterfall = 184212; + public AreaTrigger_at_coilfang_waterfall() : base("at_coilfang_waterfall") { } public override bool OnTrigger(Player player, AreaTriggerRecord areaTrigger) { - GameObject go = player.FindNearestGameObject(GameObjectIds.CoilfangWaterfall, 35.0f); - if (go) + GameObject go = player.FindNearestGameObject(GoCoilfangWaterfall, 35.0f); + if (go != null) if (go.GetLootState() == LootState.Ready) go.UseDoorOrButton(); @@ -144,21 +33,27 @@ namespace Scripts.World.Areatriggers [Script] class AreaTrigger_at_legion_teleporter : AreaTriggerScript { + const uint SpellTeleATo = 37387; + const uint QuestGainingAccessA = 10589; + + const uint SpellTeleHTo = 37389; + const uint QuestGainingAccessH = 10604; + public AreaTrigger_at_legion_teleporter() : base("at_legion_teleporter") { } public override bool OnTrigger(Player player, AreaTriggerRecord areaTrigger) { if (player.IsAlive() && !player.IsInCombat()) { - if (player.GetTeam() == Team.Alliance && player.GetQuestRewardStatus(QuestIds.GainingAccessA)) + if (player.GetTeam() == Team.Alliance && player.GetQuestRewardStatus(QuestGainingAccessA)) { - player.CastSpell(player, SpellIds.TeleATo, false); + player.CastSpell(player, SpellTeleATo, false); return true; } - if (player.GetTeam() == Team.Horde && player.GetQuestRewardStatus(QuestIds.GainingAccessH)) + if (player.GetTeam() == Team.Horde && player.GetQuestRewardStatus(QuestGainingAccessH)) { - player.CastSpell(player, SpellIds.TeleHTo, false); + player.CastSpell(player, SpellTeleHTo, false); return true; } @@ -171,14 +66,17 @@ namespace Scripts.World.Areatriggers [Script] class AreaTrigger_at_scent_larkorwi : AreaTriggerScript { + const uint QuestScentOfLarkorwi = 4291; + const uint NpcLarkorwiMate = 9683; + public AreaTrigger_at_scent_larkorwi() : base("at_scent_larkorwi") { } public override bool OnTrigger(Player player, AreaTriggerRecord areaTrigger) { - if (!player.IsDead() && player.GetQuestStatus(QuestIds.ScentOfLarkorwi) == QuestStatus.Incomplete) + if (!player.IsDead() && player.GetQuestStatus(QuestScentOfLarkorwi) == QuestStatus.Incomplete) { - if (!player.FindNearestCreature(CreatureIds.LarkorwiMate, 15)) - player.SummonCreature(CreatureIds.LarkorwiMate, player.GetPositionX() + 5, player.GetPositionY(), player.GetPositionZ(), 3.3f, TempSummonType.TimedDespawnOutOfCombat, TimeSpan.FromSeconds(100)); + if (player.FindNearestCreature(NpcLarkorwiMate, 15) == null) + player.SummonCreature(NpcLarkorwiMate, player.GetPositionX() + 5, player.GetPositionY(), player.GetPositionZ(), 3.3f, TempSummonType.TimedDespawnOutOfCombat, TimeSpan.FromSeconds(100)); } return false; @@ -188,21 +86,31 @@ namespace Scripts.World.Areatriggers [Script] class AreaTrigger_at_sholazar_waygate : AreaTriggerScript { + const uint SpellSholazarToUngoroTeleport = 52056; + const uint SpellUngoroToSholazarTeleport = 52057; + + const uint AtSholazar = 5046; + const uint AtUngoro = 5047; + + const uint QuestTheMakersOverlook = 12613; + const uint QuestTheMakersPerch = 12559; + const uint QuestMeetingAGreatOne = 13956; + public AreaTrigger_at_sholazar_waygate() : base("at_sholazar_waygate") { } - public override bool OnTrigger(Player player, AreaTriggerRecord areaTrigger) + public override bool OnTrigger(Player player, AreaTriggerRecord trigger) { - if (!player.IsDead() && (player.GetQuestStatus(QuestIds.MeetingAGreatOne) != QuestStatus.None || - (player.GetQuestStatus(QuestIds.TheMakersOverlook) == QuestStatus.Rewarded && player.GetQuestStatus(QuestIds.TheMakersPerch) == QuestStatus.Rewarded))) + if (!player.IsDead() && (player.GetQuestStatus(QuestMeetingAGreatOne) != QuestStatus.None || + (player.GetQuestStatus(QuestTheMakersOverlook) == QuestStatus.Rewarded && player.GetQuestStatus(QuestTheMakersPerch) == QuestStatus.Rewarded))) { - switch (areaTrigger.Id) + switch (trigger.Id) { - case AreaTriggerIds.Sholazar: - player.CastSpell(player, SpellIds.SholazarToUngoroTeleport, true); + case AtSholazar: + player.CastSpell(player, SpellSholazarToUngoroTeleport, true); break; - case AreaTriggerIds.Ungoro: - player.CastSpell(player, SpellIds.UngoroToSholazarTeleport, true); + case AtUngoro: + player.CastSpell(player, SpellUngoroToSholazarTeleport, true); break; } } @@ -214,19 +122,23 @@ namespace Scripts.World.Areatriggers [Script] class AreaTrigger_at_nats_landing : AreaTriggerScript { + const uint QuestNatsBargain = 11209; + const uint SpellFishPaste = 42644; + const uint NpcLurkingShark = 23928; + public AreaTrigger_at_nats_landing() : base("at_nats_landing") { } public override bool OnTrigger(Player player, AreaTriggerRecord areaTrigger) { - if (!player.IsAlive() || !player.HasAura(SpellIds.FishPaste)) + if (!player.IsAlive() || !player.HasAura(SpellFishPaste)) return false; - if (player.GetQuestStatus(QuestIds.NatsBargain) == QuestStatus.Incomplete) + if (player.GetQuestStatus(QuestNatsBargain) == QuestStatus.Incomplete) { - if (!player.FindNearestCreature(CreatureIds.LurkingShark, 20.0f)) + if (player.FindNearestCreature(NpcLurkingShark, 20.0f) == null) { - Creature shark = player.SummonCreature(CreatureIds.LurkingShark, -4246.243f, -3922.356f, -7.488f, 5.0f, TempSummonType.TimedDespawnOutOfCombat, TimeSpan.FromSeconds(100)); - if (shark) + Creature shark = player.SummonCreature(NpcLurkingShark, -4246.243f, -3922.356f, -7.488f, 5.0f, TempSummonType.TimedDespawnOutOfCombat, TimeSpan.FromSeconds(100)); + if (shark != null) shark.GetAI().AttackStart(player); return false; @@ -239,36 +151,42 @@ namespace Scripts.World.Areatriggers [Script] class AreaTrigger_at_brewfest : AreaTriggerScript { - Dictionary _triggerTimes; + const uint NpcTapperSwindlekeg = 24711; + const uint NpcIpfelkoferIronkeg = 24710; + + const uint AtBrewfestDurotar = 4829; + const uint AtBrewfestDunMorogh = 4820; + + const uint SayWelcome = 4; + + const uint AreatriggerTalkCooldown = 5; // in seconds + + Dictionary _triggerTimes = new(); public AreaTrigger_at_brewfest() : base("at_brewfest") { // Initialize for cooldown - _triggerTimes = new Dictionary() - { - { AreaTriggerIds.BrewfestDurotar, 0 }, - { AreaTriggerIds.BrewfestDunMorogh,0 }, - }; + _triggerTimes[AtBrewfestDurotar] = _triggerTimes[AtBrewfestDunMorogh] = 0; } - public override bool OnTrigger(Player player, AreaTriggerRecord areaTrigger) + public override bool OnTrigger(Player player, AreaTriggerRecord trigger) { - uint triggerId = areaTrigger.Id; + uint triggerId = trigger.Id; // Second trigger happened too early after first, skip for now - if (GameTime.GetGameTime() - _triggerTimes[triggerId] < Misc.AreatriggerTalkCooldown) + if (GameTime.GetGameTime() - _triggerTimes[triggerId] < AreatriggerTalkCooldown) return false; switch (triggerId) { - case AreaTriggerIds.BrewfestDurotar: - Creature tapper = player.FindNearestCreature(CreatureIds.TapperSwindlekeg, 20.0f); - if (tapper) - tapper.GetAI().Talk(TextIds.SayWelcome, player); + case AtBrewfestDurotar: + Creature tapper = player.FindNearestCreature(NpcTapperSwindlekeg, 20.0f); + if (tapper != null) + tapper.GetAI().Talk(SayWelcome, player); break; - case AreaTriggerIds.BrewfestDunMorogh: - Creature ipfelkofer = player.FindNearestCreature(CreatureIds.IpfelkoferIronkeg, 20.0f); - if (ipfelkofer) - ipfelkofer.GetAI().Talk(TextIds.SayWelcome, player); + case AtBrewfestDunMorogh: + Creature ipfelkofer = player.FindNearestCreature(NpcIpfelkoferIronkeg, 20.0f); + if (ipfelkofer != null) + ipfelkofer.GetAI().Talk(SayWelcome, player); break; default: break; @@ -282,63 +200,74 @@ namespace Scripts.World.Areatriggers [Script] class AreaTrigger_at_area_52_entrance : AreaTriggerScript { - Dictionary _triggerTimes; + const uint SpellA52Neuralyzer = 34400; + const uint NpcSpotlight = 19913; + const uint SummonCooldown = 5; + + const uint AtArea52South = 4472; + const uint AtArea52North = 4466; + const uint AtArea52West = 4471; + const uint AtArea52East = 4422; + + Dictionary _triggerTimes = new(); public AreaTrigger_at_area_52_entrance() : base("at_area_52_entrance") { - _triggerTimes = new Dictionary() - { - { AreaTriggerIds.Area52South, 0 }, - { AreaTriggerIds.Area52North,0 }, - { AreaTriggerIds.Area52West,0}, - { AreaTriggerIds.Area52East,0}, - }; + _triggerTimes[AtArea52South] = _triggerTimes[AtArea52North] = _triggerTimes[AtArea52West] = _triggerTimes[AtArea52East] = 0; } - public override bool OnTrigger(Player player, AreaTriggerRecord areaTrigger) + public override bool OnTrigger(Player player, AreaTriggerRecord trigger) { float x = 0.0f, y = 0.0f, z = 0.0f; if (!player.IsAlive()) return false; - if (GameTime.GetGameTime() - _triggerTimes[areaTrigger.Id] < Misc.SummonCooldown) + uint triggerId = trigger.Id; + if (GameTime.GetGameTime() - _triggerTimes[triggerId] < SummonCooldown) return false; - switch (areaTrigger.Id) + switch (triggerId) { - case AreaTriggerIds.Area52East: + case AtArea52East: x = 3044.176f; y = 3610.692f; z = 143.61f; break; - case AreaTriggerIds.Area52North: + case AtArea52North: x = 3114.87f; y = 3687.619f; z = 143.62f; break; - case AreaTriggerIds.Area52West: + case AtArea52West: x = 3017.79f; y = 3746.806f; z = 144.27f; break; - case AreaTriggerIds.Area52South: + case AtArea52South: x = 2950.63f; y = 3719.905f; z = 143.33f; break; } - player.SummonCreature(CreatureIds.Spotlight, x, y, z, 0.0f, TempSummonType.TimedDespawn, TimeSpan.FromSeconds(5)); - player.AddAura(SpellIds.A52Neuralyzer, player); - _triggerTimes[areaTrigger.Id] = GameTime.GetGameTime(); + player.SummonCreature(NpcSpotlight, x, y, z, 0.0f, TempSummonType.TimedDespawn, TimeSpan.FromSeconds(5)); + player.AddAura(SpellA52Neuralyzer, player); + _triggerTimes[trigger.Id] = GameTime.GetGameTime(); return false; } } - [Script] class AreaTrigger_at_frostgrips_hollow : AreaTriggerScript { + const uint QuestTheLonesomeWatcher = 12877; + + const uint NpcStormforgedMonitor = 29862; + const uint NpcStormforgedEradictor = 29861; + + Position stormforgedMonitorPosition = new(6963.95f, 45.65f, 818.71f, 4.948f); + Position stormforgedEradictorPosition = new(6983.18f, 7.15f, 806.33f, 2.228f); + ObjectGuid stormforgedMonitorGUID; ObjectGuid stormforgedEradictorGUID; @@ -350,41 +279,43 @@ namespace Scripts.World.Areatriggers public override bool OnTrigger(Player player, AreaTriggerRecord areaTrigger) { - if (player.GetQuestStatus(QuestIds.TheLonesomeWatcher) != QuestStatus.Incomplete) + if (player.GetQuestStatus(QuestTheLonesomeWatcher) != QuestStatus.Incomplete) return false; Creature stormforgedMonitor = ObjectAccessor.GetCreature(player, stormforgedMonitorGUID); - if (stormforgedMonitor) + if (stormforgedMonitor != null) return false; Creature stormforgedEradictor = ObjectAccessor.GetCreature(player, stormforgedEradictorGUID); - if (stormforgedEradictor) + if (stormforgedEradictor != null) return false; - stormforgedMonitor = player.SummonCreature(CreatureIds.StormforgedMonitor, Misc.StormforgedMonitorPosition, TempSummonType.TimedDespawnOutOfCombat, TimeSpan.FromSeconds(60)); - if (stormforgedMonitor) + stormforgedMonitor = player.SummonCreature(NpcStormforgedMonitor, stormforgedMonitorPosition, TempSummonType.TimedDespawnOutOfCombat, TimeSpan.FromMinutes(1)); + if (stormforgedMonitor != null) { stormforgedMonitorGUID = stormforgedMonitor.GetGUID(); stormforgedMonitor.SetWalk(false); /// The npc would search an alternative way to get to the last waypoint without this unit state. stormforgedMonitor.AddUnitState(UnitState.IgnorePathfinding); - stormforgedMonitor.GetMotionMaster().MovePath((CreatureIds.StormforgedMonitor * 100) << 3, false); + stormforgedMonitor.GetMotionMaster().MovePath((NpcStormforgedMonitor * 100) << 3, false); } - stormforgedEradictor = player.SummonCreature(CreatureIds.StormforgedEradictor, Misc.StormforgedEradictorPosition, TempSummonType.TimedDespawnOutOfCombat, TimeSpan.FromSeconds(60)); - if (stormforgedEradictor) + stormforgedEradictor = player.SummonCreature(NpcStormforgedEradictor, stormforgedEradictorPosition, TempSummonType.TimedDespawnOutOfCombat, TimeSpan.FromMinutes(1)); + if (stormforgedEradictor != null) { stormforgedEradictorGUID = stormforgedEradictor.GetGUID(); - stormforgedEradictor.GetMotionMaster().MovePath((CreatureIds.StormforgedEradictor * 100) << 3, false); + stormforgedEradictor.GetMotionMaster().MovePath((NpcStormforgedEradictor * 100) << 3, false); } return true; } } - [Script] class areatrigger_stormwind_teleport_unit : AreaTriggerAI { + const uint SpellDustInTheStormwind = 312593; + const uint NpcKillCreditTeleportStormwind = 160561; + public areatrigger_stormwind_teleport_unit(AreaTrigger areatrigger) : base(areatrigger) { } public override void OnUnitEnter(Unit unit) @@ -393,12 +324,11 @@ namespace Scripts.World.Areatriggers if (player == null) return; - player.CastSpell(unit, SpellIds.DustInTheStormwind); - player.KilledMonsterCredit(CreatureIds.KillCreditTeleportStormwind); + player.CastSpell(unit, SpellDustInTheStormwind); + player.KilledMonsterCredit(NpcKillCreditTeleportStormwind); } } - [Script] class areatrigger_battleground_buffs : AreaTriggerAI { public areatrigger_battleground_buffs(AreaTrigger areatrigger) : base(areatrigger) { } @@ -408,8 +338,12 @@ namespace Scripts.World.Areatriggers if (!unit.IsPlayer()) return; - var player = unit.ToPlayer(); - GameObject buffObject = player.FindNearestGameObjectWithOptions(4.0f, new FindGameObjectOptions() { StringId = "bg_buff_object" }); + HandleBuffAreaTrigger(unit.ToPlayer()); + } + + void HandleBuffAreaTrigger(Player player) + { + GameObject buffObject = player.FindNearestGameObjectWithOptions(4.0f, new FindGameObjectOptions() { StringId = "bg_buff_obj" }); if (buffObject != null) { buffObject.ActivateObject(GameObjectActions.Disturb, 0, player); @@ -418,24 +352,27 @@ namespace Scripts.World.Areatriggers } } - [Script] class AreaTrigger_at_battleground_buffs : AreaTriggerScript { public AreaTrigger_at_battleground_buffs() : base("at_battleground_buffs") { } public override bool OnTrigger(Player player, AreaTriggerRecord areaTrigger) { - GameObject buffObject = player.FindNearestGameObjectWithOptions(4.0f, new FindGameObjectOptions() { StringId = "bg_buff_object" }); + HandleBuffAreaTrigger(player); + return true; + } + + void HandleBuffAreaTrigger(Player player) + { + GameObject buffObject = player.FindNearestGameObjectWithOptions(4.0f, new FindGameObjectOptions() { StringId = "bg_buff_obj" }); if (buffObject != null) { buffObject.ActivateObject(GameObjectActions.Disturb, 0, player); buffObject.DespawnOrUnsummon(); } - return true; } } - [Script] class areatrigger_action_capture_flag : AreaTriggerAI { public areatrigger_action_capture_flag(AreaTrigger areatrigger) : base(areatrigger) { } @@ -447,8 +384,9 @@ namespace Scripts.World.Areatriggers Player player = unit.ToPlayer(); ZoneScript zoneScript = at.GetZoneScript(); - if (zoneScript != null && zoneScript.CanCaptureFlag(at, player)) - zoneScript.OnCaptureFlag(at, player); + if (zoneScript != null) + if (zoneScript.CanCaptureFlag(at, player)) + zoneScript.OnCaptureFlag(at, player); } } } \ No newline at end of file diff --git a/Source/Scripts/World/BoostedXp.cs b/Source/Scripts/World/BoostedXp.cs index f9018602c..83ded381a 100644 --- a/Source/Scripts/World/BoostedXp.cs +++ b/Source/Scripts/World/BoostedXp.cs @@ -1,31 +1,42 @@ -// Copyright (c) CypherCore All rights reserved. +// Copyright (c) CypherCore All rights reserved. // Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information. +using Framework.Configuration; using Framework.Constants; -using Game; +using Framework.Database; +using Framework.Networking; using Game.Entities; +using System.Collections.Generic; +using Game.AI; using Game.Scripting; +using Game.Spells; +using Game; +using System; -namespace Scripts.World +namespace Scripts.World.Achievements { + [Script] class xp_boost_PlayerScript : PlayerScript { public xp_boost_PlayerScript() : base("xp_boost_PlayerScript") { } - void OnGiveXP(Player player, ref uint amount, Unit unit) + public override uint OnGiveXP(Player player, uint amount, Unit unit) { if (IsXPBoostActive()) amount *= (uint)WorldConfig.GetFloatValue(WorldCfg.RateXpBoost); + + return amount; } bool IsXPBoostActive() { long time = GameTime.GetGameTime(); - var localTm = Time.UnixTimeToDateTime(time); + DateTime localTm = Time.UnixTimeToDateTime(time); uint weekdayMaskBoosted = WorldConfig.GetUIntValue(WorldCfg.XpBoostDaymask); - uint weekdayMask = 1u << localTm.Day; + uint weekdayMask = (1u << (int)localTm.DayOfWeek); bool currentDayBoosted = (weekdayMask & weekdayMaskBoosted) != 0; return currentDayBoosted; } } } + diff --git a/Source/Scripts/World/ChatLog.cs b/Source/Scripts/World/ChatLog.cs new file mode 100644 index 000000000..a5128ac3b --- /dev/null +++ b/Source/Scripts/World/ChatLog.cs @@ -0,0 +1,115 @@ +// Copyright (c) CypherCore All rights reserved. +// Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information. + +using Framework.Constants; +using Game.Chat; +using Game.Entities; +using Game.Groups; +using Game.Guilds; +using Game.Scripting; + +namespace Scripts.World.Achievements +{ + [Script] + class ChatLogScript : PlayerScript + { + public ChatLogScript() : base("ChatLogScript") { } + + public override void OnChat(Player player, ChatMsg type, Language lang, string msg) + { + switch (type) + { + case ChatMsg.Say: + LogChat($"Player {player.GetName()} says (language {lang}): {msg}"); + break; + + case ChatMsg.Emote: + LogChat($"Player {player.GetName()} emotes: {msg}"); + break; + + case ChatMsg.Yell: + LogChat($"Player {player.GetName()} yells (language {lang}): {msg}"); + break; + } + } + + public override void OnChat(Player player, ChatMsg type, Language lang, string msg, Player receiver) + { + LogChat($"Player {player.GetName()} tells {(receiver != null ? receiver.GetName() : "")}: {msg}"); + } + + public override void OnChat(Player player, ChatMsg type, Language lang, string msg, Group group) + { + //! Note: + //! LangAddon can only be sent by client in "Party", "Raid", "Guild", "Battleground", "Whisper" + switch (type) + { + case ChatMsg.Party: + LogChat($"Player {player.GetName()} tells group with leader {(group != null ? group.GetLeaderName() : "")}: {msg}"); + break; + + case ChatMsg.PartyLeader: + LogChat($"Leader {player.GetName()} tells group: {msg}"); + break; + + case ChatMsg.Raid: + LogChat($"Player {player.GetName()} tells raid with leader {(group != null ? group.GetLeaderName() : "")}: {msg}"); + break; + + case ChatMsg.RaidLeader: + LogChat($"Leader player {player.GetName()} tells raid: {msg}"); + break; + + case ChatMsg.RaidWarning: + LogChat($"Leader player {player.GetName()} warns raid with: {msg}"); + break; + + case ChatMsg.InstanceChat: + LogChat($"Player {player.GetName()} tells instance with leader {(group != null ? group.GetLeaderName() : "")}: {msg}"); + break; + + case ChatMsg.InstanceChatLeader: + LogChat($"Leader player {player.GetName()} tells instance: {msg}"); + break; + } + } + + public override void OnChat(Player player, ChatMsg type, Language lang, string msg, Guild guild) + { + switch (type) + { + case ChatMsg.Guild: + LogChat($"Player {player.GetName()} tells guild {(guild != null ? guild.GetName() : "")}: {msg}"); + break; + + case ChatMsg.Officer: + LogChat($"Player {player.GetName()} tells guild {(guild != null ? guild.GetName() : "")} officers: {msg}"); + break; + } + } + + public override void OnChat(Player player, ChatMsg type, Language lang, string msg, Channel channel) + { + bool isSystem = channel != null && + (channel.HasFlag(ChannelFlags.Trade) || + channel.HasFlag(ChannelFlags.General) || + channel.HasFlag(ChannelFlags.City) || + channel.HasFlag(ChannelFlags.Lfg)); + + if (isSystem) + { + LogChat($"Player {player.GetName()} tells channel {channel.GetName()}: {msg}"); + } + else + { + string channelName = channel != null ? channel.GetName() : ""; + LogChat($"Player {player.GetName()} tells channel {channelName}: {msg}"); + } + } + + void LogChat(string msg) + { + Log.outDebug(LogFilter.ChatLog, msg); + } + } +} \ No newline at end of file diff --git a/Source/Scripts/World/Conversation.cs b/Source/Scripts/World/Conversation.cs index b24738ae0..939eea168 100644 --- a/Source/Scripts/World/Conversation.cs +++ b/Source/Scripts/World/Conversation.cs @@ -1,17 +1,16 @@ -// Copyright (c) CypherCore All rights reserved. +// Copyright (c) CypherCore All rights reserved. // Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information. -using Framework.Constants; using Game.Entities; using Game.Scripting; -namespace Scripts.World +namespace Scripts.World.Conversations { - [Script] class conversation_allied_race_dk_defender_of_azeroth : ConversationScript { const uint NpcTalkToYourCommanderCredit = 161709; const uint NpcListenToYourCommanderCredit = 163027; + const uint ConversationLinePlayer = 32926; public conversation_allied_race_dk_defender_of_azeroth() : base("conversation_allied_race_dk_defender_of_azeroth") { } @@ -31,4 +30,4 @@ namespace Scripts.World sender.KilledMonsterCredit(NpcListenToYourCommanderCredit); } } -} +} \ No newline at end of file diff --git a/Source/Scripts/World/DuelReset.cs b/Source/Scripts/World/DuelReset.cs index 5e478bb51..4e623209a 100644 --- a/Source/Scripts/World/DuelReset.cs +++ b/Source/Scripts/World/DuelReset.cs @@ -7,26 +7,20 @@ using Game.Entities; using Game.Scripting; using Game.Spells; using System; +using static Global; namespace Scripts.World.DuelReset { [Script] class DuelResetScript : PlayerScript { - bool _resetCooldowns; - bool _resetHealthMana; + public DuelResetScript() : base("DuelResetScript") { } - public DuelResetScript() : base("DuelResetScript") - { - _resetCooldowns = WorldConfig.GetBoolValue(WorldCfg.ResetDuelCooldowns); - _resetHealthMana = WorldConfig.GetBoolValue(WorldCfg.ResetDuelHealthMana); - } - - // Called when a duel starts (after 3s countdown) + // Called when a duel starts (after TimeSpan.FromSeconds(3) countdown) public override void OnDuelStart(Player player1, Player player2) { // Cooldowns reset - if (_resetCooldowns) + if (WorldConfig.GetBoolValue(WorldCfg.ResetDuelCooldowns)) { player1.GetSpellHistory().SaveCooldownStateBeforeDuel(); player2.GetSpellHistory().SaveCooldownStateBeforeDuel(); @@ -36,7 +30,7 @@ namespace Scripts.World.DuelReset } // Health and mana reset - if (_resetHealthMana) + if (WorldConfig.GetBoolValue(WorldCfg.ResetDuelHealthMana)) { player1.SaveHealthBeforeDuel(); player1.SaveManaBeforeDuel(); @@ -55,7 +49,7 @@ namespace Scripts.World.DuelReset if (type == DuelCompleteType.Won) { // Cooldown restore - if (_resetCooldowns) + if (WorldConfig.GetBoolValue(WorldCfg.ResetDuelCooldowns)) { ResetSpellCooldowns(winner, false); ResetSpellCooldowns(loser, false); @@ -65,7 +59,7 @@ namespace Scripts.World.DuelReset } // Health and mana restore - if (_resetHealthMana) + if (WorldConfig.GetBoolValue(WorldCfg.ResetDuelHealthMana)) { winner.RestoreHealthAfterDuel(); loser.RestoreHealthAfterDuel(); @@ -83,31 +77,31 @@ namespace Scripts.World.DuelReset static void ResetSpellCooldowns(Player player, bool onStartDuel) { - // remove cooldowns on spells that have < 10 min Cd > 30 sec and has no onHold + // Remove cooldowns on spells that have < 10 min Cd > 30 sec and has no onHold player.GetSpellHistory().ResetCooldowns(pair => { - SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(pair.Key, Difficulty.None); + SpellInfo spellInfo = SpellMgr.GetSpellInfo(pair.Key, Difficulty.None); TimeSpan remainingCooldown = player.GetSpellHistory().GetRemainingCooldown(spellInfo); TimeSpan totalCooldown = TimeSpan.FromMilliseconds(spellInfo.RecoveryTime); TimeSpan categoryCooldown = TimeSpan.FromMilliseconds(spellInfo.CategoryRecoveryTime); - void applySpellMod(ref TimeSpan value) + var applySpellMod = (TimeSpan value) => { int intValue = (int)value.TotalMilliseconds; player.ApplySpellMod(spellInfo, SpellModOp.Cooldown, ref intValue, null); value = TimeSpan.FromMilliseconds(intValue); }; - applySpellMod(ref totalCooldown); + applySpellMod(totalCooldown); int cooldownMod = player.GetTotalAuraModifier(AuraType.ModCooldown); if (cooldownMod != 0) totalCooldown += TimeSpan.FromMilliseconds(cooldownMod); - if (!spellInfo.HasAttribute(SpellAttr6.NoCategoryCooldownMods)) - applySpellMod(ref categoryCooldown); + if (spellInfo.HasAttribute(SpellAttr6.NoCategoryCooldownMods)) + applySpellMod(categoryCooldown); - return remainingCooldown > TimeSpan.Zero + return remainingCooldown > TimeSpan.FromMilliseconds(0) && !pair.Value.OnHold && totalCooldown < TimeSpan.FromMinutes(10) && categoryCooldown < TimeSpan.FromMinutes(10) @@ -118,7 +112,7 @@ namespace Scripts.World.DuelReset // pet cooldowns Pet pet = player.GetPet(); - if (pet) + if (pet != null) pet.GetSpellHistory().ResetAllCooldowns(); } } diff --git a/Source/Scripts/World/EmeraldDragons.cs b/Source/Scripts/World/EmeraldDragons.cs index 75cde3ab0..fc6aaf149 100644 --- a/Source/Scripts/World/EmeraldDragons.cs +++ b/Source/Scripts/World/EmeraldDragons.cs @@ -9,7 +9,7 @@ using Game.Spells; using System; using System.Collections.Generic; -namespace Scripts.World.EmeraldDragons +namespace Scripts.World.Achievements { struct CreatureIds { @@ -80,9 +80,10 @@ namespace Scripts.World.EmeraldDragons public const uint SayTaerarSummonShades = 1; } - class emerald_dragonAI : WorldBossAI + [Script] + class emerald_dragon : WorldBossAI { - public emerald_dragonAI(Creature creature) : base(creature) { } + public emerald_dragon(Creature creature) : base(creature) { } public override void Reset() { @@ -90,7 +91,7 @@ namespace Scripts.World.EmeraldDragons me.RemoveUnitFlag(UnitFlags.NonAttackable); me.SetUninteractible(false); me.SetReactState(ReactStates.Aggressive); - DoCast(me, SpellIds.MarkOfNatureAura, new CastSpellExtraArgs(true)); + DoCast(me, SpellIds.MarkOfNatureAura, true); _scheduler.Schedule(TimeSpan.FromSeconds(4), task => { @@ -98,20 +99,18 @@ namespace Scripts.World.EmeraldDragons DoCast(me, SpellIds.TailSweep); task.Repeat(TimeSpan.FromSeconds(2)); }); - _scheduler.Schedule(TimeSpan.FromSeconds(7.5), TimeSpan.FromSeconds(15), task => { // Noxious Breath is cast on random intervals, no less than 7.5 seconds between DoCast(me, SpellIds.NoxiousBreath); - task.Repeat(); + task.Repeat(TimeSpan.FromSeconds(7.5), TimeSpan.FromSeconds(15)); }); - _scheduler.Schedule(TimeSpan.FromSeconds(12.5), TimeSpan.FromSeconds(20), task => { - // Seeping Fog appears only as "pairs", and only ONE pair at any given time! + // Seeping Fog appears only as "pairs", and only One pair at any given time! // Despawntime is 2 minutes, so reschedule it for new cast after 2 minutes + a minor "random time" (30 seconds at max) - DoCast(me, SpellIds.SeepingFogLeft, new CastSpellExtraArgs(true)); - DoCast(me, SpellIds.SeepingFogRight, new CastSpellExtraArgs(true)); + DoCast(me, SpellIds.SeepingFogLeft, true); + DoCast(me, SpellIds.SeepingFogRight, true); task.Repeat(TimeSpan.FromMinutes(2), TimeSpan.FromMinutes(2.5)); }); } @@ -119,7 +118,7 @@ namespace Scripts.World.EmeraldDragons // Target killed during encounter, mark them as suspectible for Aura Of Nature public override void KilledUnit(Unit who) { - if (who.IsTypeId(TypeId.Player)) + if (who.IsPlayer()) who.CastSpell(who, SpellIds.MarkOfNature, true); } @@ -128,13 +127,13 @@ namespace Scripts.World.EmeraldDragons if (!UpdateVictim()) return; + _scheduler.Update(diff); + if (me.HasUnitState(UnitState.Casting)) return; - _scheduler.Update(diff); - Unit target = SelectTarget(SelectTargetMethod.MaxThreat, 0, -50.0f, true); - if (target) + if (target != null) DoCast(target, SpellIds.SummonPlayer); DoMeleeAttackIfReady(); @@ -144,8 +143,6 @@ namespace Scripts.World.EmeraldDragons [Script] class npc_dream_fog : ScriptedAI { - uint _roamTimer; - public npc_dream_fog(Creature creature) : base(creature) { Initialize(); @@ -153,7 +150,26 @@ namespace Scripts.World.EmeraldDragons void Initialize() { - _roamTimer = 0; + _scheduler.Schedule(TimeSpan.FromSeconds(0), task => + { + // Chase target, but don't attack - otherwise just roam around + Unit target = SelectTarget(SelectTargetMethod.Random, 0, 0.0f, true); + if (target != null) + { + task.Repeat(TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(30)); + me.GetMotionMaster().Clear(); + me.GetMotionMaster().MoveChase(target, 0.2f); + } + else + { + task.Repeat(TimeSpan.FromSeconds(2.5)); + me.GetMotionMaster().Clear(); + me.GetMotionMaster().MoveRandom(25.0f); + } + // Seeping fog movement is slow enough for a player to be able to walk backwards and still outpace it + me.SetWalk(true); + me.SetSpeedRate(UnitMoveType.Walk, 0.75f); + }); } public override void Reset() @@ -166,33 +182,12 @@ namespace Scripts.World.EmeraldDragons if (!UpdateVictim()) return; - if (_roamTimer == 0) - { - // Chase target, but don't attack - otherwise just roam around - Unit target = SelectTarget(SelectTargetMethod.Random, 0, 0.0f, true); - if (target) - { - _roamTimer = RandomHelper.URand(15000, 30000); - me.GetMotionMaster().Clear(); - me.GetMotionMaster().MoveChase(target, 0.2f); - } - else - { - _roamTimer = 2500; - me.GetMotionMaster().Clear(); - me.GetMotionMaster().MoveRandom(25.0f); - } - // Seeping fog movement is slow enough for a player to be able to walk backwards and still outpace it - me.SetWalk(true); - me.SetSpeedRate(UnitMoveType.Walk, 0.75f); - } - else - _roamTimer -= diff; + _scheduler.Update(diff); } } [Script] - class boss_ysondre : emerald_dragonAI + class boss_ysondre : emerald_dragon { byte _stage; @@ -232,14 +227,14 @@ namespace Scripts.World.EmeraldDragons Talk(TextIds.SayYsondreSummonDruids); for (byte i = 0; i < 10; ++i) - DoCast(me, SpellIds.SummonDruidSpirits, new CastSpellExtraArgs(true)); + DoCast(me, SpellIds.SummonDruidSpirits, true); ++_stage; } } } [Script] - class boss_lethon : emerald_dragonAI + class boss_lethon : emerald_dragon { byte _stage; @@ -257,10 +252,9 @@ namespace Scripts.World.EmeraldDragons { Initialize(); base.Reset(); - _scheduler.Schedule(TimeSpan.FromSeconds(10), task => { - me.CastSpell((Unit)null, SpellIds.ShadowBoltWhirl, false); + me.CastSpell(null, SpellIds.ShadowBoltWhirl, false); task.Repeat(TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(30)); }); } @@ -298,28 +292,28 @@ namespace Scripts.World.EmeraldDragons public npc_spirit_shade(Creature creature) : base(creature) { } - public override void IsSummonedBy(WorldObject summoner) + public override void IsSummonedBy(WorldObject summonerWO) { - Unit unitSummoner = summoner.ToUnit(); - if (unitSummoner == null) + Unit summoner = summonerWO.ToUnit(); + if (summoner == null) return; _summonerGuid = summoner.GetGUID(); - me.GetMotionMaster().MoveFollow(unitSummoner, 0.0f, 0.0f); + me.GetMotionMaster().MoveFollow(summoner, 0.0f, 0.0f); } public override void MovementInform(MovementGeneratorType moveType, uint data) { if (moveType == MovementGeneratorType.Follow && data == _summonerGuid.GetCounter()) { - me.CastSpell((Unit)null, SpellIds.DarkOffering, false); + me.CastSpell(null, SpellIds.DarkOffering, false); me.DespawnOrUnsummon(TimeSpan.FromSeconds(1)); } } } [Script] - class boss_emeriss : emerald_dragonAI + class boss_emeriss : emerald_dragon { byte _stage; @@ -347,8 +341,9 @@ namespace Scripts.World.EmeraldDragons public override void KilledUnit(Unit who) { - if (who.IsTypeId(TypeId.Player)) - DoCast(who, SpellIds.PutridMushroom, new CastSpellExtraArgs(true)); + if (who.IsPlayer()) + DoCast(who, SpellIds.PutridMushroom, true); + base.KilledUnit(who); } @@ -358,19 +353,19 @@ namespace Scripts.World.EmeraldDragons base.JustEngagedWith(who); } - public override void DamageTaken(Unit attacker, ref uint damage, DamageEffectType damageType, SpellInfo spellInfo = null) + public override void DamageTaken(Unit attacker, ref uint damage, DamageEffectType damageType, SpellInfo spellInfo = null) { if (!HealthAbovePct(100 - 25 * _stage)) { Talk(TextIds.SayEmerissCastCorruption); - DoCast(me, SpellIds.CorruptionOfEarth, new CastSpellExtraArgs(true)); + DoCast(me, SpellIds.CorruptionOfEarth, true); ++_stage; } } } [Script] - class boss_taerar : emerald_dragonAI + class boss_taerar : emerald_dragon { bool _banished; // used for shades activation testing uint _banishedTimer; // counter for banishment timeout @@ -395,6 +390,7 @@ namespace Scripts.World.EmeraldDragons me.RemoveAurasDueToSpell(SpellIds.Shade); Initialize(); + base.Reset(); _scheduler.Schedule(TimeSpan.FromSeconds(12), task => @@ -402,7 +398,6 @@ namespace Scripts.World.EmeraldDragons DoCast(SpellIds.ArcaneBlast); task.Repeat(TimeSpan.FromSeconds(7), TimeSpan.FromSeconds(12)); }); - _scheduler.Schedule(TimeSpan.FromSeconds(30), task => { DoCast(SpellIds.BellowingRoar); @@ -436,7 +431,8 @@ namespace Scripts.World.EmeraldDragons Talk(TextIds.SayTaerarSummonShades); foreach (var spell in SpellIds.TaerarShadeSpells) - DoCastVictim(spell, new CastSpellExtraArgs(true)); + DoCastVictim(spell, true); + _shades += (byte)SpellIds.TaerarShadeSpells.Length; DoCast(SpellIds.Shade); @@ -455,7 +451,7 @@ namespace Scripts.World.EmeraldDragons if (_banished) { - // If all three shades are dead, Or it has taken too long, end the current event and get Taerar back into business + // If all three shades are dead, Or it has taken too long, end the current event and get Taerar back into buMath.Siness if (_banishedTimer <= diff || _shades == 0) { _banished = false; @@ -469,7 +465,7 @@ namespace Scripts.World.EmeraldDragons else _banishedTimer -= diff; - // Update the _scheduler before we return (handled under emerald_dragonAI.UpdateAI(diff); if we're not inside this check) + // Update the events before we return (handled under emerald_dragonAI.UpdateAI(diff); if we're not inside this check) _scheduler.Update(diff); return; @@ -487,7 +483,7 @@ namespace Scripts.World.EmeraldDragons targets.RemoveAll(obj => { Unit unit = obj.ToUnit(); - if (unit) + if (unit != null) return unit.HasAura(SpellIds.Sleep); return true; }); @@ -495,7 +491,7 @@ namespace Scripts.World.EmeraldDragons public override void Register() { - OnObjectAreaTargetSelect.Add(new ObjectAreaTargetSelectHandler(FilterTargets, 0, Targets.UnitDestAreaEnemy)); + OnObjectAreaTargetSelect.Add(new(FilterTargets, 0, Targets.UnitDestAreaEnemy)); } } @@ -513,9 +509,8 @@ namespace Scripts.World.EmeraldDragons { // return those not tagged or already under the influence of Aura of Nature Unit unit = obj.ToUnit(); - if (unit) + if (unit != null) return !(unit.HasAura(SpellIds.MarkOfNature) && !unit.HasAura(SpellIds.AuraOfNature)); - return true; }); } @@ -528,8 +523,8 @@ namespace Scripts.World.EmeraldDragons public override void Register() { - OnObjectAreaTargetSelect.Add(new ObjectAreaTargetSelectHandler(FilterTargets, 0, Targets.UnitSrcAreaEnemy)); - OnEffectHitTarget.Add(new EffectHandler(HandleEffect, 0, SpellEffectName.ApplyAura)); + OnObjectAreaTargetSelect.Add(new(FilterTargets, 0, Targets.UnitSrcAreaEnemy)); + OnEffectHitTarget.Add(new(HandleEffect, 0, SpellEffectName.ApplyAura)); } } } \ No newline at end of file diff --git a/Source/Scripts/World/GameObject.cs b/Source/Scripts/World/GameObject.cs index e0329e384..fb1fbb1f3 100644 --- a/Source/Scripts/World/GameObject.cs +++ b/Source/Scripts/World/GameObject.cs @@ -3,273 +3,36 @@ using Framework.Constants; using Game.AI; -using Game.DataStorage; using Game.Entities; using Game.Scripting; using System; using System.Collections.Generic; +using static Global; namespace Scripts.World.GameObjects { - struct SpellIds - { - //CatFigurine - public const uint SummonGhostSaber = 5968; - - //EthereumPrison - public const uint RepLc = 39456; - public const uint RepShat = 39457; - public const uint RepCe = 39460; - public const uint RepCon = 39474; - public const uint RepKt = 39475; - public const uint RepSpor = 39476; - - //Southfury - public const uint Blackjack = 39865; //Stuns Player - public const uint SummonRizzle = 39866; - - //Felcrystalforge - public const uint Create1FlaskOfBeast = 40964; - public const uint Create5FlaskOfBeast = 40965; - - //Bashircrystalforge - public const uint Create1FlaskOfSorcerer = 40968; - public const uint Create5FlaskOfSorcerer = 40970; - - //Jotunheimcage - public const uint SummonBladeKnightH = 56207; - public const uint SummonBladeKnightNe = 56209; - public const uint SummonBladeKnightOrc = 56212; - public const uint SummonBladeKnightTroll = 56214; - - //Amberpineouthouse - public const uint Indisposed = 53017; - public const uint IndisposedIii = 48341; - public const uint CreateAmberseeds = 48330; - - //Thecleansing - public const uint CleansingSoul = 43351; - public const uint RecentMeditation = 61720; - - //Midsummerbonfire - public const uint StampOutBonfireQuestComplete = 45458; - - //MidsummerPoleRibbon - public static uint[] RibbonPoleSpells = { 29705, 29726, 29727 }; - - //Toy Train Set - public const uint ToyTrainPulse = 61551; - } - - struct CreatureIds - { - //GildedBrazier - public const uint Stillblade = 17716; - - //EthereumPrison - public static uint[] PrisonEntry = - { - 22810, 22811, 22812, 22813, 22814, 22815, //Good Guys - 20783, 20784, 20785, 20786, 20788, 20789, 20790 //Bad Guys - }; - - //Ethereum Stasis - public static uint[] StasisEntry = - { - 22825, 20888, 22827, 22826, 22828 - }; - - //ResoniteCask - public const uint Goggeroc = 11920; - - //Sacredfireoflife - public const uint Arikara = 10882; - - //Southfury - public const uint Rizzle = 23002; - - //Bloodfilledorb - public const uint Zelemar = 17830; - - //Jotunheimcage - public const uint EbonBladePrisonerHuman = 30186; - public const uint EbonBladePrisonerNe = 30194; - public const uint EbonBladePrisonerTroll = 30196; - public const uint EbonBladePrisonerOrc = 30195; - - //Tadpoles - public const uint WinterfinTadpole = 25201; - - //Amberpineouthouse - public const uint OuthouseBunny = 27326; - - //Missingfriends - public const uint CaptiveChild = 22314; - - //MidsummerPoleRibbon - public const uint PoleRibbonBunny = 17066; - } - - struct GameObjectIds - { - //Bellhourlyobjects - public const uint HordeBell = 175885; - public const uint AllianceBell = 176573; - public const uint KharazhanBell = 182064; - } - - struct ItemIds - { - //Amberpineouthouse - public const uint AnderholsSliderCider = 37247; - } - - struct QuestIds - { - //GildedBrazier - public const uint TheFirstTrial = 9678; - - //Dalarancrystal - public const uint LearnLeaveReturn = 12790; - public const uint TeleCrystalFlag = 12845; - - //Tadpoles - public const uint OhNoesTheTadpoles = 11560; - - //Amberpineouthouse - public const uint DoingYourDuty = 12227; - - //Missingfriends - public const uint MissingFriends = 10852; - - //Thecleansing - public const uint TheCleansingHorde = 11317; - public const uint TheCleansingAlliance = 11322; - } - - struct TextIds - { - //Missingfriends - public const uint SayFree0 = 0; - } - - struct GossipConst - { - //Dalarancrystal - public const string GoTeleToDalaranCrystalFailed = "This Teleport Crystal Cannot Be Used Until The Teleport Crystal In Dalaran Has Been Used At Least Once."; - - //Felcrystalforge - public const uint GossipFelCrystalforgeText = 31000; - public const uint GossipFelCrystalforgeItemTextReturn = 31001; - public const string GossipFelCrystalforgeItem1 = "Purchase 1 Unstable Flask Of The Beast For The Cost Of 10 Apexis Shards"; - public const string GossipFelCrystalforgeItem5 = "Purchase 5 Unstable Flask Of The Beast For The Cost Of 50 Apexis Shards"; - public const string GossipFelCrystalforgeItemReturn = "Use The Fel Crystalforge To Make Another Purchase."; - - //Bashircrystalforge - public const uint GossipBashirCrystalforgeText = 31100; - public const uint GossipBashirCrystalforgeItemTextReturn = 31101; - public const string GossipBashirCrystalforgeItem1 = "Purchase 1 Unstable Flask Of The Sorcerer For The Cost Of 10 Apexis Shards"; - public const string GossipBashirCrystalforgeItem5 = "Purchase 5 Unstable Flask Of The Sorcerer For The Cost Of 50 Apexis Shards"; - public const string GossipBashirCrystalforgeItemReturn = "Use The Bashir Crystalforge To Make Another Purchase."; - - //Amberpineouthouse - public const uint GossipOuthouseInuse = 12775; - public const uint GossipOuthouseVacant = 12779; - - public const string GossipUseOuthouse = "Use The Outhouse."; - public const string AnderholsSliderCiderNotFound = "Quest Item Anderhol'S Slider Cider Not Found."; - } - - struct SoundIds - { - //BrewfestMusic - public const uint EventBrewfestdwarf01 = 11810; // 1.35 Min - public const uint EventBrewfestdwarf02 = 11812; // 1.55 Min - public const uint EventBrewfestdwarf03 = 11813; // 0.23 Min - public const uint EventBrewfestgoblin01 = 11811; // 1.08 Min - public const uint EventBrewfestgoblin02 = 11814; // 1.33 Min - public const uint EventBrewfestgoblin03 = 11815; // 0.28 Min - - //Brewfestmusicevents - public const uint EventBmSelectMusic = 1; - public const uint EventBmStartMusic = 2; - - //Bells - //BellHourlySoundFX - public const uint BellTollHorde = 6595; // Horde - public const uint BellTollTribal = 6675; - public const uint BellTollAlliance = 6594; // Alliance - public const uint BellTollNightelf = 6674; - public const uint BellTolldwarfgnome = 7234; - public const uint BellTollKharazhan = 9154; // Kharazhan - } - - struct AreaIds - { - public const uint Silvermoon = 3430; // Horde - public const uint Undercity = 1497; - public const uint Orgrimmar1 = 1296; - public const uint Orgrimmar2 = 14; - public const uint Thunderbluff = 1638; - public const uint Ironforge1 = 809; // Alliance - public const uint Ironforge2 = 1; - public const uint Stormwind = 12; - public const uint Exodar = 3557; - public const uint Darnassus = 1657; - public const uint Shattrath = 3703; // General - public const uint TeldrassilZone = 141; - public const uint KharazhanMapid = 532; - } - - struct ZoneIds - { - public const uint Tirisfal = 85; - public const uint Undercity = 1497; - public const uint DunMorogh = 1; - public const uint Ironforge = 1537; - public const uint Teldrassil = 141; - public const uint Darnassus = 1657; - public const uint Ashenvale = 331; - public const uint HillsbradFoothills = 267; - public const uint Duskwood = 10; - } - - struct Misc - { - // These Are In Seconds - //Brewfestmusictime - public static TimeSpan EventBrewfestdwarf01Time = TimeSpan.FromSeconds(95); - public static TimeSpan EventBrewfestdwarf02Time = TimeSpan.FromSeconds(155); - public static TimeSpan EventBrewfestdwarf03Time = TimeSpan.FromSeconds(23); - public static TimeSpan EventBrewfestgoblin01Time = TimeSpan.FromSeconds(68); - public static TimeSpan EventBrewfestgoblin02Time = TimeSpan.FromSeconds(93); - public static TimeSpan EventBrewfestgoblin03Time = TimeSpan.FromSeconds(28); - - //Bellhourlymisc - public const uint GameEventHourlyBells = 73; - } - - [Script] class go_gilded_brazier : GameObjectAI { + const uint NpcStillblade = 17716; + const uint QuestTheFirstTrial = 9678; + public go_gilded_brazier(GameObject go) : base(go) { } public override bool OnGossipHello(Player player) { if (me.GetGoType() == GameObjectTypes.Goober) { - if (player.GetQuestStatus(QuestIds.TheFirstTrial) == QuestStatus.Incomplete) + if (player.GetQuestStatus(QuestTheFirstTrial) == QuestStatus.Incomplete) { - Creature Stillblade = player.SummonCreature(CreatureIds.Stillblade, 8106.11f, -7542.06f, 151.775f, 3.02598f, TempSummonType.DeadDespawn, TimeSpan.FromMinutes(1)); - if (Stillblade) - Stillblade.GetAI().AttackStart(player); + Creature stillblade = player.SummonCreature(NpcStillblade, 8106.11f, -7542.06f, 151.775f, 3.02598f, TempSummonType.DeadDespawn, TimeSpan.FromMinutes(1)); + if (stillblade != null) + stillblade.GetAI().AttackStart(player); } } return true; } } - [Script] class go_tablet_of_the_seven : GameObjectAI { public go_tablet_of_the_seven(GameObject go) : base(go) { } @@ -287,52 +50,51 @@ namespace Scripts.World.GameObjects } } - [Script] class go_ethereum_prison : GameObjectAI { + const uint SpellRepLc = 39456; + const uint SpellRepShat = 39457; + const uint SpellRepCe = 39460; + const uint SpellRepCon = 39474; + const uint SpellRepKt = 39475; + const uint SpellRepSpor = 39476; + + uint[] NpcPrisonEntry = + { + 22810, 22811, 22812, 22813, 22814, 22815, //good guys + 20783, 20784, 20785, 20786, 20788, 20789, 20790 //bad guys + }; + public go_ethereum_prison(GameObject go) : base(go) { } public override bool OnGossipHello(Player player) { me.UseDoorOrButton(); - int Random = (int)(RandomHelper.Rand32() % (CreatureIds.PrisonEntry.Length / sizeof(uint))); - Creature creature = player.SummonCreature(CreatureIds.PrisonEntry[Random], me.GetPositionX(), me.GetPositionY(), me.GetPositionZ(), me.GetAbsoluteAngle(player), TempSummonType.TimedDespawnOutOfCombat, TimeSpan.FromSeconds(30)); - if (creature) + Creature creature = player.SummonCreature(NpcPrisonEntry.SelectRandom(), me.GetPositionX(), me.GetPositionY(), me.GetPositionZ(), me.GetAbsoluteAngle(player), TempSummonType.TimedDespawnOutOfCombat, TimeSpan.FromSeconds(30)); + if (creature != null) { if (!creature.IsHostileTo(player)) { - FactionTemplateRecord pFaction = creature.GetFactionTemplateEntry(); + var pFaction = creature.GetFactionTemplateEntry(); if (pFaction != null) { uint spellId = 0; switch (pFaction.Faction) { - case 1011: - spellId = SpellIds.RepLc; - break; - case 935: - spellId = SpellIds.RepShat; - break; - case 942: - spellId = SpellIds.RepCe; - break; - case 933: - spellId = SpellIds.RepCon; - break; - case 989: - spellId = SpellIds.RepKt; - break; - case 970: - spellId = SpellIds.RepSpor; - break; + case 1011: spellId = SpellRepLc; break; + case 935: spellId = SpellRepShat; break; + case 942: spellId = SpellRepCe; break; + case 933: spellId = SpellRepCon; break; + case 989: spellId = SpellRepKt; break; + case 970: spellId = SpellRepSpor; break; } if (spellId != 0) creature.CastSpell(player, spellId, false); else - Log.outError(LogFilter.Scripts, $"go_ethereum_prison summoned Creature (entry {creature.GetEntry()}) but faction ({creature.GetFaction()}) are not expected by script."); + Log.outError(LogFilter.Scripts, $"go_ethereum_prison summoned Creature (entry {creature.GetEntry()})but faction ({creature.GetFaction()})are not expected by script."); } } } @@ -341,39 +103,44 @@ namespace Scripts.World.GameObjects } } - [Script] class go_ethereum_stasis : GameObjectAI { + uint[] NpcStasisEntry = { 22825, 20888, 22827, 22826, 22828 }; + public go_ethereum_stasis(GameObject go) : base(go) { } public override bool OnGossipHello(Player player) { me.UseDoorOrButton(); - int Random = (int)(RandomHelper.Rand32() % CreatureIds.StasisEntry.Length / sizeof(uint)); - player.SummonCreature(CreatureIds.StasisEntry[Random], me.GetPositionX(), me.GetPositionY(), me.GetPositionZ(), me.GetAbsoluteAngle(player), TempSummonType.TimedDespawnOutOfCombat, TimeSpan.FromSeconds(30)); + player.SummonCreature(NpcStasisEntry.SelectRandom(), me.GetPositionX(), me.GetPositionY(), me.GetPositionZ(), me.GetAbsoluteAngle(player), + TempSummonType.TimedDespawnOutOfCombat, TimeSpan.FromSeconds(30)); return false; } } - [Script] class go_resonite_cask : GameObjectAI { + const uint NpcGoggeroc = 11920; + public go_resonite_cask(GameObject go) : base(go) { } public override bool OnGossipHello(Player player) { if (me.GetGoType() == GameObjectTypes.Goober) - me.SummonCreature(CreatureIds.Goggeroc, 0.0f, 0.0f, 0.0f, 0.0f, TempSummonType.TimedDespawnOutOfCombat, TimeSpan.FromMinutes(5)); + me.SummonCreature(NpcGoggeroc, 0.0f, 0.0f, 0.0f, 0.0f, TempSummonType.TimedDespawnOutOfCombat, TimeSpan.FromMinutes(5)); return false; } } - [Script] class go_southfury_moonstone : GameObjectAI { + const uint NpcRizzle = 23002; + const uint SpellBlackjack = 39865; //stuns player + const uint SpellSummonRizzle = 39866; + public go_southfury_moonstone(GameObject go) : base(go) { } public override bool OnGossipHello(Player player) @@ -381,58 +148,60 @@ namespace Scripts.World.GameObjects //implicitTarget=48 not implemented as of writing this code, and manual summon may be just ok for our purpose //player.CastSpell(player, SpellSummonRizzle, false); - Creature creature = player.SummonCreature(CreatureIds.Rizzle, 0.0f, 0.0f, 0.0f, 0.0f, TempSummonType.DeadDespawn); - if (creature) - creature.CastSpell(player, SpellIds.Blackjack, false); + Creature creature = player.SummonCreature(NpcRizzle, 0.0f, 0.0f, 0.0f, 0.0f, TempSummonType.DeadDespawn); + if (creature != null) + creature.CastSpell(player, SpellBlackjack, false); return false; } } - [Script] class go_tele_to_dalaran_crystal : GameObjectAI { + const uint QuestTeleCrystalFlag = 12845; + public go_tele_to_dalaran_crystal(GameObject go) : base(go) { } public override bool OnGossipHello(Player player) { - if (player.GetQuestRewardStatus(QuestIds.TeleCrystalFlag)) + if (player.GetQuestRewardStatus(QuestTeleCrystalFlag)) return false; - player.GetSession().SendNotification(GossipConst.GoTeleToDalaranCrystalFailed); + player.GetSession().SendNotification("This teleport crystal cannot be used until the teleport crystal in Dalaran has been used at least once."); return true; } } - [Script] class go_tele_to_violet_stand : GameObjectAI { + const uint QuestLearnLeaveReturn = 12790; + public go_tele_to_violet_stand(GameObject go) : base(go) { } public override bool OnGossipHello(Player player) { - if (player.GetQuestRewardStatus(QuestIds.LearnLeaveReturn) || player.GetQuestStatus(QuestIds.LearnLeaveReturn) == QuestStatus.Incomplete) + if (player.GetQuestRewardStatus(QuestLearnLeaveReturn) || player.GetQuestStatus(QuestLearnLeaveReturn) == QuestStatus.Incomplete) return false; return true; } } - [Script] class go_blood_filled_orb : GameObjectAI { + const uint NpcZelemar = 17830; + public go_blood_filled_orb(GameObject go) : base(go) { } public override bool OnGossipHello(Player player) { if (me.GetGoType() == GameObjectTypes.Goober) - player.SummonCreature(CreatureIds.Zelemar, -369.746f, 166.759f, -21.50f, 5.235f, TempSummonType.TimedDespawnOutOfCombat, TimeSpan.FromSeconds(30)); + player.SummonCreature(NpcZelemar, -369.746f, 166.759f, -21.50f, 5.235f, TempSummonType.TimedDespawnOutOfCombat, TimeSpan.FromSeconds(30)); return true; } } - [Script] class go_soulwell : GameObjectAI { public go_soulwell(GameObject go) : base(go) { } @@ -440,27 +209,35 @@ namespace Scripts.World.GameObjects public override bool OnGossipHello(Player player) { Unit owner = me.GetOwner(); - if (!owner || !owner.IsTypeId(TypeId.Player) || !player.IsInSameRaidWith(owner.ToPlayer())) + if (owner == null || !owner.IsPlayer() || !player.IsInSameRaidWith(owner.ToPlayer())) return true; return false; } } - [Script] class go_amberpine_outhouse : GameObjectAI { + const uint ItemAnderholsSliderCider = 37247; + const uint NpcOuthouseBunny = 27326; + const uint QuestDoingYourDuty = 12227; + const uint SpellIndisposed = 53017; + const uint SpellIndisposedIii = 48341; + const uint SpellCreateAmberseeds = 48330; + const uint GossipOuthouseInuse = 12775; + const uint GossipOuthouseVacant = 12779; + public go_amberpine_outhouse(GameObject go) : base(go) { } public override bool OnGossipHello(Player player) { - QuestStatus status = player.GetQuestStatus(QuestIds.DoingYourDuty); + QuestStatus status = player.GetQuestStatus(QuestDoingYourDuty); if (status == QuestStatus.Incomplete || status == QuestStatus.Complete || status == QuestStatus.Rewarded) { - player.AddGossipItem(GossipOptionNpc.None, GossipConst.GossipUseOuthouse, eTradeskill.GossipSenderMain, eTradeskill.GossipActionInfoDef + 1); - player.SendGossipMenu(GossipConst.GossipOuthouseVacant, me.GetGUID()); + player.AddGossipItem(GossipOptionNpc.None, "Use the outhouse.", eTradeskill.GossipSenderMain, eTradeskill.GossipActionInfoDef + 1); + player.SendGossipMenu(GossipOuthouseVacant, me.GetGUID()); } else - player.SendGossipMenu(GossipConst.GossipOuthouseInuse, me.GetGUID()); + player.SendGossipMenu(GossipOuthouseInuse, me.GetGUID()); return true; } @@ -472,27 +249,26 @@ namespace Scripts.World.GameObjects if (action == eTradeskill.GossipActionInfoDef + 1) { player.CloseGossipMenu(); - Creature target = ScriptedAI.GetClosestCreatureWithEntry(player, CreatureIds.OuthouseBunny, 3.0f); - if (target) + Creature target = ScriptedAI.GetClosestCreatureWithEntry(player, NpcOuthouseBunny, 3.0f); + if (target != null) { target.GetAI().SetData(1, (uint)player.GetNativeGender()); - me.CastSpell(target, SpellIds.IndisposedIii); + me.CastSpell(target, SpellIndisposedIii); } - me.CastSpell(player, SpellIds.Indisposed); - if (player.HasItemCount(ItemIds.AnderholsSliderCider)) - me.CastSpell(player, SpellIds.CreateAmberseeds); + me.CastSpell(player, SpellIndisposed); + if (player.HasItemCount(ItemAnderholsSliderCider)) + me.CastSpell(player, SpellCreateAmberseeds); return true; } else { player.CloseGossipMenu(); - player.GetSession().SendNotification(GossipConst.AnderholsSliderCiderNotFound); + player.GetSession().SendNotification("Quest item Anderhol's Slider Cider not found."); return false; } } } - [Script] class go_massive_seaforium_charge : GameObjectAI { public go_massive_seaforium_charge(GameObject go) : base(go) { } @@ -504,23 +280,26 @@ namespace Scripts.World.GameObjects } } - [Script] class go_veil_skith_cage : GameObjectAI { + const uint QuestMissingFriends = 10852; + const uint NpcCaptiveChild = 22314; + const uint SayFree0 = 0; + public go_veil_skith_cage(GameObject go) : base(go) { } public override bool OnGossipHello(Player player) { me.UseDoorOrButton(); - if (player.GetQuestStatus(QuestIds.MissingFriends) == QuestStatus.Incomplete) + if (player.GetQuestStatus(QuestMissingFriends) == QuestStatus.Incomplete) { - List childrenList = me.GetCreatureListWithEntryInGrid(CreatureIds.CaptiveChild, SharedConst.InteractionDistance); + List childrenList = me.GetCreatureListWithEntryInGrid(NpcCaptiveChild, SharedConst.InteractionDistance); foreach (Creature creature in childrenList) { - player.KilledMonsterCredit(CreatureIds.CaptiveChild, creature.GetGUID()); + player.KilledMonsterCredit(NpcCaptiveChild, creature.GetGUID()); creature.DespawnOrUnsummon(TimeSpan.FromSeconds(5)); creature.GetMotionMaster().MovePoint(1, me.GetPositionX() + 5, me.GetPositionY(), me.GetPositionZ()); - creature.GetAI().Talk(TextIds.SayFree0); + creature.GetAI().Talk(SayFree0); creature.GetMotionMaster().Clear(); } } @@ -528,145 +307,194 @@ namespace Scripts.World.GameObjects } } - [Script] class go_midsummer_bonfire : GameObjectAI { + const uint StampOutBonfireQuestComplete = 45458; + public go_midsummer_bonfire(GameObject go) : base(go) { } - public override bool OnGossipSelect(Player player, uint menuId, uint ssipListId) + public override bool OnGossipSelect(Player player, uint menuId, uint gossipListId) { - player.CastSpell(player, SpellIds.StampOutBonfireQuestComplete, true); + player.CastSpell(player, StampOutBonfireQuestComplete, true); player.CloseGossipMenu(); return false; } } - [Script] class go_midsummer_ribbon_pole : GameObjectAI { + const uint SpellTestRibbonPole1 = 29705; + const uint SpellTestRibbonPole2 = 29726; + const uint SpellTestRibbonPole3 = 29727; + const uint NpcPoleRibbonBunny = 17066; + const int ActionCosmeticFires = 0; + + uint[] RibbonPoleSpells = + { + SpellTestRibbonPole1, + SpellTestRibbonPole2, + SpellTestRibbonPole3 + }; + public go_midsummer_ribbon_pole(GameObject go) : base(go) { } public override bool OnGossipHello(Player player) { - Creature creature = me.FindNearestCreature(CreatureIds.PoleRibbonBunny, 10.0f); - if (creature) + Creature creature = me.FindNearestCreature(NpcPoleRibbonBunny, 10.0f); + if (creature != null) { - creature.GetAI().DoAction(0); - player.CastSpell(player, SpellIds.RibbonPoleSpells[RandomHelper.IRand(0, 2)], true); + creature.GetAI().DoAction(ActionCosmeticFires); + player.CastSpell(player, RibbonPoleSpells[RandomHelper.URand(0, 2)], true); } return true; } } + struct BrewfestMusicConst + { + public const uint Dwarf01 = 11810; // 1.35 min + public const uint Dwarf02 = 11812; // 1.55 min + public const uint Dwarf03 = 11813; // 0.23 min + public const uint Goblin01 = 11811; // 1.08 min + public const uint Goblin02 = 11814; // 1.33 min + public const uint Goblin03 = 11815; // 0.28 min + + public static TimeSpan Dwarf01Time = TimeSpan.FromSeconds(95); + public static TimeSpan Dwarf02Time = TimeSpan.FromSeconds(155); + public static TimeSpan Dwarf03Time = TimeSpan.FromSeconds(23); + public static TimeSpan Goblin01Time = TimeSpan.FromSeconds(68); + public static TimeSpan Goblin02Time = TimeSpan.FromSeconds(93); + public static TimeSpan Goblin03Time = TimeSpan.FromSeconds(28); + } + + enum BrewfestMusicAreasIds + { + Silvermoon = 3430, // Horde + Undercity = 1497, + Orgrimmar1 = 1296, + Orgrimmar2 = 14, + Thunderbluff = 1638, + Ironforge1 = 809, // Alliance + Ironforge2 = 1, + Stormwind = 12, + Exodar = 3557, + Darnassus = 1657, + Shattrath = 3703 // General + } + [Script] class go_brewfest_music : GameObjectAI { - uint rnd = 0; + uint rnd; TimeSpan musicTime = TimeSpan.FromSeconds(1); public go_brewfest_music(GameObject go) : base(go) { _scheduler.Schedule(TimeSpan.FromSeconds(1), task => { - if (Global.GameEventMgr.IsHolidayActive(HolidayIds.Brewfest)) // Check if Brewfest is active - { - rnd = RandomHelper.URand(0, 2); // Select random music sample - task.Repeat(musicTime); // Select new song music after play time is over - } + if (!GameEventMgr.IsHolidayActive(HolidayIds.Brewfest)) // Check if Brewfest is active + return; + rnd = RandomHelper.URand(0, 2); // Select random music sample + task.Repeat(musicTime); // Select new song music after play time is over }); + _scheduler.Schedule(TimeSpan.FromSeconds(2), task => { - if (Global.GameEventMgr.IsHolidayActive(HolidayIds.Brewfest)) // Check if Brewfest is active + if (!GameEventMgr.IsHolidayActive(HolidayIds.Brewfest)) // Check if Brewfest is active + return; + + switch ((BrewfestMusicAreasIds)me.GetAreaId()) { - switch (me.GetAreaId()) - { - case AreaIds.Silvermoon: - case AreaIds.Undercity: - case AreaIds.Orgrimmar1: - case AreaIds.Orgrimmar2: - case AreaIds.Thunderbluff: - switch (rnd) + // Horde + case BrewfestMusicAreasIds.Silvermoon: + case BrewfestMusicAreasIds.Undercity: + case BrewfestMusicAreasIds.Orgrimmar1: + case BrewfestMusicAreasIds.Orgrimmar2: + case BrewfestMusicAreasIds.Thunderbluff: + if (rnd == 0) + { + me.PlayDirectMusic(BrewfestMusicConst.Goblin01); + musicTime = BrewfestMusicConst.Goblin01Time; + } + else if (rnd == 1) + { + me.PlayDirectMusic(BrewfestMusicConst.Goblin02); + musicTime = BrewfestMusicConst.Goblin02Time; + } + else + { + me.PlayDirectMusic(BrewfestMusicConst.Goblin03); + musicTime = BrewfestMusicConst.Goblin03Time; + } + break; + // Alliance + case BrewfestMusicAreasIds.Ironforge1: + case BrewfestMusicAreasIds.Ironforge2: + case BrewfestMusicAreasIds.Stormwind: + case BrewfestMusicAreasIds.Exodar: + case BrewfestMusicAreasIds.Darnassus: + if (rnd == 0) + { + me.PlayDirectMusic(BrewfestMusicConst.Dwarf01); + musicTime = BrewfestMusicConst.Dwarf01Time; + } + else if (rnd == 1) + { + me.PlayDirectMusic(BrewfestMusicConst.Dwarf02); + musicTime = BrewfestMusicConst.Dwarf02Time; + } + else + { + me.PlayDirectMusic(BrewfestMusicConst.Dwarf03); + musicTime = BrewfestMusicConst.Dwarf03Time; + } + break; + // Neurtal + case BrewfestMusicAreasIds.Shattrath: + List playersNearby = me.GetPlayerListInGrid(me.GetVisibilityRange()); + foreach (Player player in playersNearby) + { + if (player.GetTeam() == Team.Horde) { - case 0: - me.PlayDirectMusic(SoundIds.EventBrewfestgoblin01); - musicTime = Misc.EventBrewfestgoblin01Time; - break; - case 1: - me.PlayDirectMusic(SoundIds.EventBrewfestgoblin02); - musicTime = Misc.EventBrewfestgoblin02Time; - break; - default: - me.PlayDirectMusic(SoundIds.EventBrewfestgoblin03); - musicTime = Misc.EventBrewfestgoblin03Time; - break; - } - break; - case AreaIds.Ironforge1: - case AreaIds.Ironforge2: - case AreaIds.Stormwind: - case AreaIds.Exodar: - case AreaIds.Darnassus: - switch (rnd) - { - case 0: - me.PlayDirectMusic(SoundIds.EventBrewfestdwarf01); - musicTime = Misc.EventBrewfestdwarf01Time; - break; - case 1: - me.PlayDirectMusic(SoundIds.EventBrewfestdwarf02); - musicTime = Misc.EventBrewfestdwarf02Time; - break; - default: - me.PlayDirectMusic(SoundIds.EventBrewfestdwarf03); - musicTime = Misc.EventBrewfestdwarf03Time; - break; - } - break; - case AreaIds.Shattrath: - List playersNearby = me.GetPlayerListInGrid(me.GetVisibilityRange()); - foreach (Player player in playersNearby) - { - if (player.GetTeamId() == TeamId.Horde) + if (rnd == 0) { - switch (rnd) - { - case 0: - me.PlayDirectMusic(SoundIds.EventBrewfestgoblin01); - musicTime = Misc.EventBrewfestgoblin01Time; - break; - case 1: - me.PlayDirectMusic(SoundIds.EventBrewfestgoblin02); - musicTime = Misc.EventBrewfestgoblin02Time; - break; - default: - me.PlayDirectMusic(SoundIds.EventBrewfestgoblin03); - musicTime = Misc.EventBrewfestgoblin03Time; - break; - } + me.PlayDirectMusic(BrewfestMusicConst.Goblin01); + musicTime = BrewfestMusicConst.Goblin01Time; + } + else if (rnd == 1) + { + me.PlayDirectMusic(BrewfestMusicConst.Goblin02); + musicTime = BrewfestMusicConst.Goblin02Time; } else { - switch (rnd) - { - case 0: - me.PlayDirectMusic(SoundIds.EventBrewfestdwarf01); - musicTime = Misc.EventBrewfestdwarf01Time; - break; - case 1: - me.PlayDirectMusic(SoundIds.EventBrewfestdwarf02); - musicTime = Misc.EventBrewfestdwarf02Time; - break; - default: - me.PlayDirectMusic(SoundIds.EventBrewfestdwarf03); - musicTime = Misc.EventBrewfestdwarf03Time; - break; - } + me.PlayDirectMusic(BrewfestMusicConst.Goblin03); + musicTime = BrewfestMusicConst.Goblin03Time; } } - break; - } - task.Repeat(TimeSpan.FromSeconds(5)); // Every 5 second's SMSG_PLAY_MUSIC packet (PlayDirectMusic) is pushed to the client + else + { + if (rnd == 0) + { + me.PlayDirectMusic(BrewfestMusicConst.Dwarf01); + musicTime = BrewfestMusicConst.Dwarf01Time; + } + else if (rnd == 1) + { + me.PlayDirectMusic(BrewfestMusicConst.Dwarf02); + musicTime = BrewfestMusicConst.Dwarf02Time; + } + else + { + me.PlayDirectMusic(BrewfestMusicConst.Dwarf03); + musicTime = BrewfestMusicConst.Dwarf03Time; + } + } + } + break; } + + task.Repeat(TimeSpan.FromSeconds(5)); // Every 5 second's SmsgPlayMusic packet (PlayDirectMusic) is pushed to the client }); } @@ -679,23 +507,25 @@ namespace Scripts.World.GameObjects [Script] class go_midsummer_music : GameObjectAI { + const uint EventmidsummerfirefestivalA = 12319; // 1.08 min + const uint EventmidsummerfirefestivalH = 12325; // 1.12 min + public go_midsummer_music(GameObject go) : base(go) { _scheduler.Schedule(TimeSpan.FromSeconds(1), task => { - if (!Global.GameEventMgr.IsHolidayActive(HolidayIds.MidsummerFireFestival)) + if (!GameEventMgr.IsHolidayActive(HolidayIds.MidsummerFireFestival)) return; - var playersNearby = me.GetPlayerListInGrid(me.GetMap().GetVisibilityRange()); + List playersNearby = me.GetPlayerListInGrid(me.GetVisibilityRange()); foreach (Player player in playersNearby) { if (player.GetTeam() == Team.Horde) - me.PlayDirectMusic(12325, player); + me.PlayDirectMusic(EventmidsummerfirefestivalH, player); else - me.PlayDirectMusic(12319, player); + me.PlayDirectMusic(EventmidsummerfirefestivalA, player); } - - task.Repeat(TimeSpan.FromSeconds(5)); // Every 5 second's SMSG_PLAY_MUSIC packet (PlayDirectMusic) is pushed to the client (sniffed value) + task.Repeat(TimeSpan.FromSeconds(5)); // Every 5 second's SmsgPlayMusic packet (PlayDirectMusic) is pushed to the client (sniffed value) }); } @@ -708,15 +538,17 @@ namespace Scripts.World.GameObjects [Script] class go_darkmoon_faire_music : GameObjectAI { + const uint MusicDarkmoonFaireMusic = 8440; + public go_darkmoon_faire_music(GameObject go) : base(go) { _scheduler.Schedule(TimeSpan.FromSeconds(1), task => { - if (Global.GameEventMgr.IsHolidayActive(HolidayIds.DarkmoonFaire)) - { - me.PlayDirectMusic(8440); - task.Repeat(TimeSpan.FromSeconds(5)); // Every 5 second's SMSG_PLAY_MUSIC packet (PlayDirectMusic) is pushed to the client (sniffed value) - } + if (!GameEventMgr.IsHolidayActive(HolidayIds.DarkmoonFaire)) + return; + + me.PlayDirectMusic(MusicDarkmoonFaireMusic); + task.Repeat(TimeSpan.FromSeconds(5)); // Every 5 second's SmsgPlayMusic packet (PlayDirectMusic) is pushed to the client (sniffed value) }); } @@ -729,14 +561,17 @@ namespace Scripts.World.GameObjects [Script] class go_pirate_day_music : GameObjectAI { + const uint MusicPirateDayMusic = 12845; + public go_pirate_day_music(GameObject go) : base(go) { _scheduler.Schedule(TimeSpan.FromSeconds(1), task => { - if (!Global.GameEventMgr.IsHolidayActive(HolidayIds.PiratesDay)) + if (!GameEventMgr.IsHolidayActive(HolidayIds.PiratesDay)) return; - me.PlayDirectMusic(12845); - task.Repeat(TimeSpan.FromSeconds(5)); // Every 5 second's SMSG_PLAY_MUSIC packet (PlayDirectMusic) is pushed to the client (sniffed value) + + me.PlayDirectMusic(MusicPirateDayMusic); + task.Repeat(TimeSpan.FromSeconds(5)); // Every 5 second's SmsgPlayMusic packet (PlayDirectMusic) is pushed to the client (sniffed value) }); } @@ -746,9 +581,40 @@ namespace Scripts.World.GameObjects } } + + struct BellHourlyConst + { + public const uint Belltollhorde = 6595; // Undercity + public const uint Belltolltribal = 6675; // Orgrimma/Thunderbluff + public const uint Belltollalliance = 6594; // Stormwind + public const uint Belltollnightelf = 6674; // Darnassus + public const uint Belltolldwarfgnome = 7234; // Ironforge + public const uint Belltollkharazhan = 9154; // Kharazhan + + public const uint GoHordeBell = 175885; + public const uint GoAllianceBell = 176573; + public const uint GoKharazhanBell = 182064; + + public const uint GameEventHourlyBells = 73; + public const uint EventRingBell = 1; + } + + enum BellHourlySoundZoneIds + { + TirisfalZone = 85, + UndercityZone = 1497, + DunMoroghZone = 1, + IronforgeZone = 1537, + TeldrassilZone = 141, + DarnassusZone = 1657, + AshenvaleZone = 331, + HillsbradFoothillsZone = 267, + DuskwoodZone = 10 + } + [Script] class go_bells : GameObjectAI - { + { uint _soundId; public go_bells(GameObject go) : base(go) { } @@ -759,59 +625,65 @@ namespace Scripts.World.GameObjects switch (me.GetEntry()) { - case GameObjectIds.HordeBell: + case BellHourlyConst.GoHordeBell: { - switch (zoneId) + switch ((BellHourlySoundZoneIds)zoneId) { - case ZoneIds.Tirisfal: - case ZoneIds.Undercity: - case ZoneIds.HillsbradFoothills: - case ZoneIds.Duskwood: - _soundId = SoundIds.BellTollHorde; // undead bell sound + case BellHourlySoundZoneIds.TirisfalZone: + case BellHourlySoundZoneIds.UndercityZone: + case BellHourlySoundZoneIds.HillsbradFoothillsZone: + case BellHourlySoundZoneIds.DuskwoodZone: + _soundId = BellHourlyConst.Belltollhorde; // undead bell sound break; default: - _soundId = SoundIds.BellTollTribal; // orc drum sound + _soundId = BellHourlyConst.Belltolltribal; // orc drum sound break; } break; } - case GameObjectIds.AllianceBell: + case BellHourlyConst.GoAllianceBell: { - switch (zoneId) + switch ((BellHourlySoundZoneIds)zoneId) { - case ZoneIds.Ironforge: - case ZoneIds.DunMorogh: - _soundId = SoundIds.BellTolldwarfgnome; // horn sound + case BellHourlySoundZoneIds.IronforgeZone: + case BellHourlySoundZoneIds.DunMoroghZone: + _soundId = BellHourlyConst.Belltolldwarfgnome; // horn sound break; - case ZoneIds.Darnassus: - case ZoneIds.Teldrassil: - case ZoneIds.Ashenvale: - _soundId = SoundIds.BellTollNightelf; // nightelf bell sound + case BellHourlySoundZoneIds.DarnassusZone: + case BellHourlySoundZoneIds.TeldrassilZone: + case BellHourlySoundZoneIds.AshenvaleZone: + _soundId = BellHourlyConst.Belltollnightelf; // nightelf bell sound break; default: - _soundId = SoundIds.BellTollAlliance; // human bell sound + _soundId = BellHourlyConst.Belltollalliance; // human bell sound break; } break; } - case GameObjectIds.KharazhanBell: - _soundId = SoundIds.BellTollKharazhan; + case BellHourlyConst.GoKharazhanBell: + { + _soundId = BellHourlyConst.Belltollkharazhan; break; + } } } public override void OnGameEvent(bool start, ushort eventId) { - if (eventId == Misc.GameEventHourlyBells && start) + if (eventId == BellHourlyConst.GameEventHourlyBells && start) { - var localTm = Time.UnixTimeToDateTime(GameTime.GetGameTime()).ToLocalTime(); + var localTm = GameTime.GetDateAndTime(); int _rings = localTm.Hour % 12; if (_rings == 0) // 00:00 and 12:00 + { _rings = 12; + } - // Dwarf hourly horn should only play a single time, each time the next hour begins. - if (_soundId == SoundIds.BellTolldwarfgnome) + // Dwarf hourly horn should only play a Math.Single time, each time the next hour begins. + if (_soundId == BellHourlyConst.Belltolldwarfgnome) + { _rings = 1; + } for (var i = 0; i < _rings; ++i) _scheduler.Schedule(TimeSpan.FromSeconds(i * 4 + 1), task => me.PlayDirectSound(_soundId)); diff --git a/Source/Scripts/World/ItemScripts.cs b/Source/Scripts/World/Item.cs similarity index 69% rename from Source/Scripts/World/ItemScripts.cs rename to Source/Scripts/World/Item.cs index 23240b234..1c5193841 100644 --- a/Source/Scripts/World/ItemScripts.cs +++ b/Source/Scripts/World/Item.cs @@ -5,67 +5,16 @@ using Framework.Constants; using Game.Entities; using Game.Scripting; using Game.Spells; -using System; using System.Collections.Generic; -using System.Numerics; +using static Global; -namespace Scripts.World.ItemScripts +namespace Scripts.World.Items { - struct SpellIds - { - //Onlyforflight - public const uint ArcaneCharges = 45072; - - //Petrovclusterbombs - public const uint PetrovBomb = 42406; - } - - struct CreatureIds - { - //Pilefakefur - public const uint NesingwaryTrapper = 25835; - - //Theemissary - public const uint Leviroth = 26452; - - //Capturedfrog - public const uint VanirasSentryTotem = 40187; - } - - struct GameObjectIds - { - //Pilefakefur - public const uint HighQualityFur = 187983; - public static uint[] CaribouTraps = - { - 187982, 187995, 187996, 187997, 187998, - 187999, 188000, 188001, 188002, 188003, - 188004, 188005, 188006, 188007, 188008, - }; - } - - struct QuestIds - { - //Helpthemselves - public const uint CannotHelpThemselves = 11876; - - //Theemissary - public const uint TheEmissary = 11626; - - //Capturedfrog - public const uint ThePerfectSpies = 25444; - } - - struct Misc - { - //Petrovclusterbombs - public const uint AreaIdShatteredStraits = 4064; - public const uint ZoneIdHowling = 495; - } - [Script] class item_only_for_flight : ItemScript { + const uint SpellArcaneCharges = 45072; + public item_only_for_flight() : base("item_only_for_flight") { } public override bool OnUse(Player player, Item item, SpellCastTargets targets, ObjectGuid castId) @@ -85,7 +34,7 @@ namespace Scripts.World.ItemScripts disabled = true; break; case 34475: - SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(SpellIds.ArcaneCharges, player.GetMap().GetDifficultyID()); + SpellInfo spellInfo = SpellMgr.GetSpellInfo(SpellArcaneCharges, player.GetMap().GetDifficultyID()); if (spellInfo != null) Spell.SendCastResult(player, spellInfo, default, castId, SpellCastResult.NotOnGround); break; @@ -108,7 +57,7 @@ namespace Scripts.World.ItemScripts public override bool OnUse(Player player, Item item, SpellCastTargets targets, ObjectGuid castId) { - if (targets.GetUnitTarget() && targets.GetUnitTarget().IsTypeId(TypeId.Unit) && + if (targets.GetUnitTarget() != null && targets.GetUnitTarget().GetTypeId() == TypeId.Unit && targets.GetUnitTarget().GetEntry() == 20748 && !targets.GetUnitTarget().HasAura(32578)) return false; @@ -125,7 +74,7 @@ namespace Scripts.World.ItemScripts public override bool OnExpire(Player player, ItemTemplate pItemProto) { List dest = new(); - InventoryResult msg = player.CanStoreNewItem(ItemConst.NullBag, ItemConst.NullSlot, dest, 39883, 1); // Cracked Egg + var msg = player.CanStoreNewItem(ItemConst.NullBag, ItemConst.NullSlot, dest, 39883, 1); // Cracked Egg if (msg == InventoryResult.Ok) player.StoreNewItem(dest, 39883, true, ItemEnchantmentManager.GenerateItemRandomBonusListId(39883)); @@ -141,7 +90,7 @@ namespace Scripts.World.ItemScripts public override bool OnExpire(Player player, ItemTemplate pItemProto) { List dest = new(); - InventoryResult msg = player.CanStoreNewItem(ItemConst.NullBag, ItemConst.NullSlot, dest, 44718, 1); // Ripe Disgusting Jar + var msg = player.CanStoreNewItem(ItemConst.NullBag, ItemConst.NullSlot, dest, 44718, 1); // Ripe Disgusting Jar if (msg == InventoryResult.Ok) player.StoreNewItem(dest, 44718, true, ItemEnchantmentManager.GenerateItemRandomBonusListId(44718)); @@ -152,16 +101,20 @@ namespace Scripts.World.ItemScripts [Script] class item_petrov_cluster_bombs : ItemScript { + const uint SpellPetrovBomb = 42406; + const uint AreaIdShatteredStraits = 4064; + const uint ZoneIdHowling = 495; + public item_petrov_cluster_bombs() : base("item_petrov_cluster_bombs") { } public override bool OnUse(Player player, Item item, SpellCastTargets targets, ObjectGuid castId) { - if (player.GetZoneId() != Misc.ZoneIdHowling) + if (player.GetZoneId() != ZoneIdHowling) return false; - if (player.GetTransport() == null || player.GetAreaId() != Misc.AreaIdShatteredStraits) + if (player.GetTransport() == null || player.GetAreaId() != AreaIdShatteredStraits) { - SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(SpellIds.PetrovBomb, Difficulty.None); + SpellInfo spellInfo = SpellMgr.GetSpellInfo(SpellPetrovBomb, Difficulty.None); if (spellInfo != null) Spell.SendCastResult(player, spellInfo, default, castId, SpellCastResult.NotHere); @@ -175,13 +128,16 @@ namespace Scripts.World.ItemScripts [Script] class item_captured_frog : ItemScript { + const uint QuestThePerfectSpies = 25444; + const uint NpcVanirasSentryTotem = 40187; + public item_captured_frog() : base("item_captured_frog") { } public override bool OnUse(Player player, Item item, SpellCastTargets targets, ObjectGuid castId) { - if (player.GetQuestStatus(QuestIds.ThePerfectSpies) == QuestStatus.Incomplete) + if (player.GetQuestStatus(QuestThePerfectSpies) == QuestStatus.Incomplete) { - if (player.FindNearestCreature(CreatureIds.VanirasSentryTotem, 10.0f)) + if (player.FindNearestCreature(NpcVanirasSentryTotem, 10.0f) != null) return false; else player.SendEquipError(InventoryResult.OutOfRange, item, null); @@ -192,8 +148,8 @@ namespace Scripts.World.ItemScripts } } - [Script] // Only used currently for - // 19169: Nightfall + // Only used currently for + [Script] // 19169: Nightfall class item_generic_limit_chance_above_60 : ItemScript { public item_generic_limit_chance_above_60() : base("item_generic_limit_chance_above_60") { } @@ -214,5 +170,4 @@ namespace Scripts.World.ItemScripts return true; } } -} - +} \ No newline at end of file diff --git a/Source/Scripts/World/NpcGuard.cs b/Source/Scripts/World/NpcGuard.cs index 4e46ebcce..73affa05a 100644 --- a/Source/Scripts/World/NpcGuard.cs +++ b/Source/Scripts/World/NpcGuard.cs @@ -8,41 +8,24 @@ using Game.Entities; using Game.Scripting; using Game.Spells; using System; +using static Global; namespace Scripts.World.NpcGuard { - struct SpellIds - { - public const uint BanishedShattrathA = 36642; - public const uint BanishedShattrathS = 36671; - public const uint BanishTeleport = 36643; - public const uint Exile = 39533; - } - - struct TextIds - { - public const uint SayGuardSilAggro = 0; - } - - - struct CreatureIds - { - public const uint CenarionHoldInfantry = 15184; - public const uint StormwindCityGuard = 68; - public const uint StormwindCityPatroller = 1976; - public const uint OrgrimmarGrunt = 3296; - public const uint AldorVindicator = 18549; - } - [Script] class npc_guard_generic : GuardAI { - TaskScheduler _combatScheduler; + const uint SayGuardSilAggro = 0; + const uint NpcCenarionHoldInfantry = 15184; + const uint NpcStormwindCityGuard = 68; + const uint NpcStormwindCityPatroller = 1976; + const uint NpcOrgrimmarGrunt = 3296; + + TaskScheduler _combatScheduler = new(); public npc_guard_generic(Creature creature) : base(creature) { _scheduler.SetValidator(() => !me.HasUnitState(UnitState.Casting) && !me.IsInEvadeMode() && me.IsAlive()); - _combatScheduler = new TaskScheduler(); _combatScheduler.SetValidator(() => !me.HasUnitState(UnitState.Casting)); } @@ -50,14 +33,14 @@ namespace Scripts.World.NpcGuard { _scheduler.CancelAll(); _combatScheduler.CancelAll(); - _scheduler.Schedule(TimeSpan.FromSeconds(1), task => + _scheduler.Schedule(TimeSpan.FromSeconds(1), context => { // Find a spell that targets friendly and applies an aura (these are generally buffs) SpellInfo spellInfo = SelectSpell(me, 0, 0, SelectTargetType.AnyFriend, 0, 0, SelectEffect.Aura); if (spellInfo != null) DoCast(me, spellInfo.Id); - task.Repeat(TimeSpan.FromMinutes(10)); + context.Repeat(TimeSpan.FromMinutes(10)); }); } @@ -90,9 +73,9 @@ namespace Scripts.World.NpcGuard { switch (me.GetEntry()) { - case CreatureIds.StormwindCityGuard: - case CreatureIds.StormwindCityPatroller: - case CreatureIds.OrgrimmarGrunt: + case NpcStormwindCityGuard: + case NpcStormwindCityPatroller: + case NpcOrgrimmarGrunt: break; default: return; @@ -106,15 +89,15 @@ namespace Scripts.World.NpcGuard public override void JustEngagedWith(Unit who) { - if (me.GetEntry() == CreatureIds.CenarionHoldInfantry) - Talk(TextIds.SayGuardSilAggro, who); + if (me.GetEntry() == NpcCenarionHoldInfantry) + Talk(SayGuardSilAggro, who); - _combatScheduler.Schedule(TimeSpan.FromSeconds(1), task => + _combatScheduler.Schedule(TimeSpan.FromSeconds(1), meleeContext => { Unit victim = me.GetVictim(); if (!me.IsAttackReady() || !me.IsWithinMeleeRange(victim)) { - task.Repeat(); + meleeContext.Repeat(); return; } if (RandomHelper.randChance(20)) @@ -124,16 +107,14 @@ namespace Scripts.World.NpcGuard { me.ResetAttackTimer(); DoCastVictim(spellInfo.Id); - task.Repeat(); + meleeContext.Repeat(); return; } } - me.AttackerStateUpdate(victim); me.ResetAttackTimer(); - task.Repeat(); - }); - _combatScheduler.Schedule(TimeSpan.FromSeconds(5), task => + meleeContext.Repeat(); + }).Schedule(TimeSpan.FromSeconds(5), spellContext => { bool healing = false; SpellInfo spellInfo = null; @@ -155,10 +136,10 @@ namespace Scripts.World.NpcGuard DoCast(me, spellInfo.Id); else DoCastVictim(spellInfo.Id); - task.Repeat(TimeSpan.FromSeconds(5)); + spellContext.Repeat(TimeSpan.FromSeconds(5)); } else - task.Repeat(TimeSpan.FromSeconds(1)); + spellContext.Repeat(TimeSpan.FromSeconds(1)); }); } @@ -176,6 +157,12 @@ namespace Scripts.World.NpcGuard [Script] class npc_guard_shattrath_faction : GuardAI { + const uint NpcAldorVindicator = 18549; + const uint SpellBanishedShattrathA = 36642; + const uint SpellBanishedShattrathS = 36671; + const uint SpellBanishTeleport = 36643; + const uint SpellExile = 39533; + public npc_guard_shattrath_faction(Creature creature) : base(creature) { _scheduler.SetValidator(() => !me.HasUnitState(UnitState.Casting)); @@ -196,31 +183,33 @@ namespace Scripts.World.NpcGuard if (!UpdateVictim()) return; - _scheduler.Update(diff, base.DoMeleeAttackIfReady); + _scheduler.Update(diff, DoMeleeAttackIfReady); } void ScheduleVanish() { - _scheduler.Schedule(TimeSpan.FromSeconds(5), task => + _scheduler.Schedule(TimeSpan.FromSeconds(5), banishContext => { Unit temp = me.GetVictim(); - if (temp && temp.IsTypeId(TypeId.Player)) + if (temp != null && temp.IsPlayer()) { - DoCast(temp, me.GetEntry() == CreatureIds.AldorVindicator ? SpellIds.BanishedShattrathS : SpellIds.BanishedShattrathA); + DoCast(temp, me.GetEntry() == NpcAldorVindicator ? SpellBanishedShattrathS : SpellBanishedShattrathA); ObjectGuid playerGUID = temp.GetGUID(); - task.Schedule(TimeSpan.FromSeconds(9), task => + banishContext.Schedule(TimeSpan.FromSeconds(9), exileContext => { - Unit temp = Global.ObjAccessor.GetUnit(me, playerGUID); - if (temp) + Unit temp = ObjAccessor.GetUnit(me, playerGUID); + if (temp != null) { - temp.CastSpell(temp, SpellIds.Exile, true); - temp.CastSpell(temp, SpellIds.BanishTeleport, true); + temp.CastSpell(temp, SpellExile, true); + temp.CastSpell(temp, SpellBanishTeleport, true); } ScheduleVanish(); + + }); } else - task.Repeat(); + banishContext.Repeat(); }); } } diff --git a/Source/Scripts/World/NpcInnkeeper.cs b/Source/Scripts/World/NpcInnkeeper.cs index 61f6cad3a..86c7a97b0 100644 --- a/Source/Scripts/World/NpcInnkeeper.cs +++ b/Source/Scripts/World/NpcInnkeeper.cs @@ -1,44 +1,38 @@ // Copyright (c) CypherCore All rights reserved. // Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information. +using Framework.Constants; +using Game.AI; using Game.Entities; using Game.Scripting; -using Game.AI; -using Framework.Constants; +using static Global; namespace Scripts.World.NpcInnkeeper { - struct SpellIds - { - public const uint TrickOrTreated = 24755; - public const uint Treat = 24715; - } - - struct Gossip - { - public const uint MenuId = 9733; - public const uint MenuEventId = 342; - } - [Script] class npc_innkeeper : ScriptedAI { + const uint SpellTrickOrTreated = 24755; + const uint SpellTreat = 24715; + + const uint NpcGossipMenu = 9733; + const uint NpcGossipMenuEvent = 342; + public npc_innkeeper(Creature creature) : base(creature) { } public override bool OnGossipHello(Player player) { - player.InitGossipMenu(Gossip.MenuId); - if (Global.GameEventMgr.IsHolidayActive(HolidayIds.HallowsEnd) && !player.HasAura(SpellIds.TrickOrTreated)) - player.AddGossipItem(Gossip.MenuEventId, 0, eTradeskill.GossipSenderMain, eTradeskill.GossipActionInfoDef + 1); + player.InitGossipMenu(NpcGossipMenu); + if (GameEventMgr.IsHolidayActive(HolidayIds.HallowsEnd) && !player.HasAura(SpellTrickOrTreated)) + player.AddGossipItem(NpcGossipMenuEvent, 0, eTradeskill.GossipSenderMain, eTradeskill.GossipActionInfoDef + 1); if (me.IsQuestGiver()) - player.PrepareQuestMenu(me.GetGUID()); - if (me.IsVendor()) - player.AddGossipItem(Gossip.MenuId, 2, eTradeskill.GossipSenderMain, eTradeskill.GossipActionTrade); + if (me.IsVendor()) + player.AddGossipItem(NpcGossipMenu, 2, eTradeskill.GossipSenderMain, eTradeskill.GossipActionTrade); if (me.IsInnkeeper()) - player.AddGossipItem(Gossip.MenuId, 1, eTradeskill.GossipSenderMain, eTradeskill.GossipActionInn); + player.AddGossipItem(NpcGossipMenu, 1, eTradeskill.GossipSenderMain, eTradeskill.GossipActionInn); player.TalkedToCreature(me.GetEntry(), me.GetGUID()); player.SendGossipMenu(player.GetGossipTextId(me), me.GetGUID()); @@ -49,67 +43,39 @@ namespace Scripts.World.NpcInnkeeper { uint action = player.PlayerTalkClass.GetGossipOptionAction(gossipListId); player.ClearGossipMenu(); - if (action == eTradeskill.GossipActionInfoDef + 1 && Global.GameEventMgr.IsHolidayActive(HolidayIds.HallowsEnd) && !player.HasAura(SpellIds.TrickOrTreated)) + if (action == eTradeskill.GossipActionInfoDef + 1 && GameEventMgr.IsHolidayActive(HolidayIds.HallowsEnd) && !player.HasAura(SpellTrickOrTreated)) { - player.CastSpell(player, SpellIds.TrickOrTreated, true); + player.CastSpell(player, SpellTrickOrTreated, true); - if (RandomHelper.IRand(0, 1) != 0) - player.CastSpell(player, SpellIds.Treat, true); + if (RandomHelper.URand(0, 1) != 0) + player.CastSpell(player, SpellTreat, true); else { uint trickspell = 0; - switch (RandomHelper.IRand(0, 13)) + switch (RandomHelper.URand(0, 13)) { - case 0: - trickspell = 24753; - break; // cannot cast, random 30sec - case 1: - trickspell = 24713; - break; // lepper gnome costume - case 2: - trickspell = 24735; - break; // male ghost costume - case 3: - trickspell = 24736; - break; // female ghostcostume - case 4: - trickspell = 24710; - break; // male ninja costume - case 5: - trickspell = 24711; - break; // female ninja costume - case 6: - trickspell = 24708; - break; // male pirate costume - case 7: - trickspell = 24709; - break; // female pirate costume - case 8: - trickspell = 24723; - break; // skeleton costume - case 9: - trickspell = 24753; - break; // Trick - case 10: - trickspell = 24924; - break; // Hallow's End Candy - case 11: - trickspell = 24925; - break; // Hallow's End Candy - case 12: - trickspell = 24926; - break; // Hallow's End Candy - case 13: - trickspell = 24927; - break; // Hallow's End Candy + case 0: trickspell = 24753; break; // cannot cast, random TimeSpan.FromSeconds(30)ec + case 1: trickspell = 24713; break; // lepper gnome Costume + case 2: trickspell = 24735; break; // male ghost Costume + case 3: trickspell = 24736; break; // female ghostCostume + case 4: trickspell = 24710; break; // male ninja Costume + case 5: trickspell = 24711; break; // female ninja Costume + case 6: trickspell = 24708; break; // male pirate Costume + case 7: trickspell = 24709; break; // female pirate Costume + case 8: trickspell = 24723; break; // skeleton Costume + case 9: trickspell = 24753; break; // Trick + case 10: trickspell = 24924; break; // Hallow's End Candy + case 11: trickspell = 24925; break; // Hallow's End Candy + case 12: trickspell = 24926; break; // Hallow's End Candy + case 13: trickspell = 24927; break; // Hallow's End Candy } player.CastSpell(player, trickspell, true); } - player.CloseGossipMenu(); + player.ClearGossipMenu(); return true; } - player.CloseGossipMenu(); + player.ClearGossipMenu(); switch (action) { diff --git a/Source/Scripts/World/NpcProfessions.cs b/Source/Scripts/World/NpcProfessions.cs new file mode 100644 index 000000000..6e153f9a4 --- /dev/null +++ b/Source/Scripts/World/NpcProfessions.cs @@ -0,0 +1,1175 @@ +// Copyright (c) CypherCore All rights reserved. +// Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information. + +using Framework.Constants; +using Game.AI; +using Game.Entities; +using Game.Scripting; +using Game.Spells; +using static Global; + +namespace Scripts.World.NpcProfessions +{ + struct SpellIds + { + public const uint Weapon = 9787; + public const uint Armor = 9788; + public const uint Hammer = 17040; + public const uint Axe = 17041; + public const uint Sword = 17039; + + public const uint LearnWeapon = 9789; + public const uint LearnArmor = 9790; + public const uint LearnHammer = 39099; + public const uint LearnAxe = 39098; + public const uint LearnSword = 39097; + + public const uint UnlearnWeapon = 36436; + public const uint UnlearnArmor = 36435; + public const uint UnlearnHammer = 36441; + public const uint UnlearnAxe = 36439; + public const uint UnlearnSword = 36438; + + public const uint RepArmor = 17451; + public const uint RepWeapon = 17452; + + public const uint Dragon = 10656; + public const uint Elemental = 10658; + public const uint Tribal = 10660; + + public const uint LearnDragon = 10657; + public const uint LearnElemental = 10659; + public const uint LearnTribal = 10661; + + public const uint UnlearnDragon = 36434; + public const uint UnlearnElemental = 36328; + public const uint UnlearnTribal = 36433; + + public const uint Goblin = 20222; + public const uint Gnomish = 20219; + + public const uint LearnGoblin = 20221; + public const uint LearnGnomish = 20220; + + public const uint Spellfire = 26797; + public const uint Mooncloth = 26798; + public const uint Shadoweave = 26801; + + public const uint LearnSpellfire = 26796; + public const uint LearnMooncloth = 26799; + public const uint LearnShadoweave = 26800; + + public const uint UnlearnSpellfire = 41299; + public const uint UnlearnMooncloth = 41558; + public const uint UnlearnShadoweave = 41559; + + public const uint Transmute = 28672; + public const uint Elixir = 28677; + public const uint Potion = 28675; + + public const uint LearnTransmute = 28674; + public const uint LearnElixir = 28678; + public const uint LearnPotion = 28676; + + public const uint UnlearnTransmute = 41565; + public const uint UnlearnElixir = 41564; + public const uint UnlearnPotion = 41563; + + // EngineeringTrinkets + public const uint LearnToEverlook = 23490; + public const uint LearnToGadget = 23491; + public const uint LearnToArea52 = 36956; + public const uint LearnToToshley = 36957; + + public const uint ToEverlook = 23486; + public const uint ToGadget = 23489; + public const uint ToArea52 = 36954; + public const uint ToToshley = 36955; + } + + struct CreatureIds + { + //Alchemy + public const uint TrainerTransmute = 22427; // Zarevhi + public const uint TrainerElixir = 19052; // Lorokeem + public const uint TrainerPotion = 17909; // Lauranna Thar'well + + //Blacksmithing + public const uint TrainerSmithomni1 = 11145; // Myolor Sunderfury + public const uint TrainerSmithomni2 = 11176; // Krathok Moltenfist + public const uint TrainerWeapon1 = 11146; // Ironus Coldsteel + public const uint TrainerWeapon2 = 11178; // Borgosh Corebender + public const uint TrainerArmor1 = 5164; // Grumnus Steelshaper + public const uint TrainerArmor2 = 11177; // Okothos Ironrager + public const uint TrainerHammer = 11191; // Lilith the Lithe + public const uint TrainerAxe = 11192; // Kilram + public const uint TrainerSword = 11193; // Seril Scourgebane + + //Leatherworking + public const uint TrainerDragon1 = 7866; // Peter Galen + public const uint TrainerDragon2 = 7867; // Thorkaf Dragoneye + public const uint TrainerElemental1 = 7868; // Sarah Tanner + public const uint TrainerElemental2 = 7869; // Brumn Winterhoof + public const uint TrainerTribal1 = 7870; // Caryssia Moonhunter + public const uint TrainerTribal2 = 7871; // Se'Jib + + //Tailoring + public const uint TrainerSpellfire = 22213; // Gidge Spellweaver + public const uint TrainerMooncloth = 22208; // Nasmara Moonsong + public const uint TrainerShadoweave = 22212; // Andrion Darkspinner + + // EngineeringTrinkets + public const uint Zap = 14742; + public const uint Jhordy = 14743; + public const uint Kablam = 21493; + public const uint Smiles = 21494; + } + + struct QuestIds + { + //Alchemy + public const uint MasterTransmute = 10899; + public const uint MasterElixir = 10902; + public const uint MasterPotion = 10897; + } + + struct ProfessionConst + { + public const string GossipTextBrowseGoods = "I'd like to browse your goods."; + public const string GossipTextTrain = "Train me!"; + + public const uint RepArmor = 46; + public const uint RepWeapon = 289; + public const uint RepHammer = 569; + public const uint RepAxe = 570; + public const uint RepSword = 571; + + public const uint TrainerIdAlchemy = 122; + public const uint TrainerIdBlacksmithing = 80; + public const uint TrainerIdLeatherworking = 103; + public const uint TrainerIdTailoring = 117; + + public const string TalkMustUnlearnWeapon = "You must forget your weapon type specialty before I can help you. Go to Everlook in Winterspring and seek help there."; + + public const string TalkHammerLearn = "Ah, a seasoned veteran you once were. I know you are capable, you merely need to ask and I shall teach you the way of the hammersmith."; + public const string TalkAxeLearn = "Ah, a seasoned veteran you once were. I know you are capable, you merely need to ask and I shall teach you the way of the axesmith."; + public const string TalkSwordLearn = "Ah, a seasoned veteran you once were. I know you are capable, you merely need to ask and I shall teach you the way of the swordsmith."; + + public const string TalkHammerUnlearn = "Forgetting your Hammersmithing skill is not something to do lightly. If you choose to abandon it you will forget all recipes that require Hammersmithing to create!"; + public const string TalkAxeUnlearn = "Forgetting your Axesmithing skill is not something to do lightly. If you choose to abandon it you will forget all recipes that require Axesmithing to create!"; + public const string TalkSwordUnlearn = "Forgetting your Swordsmithing skill is not something to do lightly. If you choose to abandon it you will forget all recipes that require Swordsmithing to create!"; + + public const uint GossipSenderLearn = 50; + public const uint GossipSenderUnlearn = 51; + public const uint GossipSenderCheck = 52; + + public const string GossipLearnPotion = "Please teach me how to become a Master of Potions, Lauranna"; + public const string GossipUnlearnPotion = "I wish to unlearn Potion Mastery"; + public const string GossipLearnTransmute = "Please teach me how to become a Master of Transmutations, Zarevhi"; + public const string GossipUnlearnTransmute = "I wish to unlearn Transmutation Mastery"; + public const string GossipLearnElixir = "Please teach me how to become a Master of Elixirs, Lorokeem"; + public const string GossipUnlearnElixir = "I wish to unlearn Elixir Mastery"; + + public const string BoxUnlearnAlchemySpec = "Do you really want to unlearn your alchemy specialty and lose all associated recipes? \n Cost: "; + + public const string GossipWeaponLearn = "Please teach me how to become a Weaponsmith"; + public const string GossipWeaponUnlearn = "I wish to unlearn the art of Weaponsmithing"; + public const string GossipArmorLearn = "Please teach me how to become a Armorsmith"; + public const string GossipArmorUnlearn = "I wish to unlearn the art of Armorsmithing"; + + public const string GossipUnlearnSmithSpec = "I wish to unlearn my blacksmith specialty"; + public const string BoxUnlearnAmorOrWeapon = "Do you really want to unlearn your blacksmith specialty and lose all associated recipes? \n Cost: "; + + public const string GossipLearnHammer = "Please teach me how to become a Hammersmith, Lilith"; + public const string GossipUnlearnHammer = "I wish to unlearn Hammersmithing"; + public const string GossipLearnAxe = "Please teach me how to become a Axesmith, Kilram"; + public const string GossipUnlearnAxe = "I wish to unlearn Axesmithing"; + public const string GossipLearnSword = "Please teach me how to become a Swordsmith, Seril"; + public const string GossipUnlearnSword = "I wish to unlearn Swordsmithing"; + + public const string BoxUnlearnWeaponSpec = "Do you really want to unlearn your weaponsmith specialty and lose all associated recipes? \n Cost: "; + + public const string GossipUnlearnDragon = "I wish to unlearn Dragonscale Leatherworking"; + public const string GossipUnlearnElemental = "I wish to unlearn Elemental Leatherworking"; + public const string GossipUnlearnTribal = "I wish to unlearn Tribal Leatherworking"; + + public const string BoxUnlearnLeatherSpec = "Do you really want to unlearn your leatherworking specialty and lose all associated recipes? \n Cost: "; + + public const string GossipLearnSpellfire = "Please teach me how to become a Spellcloth tailor"; + public const string GossipUnlearnSpellfire = "I wish to unlearn Spellfire Tailoring"; + public const string GossipLearnMooncloth = "Please teach me how to become a Mooncloth tailor"; + public const string GossipUnlearnMooncloth = "I wish to unlearn Mooncloth Tailoring"; + public const string GossipLearnShadoweave = "Please teach me how to become a Shadoweave tailor"; + public const string GossipUnlearnShadoweave = "I wish to unlearn Shadoweave Tailoring"; + + public const string BoxUnlearnTailorSpec = "Do you really want to unlearn your tailoring specialty and lose all associated recipes? \n Cost: "; + + public const uint GossipOptionAlchemy = 0; + public const uint GossipOptionBlacksmithing = 1; + public const uint GossipOptionEnchanting = 2; + public const uint GossipOptionEngineering = 3; + public const uint GossipOptionHerbalism = 4; + public const uint GossipOptionInscription = 5; + public const uint GossipOptionJewelcrafting = 6; + public const uint GossipOptionLeatherworking = 7; + public const uint GossipOptionMining = 8; + public const uint GossipOptionSkinning = 9; + public const uint GossipOptionTailoring = 10; + public const uint GossipOptionMulti = 11; + + public const uint GossipMenuHerbalism = 12188; + public const uint GossipMenuMining = 12189; + public const uint GossipMenuSkinning = 12190; + public const uint GossipMenuAlchemy = 12191; + public const uint GossipMenuBlacksmithing = 12192; + public const uint GossipMenuEnchanting = 12193; + public const uint GossipMenuEngineering = 12195; + public const uint GossipMenuInscription = 12196; + public const uint GossipMenuJewelcrafting = 12197; + public const uint GossipMenuLeatherworking = 12198; + public const uint GossipMenuTailoring = 12199; + + public const string GossipItemZap = "This Dimensional Imploder sounds dangerous! How can I make one?"; + public const string GossipItemJhordy = "I must build a beacon for this marvelous device!"; + public const string GossipItemKablam = "[PH] Unknown"; + + public static int DoLearnCost(Player player) //tailor, alchemy + { + return 200000; + } + + public static int DoHighUnlearnCost(Player player) //tailor, alchemy + { + return 1500000; + } + + public static int DoMedUnlearnCost(Player player) //blacksmith, leatherwork + { + uint level = player.GetLevel(); + if (level < 51) + return 250000; + else if (level < 66) + return 500000; + else + return 1000000; + } + + public static int DoLowUnlearnCost(Player player) //blacksmith + { + uint level = player.GetLevel(); + if (level < 66) + return 50000; + else + return 100000; + } + + public static void ProcessCastaction(Player player, Creature creature, uint spellId, uint triggeredSpellId, int Cost) + { + if (!(spellId != 0 && player.HasSpell(spellId)) && player.HasEnoughMoney(Cost)) + { + player.CastSpell(player, triggeredSpellId, true); + player.ModifyMoney(-Cost); + } + else + player.SendBuyError(BuyResult.NotEnoughtMoney, creature, 0); + player.CloseGossipMenu(); + } + + static bool EquippedOk(Player player, uint spellId) + { + SpellInfo spell = SpellMgr.GetSpellInfo(spellId, Difficulty.None); + if (spell == null) + return false; + + foreach (SpellEffectInfo spellEffectInfo in spell.GetEffects()) + { + uint reqSpell = spellEffectInfo.TriggerSpell; + if (reqSpell == 0) + continue; + + Item item; + for (byte j = EquipmentSlot.Start; j < EquipmentSlot.End; ++j) + { + item = player.GetItemByPos(InventorySlots.Bag0, j); + if (item != null && item.GetTemplate().GetRequiredSpell() == reqSpell) + { + //player has item equipped that require specialty. Not allow to unlearn, player has to unequip first + Log.outDebug(LogFilter.Scripts, $"player attempt to unlearn spell {reqSpell}, but item {item.GetEntry()} is equipped."); + return false; + } + } + } + return true; + } + + static void ProfessionUnlearnSpells(Player player, uint type) + { + switch (type) + { + case SpellIds.UnlearnWeapon: // SUnlearnWeapon + player.RemoveSpell(36125); // Light Earthforged Blade + player.RemoveSpell(36128); // Light Emberforged Hammer + player.RemoveSpell(36126); // Light Skyforged Axe + break; + case SpellIds.UnlearnArmor: // SUnlearnArmor + player.RemoveSpell(36122); // Earthforged Leggings + player.RemoveSpell(36129); // Heavy Earthforged Breastplate + player.RemoveSpell(36130); // Stormforged Hauberk + player.RemoveSpell(34533); // Breastplate of Kings + player.RemoveSpell(34529); // Nether Chain Shirt + player.RemoveSpell(34534); // Bulwark of Kings + player.RemoveSpell(36257); // Bulwark of the Ancient Kings + player.RemoveSpell(36256); // Embrace of the Twisting Nether + player.RemoveSpell(34530); // Twisting Nether Chain Shirt + player.RemoveSpell(36124); // Windforged Leggings + break; + case SpellIds.UnlearnHammer: // SUnlearnHammer + player.RemoveSpell(36262); // Dragonstrike + player.RemoveSpell(34546); // Dragonmaw + player.RemoveSpell(34545); // Drakefist Hammer + player.RemoveSpell(36136); // Lavaforged Warhammer + player.RemoveSpell(34547); // Thunder + player.RemoveSpell(34567); // Deep Thunder + player.RemoveSpell(36263); // Stormherald + player.RemoveSpell(36137); // Great Earthforged Hammer + break; + case SpellIds.UnlearnAxe: // SUnlearnAxe + player.RemoveSpell(36260); // Wicked Edge of the Planes + player.RemoveSpell(34562); // Black Planar Edge + player.RemoveSpell(34541); // The Planar Edge + player.RemoveSpell(36134); // Stormforged Axe + player.RemoveSpell(36135); // Skyforged Great Axe + player.RemoveSpell(36261); // Bloodmoon + player.RemoveSpell(34543); // Lunar Crescent + player.RemoveSpell(34544); // Mooncleaver + break; + case SpellIds.UnlearnSword: // SUnlearnSword + player.RemoveSpell(36258); // Blazefury + player.RemoveSpell(34537); // Blazeguard + player.RemoveSpell(34535); // Fireguard + player.RemoveSpell(36131); // Windforged Rapier + player.RemoveSpell(36133); // Stoneforged Claymore + player.RemoveSpell(34538); // Lionheart Blade + player.RemoveSpell(34540); // Lionheart Chapion + player.RemoveSpell(36259); // Lionheart Executioner + break; + case SpellIds.UnlearnDragon: // SUnlearnDragon + player.RemoveSpell(36076); // Dragonstrike Leggings + player.RemoveSpell(36079); // Golden Dragonstrike Breastplate + player.RemoveSpell(35576); // Ebon Netherscale Belt + player.RemoveSpell(35577); // Ebon Netherscale Bracers + player.RemoveSpell(35575); // Ebon Netherscale Breastplate + player.RemoveSpell(35582); // Netherstrike Belt + player.RemoveSpell(35584); // Netherstrike Bracers + player.RemoveSpell(35580); // Netherstrike Breastplate + break; + case SpellIds.UnlearnElemental: // SUnlearnElemental + player.RemoveSpell(36074); // Blackstorm Leggings + player.RemoveSpell(36077); // Primalstorm Breastplate + player.RemoveSpell(35590); // Primalstrike Belt + player.RemoveSpell(35591); // Primalstrike Bracers + player.RemoveSpell(35589); // Primalstrike Vest + break; + case SpellIds.UnlearnTribal: // SUnlearnTribal + player.RemoveSpell(35585); // Windhawk Hauberk + player.RemoveSpell(35587); // Windhawk Belt + player.RemoveSpell(35588); // Windhawk Bracers + player.RemoveSpell(36075); // Wildfeather Leggings + player.RemoveSpell(36078); // Living Crystal Breastplate + break; + case SpellIds.UnlearnSpellfire: // SUnlearnSpellfire + player.RemoveSpell(26752); // Spellfire Belt + player.RemoveSpell(26753); // Spellfire Gloves + player.RemoveSpell(26754); // Spellfire Robe + break; + case SpellIds.UnlearnMooncloth: // SUnlearnMooncloth + player.RemoveSpell(26760); // Primal Mooncloth Belt + player.RemoveSpell(26761); // Primal Mooncloth Shoulders + player.RemoveSpell(26762); // Primal Mooncloth Robe + break; + case SpellIds.UnlearnShadoweave: // SUnlearnShadoweave + player.RemoveSpell(26756); // Frozen Shadoweave Shoulders + player.RemoveSpell(26757); // Frozen Shadoweave Boots + player.RemoveSpell(26758); // Frozen Shadoweave Robe + break; + } + } + + public static void ProcessUnlearnAction(Player player, Creature creature, uint spellId, uint alternativeSpellId, int Cost) + { + if (EquippedOk(player, spellId)) + { + if (player.HasEnoughMoney(Cost)) + { + player.CastSpell(player, spellId, true); + ProfessionUnlearnSpells(player, spellId); + player.ModifyMoney(-Cost); + if (alternativeSpellId != 0) + creature.CastSpell(player, alternativeSpellId, true); + } + else + player.SendBuyError(BuyResult.NotEnoughtMoney, creature, 0); + } + else + player.SendEquipError(InventoryResult.ClientLockedOut, null, null); + player.CloseGossipMenu(); + } + + } + [Script] + class npc_prof_alchemy : ScriptedAI + { + public npc_prof_alchemy(Creature creature) : base(creature) { } + + bool HasAlchemySpell(Player player) + { + return player.HasSpell(SpellIds.Transmute) || player.HasSpell(SpellIds.Elixir) || player.HasSpell(SpellIds.Potion); + } + + public override bool OnGossipHello(Player player) + { + if (me.IsQuestGiver()) + + if (me.IsVendor()) + player.AddGossipItem(GossipOptionNpc.Vendor, "I'd like to browse your goods.", eTradeskill.GossipSenderMain, eTradeskill.GossipActionTrade); + + if (me.IsTrainer()) + player.AddGossipItem(GossipOptionNpc.Trainer, "Train me!", eTradeskill.GossipSenderMain, eTradeskill.GossipActionTrain); + + if (player.HasSkill(SkillType.Alchemy) && player.GetBaseSkillValue(SkillType.Alchemy) >= 350 && player.GetLevel() > 67) + { + if (player.GetQuestRewardStatus(QuestIds.MasterTransmute) || player.GetQuestRewardStatus(QuestIds.MasterElixir) || player.GetQuestRewardStatus(QuestIds.MasterPotion)) + { + switch (me.GetEntry()) + { + case CreatureIds.TrainerTransmute: //Zarevhi + if (!HasAlchemySpell(player)) + player.AddGossipItem(GossipOptionNpc.None, ProfessionConst.GossipLearnTransmute, ProfessionConst.GossipSenderLearn, eTradeskill.GossipActionInfoDef + 1); + if (player.HasSpell(SpellIds.Transmute)) + player.AddGossipItem(GossipOptionNpc.None, ProfessionConst.GossipUnlearnTransmute, ProfessionConst.GossipSenderUnlearn, eTradeskill.GossipActionInfoDef + 4); + break; + case CreatureIds.TrainerElixir: //Lorokeem + if (!HasAlchemySpell(player)) + player.AddGossipItem(GossipOptionNpc.None, ProfessionConst.GossipLearnElixir, ProfessionConst.GossipSenderLearn, eTradeskill.GossipActionInfoDef + 2); + if (player.HasSpell(SpellIds.Elixir)) + player.AddGossipItem(GossipOptionNpc.None, ProfessionConst.GossipUnlearnElixir, ProfessionConst.GossipSenderUnlearn, eTradeskill.GossipActionInfoDef + 5); + break; + case CreatureIds.TrainerPotion: //Lauranna Thar'well + if (!HasAlchemySpell(player)) + player.AddGossipItem(GossipOptionNpc.None, ProfessionConst.GossipLearnPotion, ProfessionConst.GossipSenderLearn, eTradeskill.GossipActionInfoDef + 3); + if (player.HasSpell(SpellIds.Potion)) + player.AddGossipItem(GossipOptionNpc.None, ProfessionConst.GossipUnlearnPotion, ProfessionConst.GossipSenderUnlearn, eTradeskill.GossipActionInfoDef + 6); + break; + } + } + } + + player.SendGossipMenu(player.GetGossipTextId(me), me.GetGUID()); + return true; + } + + void SendActionMenu(Player player, uint action) + { + switch (action) + { + case eTradeskill.GossipActionTrade: + player.GetSession().SendListInventory(me.GetGUID()); + break; + case eTradeskill.GossipActionTrain: + player.GetSession().SendTrainerList(me, ProfessionConst.TrainerIdAlchemy); + break; + //Learn Alchemy + case eTradeskill.GossipActionInfoDef + 1: + ProfessionConst.ProcessCastaction(player, me, SpellIds.Transmute, SpellIds.LearnTransmute, ProfessionConst.DoLearnCost(player)); + break; + case eTradeskill.GossipActionInfoDef + 2: + ProfessionConst.ProcessCastaction(player, me, SpellIds.Elixir, SpellIds.LearnElixir, ProfessionConst.DoLearnCost(player)); + break; + case eTradeskill.GossipActionInfoDef + 3: + ProfessionConst.ProcessCastaction(player, me, SpellIds.Potion, SpellIds.LearnPotion, ProfessionConst.DoLearnCost(player)); + break; + //Unlearn Alchemy + case eTradeskill.GossipActionInfoDef + 4: + ProfessionConst.ProcessCastaction(player, me, 0, SpellIds.UnlearnTransmute, ProfessionConst.DoHighUnlearnCost(player)); + break; + case eTradeskill.GossipActionInfoDef + 5: + ProfessionConst.ProcessCastaction(player, me, 0, SpellIds.UnlearnElixir, ProfessionConst.DoHighUnlearnCost(player)); + break; + case eTradeskill.GossipActionInfoDef + 6: + ProfessionConst.ProcessCastaction(player, me, 0, SpellIds.UnlearnPotion, ProfessionConst.DoHighUnlearnCost(player)); + break; + } + } + + void SendConfirmLearn(Player player, uint action) + { + if (action != 0) + { + switch (me.GetEntry()) + { + case CreatureIds.TrainerTransmute: + player.AddGossipItem(GossipOptionNpc.None, ProfessionConst.GossipLearnTransmute, ProfessionConst.GossipSenderCheck, action); + //unknown textID () + player.SendGossipMenu(player.GetGossipTextId(me), me.GetGUID()); + break; + case CreatureIds.TrainerElixir: + player.AddGossipItem(GossipOptionNpc.None, ProfessionConst.GossipLearnElixir, ProfessionConst.GossipSenderCheck, action); + //unknown textID () + player.SendGossipMenu(player.GetGossipTextId(me), me.GetGUID()); + break; + case CreatureIds.TrainerPotion: + player.AddGossipItem(GossipOptionNpc.None, ProfessionConst.GossipLearnPotion, ProfessionConst.GossipSenderCheck, action); + //unknown textID () + player.SendGossipMenu(player.GetGossipTextId(me), me.GetGUID()); + break; + } + } + } + + void SendConfirmUnlearn(Player player, uint action) + { + if (action != 0) + { + switch (me.GetEntry()) + { + case CreatureIds.TrainerTransmute: + player.AddGossipItem(GossipOptionNpc.None, ProfessionConst.GossipUnlearnTransmute, ProfessionConst.GossipSenderCheck, action, ProfessionConst.BoxUnlearnAlchemySpec, (uint)ProfessionConst.DoHighUnlearnCost(player), false); + //unknown textID () + player.SendGossipMenu(player.GetGossipTextId(me), me.GetGUID()); + break; + case CreatureIds.TrainerElixir: + player.AddGossipItem(GossipOptionNpc.None, ProfessionConst.GossipUnlearnElixir, ProfessionConst.GossipSenderCheck, action, ProfessionConst.BoxUnlearnAlchemySpec, (uint)ProfessionConst.DoHighUnlearnCost(player), false); + //unknown textID () + player.SendGossipMenu(player.GetGossipTextId(me), me.GetGUID()); + break; + case CreatureIds.TrainerPotion: + player.AddGossipItem(GossipOptionNpc.None, ProfessionConst.GossipUnlearnPotion, ProfessionConst.GossipSenderCheck, action, ProfessionConst.BoxUnlearnAlchemySpec, (uint)ProfessionConst.DoHighUnlearnCost(player), false); + //unknown textID () + player.SendGossipMenu(player.GetGossipTextId(me), me.GetGUID()); + break; + } + } + } + + public override bool OnGossipSelect(Player player, uint menuId, uint gossipListId) + { + uint sender = player.PlayerTalkClass.GetGossipOptionSender(gossipListId); + uint action = player.PlayerTalkClass.GetGossipOptionAction(gossipListId); + player.ClearGossipMenu(); + switch (sender) + { + case eTradeskill.GossipSenderMain: + SendActionMenu(player, action); + break; + + case ProfessionConst.GossipSenderLearn: + SendConfirmLearn(player, action); + break; + + case ProfessionConst.GossipSenderUnlearn: + SendConfirmUnlearn(player, action); + break; + + case ProfessionConst.GossipSenderCheck: + SendActionMenu(player, action); + break; + } + return true; + } + } + + [Script] + class npc_prof_blacksmith : ScriptedAI + { + public npc_prof_blacksmith(Creature creature) : base(creature) { } + + bool HasWeaponSub(Player player) + { + return (player.HasSpell(SpellIds.Hammer) || player.HasSpell(SpellIds.Axe) || player.HasSpell(SpellIds.Sword)); + } + + public override bool OnGossipHello(Player player) + { + if (me.IsQuestGiver()) + + if (me.IsVendor()) + player.AddGossipItem(GossipOptionNpc.Vendor, ProfessionConst.GossipTextBrowseGoods, eTradeskill.GossipSenderMain, eTradeskill.GossipActionTrade); + + if (me.IsTrainer()) + player.AddGossipItem(GossipOptionNpc.Trainer, ProfessionConst.GossipTextTrain, eTradeskill.GossipSenderMain, eTradeskill.GossipActionTrain); + + uint creatureId = me.GetEntry(); + //Weaponsmith & Armorsmith + if (player.GetBaseSkillValue(SkillType.Blacksmithing) >= 225) + { + switch (creatureId) + { + case CreatureIds.TrainerSmithomni1: + case CreatureIds.TrainerSmithomni2: + if (!player.HasSpell(SpellIds.Armor) && !player.HasSpell(SpellIds.Weapon) && player.GetReputationRank(ProfessionConst.RepArmor) >= ReputationRank.Friendly) + player.AddGossipItem(GossipOptionNpc.None, ProfessionConst.GossipArmorLearn, eTradeskill.GossipSenderMain, eTradeskill.GossipActionInfoDef + 1); + if (!player.HasSpell(SpellIds.Weapon) && !player.HasSpell(SpellIds.Armor) && player.GetReputationRank(ProfessionConst.RepWeapon) >= ReputationRank.Friendly) + player.AddGossipItem(GossipOptionNpc.None, ProfessionConst.GossipWeaponLearn, eTradeskill.GossipSenderMain, eTradeskill.GossipActionInfoDef + 2); + break; + case CreatureIds.TrainerWeapon1: + case CreatureIds.TrainerWeapon2: + if (player.HasSpell(SpellIds.Weapon)) + player.AddGossipItem(GossipOptionNpc.None, ProfessionConst.GossipWeaponUnlearn, ProfessionConst.GossipSenderUnlearn, eTradeskill.GossipActionInfoDef + 3); + break; + case CreatureIds.TrainerArmor1: + case CreatureIds.TrainerArmor2: + if (player.HasSpell(SpellIds.Armor)) + player.AddGossipItem(GossipOptionNpc.None, ProfessionConst.GossipArmorUnlearn, ProfessionConst.GossipSenderUnlearn, eTradeskill.GossipActionInfoDef + 4); + break; + } + } + //Weaponsmith Spec + if (player.HasSpell(SpellIds.Weapon) && player.GetLevel() > 49 && player.GetBaseSkillValue(SkillType.Blacksmithing) >= 250) + { + switch (creatureId) + { + case CreatureIds.TrainerHammer: + if (!HasWeaponSub(player)) + player.AddGossipItem(GossipOptionNpc.None, ProfessionConst.GossipLearnHammer, ProfessionConst.GossipSenderLearn, eTradeskill.GossipActionInfoDef + 5); + if (player.HasSpell(SpellIds.Hammer)) + player.AddGossipItem(GossipOptionNpc.None, ProfessionConst.GossipUnlearnHammer, ProfessionConst.GossipSenderUnlearn, eTradeskill.GossipActionInfoDef + 8); + break; + case CreatureIds.TrainerAxe: + if (!HasWeaponSub(player)) + player.AddGossipItem(GossipOptionNpc.None, ProfessionConst.GossipLearnAxe, ProfessionConst.GossipSenderLearn, eTradeskill.GossipActionInfoDef + 6); + if (player.HasSpell(SpellIds.Axe)) + player.AddGossipItem(GossipOptionNpc.None, ProfessionConst.GossipUnlearnAxe, ProfessionConst.GossipSenderUnlearn, eTradeskill.GossipActionInfoDef + 9); + break; + case CreatureIds.TrainerSword: + if (!HasWeaponSub(player)) + player.AddGossipItem(GossipOptionNpc.None, ProfessionConst.GossipLearnSword, ProfessionConst.GossipSenderLearn, eTradeskill.GossipActionInfoDef + 7); + if (player.HasSpell(SpellIds.Sword)) + player.AddGossipItem(GossipOptionNpc.None, ProfessionConst.GossipUnlearnSword, ProfessionConst.GossipSenderUnlearn, eTradeskill.GossipActionInfoDef + 10); + break; + } + } + + player.SendGossipMenu(player.GetGossipTextId(me), me.GetGUID()); + return true; + } + + void SendActionMenu(Player player, uint action) + { + switch (action) + { + case eTradeskill.GossipActionTrade: + player.GetSession().SendListInventory(me.GetGUID()); + break; + case eTradeskill.GossipActionTrain: + player.GetSession().SendTrainerList(me, ProfessionConst.TrainerIdBlacksmithing); + break; + //Learn Armor/Weapon + case eTradeskill.GossipActionInfoDef + 1: + if (!player.HasSpell(SpellIds.Armor)) + { + player.CastSpell(player, SpellIds.LearnArmor, true); + //_Creature.CastSpell(player, SRepArmor, true); + } + player.CloseGossipMenu(); + break; + case eTradeskill.GossipActionInfoDef + 2: + if (!player.HasSpell(SpellIds.Weapon)) + { + player.CastSpell(player, SpellIds.LearnWeapon, true); + //_Creature.CastSpell(player, SRepWeapon, true); + } + player.CloseGossipMenu(); + break; + //Unlearn Armor/Weapon + case eTradeskill.GossipActionInfoDef + 3: + if (HasWeaponSub(player)) + { + //unknown textID (TalkMustUnlearnWeapon) + player.SendGossipMenu(player.GetGossipTextId(me), me.GetGUID()); + } + else + ProfessionConst.ProcessUnlearnAction(player, me, SpellIds.UnlearnWeapon, SpellIds.RepArmor, ProfessionConst.DoLowUnlearnCost(player)); + break; + case eTradeskill.GossipActionInfoDef + 4: + ProfessionConst.ProcessUnlearnAction(player, me, SpellIds.UnlearnArmor, SpellIds.RepWeapon, ProfessionConst.DoLowUnlearnCost(player)); + break; + //Learn Hammer/Axe/Sword + case eTradeskill.GossipActionInfoDef + 5: + player.CastSpell(player, SpellIds.LearnHammer, true); + player.CloseGossipMenu(); + break; + case eTradeskill.GossipActionInfoDef + 6: + player.CastSpell(player, SpellIds.LearnAxe, true); + player.CloseGossipMenu(); + break; + case eTradeskill.GossipActionInfoDef + 7: + player.CastSpell(player, SpellIds.LearnSword, true); + player.CloseGossipMenu(); + break; + //Unlearn Hammer/Axe/Sword + case eTradeskill.GossipActionInfoDef + 8: + ProfessionConst.ProcessUnlearnAction(player, me, SpellIds.UnlearnHammer, 0, ProfessionConst.DoMedUnlearnCost(player)); + break; + case eTradeskill.GossipActionInfoDef + 9: + ProfessionConst.ProcessUnlearnAction(player, me, SpellIds.UnlearnAxe, 0, ProfessionConst.DoMedUnlearnCost(player)); + break; + case eTradeskill.GossipActionInfoDef + 10: + ProfessionConst.ProcessUnlearnAction(player, me, SpellIds.UnlearnSword, 0, ProfessionConst.DoMedUnlearnCost(player)); + break; + } + } + + void SendConfirmLearn(Player player, uint action) + { + if (action != 0) + { + switch (me.GetEntry()) + { + case CreatureIds.TrainerHammer: + player.AddGossipItem(GossipOptionNpc.None, ProfessionConst.GossipLearnHammer, ProfessionConst.GossipSenderCheck, action); + //unknown textID (TalkHammerLearn) + player.SendGossipMenu(player.GetGossipTextId(me), me.GetGUID()); + break; + case CreatureIds.TrainerAxe: + player.AddGossipItem(GossipOptionNpc.None, ProfessionConst.GossipLearnAxe, ProfessionConst.GossipSenderCheck, action); + //unknown textID (TalkAxeLearn) + player.SendGossipMenu(player.GetGossipTextId(me), me.GetGUID()); + break; + case CreatureIds.TrainerSword: + player.AddGossipItem(GossipOptionNpc.None, ProfessionConst.GossipLearnSword, ProfessionConst.GossipSenderCheck, action); + //unknown textID (TalkSwordLearn) + player.SendGossipMenu(player.GetGossipTextId(me), me.GetGUID()); + break; + } + } + } + + void SendConfirmUnlearn(Player player, uint action) + { + if (action != 0) + { + switch (me.GetEntry()) + { + case CreatureIds.TrainerWeapon1: + case CreatureIds.TrainerWeapon2: + case CreatureIds.TrainerArmor1: + case CreatureIds.TrainerArmor2: + player.AddGossipItem(GossipOptionNpc.None, ProfessionConst.GossipUnlearnSmithSpec, ProfessionConst.GossipSenderCheck, action, ProfessionConst.BoxUnlearnAmorOrWeapon, (uint)ProfessionConst.DoLowUnlearnCost(player), false); + //unknown textID (TalkUnlearnAxeorweapon) + player.SendGossipMenu(player.GetGossipTextId(me), me.GetGUID()); + break; + + case CreatureIds.TrainerHammer: + player.AddGossipItem(GossipOptionNpc.None, ProfessionConst.GossipUnlearnHammer, ProfessionConst.GossipSenderCheck, action, ProfessionConst.BoxUnlearnWeaponSpec, (uint)ProfessionConst.DoMedUnlearnCost(player), false); + //unknown textID (TalkHammerUnlearn) + player.SendGossipMenu(player.GetGossipTextId(me), me.GetGUID()); + break; + case CreatureIds.TrainerAxe: + player.AddGossipItem(GossipOptionNpc.None, ProfessionConst.GossipUnlearnAxe, ProfessionConst.GossipSenderCheck, action, ProfessionConst.BoxUnlearnWeaponSpec, (uint)ProfessionConst.DoMedUnlearnCost(player), false); + //unknown textID (TalkAxeUnlearn) + player.SendGossipMenu(player.GetGossipTextId(me), me.GetGUID()); + break; + case CreatureIds.TrainerSword: + player.AddGossipItem(GossipOptionNpc.None, ProfessionConst.GossipUnlearnSword, ProfessionConst.GossipSenderCheck, action, ProfessionConst.BoxUnlearnWeaponSpec, (uint)ProfessionConst.DoMedUnlearnCost(player), false); + //unknown textID (TalkSwordUnlearn) + player.SendGossipMenu(player.GetGossipTextId(me), me.GetGUID()); + break; + } + } + } + + public override bool OnGossipSelect(Player player, uint menuId, uint gossipListId) + { + uint sender = player.PlayerTalkClass.GetGossipOptionSender(gossipListId); + uint action = player.PlayerTalkClass.GetGossipOptionAction(gossipListId); + player.ClearGossipMenu(); + switch (sender) + { + case eTradeskill.GossipSenderMain: + SendActionMenu(player, action); + break; + + case ProfessionConst.GossipSenderLearn: + SendConfirmLearn(player, action); + break; + + case ProfessionConst.GossipSenderUnlearn: + SendConfirmUnlearn(player, action); + break; + + case ProfessionConst.GossipSenderCheck: + SendActionMenu(player, action); + break; + } + return true; + } + } + + [Script] + class npc_engineering_tele_trinket : ScriptedAI + { + public npc_engineering_tele_trinket(Creature creature) : base(creature) { } + + bool CanLearn(Player player, uint textId, uint altTextId, uint skillValue, uint reqSpellId, uint spellId, ref uint npcTextId) + { + bool res = false; + npcTextId = textId; + if (player.GetBaseSkillValue(SkillType.Engineering) >= skillValue && player.HasSpell(reqSpellId)) + { + if (!player.HasSpell(spellId)) + res = true; + else + npcTextId = altTextId; + } + return res; + } + + public override bool OnGossipHello(Player player) + { + uint npcTextId = 0; + string gossipItem = ""; + bool canLearn = false; + + if (player.HasSkill(SkillType.Engineering)) + { + switch (me.GetEntry()) + { + case CreatureIds.Zap: + canLearn = CanLearn(player, 6092, 0, 260, SpellIds.Goblin, SpellIds.ToEverlook, ref npcTextId); + if (canLearn) + gossipItem = ProfessionConst.GossipItemZap; + break; + case CreatureIds.Jhordy: + canLearn = CanLearn(player, 7251, 7252, 260, SpellIds.Gnomish, SpellIds.ToGadget, ref npcTextId); + if (canLearn) + gossipItem = ProfessionConst.GossipItemJhordy; + break; + case CreatureIds.Kablam: + canLearn = CanLearn(player, 10365, 0, 350, SpellIds.Goblin, SpellIds.ToArea52, ref npcTextId); + if (canLearn) + gossipItem = ProfessionConst.GossipItemKablam; + break; + case CreatureIds.Smiles: + canLearn = CanLearn(player, 10363, 0, 350, SpellIds.Gnomish, SpellIds.ToToshley, ref npcTextId); + if (canLearn) + gossipItem = ProfessionConst.GossipItemKablam; + break; + } + } + + if (canLearn) + player.AddGossipItem(GossipOptionNpc.None, gossipItem, me.GetEntry(), eTradeskill.GossipActionInfoDef + 1); + + player.SendGossipMenu(npcTextId != 0 ? npcTextId : player.GetGossipTextId(me), me.GetGUID()); + return true; + } + + public override bool OnGossipSelect(Player player, uint menuId, uint gossipListId) + { + uint sender = player.PlayerTalkClass.GetGossipOptionSender(gossipListId); + uint action = player.PlayerTalkClass.GetGossipOptionAction(gossipListId); + player.ClearGossipMenu(); + if (action == eTradeskill.GossipActionInfoDef + 1) + player.CloseGossipMenu(); + + if (sender != me.GetEntry()) + return true; + + switch (sender) + { + case CreatureIds.Zap: + player.CastSpell(player, SpellIds.LearnToEverlook, false); + break; + case CreatureIds.Jhordy: + player.CastSpell(player, SpellIds.LearnToGadget, false); + break; + case CreatureIds.Kablam: + player.CastSpell(player, SpellIds.LearnToArea52, false); + break; + case CreatureIds.Smiles: + player.CastSpell(player, SpellIds.LearnToToshley, false); + break; + } + + return true; + } + } + + [Script] + class npc_prof_leather : ScriptedAI + { + public npc_prof_leather(Creature creature) : base(creature) { } + + public override bool OnGossipHello(Player player) + { + if (me.IsQuestGiver()) + + if (me.IsVendor()) + player.AddGossipItem(GossipOptionNpc.Vendor, ProfessionConst.GossipTextBrowseGoods, eTradeskill.GossipSenderMain, eTradeskill.GossipActionTrade); + + if (me.IsTrainer()) + player.AddGossipItem(GossipOptionNpc.Trainer, ProfessionConst.GossipTextTrain, eTradeskill.GossipSenderMain, eTradeskill.GossipActionTrain); + + if (player.HasSkill(SkillType.Tailoring) && player.GetBaseSkillValue(SkillType.Tailoring) >= 250 && player.GetLevel() > 49) + { + switch (me.GetEntry()) + { + case CreatureIds.TrainerDragon1: + case CreatureIds.TrainerDragon2: + if (player.HasSpell(SpellIds.Dragon)) + player.AddGossipItem(GossipOptionNpc.None, ProfessionConst.GossipUnlearnDragon, ProfessionConst.GossipSenderUnlearn, eTradeskill.GossipActionInfoDef + 1); + break; + case CreatureIds.TrainerElemental1: + case CreatureIds.TrainerElemental2: + if (player.HasSpell(SpellIds.Elemental)) + player.AddGossipItem(GossipOptionNpc.None, ProfessionConst.GossipUnlearnElemental, ProfessionConst.GossipSenderUnlearn, eTradeskill.GossipActionInfoDef + 2); + break; + case CreatureIds.TrainerTribal1: + case CreatureIds.TrainerTribal2: + if (player.HasSpell(SpellIds.Tribal)) + player.AddGossipItem(GossipOptionNpc.None, ProfessionConst.GossipUnlearnTribal, ProfessionConst.GossipSenderUnlearn, eTradeskill.GossipActionInfoDef + 3); + break; + } + } + + player.SendGossipMenu(player.GetGossipTextId(me), me.GetGUID()); + return true; + } + + void SendActionMenu(Player player, uint action) + { + switch (action) + { + case eTradeskill.GossipActionTrade: + player.GetSession().SendListInventory(me.GetGUID()); + break; + case eTradeskill.GossipActionTrain: + player.GetSession().SendTrainerList(me, ProfessionConst.TrainerIdLeatherworking); + break; + //Unlearn Leather + case eTradeskill.GossipActionInfoDef + 1: + ProfessionConst.ProcessUnlearnAction(player, me, SpellIds.UnlearnDragon, 0, ProfessionConst.DoMedUnlearnCost(player)); + break; + case eTradeskill.GossipActionInfoDef + 2: + ProfessionConst.ProcessUnlearnAction(player, me, SpellIds.UnlearnElemental, 0, ProfessionConst.DoMedUnlearnCost(player)); + break; + case eTradeskill.GossipActionInfoDef + 3: + ProfessionConst.ProcessUnlearnAction(player, me, SpellIds.UnlearnTribal, 0, ProfessionConst.DoMedUnlearnCost(player)); + break; + } + } + + void SendConfirmUnlearn(Player player, uint action) + { + if (action != 0) + { + switch (me.GetEntry()) + { + case CreatureIds.TrainerDragon1: + case CreatureIds.TrainerDragon2: + player.AddGossipItem(GossipOptionNpc.None, ProfessionConst.GossipUnlearnDragon, ProfessionConst.GossipSenderCheck, action, ProfessionConst.BoxUnlearnLeatherSpec, (uint)ProfessionConst.DoMedUnlearnCost(player), false); + //unknown textID () + player.SendGossipMenu(player.GetGossipTextId(me), me.GetGUID()); + break; + case CreatureIds.TrainerElemental1: + case CreatureIds.TrainerElemental2: + player.AddGossipItem(GossipOptionNpc.None, ProfessionConst.GossipUnlearnElemental, ProfessionConst.GossipSenderCheck, action, ProfessionConst.BoxUnlearnLeatherSpec, (uint)ProfessionConst.DoMedUnlearnCost(player), false); + //unknown textID () + player.SendGossipMenu(player.GetGossipTextId(me), me.GetGUID()); + break; + case CreatureIds.TrainerTribal1: + case CreatureIds.TrainerTribal2: + player.AddGossipItem(GossipOptionNpc.None, ProfessionConst.GossipUnlearnTribal, ProfessionConst.GossipSenderCheck, action, ProfessionConst.BoxUnlearnLeatherSpec, (uint)ProfessionConst.DoMedUnlearnCost(player), false); + //unknown textID () + player.SendGossipMenu(player.GetGossipTextId(me), me.GetGUID()); + break; + } + } + } + + public override bool OnGossipSelect(Player player, uint menuId, uint gossipListId) + { + uint sender = player.PlayerTalkClass.GetGossipOptionSender(gossipListId); + uint action = player.PlayerTalkClass.GetGossipOptionAction(gossipListId); + player.ClearGossipMenu(); + switch (sender) + { + case eTradeskill.GossipSenderMain: + SendActionMenu(player, action); + break; + + case ProfessionConst.GossipSenderUnlearn: + SendConfirmUnlearn(player, action); + break; + + case ProfessionConst.GossipSenderCheck: + SendActionMenu(player, action); + break; + } + return true; + } + } + + [Script] + class npc_prof_tailor : ScriptedAI + { + public npc_prof_tailor(Creature creature) : base(creature) { } + + bool HasTailorSpell(Player player) + { + return (player.HasSpell(SpellIds.Mooncloth) || player.HasSpell(SpellIds.Shadoweave) || player.HasSpell(SpellIds.Spellfire)); + } + + public override bool OnGossipHello(Player player) + { + if (me.IsQuestGiver()) + + if (me.IsVendor()) + player.AddGossipItem(GossipOptionNpc.Vendor, ProfessionConst.GossipTextBrowseGoods, eTradeskill.GossipSenderMain, eTradeskill.GossipActionTrade); + + if (me.IsTrainer()) + player.AddGossipItem(GossipOptionNpc.Trainer, ProfessionConst.GossipTextTrain, eTradeskill.GossipSenderMain, eTradeskill.GossipActionTrain); + + //Tailoring Spec + if (player.HasSkill(SkillType.Tailoring) && player.GetBaseSkillValue(SkillType.Tailoring) >= 350 && player.GetLevel() > 59) + { + if (player.GetQuestRewardStatus(10831) || player.GetQuestRewardStatus(10832) || player.GetQuestRewardStatus(10833)) + { + switch (me.GetEntry()) + { + case CreatureIds.TrainerSpellfire: + if (!HasTailorSpell(player)) + player.AddGossipItem(GossipOptionNpc.None, ProfessionConst.GossipLearnSpellfire, ProfessionConst.GossipSenderLearn, eTradeskill.GossipActionInfoDef + 1); + if (player.HasSpell(SpellIds.Spellfire)) + player.AddGossipItem(GossipOptionNpc.None, ProfessionConst.GossipUnlearnSpellfire, ProfessionConst.GossipSenderUnlearn, eTradeskill.GossipActionInfoDef + 4); + break; + case CreatureIds.TrainerMooncloth: + if (!HasTailorSpell(player)) + player.AddGossipItem(GossipOptionNpc.None, ProfessionConst.GossipLearnMooncloth, ProfessionConst.GossipSenderLearn, eTradeskill.GossipActionInfoDef + 2); + if (player.HasSpell(SpellIds.Mooncloth)) + player.AddGossipItem(GossipOptionNpc.None, ProfessionConst.GossipUnlearnMooncloth, ProfessionConst.GossipSenderUnlearn, eTradeskill.GossipActionInfoDef + 5); + break; + case CreatureIds.TrainerShadoweave: + if (!HasTailorSpell(player)) + player.AddGossipItem(GossipOptionNpc.None, ProfessionConst.GossipLearnShadoweave, ProfessionConst.GossipSenderLearn, eTradeskill.GossipActionInfoDef + 3); + if (player.HasSpell(SpellIds.Shadoweave)) + player.AddGossipItem(GossipOptionNpc.None, ProfessionConst.GossipUnlearnShadoweave, ProfessionConst.GossipSenderUnlearn, eTradeskill.GossipActionInfoDef + 6); + break; + } + } + } + + player.SendGossipMenu(player.GetGossipTextId(me), me.GetGUID()); + return true; + } + + void SendActionMenu(Player player, uint action) + { + switch (action) + { + case eTradeskill.GossipActionTrade: + player.GetSession().SendListInventory(me.GetGUID()); + break; + case eTradeskill.GossipActionTrain: + player.GetSession().SendTrainerList(me, ProfessionConst.TrainerIdTailoring); + break; + //Learn Tailor + case eTradeskill.GossipActionInfoDef + 1: + ProfessionConst.ProcessCastaction(player, me, SpellIds.Spellfire, SpellIds.LearnSpellfire, ProfessionConst.DoLearnCost(player)); + break; + case eTradeskill.GossipActionInfoDef + 2: + ProfessionConst.ProcessCastaction(player, me, SpellIds.Mooncloth, SpellIds.LearnMooncloth, ProfessionConst.DoLearnCost(player)); + break; + case eTradeskill.GossipActionInfoDef + 3: + ProfessionConst.ProcessCastaction(player, me, SpellIds.Shadoweave, SpellIds.LearnShadoweave, ProfessionConst.DoLearnCost(player)); + break; + //Unlearn Tailor + case eTradeskill.GossipActionInfoDef + 4: + ProfessionConst.ProcessUnlearnAction(player, me, SpellIds.UnlearnSpellfire, 0, ProfessionConst.DoHighUnlearnCost(player)); + break; + case eTradeskill.GossipActionInfoDef + 5: + ProfessionConst.ProcessUnlearnAction(player, me, SpellIds.UnlearnMooncloth, 0, ProfessionConst.DoHighUnlearnCost(player)); + break; + case eTradeskill.GossipActionInfoDef + 6: + ProfessionConst.ProcessUnlearnAction(player, me, SpellIds.UnlearnShadoweave, 0, ProfessionConst.DoHighUnlearnCost(player)); + break; + } + } + + void SendConfirmLearn(Player player, uint action) + { + if (action != 0) + { + switch (me.GetEntry()) + { + case CreatureIds.TrainerSpellfire: + player.AddGossipItem(GossipOptionNpc.None, ProfessionConst.GossipLearnSpellfire, ProfessionConst.GossipSenderCheck, action); + //unknown textID () + player.SendGossipMenu(player.GetGossipTextId(me), me.GetGUID()); + break; + case CreatureIds.TrainerMooncloth: + player.AddGossipItem(GossipOptionNpc.None, ProfessionConst.GossipLearnMooncloth, ProfessionConst.GossipSenderCheck, action); + //unknown textID () + player.SendGossipMenu(player.GetGossipTextId(me), me.GetGUID()); + break; + case CreatureIds.TrainerShadoweave: + player.AddGossipItem(GossipOptionNpc.None, ProfessionConst.GossipLearnShadoweave, ProfessionConst.GossipSenderCheck, action); + //unknown textID () + player.SendGossipMenu(player.GetGossipTextId(me), me.GetGUID()); + break; + } + } + } + + void SendConfirmUnlearn(Player player, uint action) + { + if (action != 0) + { + switch (me.GetEntry()) + { + case CreatureIds.TrainerSpellfire: + player.AddGossipItem(GossipOptionNpc.None, ProfessionConst.GossipUnlearnSpellfire, ProfessionConst.GossipSenderCheck, action, ProfessionConst.BoxUnlearnTailorSpec, (uint)ProfessionConst.DoHighUnlearnCost(player), false); + //unknown textID () + player.SendGossipMenu(player.GetGossipTextId(me), me.GetGUID()); + break; + case CreatureIds.TrainerMooncloth: + player.AddGossipItem(GossipOptionNpc.None, ProfessionConst.GossipUnlearnMooncloth, ProfessionConst.GossipSenderCheck, action, ProfessionConst.BoxUnlearnTailorSpec, (uint)ProfessionConst.DoHighUnlearnCost(player), false); + //unknown textID () + player.SendGossipMenu(player.GetGossipTextId(me), me.GetGUID()); + break; + case CreatureIds.TrainerShadoweave: + player.AddGossipItem(GossipOptionNpc.None, ProfessionConst.GossipUnlearnShadoweave, ProfessionConst.GossipSenderCheck, action, ProfessionConst.BoxUnlearnTailorSpec, (uint)ProfessionConst.DoHighUnlearnCost(player), false); + //unknown textID () + player.SendGossipMenu(player.GetGossipTextId(me), me.GetGUID()); + break; + } + } + } + + public override bool OnGossipSelect(Player player, uint menuId, uint gossipListId) + { + uint sender = player.PlayerTalkClass.GetGossipOptionSender(gossipListId); + uint action = player.PlayerTalkClass.GetGossipOptionAction(gossipListId); + player.ClearGossipMenu(); + switch (sender) + { + case eTradeskill.GossipSenderMain: + SendActionMenu(player, action); + break; + + case ProfessionConst.GossipSenderLearn: + SendConfirmLearn(player, action); + break; + + case ProfessionConst.GossipSenderUnlearn: + SendConfirmUnlearn(player, action); + break; + + case ProfessionConst.GossipSenderCheck: + SendActionMenu(player, action); + break; + } + return true; + } + } +} \ No newline at end of file diff --git a/Source/Scripts/World/NpcSpecial.cs b/Source/Scripts/World/NpcSpecial.cs deleted file mode 100644 index 6e995ac5b..000000000 --- a/Source/Scripts/World/NpcSpecial.cs +++ /dev/null @@ -1,1910 +0,0 @@ -// Copyright (c) CypherCore All rights reserved. -// Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information. - -using Framework.Constants; -using Framework.Dynamic; -using Game; -using Game.AI; -using Game.Entities; -using Game.Maps; -using Game.Movement; -using Game.Scripting; -using Game.Spells; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Numerics; - -namespace Scripts.World.NpcSpecial -{ - enum SpawnType - { - Tripwire, // no warning, summon Creature at smaller range - AlarmBot, // cast guards mark and summon npc - if player shows up with that buff duration < 5 seconds attack - } - - class AirForceSpawn - { - public AirForceSpawn(uint _myEntry, uint _otherEntry, SpawnType _spawnType) - { - myEntry = _myEntry; - otherEntry = _otherEntry; - spawnType = _spawnType; - } - - public uint myEntry; - public uint otherEntry; - public SpawnType spawnType; - } - - struct CreatureIds - { - //Torchtossingtarget - public const uint TorchTossingTargetBunny = 25535; - - //Garments - public const uint Shaya = 12429; - public const uint Roberts = 12423; - public const uint Dolf = 12427; - public const uint Korja = 12430; - public const uint DgKel = 12428; - - //Doctor - public const uint DoctorAlliance = 12939; - public const uint DoctorHorde = 12920; - - //Fireworks - public const uint Omen = 15467; - public const uint MinionOfOmen = 15466; - public const uint FireworkBlue = 15879; - public const uint FireworkGreen = 15880; - public const uint FireworkPurple = 15881; - public const uint FireworkRed = 15882; - public const uint FireworkYellow = 15883; - public const uint FireworkWhite = 15884; - public const uint FireworkBigBlue = 15885; - public const uint FireworkBigGreen = 15886; - public const uint FireworkBigPurple = 15887; - public const uint FireworkBigRed = 15888; - public const uint FireworkBigYellow = 15889; - public const uint FireworkBigWhite = 15890; - - public const uint ClusterBlue = 15872; - public const uint ClusterRed = 15873; - public const uint ClusterGreen = 15874; - public const uint ClusterPurple = 15875; - public const uint ClusterWhite = 15876; - public const uint ClusterYellow = 15877; - public const uint ClusterBigBlue = 15911; - public const uint ClusterBigGreen = 15912; - public const uint ClusterBigPurple = 15913; - public const uint ClusterBigRed = 15914; - public const uint ClusterBigWhite = 15915; - public const uint ClusterBigYellow = 15916; - public const uint ClusterElune = 15918; - - // Rabbitspells - public const uint SpringRabbit = 32791; - - // TrainWrecker - public const uint ExultingWindUpTrainWrecker = 81071; - - // Argent squire/gruntling - public const uint ArgentSquire = 33238; - - // BountifulTable - public const uint TheTurkeyChair = 34812; - public const uint TheCranberryChair = 34823; - public const uint TheStuffingChair = 34819; - public const uint TheSweetPotatoChair = 34824; - public const uint ThePieChair = 34822; - - // TravelerTundraMammothNPCs - public const uint HakmudOfArgus = 32638; - public const uint Gnimo = 32639; - public const uint DrixBlackwrench = 32641; - public const uint Mojodishu = 32642; - - // BrewfestReveler2 - public const uint BrewfestReveler = 24484; - } - - struct GameobjectIds - { - //Fireworks - public const uint FireworkLauncher1 = 180771; - public const uint FireworkLauncher2 = 180868; - public const uint FireworkLauncher3 = 180850; - public const uint ClusterLauncher1 = 180772; - public const uint ClusterLauncher2 = 180859; - public const uint ClusterLauncher3 = 180869; - public const uint ClusterLauncher4 = 180874; - - //TrainWrecker - public const uint ToyTrain = 193963; - - //RibbonPole - public const uint RibbonPole = 181605; - } - - struct SpellIds - { - public const uint GuardsMark = 38067; - - //Dancingflames - public const uint SummonBrazier = 45423; - public const uint BrazierDance = 45427; - public const uint FierySeduction = 47057; - - //RibbonPole - public const uint RibbonDanceCosmetic = 29726; - public const uint RedFireRing = 46836; - public const uint BlueFireRing = 46842; - - //Torchtossingtarget - public const uint TargetIndicator = 45723; - - //Garments - public const uint LesserHealR2 = 2052; - public const uint FortitudeR1 = 1243; - - //Guardianspells - public const uint Deathtouch = 5; - - //Brewfestreveler - public const uint BrewfestToast = 41586; - - //Wormholespells - public const uint BoreanTundra = 67834; - public const uint SholazarBasin = 67835; - public const uint Icecrown = 67836; - public const uint StormPeaks = 67837; - public const uint HowlingFjord = 67838; - public const uint Underground = 68081; - - //Rabbitspells - public const uint SpringFling = 61875; - public const uint SpringRabbitJump = 61724; - public const uint SpringRabbitWander = 61726; - public const uint SummonBabyBunny = 61727; - public const uint SpringRabbitInLove = 61728; - - //TrainWrecker - public const uint ToyTrainPulse = 61551; - public const uint WreckTrain = 62943; - - //Argent squire/gruntling - public const uint DarnassusPennant = 63443; - public const uint ExodarPennant = 63439; - public const uint GnomereganPennant = 63442; - public const uint IronforgePennant = 63440; - public const uint StormwindPennant = 62727; - public const uint SenjinPennant = 63446; - public const uint UndercityPennant = 63441; - public const uint OrgrimmarPennant = 63444; - public const uint SilvermoonPennant = 63438; - public const uint ThunderbluffPennant = 63445; - public const uint AuraPostmanS = 67376; - public const uint AuraShopS = 67377; - public const uint AuraBankS = 67368; - public const uint AuraTiredS = 67401; - public const uint AuraBankG = 68849; - public const uint AuraPostmanG = 68850; - public const uint AuraShopG = 68851; - public const uint AuraTiredG = 68852; - public const uint TiredPlayer = 67334; - - //BountifulTable - public const uint CranberryServer = 61793; - public const uint PieServer = 61794; - public const uint StuffingServer = 61795; - public const uint TurkeyServer = 61796; - public const uint SweetPotatoesServer = 61797; - - //VoidZone - public const uint Consumption = 28874; - } - - struct QuestConst - { - //Lunaclawspirit - public const uint BodyHeartA = 6001; - public const uint BodyHeartH = 6002; - - //ChickenCluck - public const uint Cluck = 3861; - - //Garments - public const uint Moon = 5621; - public const uint Light1 = 5624; - public const uint Light2 = 5625; - public const uint Spirit = 5648; - public const uint Darkness = 5650; - } - - struct TextIds - { - //Lunaclawspirit - public const uint TextIdDefault = 4714; - public const uint TextIdProgress = 4715; - - //Chickencluck - public const uint EmoteHelloA = 0; - public const uint EmoteHelloH = 1; - public const uint EmoteCluck = 2; - - //Doctor - public const uint SayDoc = 0; - - // Garments - // Used By 12429; 12423; 12427; 12430; 12428; But Signed For 12429 - public const uint SayThanks = 0; - public const uint SayGoodbye = 1; - public const uint SayHealed = 2; - - //Wormholespells - public const uint Wormhole = 14785; - - //NpcExperience - public const uint XpOnOff = 14736; - } - - struct GossipMenus - { - //Wormhole - public const int MenuIdWormhole = 10668; // "This tear in the fabric of time and space looks ominous." - public const int OptionIdWormhole1 = 0; // "Borean Tundra" - public const int OptionIdWormhole2 = 1; // "Howling Fjord" - public const int OptionIdWormhole3 = 2; // "Sholazar Basin" - public const int OptionIdWormhole4 = 3; // "Icecrown" - public const int OptionIdWormhole5 = 4; // "Storm Peaks" - public const int OptionIdWormhole6 = 5; // "Underground..." - - //Lunaclawspirit - public const string ItemGrant = "You Have Thought Well; Spirit. I Ask You To Grant Me The Strength Of Your Body And The Strength Of Your Heart."; - - //Pettrainer - public const uint MenuIdPetUnlearn = 6520; - public const uint OptionIdPleaseDo = 0; - - //NpcExperience - public const uint MenuIdXpOnOff = 10638; - public const uint OptionIdXpOff = 0; - public const uint OptionIdXpOn = 1; - - //Argent squire/gruntling - public const uint OptionIdBank = 0; - public const uint OptionIdShop = 1; - public const uint OptionIdMail = 2; - public const uint OptionIdDarnassusSenjinPennant = 3; - public const uint OptionIdExodarUndercityPennant = 4; - public const uint OptionIdGnomereganOrgrimmarPennant = 5; - public const uint OptionIdIronforgeSilvermoonPennant = 6; - public const uint OptionIdStormwindThunderbluffPennant = 7; - } - - enum SeatIds - { - //BountifulTable - TurkeyChair = 0, - CranberryChair = 1, - StuffingChair = 2, - SweetPotatoChair = 3, - PieChair = 4, - FoodHolder = 5, - PlateHolder = 6 - } - - struct Misc - { - public static AirForceSpawn[] AirforceSpawns = - { - new AirForceSpawn(2614, 15241, SpawnType.AlarmBot), //Air Force Alarm Bot (Alliance) - new AirForceSpawn(2615, 15242, SpawnType.AlarmBot), //Air Force Alarm Bot (Horde) - new AirForceSpawn(21974, 21976, SpawnType.AlarmBot), //Air Force Alarm Bot (Area 52) - new AirForceSpawn(21993, 15242, SpawnType.AlarmBot), //Air Force Guard Post (Horde - Bat Rider) - new AirForceSpawn(21996, 15241, SpawnType.AlarmBot), //Air Force Guard Post (Alliance - Gryphon) - new AirForceSpawn(21997, 21976, SpawnType.AlarmBot), //Air Force Guard Post (Goblin - Area 52 - Zeppelin) - new AirForceSpawn(21999, 15241, SpawnType.Tripwire), //Air Force Trip Wire - Rooftop (Alliance) - new AirForceSpawn(22001, 15242, SpawnType.Tripwire), //Air Force Trip Wire - Rooftop (Horde) - new AirForceSpawn(22002, 15242, SpawnType.Tripwire), //Air Force Trip Wire - Ground (Horde) - new AirForceSpawn(22003, 15241, SpawnType.Tripwire), //Air Force Trip Wire - Ground (Alliance) - new AirForceSpawn(22063, 21976, SpawnType.Tripwire), //Air Force Trip Wire - Rooftop (Goblin - Area 52) - new AirForceSpawn(22065, 22064, SpawnType.AlarmBot), //Air Force Guard Post (Ethereal - Stormspire) - new AirForceSpawn(22066, 22067, SpawnType.AlarmBot), //Air Force Guard Post (Scryer - Dragonhawk) - new AirForceSpawn(22068, 22064, SpawnType.Tripwire), //Air Force Trip Wire - Rooftop (Ethereal - Stormspire) - new AirForceSpawn(22069, 22064, SpawnType.AlarmBot), //Air Force Alarm Bot (Stormspire) - new AirForceSpawn(22070, 22067, SpawnType.Tripwire), //Air Force Trip Wire - Rooftop (Scryer) - new AirForceSpawn(22071, 22067, SpawnType.AlarmBot), //Air Force Alarm Bot (Scryer) - new AirForceSpawn(22078, 22077, SpawnType.AlarmBot), //Air Force Alarm Bot (Aldor) - new AirForceSpawn(22079, 22077, SpawnType.AlarmBot), //Air Force Guard Post (Aldor - Gryphon) - new AirForceSpawn(22080, 22077, SpawnType.Tripwire), //Air Force Trip Wire - Rooftop (Aldor) - new AirForceSpawn(22086, 22085, SpawnType.AlarmBot), //Air Force Alarm Bot (Sporeggar) - new AirForceSpawn(22087, 22085, SpawnType.AlarmBot), //Air Force Guard Post (Sporeggar - Spore Bat) - new AirForceSpawn(22088, 22085, SpawnType.Tripwire), //Air Force Trip Wire - Rooftop (Sporeggar) - new AirForceSpawn(22090, 22089, SpawnType.AlarmBot), //Air Force Guard Post (Toshley's Station - Flying Machine) - new AirForceSpawn(22124, 22122, SpawnType.AlarmBot), //Air Force Alarm Bot (Cenarion) - new AirForceSpawn(22125, 22122, SpawnType.AlarmBot), //Air Force Guard Post (Cenarion - Stormcrow) - new AirForceSpawn(22126, 22122, SpawnType.AlarmBot) //Air Force Trip Wire - Rooftop (Cenarion Expedition) - }; - - public const float RangeTripwire = 15.0f; - public const float RangeAlarmbot = 100.0f; - - //ChickenCluck - public const uint FactionFriendly = 35; - public const uint FactionChicken = 31; - - //Doctor - public static Position[] DoctorAllianceCoords = - { - new Position(-3757.38f, -4533.05f, 14.16f, 3.62f), // Top-far-right bunk as seen from entrance - new Position(-3754.36f, -4539.13f, 14.16f, 5.13f), // Top-far-left bunk - new Position(-3749.54f, -4540.25f, 14.28f, 3.34f), // Far-right bunk - new Position(-3742.10f, -4536.85f, 14.28f, 3.64f), // Right bunk near entrance - new Position(-3755.89f, -4529.07f, 14.05f, 0.57f), // Far-left bunk - new Position(-3749.51f, -4527.08f, 14.07f, 5.26f), // Mid-left bunk - new Position(-3746.37f, -4525.35f, 14.16f, 5.22f), // Left bunk near entrance - }; - - //alliance run to where - public static Position DoctorAllianceRunTo = new(-3742.96f, -4531.52f, 11.91f); - - public static Position[] DoctorHordeCoords = - { - new Position(-1013.75f, -3492.59f, 62.62f, 4.34f), // Left, Behind - new Position(-1017.72f, -3490.92f, 62.62f, 4.34f), // Right, Behind - new Position(-1015.77f, -3497.15f, 62.82f, 4.34f), // Left, Mid - new Position(-1019.51f, -3495.49f, 62.82f, 4.34f), // Right, Mid - new Position(-1017.25f, -3500.85f, 62.98f, 4.34f), // Left, front - new Position(-1020.95f, -3499.21f, 62.98f, 4.34f) // Right, Front - }; - - //horde run to where - public static Position DoctorHordeRunTo = new(-1016.44f, -3508.48f, 62.96f); - - public static uint[] AllianceSoldierId = - { - 12938, // 12938 Injured Alliance Soldier - 12936, // 12936 Badly injured Alliance Soldier - 12937 // 12937 Critically injured Alliance Soldier - }; - - public static uint[] HordeSoldierId = - { - 12923, //12923 Injured Soldier - 12924, //12924 Badly injured Soldier - 12925 //12925 Critically injured Soldier - }; - - // WormholeSpells - public const uint DataShowUnderground = 1; - - //Fireworks - public const uint AnimGoLaunchFirework = 3; - public const uint ZoneMoonglade = 493; - - public static Position omenSummonPos = new(7558.993f, -2839.999f, 450.0214f, 4.46f); - - public const uint AuraDurationTimeLeft = 30000; - - //Argent squire/gruntling - public const uint AchievementPonyUp = 3736; - public static Tuple[] bannerSpells = - { - Tuple.Create(SpellIds.DarnassusPennant, SpellIds.SenjinPennant), - Tuple.Create(SpellIds.ExodarPennant, SpellIds.UndercityPennant), - Tuple.Create(SpellIds.GnomereganPennant, SpellIds.OrgrimmarPennant), - Tuple.Create(SpellIds.IronforgePennant, SpellIds.SilvermoonPennant), - Tuple.Create(SpellIds.StormwindPennant, SpellIds.ThunderbluffPennant) - }; - } - - [Script] - class npc_air_force_bots : NullCreatureAI - { - AirForceSpawn _spawn; - ObjectGuid _myGuard; - List _toAttack = new(); - - static AirForceSpawn FindSpawnFor(uint entry) - { - foreach (AirForceSpawn spawn in Misc.AirforceSpawns) - { - if (spawn.myEntry == entry) - { - Cypher.Assert(Global.ObjectMgr.GetCreatureTemplate(spawn.otherEntry) != null, $"Invalid creature entry {spawn.otherEntry} in 'npc_air_force_bots' script"); - return spawn; - } - } - Cypher.Assert(false, $"Unhandled creature with entry {entry} is assigned 'npc_air_force_bots' script"); - - return null; - } - - public npc_air_force_bots(Creature creature) : base(creature) - { - _spawn = FindSpawnFor(creature.GetEntry()); - } - - Creature GetOrSummonGuard() - { - Creature guard = ObjectAccessor.GetCreature(me, _myGuard); - - if (guard == null && (guard = me.SummonCreature(_spawn.otherEntry, 0.0f, 0.0f, 0.0f, 0.0f, TempSummonType.TimedDespawnOutOfCombat, TimeSpan.FromMinutes(5)))) - _myGuard = guard.GetGUID(); - - return guard; - } - - public override void UpdateAI(uint diff) - { - if (_toAttack.Empty()) - return; - - Creature guard = GetOrSummonGuard(); - if (guard == null) - return; - - // Keep the list of targets for later on when the guards will be alive - if (!guard.IsAlive()) - return; - - for (var i = 0; i < _toAttack.Count; ++i) - { - ObjectGuid guid = _toAttack[i]; - - Unit target = Global.ObjAccessor.GetUnit(me, guid); - if (!target) - continue; - - if (guard.IsEngagedBy(target)) - continue; - - guard.EngageWithTarget(target); - if (_spawn.spawnType == SpawnType.AlarmBot) - guard.CastSpell(target, SpellIds.GuardsMark, true); - } - - _toAttack.Clear(); - } - - public override void MoveInLineOfSight(Unit who) - { - // guards are only spawned against players - if (!who.IsPlayer()) - return; - - // we're already scheduled to attack this player on our next tick, don't bother checking - if (_toAttack.Contains(who.GetGUID())) - return; - - // check if they're in range - if (!who.IsWithinDistInMap(me, (_spawn.spawnType == SpawnType.AlarmBot) ? Misc.RangeAlarmbot : Misc.RangeTripwire)) - return; - - // check if they're hostile - if (!(me.IsHostileTo(who) || who.IsHostileTo(me))) - return; - - // check if they're a valid attack target - if (!me.IsValidAttackTarget(who)) - return; - - if ((_spawn.spawnType == SpawnType.Tripwire) && who.IsFlying()) - return; - - _toAttack.Add(who.GetGUID()); - } - } - - [Script] - class npc_chicken_cluck : ScriptedAI - { - public npc_chicken_cluck(Creature creature) : base(creature) - { - Initialize(); - } - - void Initialize() - { - ResetFlagTimer = 120000; - } - - uint ResetFlagTimer; - - public override void Reset() - { - Initialize(); - me.SetFaction(Misc.FactionChicken); - me.RemoveNpcFlag(NPCFlags.QuestGiver); - } - - public override void JustEngagedWith(Unit who) { } - - public override void UpdateAI(uint diff) - { - // Reset flags after a certain time has passed so that the next player has to start the 'event' again - if (me.HasNpcFlag(NPCFlags.QuestGiver)) - { - if (ResetFlagTimer <= diff) - { - EnterEvadeMode(); - return; - } - else - ResetFlagTimer -= diff; - } - - if (UpdateVictim()) - DoMeleeAttackIfReady(); - } - - public override void ReceiveEmote(Player player, TextEmotes emote) - { - switch (emote) - { - case TextEmotes.Chicken: - if (player.GetQuestStatus(QuestConst.Cluck) == QuestStatus.None && RandomHelper.Rand32() % 30 == 1) - { - me.SetNpcFlag(NPCFlags.QuestGiver); - me.SetFaction(Misc.FactionFriendly); - Talk(player.GetTeam() == Team.Horde ? TextIds.EmoteHelloH : TextIds.EmoteHelloA); - } - break; - case TextEmotes.Cheer: - if (player.GetQuestStatus(QuestConst.Cluck) == QuestStatus.Complete) - { - me.SetNpcFlag(NPCFlags.QuestGiver); - me.SetFaction(Misc.FactionFriendly); - Talk(TextIds.EmoteCluck); - } - break; - } - } - - public override void OnQuestAccept(Player player, Quest quest) - { - if (quest.Id == QuestConst.Cluck) - Reset(); - } - - public override void OnQuestReward(Player player, Quest quest, LootItemType type, uint opt) - { - if (quest.Id == QuestConst.Cluck) - Reset(); - } - } - - [Script] - class npc_dancing_flames : ScriptedAI - { - public npc_dancing_flames(Creature creature) : base(creature) { } - - public override void Reset() - { - DoCastSelf(SpellIds.SummonBrazier, new CastSpellExtraArgs(true)); - DoCastSelf(SpellIds.BrazierDance, new CastSpellExtraArgs(false)); - me.SetEmoteState(Emote.StateDance); - float x, y, z; - me.GetPosition(out x, out y, out z); - me.Relocate(x, y, z + 1.05f); - } - - public override void UpdateAI(uint diff) - { - _scheduler.Update(diff); - } - - public override void ReceiveEmote(Player player, TextEmotes emote) - { - if (me.IsWithinLOS(player.GetPositionX(), player.GetPositionY(), player.GetPositionZ()) && me.IsWithinDistInMap(player, 30.0f)) - { - // She responds to emotes not instantly but ~1500ms later - // If you first /bow, then /wave before dancing flames bow back, it doesnt bow at all and only does wave - // If you're performing emotes too fast, she will not respond to them - // Means she just replaces currently scheduled event with new after receiving new emote - _scheduler.CancelAll(); - - switch (emote) - { - case TextEmotes.Kiss: - _scheduler.Schedule(TimeSpan.FromMilliseconds(1500), context => me.HandleEmoteCommand(Emote.OneshotShy)); - break; - case TextEmotes.Wave: - _scheduler.Schedule(TimeSpan.FromMilliseconds(1500), context => me.HandleEmoteCommand(Emote.OneshotWave)); - break; - case TextEmotes.Bow: - _scheduler.Schedule(TimeSpan.FromMilliseconds(1500), context => me.HandleEmoteCommand(Emote.OneshotBow)); - break; - case TextEmotes.Joke: - _scheduler.Schedule(TimeSpan.FromMilliseconds(1500), context => me.HandleEmoteCommand(Emote.OneshotLaugh)); - break; - case TextEmotes.Dance: - if (!player.HasAura(SpellIds.FierySeduction)) - { - DoCast(player, SpellIds.FierySeduction, new CastSpellExtraArgs(true)); - me.SetFacingTo(me.GetAbsoluteAngle(player)); - } - break; - } - } - } - } - - [Script] - class npc_torch_tossing_target_bunny_controller : ScriptedAI - { - public npc_torch_tossing_target_bunny_controller(Creature creature) : base(creature) - { - _targetTimer = 3000; - } - - ObjectGuid DoSearchForTargets(ObjectGuid lastTargetGUID) - { - List targets = me.GetCreatureListWithEntryInGrid(CreatureIds.TorchTossingTargetBunny, 60.0f); - targets.RemoveAll(creature => creature.GetGUID() == lastTargetGUID); - - if (!targets.Empty()) - { - _lastTargetGUID = targets.SelectRandom().GetGUID(); - - return _lastTargetGUID; - } - return ObjectGuid.Empty; - } - - public override void UpdateAI(uint diff) - { - if (_targetTimer < diff) - { - Unit target = Global.ObjAccessor.GetUnit(me, DoSearchForTargets(_lastTargetGUID)); - if (target) - target.CastSpell(target, SpellIds.TargetIndicator, true); - - _targetTimer = 3000; - } - else - _targetTimer -= diff; - } - - uint _targetTimer; - ObjectGuid _lastTargetGUID; - } - - [Script] - class npc_midsummer_bunny_pole : ScriptedAI - { - public npc_midsummer_bunny_pole(Creature creature) : base(creature) - { - Initialize(); - } - - void Initialize() - { - _scheduler.CancelAll(); - running = false; - } - - public override void Reset() - { - Initialize(); - - _scheduler.SetValidator(() => running); - - _scheduler.Schedule(TimeSpan.FromMilliseconds(1), task => - { - if (checkNearbyPlayers()) - { - Reset(); - return; - } - - GameObject go = me.FindNearestGameObject(GameobjectIds.RibbonPole, 10.0f); - if (go) - me.CastSpell(go, SpellIds.RedFireRing, true); - - task.Schedule(TimeSpan.FromSeconds(5), task1 => - { - if (checkNearbyPlayers()) - { - Reset(); - return; - } - - go = me.FindNearestGameObject(GameobjectIds.RibbonPole, 10.0f); - if (go) - me.CastSpell(go, SpellIds.BlueFireRing, true); - - task.Repeat(TimeSpan.FromSeconds(5)); - }); - }); - } - - public override void DoAction(int action) - { - // Don't start event if it's already running. - if (running) - return; - - running = true; - //events.ScheduleEvent(EVENT_CAST_RED_FIRE_RING, 1); - } - - bool checkNearbyPlayers() - { - // Returns true if no nearby player has aura "Test Ribbon Pole Channel". - List players = new(); - var check = new UnitAuraCheck(true, SpellIds.RibbonDanceCosmetic); - var searcher = new PlayerListSearcher(me, players, check); - Cell.VisitWorldObjects(me, searcher, 10.0f); - - return players.Empty(); - } - - public override void UpdateAI(uint diff) - { - if (!running) - return; - - _scheduler.Update(diff); - } - - bool running; - } - - [Script] - class npc_doctor : ScriptedAI - { - public npc_doctor(Creature creature) : base(creature) - { - Initialize(); - } - - void Initialize() - { - PlayerGUID.Clear(); - - SummonPatientTimer = 10000; - SummonPatientCount = 0; - PatientDiedCount = 0; - PatientSavedCount = 0; - - Patients.Clear(); - Coordinates.Clear(); - - Event = false; - } - - public override void Reset() - { - Initialize(); - me.SetUninteractible(false); - } - - public void BeginEvent(Player player) - { - PlayerGUID = player.GetGUID(); - - SummonPatientTimer = 10000; - SummonPatientCount = 0; - PatientDiedCount = 0; - PatientSavedCount = 0; - - switch (me.GetEntry()) - { - case CreatureIds.DoctorAlliance: - foreach (var coord in Misc.DoctorAllianceCoords) - Coordinates.Add(coord); - break; - case CreatureIds.DoctorHorde: - foreach (var coord in Misc.DoctorHordeCoords) - Coordinates.Add(coord); - break; - } - - Event = true; - me.SetUninteractible(true); - } - - public void PatientDied(Position point) - { - Player player = Global.ObjAccessor.GetPlayer(me, PlayerGUID); - if (player && ((player.GetQuestStatus(6624) == QuestStatus.Incomplete) || (player.GetQuestStatus(6622) == QuestStatus.Incomplete))) - { - ++PatientDiedCount; - - if (PatientDiedCount > 5 && Event) - { - if (player.GetQuestStatus(6624) == QuestStatus.Incomplete) - player.FailQuest(6624); - else if (player.GetQuestStatus(6622) == QuestStatus.Incomplete) - player.FailQuest(6622); - - Reset(); - return; - } - - Coordinates.Add(point); - } - else - // If no player or player abandon quest in progress - Reset(); - } - - public void PatientSaved(Creature soldier, Player player, Position point) - { - if (player && PlayerGUID == player.GetGUID()) - { - if ((player.GetQuestStatus(6624) == QuestStatus.Incomplete) || (player.GetQuestStatus(6622) == QuestStatus.Incomplete)) - { - ++PatientSavedCount; - - if (PatientSavedCount == 15) - { - if (!Patients.Empty()) - { - foreach (var guid in Patients) - { - Creature patient = ObjectAccessor.GetCreature(me, guid); - if (patient) - patient.SetDeathState(DeathState.JustDied); - } - } - - if (player.GetQuestStatus(6624) == QuestStatus.Incomplete) - player.AreaExploredOrEventHappens(6624); - else if (player.GetQuestStatus(6622) == QuestStatus.Incomplete) - player.AreaExploredOrEventHappens(6622); - - Reset(); - return; - } - - Coordinates.Add(point); - } - } - } - - public override void UpdateAI(uint diff) - { - if (Event && SummonPatientCount >= 20) - { - Reset(); - return; - } - - if (Event) - { - if (SummonPatientTimer <= diff) - { - if (Coordinates.Empty()) - return; - - uint patientEntry; - switch (me.GetEntry()) - { - case CreatureIds.DoctorAlliance: - patientEntry = Misc.AllianceSoldierId[RandomHelper.Rand32() % 3]; - break; - case CreatureIds.DoctorHorde: - patientEntry = Misc.HordeSoldierId[RandomHelper.Rand32() % 3]; - break; - default: - Log.outError(LogFilter.Scripts, "Invalid entry for Triage doctor. Please check your database"); - return; - } - - var index = RandomHelper.IRand(0, Coordinates.Count - 1); - - Creature Patient = me.SummonCreature(patientEntry, Coordinates[index], TempSummonType.TimedDespawnOutOfCombat, TimeSpan.FromSeconds(5)); - if (Patient) - { - //303, this flag appear to be required for client side item.spell to work (TARGET_SINGLE_FRIEND) - Patient.SetUnitFlag(UnitFlags.PlayerControlled); - - Patients.Add(Patient.GetGUID()); - ((npc_injured_patient)Patient.GetAI()).DoctorGUID = me.GetGUID(); - ((npc_injured_patient)Patient.GetAI()).Coord = Coordinates[index]; - - Coordinates.RemoveAt(index); - } - - SummonPatientTimer = 10000; - ++SummonPatientCount; - } - else - SummonPatientTimer -= diff; - } - } - - public override void JustEngagedWith(Unit who) { } - - public override void OnQuestAccept(Player player, Quest quest) - { - if ((quest.Id == 6624) || (quest.Id == 6622)) - BeginEvent(player); - } - - ObjectGuid PlayerGUID; - - uint SummonPatientTimer; - uint SummonPatientCount; - uint PatientDiedCount; - uint PatientSavedCount; - - bool Event; - - List Patients = new(); - List Coordinates = new(); - } - - [Script] - public class npc_injured_patient : ScriptedAI - { - public npc_injured_patient(Creature creature) : base(creature) - { - Initialize(); - } - - void Initialize() - { - DoctorGUID.Clear(); - Coord = null; - } - - public ObjectGuid DoctorGUID; - public Position Coord; - - public override void Reset() - { - Initialize(); - - //no select - me.SetUninteractible(false); - - //no regen health - me.SetUnitFlag(UnitFlags.InCombat); - - //to make them lay with face down - me.SetStandState(UnitStandStateType.Dead); - - uint mobId = me.GetEntry(); - - switch (mobId) - { //lower max health - case 12923: - case 12938: //Injured Soldier - me.SetHealth(me.CountPctFromMaxHealth(75)); - break; - case 12924: - case 12936: //Badly injured Soldier - me.SetHealth(me.CountPctFromMaxHealth(50)); - break; - case 12925: - case 12937: //Critically injured Soldier - me.SetHealth(me.CountPctFromMaxHealth(25)); - break; - } - } - - public override void JustEngagedWith(Unit who) { } - - public override void SpellHit(WorldObject caster, SpellInfo spellInfo) - { - Player player = caster.ToPlayer(); - if (!player || !me.IsAlive() || spellInfo.Id != 20804) - return; - - if (player.GetQuestStatus(6624) == QuestStatus.Incomplete || player.GetQuestStatus(6622) == QuestStatus.Incomplete) - { - if (!DoctorGUID.IsEmpty()) - { - Creature doctor = ObjectAccessor.GetCreature(me, DoctorGUID); - if (doctor) - ((npc_doctor)doctor.GetAI()).PatientSaved(me, player, Coord); - } - } - - //make not selectable - me.SetUninteractible(true); - - //regen health - me.RemoveUnitFlag(UnitFlags.InCombat); - - //stand up - me.SetStandState(UnitStandStateType.Stand); - - Talk(TextIds.SayDoc); - - uint mobId = me.GetEntry(); - me.SetWalk(false); - - switch (mobId) - { - case 12923: - case 12924: - case 12925: - me.GetMotionMaster().MovePoint(0, Misc.DoctorHordeRunTo); - break; - case 12936: - case 12937: - case 12938: - me.GetMotionMaster().MovePoint(0, Misc.DoctorAllianceRunTo); - break; - } - } - - public override void UpdateAI(uint diff) - { - //lower HP on every world tick makes it a useful counter, not officlone though - if (me.IsAlive() && me.GetHealth() > 6) - me.ModifyHealth(-5); - - if (me.IsAlive() && me.GetHealth() <= 6) - { - me.RemoveUnitFlag(UnitFlags.InCombat); - me.SetUninteractible(true); - me.SetDeathState(DeathState.JustDied); - me.SetUnitFlag3(UnitFlags3.FakeDead); - - if (!DoctorGUID.IsEmpty()) - { - Creature doctor = ObjectAccessor.GetCreature((me), DoctorGUID); - if (doctor) - ((npc_doctor)doctor.GetAI()).PatientDied(Coord); - } - } - } - } - - [Script] - class npc_garments_of_quests : EscortAI - { - ObjectGuid CasterGUID; - - bool IsHealed; - bool CanRun; - - uint RunAwayTimer; - uint quest; - - public npc_garments_of_quests(Creature creature) : base(creature) - { - switch (me.GetEntry()) - { - case CreatureIds.Shaya: - quest = QuestConst.Moon; - break; - case CreatureIds.Roberts: - quest = QuestConst.Light1; - break; - case CreatureIds.Dolf: - quest = QuestConst.Light2; - break; - case CreatureIds.Korja: - quest = QuestConst.Spirit; - break; - case CreatureIds.DgKel: - quest = QuestConst.Darkness; - break; - default: - quest = 0; - break; - } - - Initialize(); - } - - void Initialize() - { - IsHealed = false; - CanRun = false; - - RunAwayTimer = 5000; - } - - public override void Reset() - { - CasterGUID.Clear(); - - Initialize(); - - me.SetStandState(UnitStandStateType.Kneel); - // expect database to have RegenHealth=0 - me.SetHealth(me.CountPctFromMaxHealth(70)); - } - - public override void JustEngagedWith(Unit who) { } - - public override void SpellHit(WorldObject caster, SpellInfo spellInfo) - { - if (spellInfo.Id == SpellIds.LesserHealR2 || spellInfo.Id == SpellIds.FortitudeR1) - { - //not while in combat - if (me.IsInCombat()) - return; - - //nothing to be done now - if (IsHealed && CanRun) - return; - - Player player = caster.ToPlayer(); - if (player) - { - if (quest != 0 && player.GetQuestStatus(quest) == QuestStatus.Incomplete) - { - if (IsHealed && !CanRun && spellInfo.Id == SpellIds.FortitudeR1) - { - Talk(TextIds.SayThanks, player); - CanRun = true; - } - else if (!IsHealed && spellInfo.Id == SpellIds.LesserHealR2) - { - CasterGUID = player.GetGUID(); - me.SetStandState(UnitStandStateType.Stand); - Talk(TextIds.SayHealed, player); - IsHealed = true; - } - } - - // give quest credit, not expect any special quest objectives - if (CanRun) - player.TalkedToCreature(me.GetEntry(), me.GetGUID()); - } - } - } - - public override void WaypointReached(uint waypointId, uint pathId) - { - } - - public override void UpdateAI(uint diff) - { - if (CanRun && !me.IsInCombat()) - { - if (RunAwayTimer <= diff) - { - Unit unit = Global.ObjAccessor.GetUnit(me, CasterGUID); - if (unit) - { - switch (me.GetEntry()) - { - case CreatureIds.Shaya: - case CreatureIds.Roberts: - case CreatureIds.Dolf: - case CreatureIds.Korja: - case CreatureIds.DgKel: - Talk(TextIds.SayGoodbye, unit); - break; - } - - LoadPath((me.GetEntry() << 3) | 2); - Start(false); - } - else - EnterEvadeMode(); //something went wrong - - RunAwayTimer = 30000; - } - else - RunAwayTimer -= diff; - } - - base.UpdateAI(diff); - } - } - - [Script] - class npc_guardian : ScriptedAI - { - public npc_guardian(Creature creature) : base(creature) { } - - public override void Reset() - { - me.SetUnitFlag(UnitFlags.NonAttackable); - } - - public override void JustEngagedWith(Unit who) { } - - public override void UpdateAI(uint diff) - { - if (!UpdateVictim()) - return; - - if (me.IsAttackReady()) - { - DoCastVictim(SpellIds.Deathtouch, new CastSpellExtraArgs(true)); - me.ResetAttackTimer(); - } - } - } - - [Script] - class npc_steam_tonk : ScriptedAI - { - public npc_steam_tonk(Creature creature) : base(creature) { } - - public override void Reset() { } - public override void JustEngagedWith(Unit who) { } - - public void OnPossess(bool apply) - { - if (apply) - { - // Initialize the action bar without the melee attack command - me.InitCharmInfo(); - me.GetCharmInfo().InitEmptyActionBar(false); - - me.SetReactState(ReactStates.Passive); - } - else - me.SetReactState(ReactStates.Aggressive); - } - } - - [Script] - class npc_brewfest_reveler : ScriptedAI - { - public npc_brewfest_reveler(Creature creature) : base(creature) { } - - public override void ReceiveEmote(Player player, TextEmotes emote) - { - if (!Global.GameEventMgr.IsHolidayActive(HolidayIds.Brewfest)) - return; - - if (emote == TextEmotes.Dance) - me.CastSpell(player, SpellIds.BrewfestToast, false); - } - } - - [Script] - class npc_brewfest_reveler_2 : ScriptedAI - { - Emote[] BrewfestRandomEmote = - { - Emote.OneshotQuestion, - Emote.OneshotApplaud, - Emote.OneshotShout, - Emote.OneshotEatNoSheathe, - Emote.OneshotLaughNoSheathe - }; - - List _revelerGuids = new(); - - public npc_brewfest_reveler_2(Creature creature) : base(creature) { } - - public override void Reset() - { - _scheduler.CancelAll(); - _scheduler.Schedule(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2), fillListTask => - { - List creatureList = me.GetCreatureListWithEntryInGrid(CreatureIds.BrewfestReveler, 5.0f); - foreach (Creature creature in creatureList) - if (creature != me) - _revelerGuids.Add(creature.GetGUID()); - - fillListTask.Schedule(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2), faceToTask => - { - // Turn to random brewfest reveler within set range - if (!_revelerGuids.Empty()) - { - Creature creature = ObjectAccessor.GetCreature(me, _revelerGuids.SelectRandom()); - if (creature != null) - me.SetFacingToObject(creature); - } - - _scheduler.Schedule(TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(6), emoteTask => - { - var nextTask = (TaskContext task) => - { - // If dancing stop before next random state - if (me.GetEmoteState() == Emote.StateDance) - me.SetEmoteState(Emote.OneshotNone); - - // Random EVENT_EMOTE or EVENT_FACETO - if (RandomHelper.randChance(50)) - faceToTask.Repeat(TimeSpan.FromSeconds(1)); - else - emoteTask.Repeat(TimeSpan.FromSeconds(1)); - }; - - // Play random emote or dance - if (RandomHelper.randChance(50)) - { - me.HandleEmoteCommand(BrewfestRandomEmote.SelectRandom()); - _scheduler.Schedule(TimeSpan.FromSeconds(4), TimeSpan.FromSeconds(6), nextTask); - } - else - { - me.SetEmoteState(Emote.StateDance); - _scheduler.Schedule(TimeSpan.FromSeconds(8), TimeSpan.FromSeconds(12), nextTask); - } - }); - }); - }); - } - - // Copied from old script. I don't know if this is 100% correct. - public override void ReceiveEmote(Player player, TextEmotes emote) - { - if (!Global.GameEventMgr.IsHolidayActive(HolidayIds.Brewfest)) - return; - - if (emote == TextEmotes.Dance) - me.CastSpell(player, SpellIds.BrewfestToast, false); - } - - public override void UpdateAI(uint diff) - { - UpdateVictim(); - - _scheduler.Update(diff); - } - } - - [Script] - class npc_training_dummy : NullCreatureAI - { - Dictionary _combatTimer = new(); - - public npc_training_dummy(Creature creature) : base(creature) { } - - public override void JustEnteredCombat(Unit who) - { - _combatTimer[who.GetGUID()] = TimeSpan.FromSeconds(5); - } - - public override void DamageTaken(Unit attacker, ref uint damage, DamageEffectType damageType, SpellInfo spellInfo = null) - { - damage = 0; - - if (!attacker || damageType == DamageEffectType.DOT) - return; - - _combatTimer[attacker.GetGUID()] = TimeSpan.FromSeconds(5); - } - - public override void UpdateAI(uint diff) - { - foreach (var key in _combatTimer.Keys.ToList()) - { - _combatTimer[key] -= TimeSpan.FromMilliseconds(diff); - if (_combatTimer[key] <= TimeSpan.Zero) - { - // The attacker has not dealt any damage to the dummy for over 5 seconds. End combat. - var pveRefs = me.GetCombatManager().GetPvECombatRefs(); - var it = pveRefs.LookupByKey(key); - if (it != null) - it.EndCombat(); - - _combatTimer.Remove(key); - } - } - } - } - - [Script] - class npc_wormhole : PassiveAI - { - public npc_wormhole(Creature creature) : base(creature) - { - Initialize(); - } - - void Initialize() - { - _showUnderground = RandomHelper.URand(0, 100) == 0; // Guessed value, it is really rare though - } - - public override void InitializeAI() - { - Initialize(); - } - - public override bool OnGossipHello(Player player) - { - player.InitGossipMenu(GossipMenus.MenuIdWormhole); - if (me.IsSummon()) - { - if (player == me.ToTempSummon().GetSummoner()) - { - player.AddGossipItem(GossipMenus.MenuIdWormhole, GossipMenus.OptionIdWormhole1, eTradeskill.GossipSenderMain, eTradeskill.GossipActionInfoDef + 1); - player.AddGossipItem(GossipMenus.MenuIdWormhole, GossipMenus.OptionIdWormhole2, eTradeskill.GossipSenderMain, eTradeskill.GossipActionInfoDef + 2); - player.AddGossipItem(GossipMenus.MenuIdWormhole, GossipMenus.OptionIdWormhole3, eTradeskill.GossipSenderMain, eTradeskill.GossipActionInfoDef + 3); - player.AddGossipItem(GossipMenus.MenuIdWormhole, GossipMenus.OptionIdWormhole4, eTradeskill.GossipSenderMain, eTradeskill.GossipActionInfoDef + 4); - player.AddGossipItem(GossipMenus.MenuIdWormhole, GossipMenus.OptionIdWormhole5, eTradeskill.GossipSenderMain, eTradeskill.GossipActionInfoDef + 5); - - if (_showUnderground) - player.AddGossipItem(GossipMenus.MenuIdWormhole, GossipMenus.OptionIdWormhole6, eTradeskill.GossipSenderMain, eTradeskill.GossipActionInfoDef + 6); - - player.SendGossipMenu(TextIds.Wormhole, me.GetGUID()); - } - } - - return true; - } - - public override bool OnGossipSelect(Player player, uint menuId, uint gossipListId) - { - uint action = player.PlayerTalkClass.GetGossipOptionAction(gossipListId); - player.PlayerTalkClass.ClearMenus(); - - switch (action) - { - case eTradeskill.GossipActionInfoDef + 1: // Borean Tundra - player.CloseGossipMenu(); - DoCast(player, SpellIds.BoreanTundra, new CastSpellExtraArgs(false)); - break; - case eTradeskill.GossipActionInfoDef + 2: // Howling Fjord - player.CloseGossipMenu(); - DoCast(player, SpellIds.HowlingFjord, new CastSpellExtraArgs(false)); - break; - case eTradeskill.GossipActionInfoDef + 3: // Sholazar Basin - player.CloseGossipMenu(); - DoCast(player, SpellIds.SholazarBasin, new CastSpellExtraArgs(false)); - break; - case eTradeskill.GossipActionInfoDef + 4: // Icecrown - player.CloseGossipMenu(); - DoCast(player, SpellIds.Icecrown, new CastSpellExtraArgs(false)); - break; - case eTradeskill.GossipActionInfoDef + 5: // Storm peaks - player.CloseGossipMenu(); - DoCast(player, SpellIds.StormPeaks, new CastSpellExtraArgs(false)); - break; - case eTradeskill.GossipActionInfoDef + 6: // Underground - player.CloseGossipMenu(); - DoCast(player, SpellIds.Underground, new CastSpellExtraArgs(false)); - break; - } - - return true; - } - - bool _showUnderground; - } - - [Script] - class npc_spring_rabbit : ScriptedAI - { - public npc_spring_rabbit(Creature creature) : base(creature) - { - Initialize(); - } - - void Initialize() - { - inLove = false; - rabbitGUID.Clear(); - jumpTimer = RandomHelper.URand(5000, 10000); - bunnyTimer = RandomHelper.URand(10000, 20000); - searchTimer = RandomHelper.URand(5000, 10000); - } - - bool inLove; - uint jumpTimer; - uint bunnyTimer; - uint searchTimer; - ObjectGuid rabbitGUID; - - public override void Reset() - { - Initialize(); - Unit owner = me.GetOwner(); - if (owner) - me.GetMotionMaster().MoveFollow(owner, SharedConst.PetFollowDist, SharedConst.PetFollowAngle); - } - - public override void JustEngagedWith(Unit who) { } - - public override void DoAction(int param) - { - inLove = true; - Unit owner = me.GetOwner(); - if (owner) - owner.CastSpell(owner, SpellIds.SpringFling, true); - } - - public override void UpdateAI(uint diff) - { - if (inLove) - { - if (jumpTimer <= diff) - { - Unit rabbit = Global.ObjAccessor.GetUnit(me, rabbitGUID); - if (rabbit) - DoCast(rabbit, SpellIds.SpringRabbitJump); - jumpTimer = RandomHelper.URand(5000, 10000); - } - else jumpTimer -= diff; - - if (bunnyTimer <= diff) - { - DoCast(SpellIds.SummonBabyBunny); - bunnyTimer = RandomHelper.URand(20000, 40000); - } - else bunnyTimer -= diff; - } - else - { - if (searchTimer <= diff) - { - Creature rabbit = me.FindNearestCreature(CreatureIds.SpringRabbit, 10.0f); - if (rabbit) - { - if (rabbit == me || rabbit.HasAura(SpellIds.SpringRabbitInLove)) - return; - - me.AddAura(SpellIds.SpringRabbitInLove, me); - DoAction(1); - rabbit.AddAura(SpellIds.SpringRabbitInLove, rabbit); - rabbit.GetAI().DoAction(1); - rabbit.CastSpell(rabbit, SpellIds.SpringRabbitJump, true); - rabbitGUID = rabbit.GetGUID(); - } - searchTimer = RandomHelper.URand(5000, 10000); - } - else searchTimer -= diff; - } - } - } - - [Script] - class npc_imp_in_a_ball : ScriptedAI - { - public npc_imp_in_a_ball(Creature creature) : base(creature) - { - summonerGUID.Clear(); - } - - public override void IsSummonedBy(WorldObject summoner) - { - if (summoner.IsTypeId(TypeId.Player)) - { - summonerGUID = summoner.GetGUID(); - - _scheduler.Schedule(TimeSpan.FromSeconds(3), task => - { - Player owner = Global.ObjAccessor.GetPlayer(me, summonerGUID); - if (owner) - Global.CreatureTextMgr.SendChat(me, 0, owner, owner.GetGroup() ? ChatMsg.MonsterParty : ChatMsg.MonsterWhisper, Language.Addon, CreatureTextRange.Normal); - }); - } - } - - public override void UpdateAI(uint diff) - { - _scheduler.Update(diff); - } - - ObjectGuid summonerGUID; - } - - struct TrainWrecker - { - public const int EventDoJump = 1; - public const int EventDoFacing = 2; - public const int EventDoWreck = 3; - public const int EventDoDance = 4; - public const uint MoveidChase = 1; - public const uint MoveidJump = 2; - } - - [Script] - class npc_train_wrecker : NullCreatureAI - { - public npc_train_wrecker(Creature creature) : base(creature) - { - _isSearching = true; - _nextAction = 0; - _timer = 1 * Time.InMilliseconds; - } - - GameObject VerifyTarget() - { - GameObject target = ObjectAccessor.GetGameObject(me, _target); - if (target) - return target; - - me.HandleEmoteCommand(Emote.OneshotRude); - me.DespawnOrUnsummon(TimeSpan.FromSeconds(3)); - return null; - } - - public override void UpdateAI(uint diff) - { - if (_isSearching) - { - if (diff < _timer) - _timer -= diff; - else - { - GameObject target = me.FindNearestGameObject(GameobjectIds.ToyTrain, 15.0f); - if (target) - { - _isSearching = false; - _target = target.GetGUID(); - me.SetWalk(true); - me.GetMotionMaster().MovePoint(TrainWrecker.MoveidChase, target.GetNearPosition(3.0f, target.GetAbsoluteAngle(me))); - } - else - _timer = 3 * Time.InMilliseconds; - } - } - else - { - switch (_nextAction) - { - case TrainWrecker.EventDoJump: - { - GameObject target = VerifyTarget(); - if (target) - me.GetMotionMaster().MoveJump(target, 5.0f, 10.0f, TrainWrecker.MoveidJump); - _nextAction = 0; - } - break; - case TrainWrecker.EventDoFacing: - { - GameObject target = VerifyTarget(); - if (target) - { - me.SetFacingTo(target.GetOrientation()); - me.HandleEmoteCommand(Emote.OneshotAttack1h); - _timer = (uint)(1.5 * Time.InMilliseconds); - _nextAction = TrainWrecker.EventDoWreck; - } - else - _nextAction = 0; - } - break; - case TrainWrecker.EventDoWreck: - { - if (diff < _timer) - { - _timer -= diff; - break; - } - - GameObject target = VerifyTarget(); - if (target) - { - me.CastSpell(target, SpellIds.WreckTrain, false); - _timer = 2 * Time.InMilliseconds; - _nextAction = TrainWrecker.EventDoDance; - } - else - _nextAction = 0; - } - break; - case TrainWrecker.EventDoDance: - if (diff < _timer) - { - _timer -= diff; - break; - } - me.UpdateEntry(CreatureIds.ExultingWindUpTrainWrecker); - me.SetEmoteState(Emote.OneshotDance); - me.DespawnOrUnsummon(TimeSpan.FromSeconds(5)); - _nextAction = 0; - break; - default: - break; - } - } - } - - public override void MovementInform(MovementGeneratorType type, uint id) - { - if (id == TrainWrecker.MoveidChase) - _nextAction = TrainWrecker.EventDoJump; - else if (id == TrainWrecker.MoveidJump) - _nextAction = TrainWrecker.EventDoFacing; - } - - bool _isSearching; - byte _nextAction; - uint _timer; - ObjectGuid _target; - } - - [Script] - class npc_argent_squire_gruntling : ScriptedAI - { - public npc_argent_squire_gruntling(Creature creature) : base(creature) { } - - public override void Reset() - { - Player owner = me.GetOwner()?.ToPlayer(); - if (owner != null) - { - Aura ownerTired = owner.GetAura(SpellIds.TiredPlayer); - if (ownerTired != null) - { - Aura squireTired = me.AddAura(IsArgentSquire() ? SpellIds.AuraTiredS : SpellIds.AuraTiredG, me); - if (squireTired != null) - squireTired.SetDuration(ownerTired.GetDuration()); - } - - if (owner.HasAchieved(Misc.AchievementPonyUp) && !me.HasAura(SpellIds.AuraTiredS) && !me.HasAura(SpellIds.AuraTiredG)) - { - me.SetNpcFlag(NPCFlags.Banker | NPCFlags.Mailbox | NPCFlags.Vendor); - return; - } - } - - me.RemoveNpcFlag(NPCFlags.Banker | NPCFlags.Mailbox | NPCFlags.Vendor); - } - - public override bool OnGossipSelect(Player player, uint menuId, uint gossipListId) - { - switch (gossipListId) - { - case GossipMenus.OptionIdBank: - { - me.RemoveNpcFlag(NPCFlags.Mailbox | NPCFlags.Vendor); - uint _bankAura = IsArgentSquire() ? SpellIds.AuraBankS : SpellIds.AuraBankG; - if (!me.HasAura(_bankAura)) - DoCastSelf(_bankAura); - - if (!player.HasAura(SpellIds.TiredPlayer)) - player.CastSpell(player, SpellIds.TiredPlayer, true); - break; - } - case GossipMenus.OptionIdShop: - { - me.RemoveNpcFlag(NPCFlags.Banker | NPCFlags.Mailbox); - uint _shopAura = IsArgentSquire() ? SpellIds.AuraShopS : SpellIds.AuraShopG; - if (!me.HasAura(_shopAura)) - DoCastSelf(_shopAura); - - if (!player.HasAura(SpellIds.TiredPlayer)) - player.CastSpell(player, SpellIds.TiredPlayer, true); - break; - } - case GossipMenus.OptionIdMail: - { - me.RemoveNpcFlag(NPCFlags.Banker | NPCFlags.Vendor); - - uint _mailAura = IsArgentSquire() ? SpellIds.AuraPostmanS : SpellIds.AuraPostmanG; - if (!me.HasAura(_mailAura)) - DoCastSelf(_mailAura); - - if (!player.HasAura(SpellIds.TiredPlayer)) - player.CastSpell(player, SpellIds.TiredPlayer, true); - break; - } - case GossipMenus.OptionIdDarnassusSenjinPennant: - case GossipMenus.OptionIdExodarUndercityPennant: - case GossipMenus.OptionIdGnomereganOrgrimmarPennant: - case GossipMenus.OptionIdIronforgeSilvermoonPennant: - case GossipMenus.OptionIdStormwindThunderbluffPennant: - if (IsArgentSquire()) - DoCastSelf(Misc.bannerSpells[gossipListId - 3].Item1, new CastSpellExtraArgs(true)); - else - DoCastSelf(Misc.bannerSpells[gossipListId - 3].Item2, new CastSpellExtraArgs(true)); - - player.PlayerTalkClass.SendCloseGossip(); - break; - default: - break; - } - - return false; - } - - bool IsArgentSquire() { return me.GetEntry() == CreatureIds.ArgentSquire; } - } - - [Script] - class npc_bountiful_table : PassiveAI - { - Dictionary ChairSpells = new() - { - { CreatureIds.TheCranberryChair, SpellIds.CranberryServer }, - { CreatureIds.ThePieChair, SpellIds.PieServer }, - { CreatureIds.TheStuffingChair, SpellIds.StuffingServer }, - { CreatureIds.TheTurkeyChair, SpellIds.TurkeyServer }, - { CreatureIds.TheSweetPotatoChair, SpellIds.SweetPotatoesServer }, - }; - - public npc_bountiful_table(Creature creature) : base(creature) { } - - public override void PassengerBoarded(Unit who, sbyte seatId, bool apply) - { - float x = 0.0f; - float y = 0.0f; - float z = 0.0f; - float o = 0.0f; - - switch ((SeatIds)seatId) - { - case SeatIds.TurkeyChair: - x = 3.87f; - y = 2.07f; - o = 3.700098f; - break; - case SeatIds.CranberryChair: - x = 3.87f; - y = -2.07f; - o = 2.460914f; - break; - case SeatIds.StuffingChair: - x = -2.52f; - break; - case SeatIds.SweetPotatoChair: - x = -0.09f; - y = -3.24f; - o = 1.186824f; - break; - case SeatIds.PieChair: - x = -0.18f; - y = 3.24f; - o = 5.009095f; - break; - case SeatIds.FoodHolder: - case SeatIds.PlateHolder: - Vehicle holders = who.GetVehicleKit(); - if (holders) - holders.InstallAllAccessories(true); - return; - default: - break; - } - - var initializer = (MoveSplineInit init) => - { - init.DisableTransportPathTransformations(); - init.MoveTo(x, y, z, false); - init.SetFacing(o); - }; - - who.GetMotionMaster().LaunchMoveSpline(initializer, EventId.VehicleBoard, MovementGeneratorPriority.Highest); - who.m_Events.AddEvent(new CastFoodSpell(who, ChairSpells[who.GetEntry()]), who.m_Events.CalculateTime(TimeSpan.FromSeconds(1))); - Creature creature = who.ToCreature(); - if (creature) - creature.SetDisplayFromModel(0); - } - } - - [Script] - class npc_gen_void_zone : ScriptedAI - { - public npc_gen_void_zone(Creature creature) : base(creature) { } - - public override void InitializeAI() - { - me.SetReactState(ReactStates.Passive); - } - - public override void JustAppeared() - { - _scheduler.Schedule(TimeSpan.FromSeconds(2), task => - { - DoCastSelf(SpellIds.Consumption); - }); - } - - public override void UpdateAI(uint diff) - { - _scheduler.Update(diff); - } - } - - class CastFoodSpell : BasicEvent - { - Unit _owner; - uint _spellId; - - public CastFoodSpell(Unit owner, uint spellId) - { - _owner = owner; - _spellId = spellId; - } - - public override bool Execute(ulong execTime, uint diff) - { - _owner.CastSpell(_owner, _spellId, true); - return true; - } - } -} diff --git a/Source/Scripts/World/NpcsSpecial.cs b/Source/Scripts/World/NpcsSpecial.cs new file mode 100644 index 000000000..5db77838b --- /dev/null +++ b/Source/Scripts/World/NpcsSpecial.cs @@ -0,0 +1,1921 @@ +// Copyright (c) CypherCore All rights reserved. +// Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information. + +using Framework.Constants; +using Framework.Dynamic; +using Game; +using Game.AI; +using Game.Entities; +using Game.Maps; +using Game.Movement; +using Game.Scripting; +using Game.Spells; +using System; +using System.Collections.Generic; +using System.Linq; +using static Global; + +namespace Scripts.World.NpcsSpecial +{ + enum AirForceBots + { + Tripwire, // do not attack flying players, smaller range + Alarmbot, // attack flying players, casts guard's mark + } + + class AirForceSpawn + { + public uint MyEntry; + public uint OtherEntry; + public AirForceBots Type; + + public AirForceSpawn(uint myEntry, uint otherEntry, AirForceBots type) + { + MyEntry = myEntry; + OtherEntry = otherEntry; + Type = type; + } + } + + [Script] + class npc_air_force_bots : NullCreatureAI + { + AirForceSpawn _spawn; + ObjectGuid _myGuard; + List _toAttack = new(); + + AirForceSpawn[] airforceSpawns = + { + new AirForceSpawn(2614, 15241, AirForceBots.Alarmbot), // Air Force Alarm Bot (Alliance) + new AirForceSpawn(2615, 15242, AirForceBots.Alarmbot), // Air Force Alarm Bot (Horde) + new AirForceSpawn(21974, 21976, AirForceBots.Alarmbot), // Air Force Alarm Bot (Area 52) + new AirForceSpawn(21993, 15242, AirForceBots.Alarmbot), // Air Force Guard Post (Horde - Bat Rider) + new AirForceSpawn(21996, 15241, AirForceBots.Alarmbot), // Air Force Guard Post (Alliance - Gryphon) + new AirForceSpawn(21997, 21976, AirForceBots.Alarmbot), // Air Force Guard Post (Goblin - Area 52 - Zeppelin) + new AirForceSpawn(21999, 15241, AirForceBots.Tripwire), // Air Force Trip Wire - Rooftop (Alliance) + new AirForceSpawn(22001, 15242, AirForceBots.Tripwire), // Air Force Trip Wire - Rooftop (Horde) + new AirForceSpawn(22002, 15242, AirForceBots.Tripwire), // Air Force Trip Wire - Ground (Horde) + new AirForceSpawn(22003, 15241, AirForceBots.Tripwire), // Air Force Trip Wire - Ground (Alliance) + new AirForceSpawn(22063, 21976, AirForceBots.Tripwire), // Air Force Trip Wire - Rooftop (Goblin - Area 52) + new AirForceSpawn(22065, 22064, AirForceBots.Alarmbot), // Air Force Guard Post (Ethereal - Stormspire) + new AirForceSpawn(22066, 22067, AirForceBots.Alarmbot), // Air Force Guard Post (Scryer - Dragonhawk) + new AirForceSpawn(22068, 22064, AirForceBots.Tripwire), // Air Force Trip Wire - Rooftop (Ethereal - Stormspire) + new AirForceSpawn(22069, 22064, AirForceBots.Alarmbot), // Air Force Alarm Bot (Stormspire) + new AirForceSpawn(22070, 22067, AirForceBots.Tripwire), // Air Force Trip Wire - Rooftop (Scryer) + new AirForceSpawn(22071, 22067, AirForceBots.Alarmbot), // Air Force Alarm Bot (Scryer) + new AirForceSpawn(22078, 22077, AirForceBots.Alarmbot), // Air Force Alarm Bot (Aldor) + new AirForceSpawn(22079, 22077, AirForceBots.Alarmbot), // Air Force Guard Post (Aldor - Gryphon) + new AirForceSpawn(22080, 22077, AirForceBots.Tripwire), // Air Force Trip Wire - Rooftop (Aldor) + new AirForceSpawn(22086, 22085, AirForceBots.Alarmbot), // Air Force Alarm Bot (Sporeggar) + new AirForceSpawn(22087, 22085, AirForceBots.Alarmbot), // Air Force Guard Post (Sporeggar - Spore Bat) + new AirForceSpawn(22088, 22085, AirForceBots.Tripwire), // Air Force Trip Wire - Rooftop (Sporeggar) + new AirForceSpawn(22090, 22089, AirForceBots.Alarmbot), // Air Force Guard Post (Toshley's Station - Flying Machine) + new AirForceSpawn(22124, 22122, AirForceBots.Alarmbot), // Air Force Alarm Bot (Cenarion) + new AirForceSpawn(22125, 22122, AirForceBots.Alarmbot), // Air Force Guard Post (Cenarion - Stormcrow) + new AirForceSpawn(22126, 22122, AirForceBots.Alarmbot) // Air Force Trip Wire - Rooftop (Cenarion Expedition) + }; + + float RangeTripwire = 15.0f; + float RangeAlarmbot = 100.0f; + const uint SpellGuardsMark = 38067; + + public npc_air_force_bots(Creature creature) : base(creature) + { + _spawn = FindSpawnFor(creature.GetEntry()); + } + + Creature GetOrSummonGuard() + { + Creature guard = ObjectAccessor.GetCreature(me, _myGuard); + + if (guard == null && (guard = me.SummonCreature(_spawn.OtherEntry, 0.0f, 0.0f, 0.0f, 0.0f, TempSummonType.TimedDespawnOutOfCombat, TimeSpan.FromMinutes(5))) != null) + _myGuard = guard.GetGUID(); + + return guard; + } + + public override void UpdateAI(uint diff) + { + if (_toAttack.Empty()) + return; + + Creature guard = GetOrSummonGuard(); + if (guard == null) + return; + + // Keep the list of targets for later on when the guards will be alive + if (!guard.IsAlive()) + return; + + foreach (ObjectGuid guid in _toAttack) + { + Unit target = ObjAccessor.GetUnit(me, guid); + if (target == null) + continue; + + if (guard.IsEngagedBy(target)) + continue; + + guard.EngageWithTarget(target); + if (_spawn.Type == AirForceBots.Alarmbot) + guard.CastSpell(target, SpellGuardsMark, true); + } + + _toAttack.Clear(); + } + + public override void MoveInLineOfSight(Unit who) + { + // guards are only spawned against players + if (!who.IsPlayer()) + return; + + // we're already scheduled to attack this player on our next tick, don't bother checking + if (_toAttack.Contains(who.GetGUID())) + return; + + // check if they're in range + if (!who.IsWithinDistInMap(me, (_spawn.Type == AirForceBots.Alarmbot) ? RangeAlarmbot : RangeTripwire)) + return; + + // check if they're hostile + if (!(me.IsHostileTo(who) || who.IsHostileTo(me))) + return; + + // check if they're a valid attack target + if (!me.IsValidAttackTarget(who)) + return; + + if ((_spawn.Type == AirForceBots.Tripwire) && who.IsFlying()) + return; + + _toAttack.Add(who.GetGUID()); + } + + AirForceSpawn FindSpawnFor(uint entry) + { + foreach (AirForceSpawn spawn in airforceSpawns) + { + if (spawn.MyEntry == entry) + { + Cypher.Assert(ObjectMgr.GetCreatureTemplate(spawn.OtherEntry) != null, $"Invalid creature entry {spawn.OtherEntry} in 'npc_air_force_bots' script"); + return spawn; + } + } + Cypher.Assert(false, $"Unhandled creature with entry {entry} is assigned 'npc_air_force_bots' script"); + return null; + } + } + + [Script] + class npc_chicken_cluck : ScriptedAI + { + const uint QuestCluck = 3861; + + public npc_chicken_cluck(Creature creature) : base(creature) + { + Initialize(); + } + + void Initialize() + { + ResetFlagTimer = 120000; + } + + uint ResetFlagTimer; + + public override void Reset() + { + Initialize(); + me.SetFaction(FactionTemplates.Prey); + me.RemoveNpcFlag(NPCFlags.QuestGiver); + } + + public override void JustEngagedWith(Unit who) { } + + public override void UpdateAI(uint diff) + { + // Reset flags after a certain time has passed so that the next player has to start the 'event' again + if (me.HasNpcFlag(NPCFlags.QuestGiver)) + { + if (ResetFlagTimer <= diff) + { + EnterEvadeMode(); + return; + } + else + ResetFlagTimer -= diff; + } + + if (UpdateVictim()) + DoMeleeAttackIfReady(); + } + + public override void ReceiveEmote(Player player, TextEmotes emote) + { + switch (emote) + { + case TextEmotes.Chicken: + if (player.GetQuestStatus(QuestCluck) == QuestStatus.None && RandomHelper.Rand32() % 30 == 1) + { + me.SetNpcFlag(NPCFlags.QuestGiver); + me.SetFaction(FactionTemplates.Friendly); + Talk(player.GetTeam() == Team.Horde ? 1 : 0u); + } + break; + case TextEmotes.Cheer: + if (player.GetQuestStatus(QuestCluck) == QuestStatus.Complete) + { + me.SetNpcFlag(NPCFlags.QuestGiver); + me.SetFaction(FactionTemplates.Friendly); + Talk(2); + } + break; + } + } + + public override void OnQuestAccept(Player player, Quest quest) + { + if (quest.Id == QuestCluck) + Reset(); + } + + public override void OnQuestReward(Player player, Quest quest, LootItemType type, uint opt) + { + if (quest.Id == QuestCluck) + Reset(); + } + } + + [Script] + class npc_dancing_flames : ScriptedAI + { + const uint SpellSummonBrazier = 45423; + const uint SpellBrazierDance = 45427; + const uint SpellFierySeduction = 47057; + + public npc_dancing_flames(Creature creature) : base(creature) { } + + public override void Reset() + { + DoCastSelf(SpellSummonBrazier, true); + DoCastSelf(SpellBrazierDance, false); + me.SetEmoteState(Emote.StateDance); + me.GetPosition(out float x, out float y, out float z); + me.Relocate(x, y, z + 1.05f); + } + + public override void UpdateAI(uint diff) + { + _scheduler.Update(diff); + } + + public override void ReceiveEmote(Player player, TextEmotes emote) + { + if (me.IsWithinLOS(player.GetPositionX(), player.GetPositionY(), player.GetPositionZ()) && me.IsWithinDistInMap(player, 30.0f)) + { + // She responds to emotes not instantly but ~TimeSpan.FromMilliseconds(1500) later + // If you first /bow, then /wave before dancing flames bow back, it doesnt bow at all and only does wave + // If you're performing emotes too fast, she will not respond to them + // Means she just replaces currently scheduled event with new after receiving new emote + _scheduler.CancelAll(); + + switch (emote) + { + case TextEmotes.Kiss: + _scheduler.Schedule(TimeSpan.FromMilliseconds(1500), context => me.HandleEmoteCommand(Emote.OneshotShy)); + break; + case TextEmotes.Wave: + _scheduler.Schedule(TimeSpan.FromMilliseconds(1500), context => me.HandleEmoteCommand(Emote.OneshotWave)); + break; + case TextEmotes.Bow: + _scheduler.Schedule(TimeSpan.FromMilliseconds(1500), context => me.HandleEmoteCommand(Emote.OneshotBow)); + break; + case TextEmotes.Joke: + _scheduler.Schedule(TimeSpan.FromMilliseconds(1500), context => me.HandleEmoteCommand(Emote.OneshotLaugh)); + break; + case TextEmotes.Dance: + if (!player.HasAura(SpellFierySeduction)) + { + DoCast(player, SpellFierySeduction, true); + me.SetFacingTo(me.GetAbsoluteAngle(player)); + } + break; + } + } + } + } + + [Script] + class npc_torch_tossing_target_bunny_controller : ScriptedAI + { + const uint SpellTorchTargetPicker = 45907; + + public npc_torch_tossing_target_bunny_controller(Creature creature) : base(creature) { } + + public override void Reset() + { + _scheduler.Schedule(TimeSpan.FromSeconds(2), context => + { + me.CastSpell(null, SpellTorchTargetPicker); + _scheduler.Schedule(TimeSpan.FromSeconds(3), context => me.CastSpell(null, SpellTorchTargetPicker)); + context.Repeat(TimeSpan.FromSeconds(5)); + }); + } + + public override void UpdateAI(uint diff) + { + _scheduler.Update(diff); + } + } + + [Script] + class npc_midsummer_bunny_pole : ScriptedAI + { + const uint GoRibbonPole = 181605; + const uint SpellRibbonDanceCosmetic = 29726; + const uint SpellRedFireRing = 46836; + const uint SpellBlueFireRing = 46842; + + bool running; + + public npc_midsummer_bunny_pole(Creature creature) : base(creature) + { + Initialize(); + } + + void Initialize() + { + _scheduler.CancelAll(); + running = false; + } + + public override void Reset() + { + Initialize(); + } + + public override void DoAction(int action) + { + // Don't start event if it's already running. + if (running) + return; + + running = true; + _scheduler.Schedule(TimeSpan.FromMilliseconds(1), context => + { + if (checkNearbyPlayers()) + { + Reset(); + return; + } + + GameObject go = me.FindNearestGameObject(GoRibbonPole, 10.0f); + if (go != null) + me.CastSpell(go, SpellRedFireRing, true); + + context.Schedule(TimeSpan.FromSeconds(5), _ => + { + if (checkNearbyPlayers()) + { + Reset(); + return; + } + + GameObject go = me.FindNearestGameObject(GoRibbonPole, 10.0f); + if (go != null) + me.CastSpell(go, SpellBlueFireRing, true); + + context.Repeat(TimeSpan.FromSeconds(5)); + }); + }); + } + + bool checkNearbyPlayers() + { + // Returns true if no nearby player has aura "Test Ribbon Pole Channel". + List players = new(); + UnitAuraCheck check = new(true, SpellRibbonDanceCosmetic); + PlayerListSearcher searcher = new(me, players, check); + Cell.VisitWorldObjects(me, searcher, 10.0f); + + return players.Empty(); + } + + public override void UpdateAI(uint diff) + { + if (!running) + return; + + _scheduler.Update(diff); + } + } + + struct DoctorConst + { + public const uint SayDoc = 0; + + public const uint DoctorAlliance = 12939; + public const uint DoctorHorde = 12920; + + public static Position[] AllianceCoords = + { + new Position(-3757.38f, -4533.05f, 14.16f, 3.62f), // Top-far-right bunk as seen from entrance + new Position(-3754.36f, -4539.13f, 14.16f, 5.13f), // Top-far-left bunk + new Position(-3749.54f, -4540.25f, 14.28f, 3.34f), // Far-right bunk + new Position(-3742.10f, -4536.85f, 14.28f, 3.64f), // Right bunk near entrance + new Position(-3755.89f, -4529.07f, 14.05f, 0.57f), // Far-left bunk + new Position(-3749.51f, -4527.08f, 14.07f, 5.26f), // Mid-left bunk + new Position(-3746.37f, -4525.35f, 14.16f, 5.22f), // Left bunk near entrance + }; + + //alliance run to where + public static Position ARunTo = new Position(-3742.96f, -4531.52f, 11.91f); + + public static Position[] HordeCoords = + { + new Position(-1013.75f, -3492.59f, 62.62f, 4.34f), // Left, Behind + new Position(-1017.72f, -3490.92f, 62.62f, 4.34f), // Right, Behind + new Position(-1015.77f, -3497.15f, 62.82f, 4.34f), // Left, Mid + new Position(-1019.51f, -3495.49f, 62.82f, 4.34f), // Right, Mid + new Position(-1017.25f, -3500.85f, 62.98f, 4.34f), // Left, front + new Position(-1020.95f, -3499.21f, 62.98f, 4.34f) // Right, Front + }; + + //horde run to where + public static Position HRunTo = new Position(-1016.44f, -3508.48f, 62.96f); + + public static uint[] AllianceSoldierId = + { + 12938, // 12938 Injured Alliance Soldier + 12936, // 12936 Badly injured Alliance Soldier + 12937 // 12937 Critically injured Alliance Soldier + }; + + public static uint[] HordeSoldierId = + { + 12923, //12923 Injured Soldier + 12924, //12924 Badly injured Soldier + 12925 //12925 Critically injured Soldier + }; + } + + [Script] + class npc_doctor : ScriptedAI + { + ObjectGuid PlayerGUID; + + uint SummonPatientTimer; + uint SummonPatientCount; + uint PatientDiedCount; + uint PatientSavedCount; + + bool Event; + + List Patients = new(); + List Coordinates = new(); + + public npc_doctor(Creature creature) : base(creature) + { + Initialize(); + } + + void Initialize() + { + PlayerGUID.Clear(); + + SummonPatientTimer = 10000; + SummonPatientCount = 0; + PatientDiedCount = 0; + PatientSavedCount = 0; + + Patients.Clear(); + Coordinates.Clear(); + + Event = false; + } + + public override void Reset() + { + Initialize(); + me.SetUninteractible(false); + } + + void BeginEvent(Player player) + { + PlayerGUID = player.GetGUID(); + + SummonPatientTimer = 10000; + SummonPatientCount = 0; + PatientDiedCount = 0; + PatientSavedCount = 0; + + switch (me.GetEntry()) + { + case DoctorConst.DoctorAlliance: + for (byte i = 0; i < DoctorConst.AllianceCoords.Length; ++i) + Coordinates.Add(DoctorConst.AllianceCoords[i]); + break; + case DoctorConst.DoctorHorde: + for (byte i = 0; i < DoctorConst.HordeCoords.Length; ++i) + Coordinates.Add(DoctorConst.HordeCoords[i]); + break; + } + + Event = true; + me.SetUninteractible(true); + } + + public void PatientDied(Position point) + { + Player player = ObjAccessor.GetPlayer(me, PlayerGUID); + if (player != null && ((player.GetQuestStatus(6624) == QuestStatus.Incomplete) || (player.GetQuestStatus(6622) == QuestStatus.Incomplete))) + { + ++PatientDiedCount; + + if (PatientDiedCount > 5 && Event) + { + if (player.GetQuestStatus(6624) == QuestStatus.Incomplete) + player.FailQuest(6624); + else if (player.GetQuestStatus(6622) == QuestStatus.Incomplete) + player.FailQuest(6622); + + Reset(); + return; + } + + Coordinates.Add(point); + } + else + // If no player or player abandon quest in progress + Reset(); + } + + public void PatientSaved(Creature soldier, Player player, Position point) + { + if (player != null && PlayerGUID == player.GetGUID()) + { + if ((player.GetQuestStatus(6624) == QuestStatus.Incomplete) || (player.GetQuestStatus(6622) == QuestStatus.Incomplete)) + { + ++PatientSavedCount; + + if (PatientSavedCount == 15) + { + if (!Patients.Empty()) + { + foreach (var guid in Patients) + { + Creature patient = ObjectAccessor.GetCreature(me, guid); + if (patient != null) + patient.SetDeathState(DeathState.JustDied); + } + } + + if (player.GetQuestStatus(6624) == QuestStatus.Incomplete) + player.AreaExploredOrEventHappens(6624); + else if (player.GetQuestStatus(6622) == QuestStatus.Incomplete) + player.AreaExploredOrEventHappens(6622); + + Reset(); + return; + } + + Coordinates.Add(point); + } + } + } + + public override void UpdateAI(uint diff) + { + if (Event && SummonPatientCount >= 20) + { + Reset(); + return; + } + + if (Event) + { + if (SummonPatientTimer <= diff) + { + if (Coordinates.Empty()) + return; + + uint patientEntry; + + switch (me.GetEntry()) + { + case DoctorConst.DoctorAlliance: + patientEntry = DoctorConst.AllianceSoldierId[RandomHelper.Rand32() % 3]; + break; + case DoctorConst.DoctorHorde: + patientEntry = DoctorConst.HordeSoldierId[RandomHelper.Rand32() % 3]; + break; + default: + Log.outError(LogFilter.Scripts, "Invalid entry for Triage doctor. Please check your database"); + return; + } + + var point = Coordinates[RandomHelper.IRand(0, Coordinates.Count - 1)]; + + Creature patient = me.SummonCreature(patientEntry, point, TempSummonType.TimedDespawnOutOfCombat, TimeSpan.FromSeconds(5)); + if (patient != null) + { + //303, this flag appear to be required for client side item.spell to work (TargetMath.SingleFriend) + patient.SetUnitFlag(UnitFlags.PlayerControlled); + + Patients.Add(patient.GetGUID()); + + var patientAI = patient.GetAI(); + if (patientAI != null) + { + patientAI.DoctorGUID = me.GetGUID(); + patientAI.Coord = point; + } + + Coordinates.Remove(point); + } + + SummonPatientTimer = 10000; + ++SummonPatientCount; + } + else + SummonPatientTimer -= diff; + } + } + + public override void JustEngagedWith(Unit who) { } + + public override void OnQuestAccept(Player player, Quest quest) + { + if ((quest.Id == 6624) || (quest.Id == 6622)) + BeginEvent(player); + } + } + + [Script] + class npc_injured_patient : ScriptedAI + { + public ObjectGuid DoctorGUID; + public Position Coord; + + public npc_injured_patient(Creature creature) : base(creature) + { + Initialize(); + } + + void Initialize() + { + DoctorGUID.Clear(); + Coord = null; + } + + public override void Reset() + { + Initialize(); + + //no select + me.SetUninteractible(false); + + //no regen health + me.SetUnitFlag(UnitFlags.InCombat); + + //to make them lay with face down + me.SetStandState(UnitStandStateType.Dead); + + uint mobId = me.GetEntry(); + + switch (mobId) + { //lower max health + case 12923: + case 12938: //Injured Soldier + me.SetHealth(me.CountPctFromMaxHealth(75)); + break; + case 12924: + case 12936: //Badly injured Soldier + me.SetHealth(me.CountPctFromMaxHealth(50)); + break; + case 12925: + case 12937: //Critically injured Soldier + me.SetHealth(me.CountPctFromMaxHealth(25)); + break; + } + } + + public override void JustEngagedWith(Unit who) { } + + public override void SpellHit(WorldObject caster, SpellInfo spellInfo) + { + Player player = caster.ToPlayer(); + if (player == null || !me.IsAlive() || spellInfo.Id != 20804) + return; + + if (player.GetQuestStatus(6624) == QuestStatus.Incomplete || player.GetQuestStatus(6622) == QuestStatus.Incomplete) + if (!DoctorGUID.IsEmpty()) + { + Creature doctor = ObjectAccessor.GetCreature(me, DoctorGUID); + if (doctor != null) + doctor.GetAI().PatientSaved(me, player, Coord); + } + + //make uninteractible + me.SetUninteractible(true); + + //regen health + me.RemoveUnitFlag(UnitFlags.InCombat); + + //stand up + me.SetStandState(UnitStandStateType.Stand); + + Talk(DoctorConst.SayDoc); + + uint mobId = me.GetEntry(); + me.SetWalk(false); + + switch (mobId) + { + case 12923: + case 12924: + case 12925: + me.GetMotionMaster().MovePoint(0, DoctorConst.HRunTo); + break; + case 12936: + case 12937: + case 12938: + me.GetMotionMaster().MovePoint(0, DoctorConst.ARunTo); + break; + } + } + + public override void UpdateAI(uint diff) + { + //lower Hp on every world tick makes it a useful counter, not officlone though + if (me.IsAlive() && me.GetHealth() > 6) + me.ModifyHealth(-5); + + if (me.IsAlive() && me.GetHealth() <= 6) + { + me.RemoveUnitFlag(UnitFlags.InCombat); + me.SetUninteractible(true); + me.SetDeathState(DeathState.JustDied); + me.SetUnitFlag3(UnitFlags3.FakeDead); + + if (!DoctorGUID.IsEmpty()) + { + Creature doctor = ObjectAccessor.GetCreature((me), DoctorGUID); + if (doctor != null) + doctor.GetAI().PatientDied(Coord); + } + } + } + } + + struct GarmentIds + { + public const uint SpellLesserHealR2 = 2052; + public const uint SpellFortitudeR1 = 1243; + + public const uint QuestMoon = 5621; + public const uint QuestLight1 = 5624; + public const uint QuestLight2 = 5625; + public const uint QuestSpirit = 5648; + public const uint QuestDarkness = 5650; + + public const uint EntryShaya = 12429; + public const uint EntryRoberts = 12423; + public const uint EntryDolf = 12427; + public const uint EntryKorja = 12430; + public const uint EntryDgKel = 12428; + + // used by 12429, 12423, 12427, 12430, 12428, but signed for 12429 + public const uint SayThanks = 0; + public const uint SayGoodbye = 1; + public const uint SayHealed = 2; + } + + [Script] + class npc_garments_of_quests : EscortAI + { + ObjectGuid CasterGUID; + + bool IsHealed; + bool CanRun; + + uint questId; + + public npc_garments_of_quests(Creature creature) : base(creature) + { + switch (me.GetEntry()) + { + case GarmentIds.EntryShaya: + questId = GarmentIds.QuestMoon; + break; + case GarmentIds.EntryRoberts: + questId = GarmentIds.QuestLight1; + break; + case GarmentIds.EntryDolf: + questId = GarmentIds.QuestLight2; + break; + case GarmentIds.EntryKorja: + questId = GarmentIds.QuestSpirit; + break; + case GarmentIds.EntryDgKel: + questId = GarmentIds.QuestDarkness; + break; + default: + questId = 0; + break; + } + + Initialize(); + } + + void Initialize() + { + IsHealed = false; + CanRun = false; + + _scheduler.SetValidator(() => CanRun && !me.IsInCombat()); + _scheduler.Schedule(TimeSpan.FromSeconds(5), task => + { + Unit unit = ObjAccessor.GetUnit(me, CasterGUID); + if (unit != null) + { + switch (me.GetEntry()) + { + case GarmentIds.EntryShaya: + case GarmentIds.EntryRoberts: + case GarmentIds.EntryDolf: + case GarmentIds.EntryKorja: + case GarmentIds.EntryDgKel: + Talk(GarmentIds.SayGoodbye, unit); + break; + } + + LoadPath((me.GetEntry() << 3) | 2); + Start(false); + } + else + EnterEvadeMode(EvadeReason.Other); //something went wrong + + task.Repeat(TimeSpan.FromSeconds(30)); + }); + } + + public override void Reset() + { + CasterGUID.Clear(); + + Initialize(); + + me.SetStandState(UnitStandStateType.Kneel); + // expect database to have RegenHealth=0 + me.SetHealth(me.CountPctFromMaxHealth(70)); + } + + public override void JustEngagedWith(Unit who) { } + + public override void SpellHit(WorldObject caster, SpellInfo spellInfo) + { + if (spellInfo.Id == GarmentIds.SpellLesserHealR2 || spellInfo.Id == GarmentIds.SpellFortitudeR1) + { + //not while in combat + if (me.IsInCombat()) + return; + + //nothing to be done now + if (IsHealed && CanRun) + return; + + Player player = caster.ToPlayer(); + if (player != null) + { + if (questId != 0 && player.GetQuestStatus(questId) == QuestStatus.Incomplete) + { + if (IsHealed && !CanRun && spellInfo.Id == GarmentIds.SpellFortitudeR1) + { + Talk(GarmentIds.SayThanks, player); + CanRun = true; + } + else if (!IsHealed && spellInfo.Id == GarmentIds.SpellLesserHealR2) + { + CasterGUID = player.GetGUID(); + me.SetStandState(UnitStandStateType.Stand); + Talk(GarmentIds.SayHealed, player); + IsHealed = true; + } + } + + // give quest credit, not expect any special quest objives + if (CanRun) + player.TalkedToCreature(me.GetEntry(), me.GetGUID()); + } + } + } + + public override void UpdateAI(uint diff) + { + _scheduler.Update(diff); + base.UpdateAI(diff); + } + } + + [Script] + class npc_guardian : ScriptedAI + { + const uint SpellDeathtouch = 5; + + public npc_guardian(Creature creature) : base(creature) { } + + public override void Reset() + { + me.SetUnitFlag(UnitFlags.NonAttackable); + } + + public override void JustEngagedWith(Unit who) { } + + public override void UpdateAI(uint diff) + { + if (!UpdateVictim()) + return; + + if (me.IsAttackReady()) + { + DoCastVictim(SpellDeathtouch, true); + me.ResetAttackTimer(); + } + } + } + + [Script] + class npc_steam_tonk : ScriptedAI + { + public npc_steam_tonk(Creature creature) : base(creature) { } + + public override void Reset() { } + + public override void JustEngagedWith(Unit who) { } + + void OnPossess(bool apply) + { + if (apply) + { + // Initialize the action bar without the melee attack command + me.InitCharmInfo(); + me.GetCharmInfo().InitEmptyActionBar(false); + + me.SetReactState(ReactStates.Passive); + } + else + me.SetReactState(ReactStates.Aggressive); + } + } + + struct TournamentPennantIds + { + public const uint SpellStormwindAspirant = 62595; + public const uint SpellStormwindValiant = 62596; + public const uint SpellStormwindChapion = 62594; + public const uint SpellGnomereganAspirant = 63394; + public const uint SpellGnomereganValiant = 63395; + public const uint SpellGnomereganChapion = 63396; + public const uint SpellSenJinAspirant = 63397; + public const uint SpellSenJinValiant = 63398; + public const uint SpellSenJinChapion = 63399; + public const uint SpellSilvermoonAspirant = 63401; + public const uint SpellSilvermoonValiant = 63402; + public const uint SpellSilvermoonChapion = 63403; + public const uint SpellDarnassusAspirant = 63404; + public const uint SpellDarnassusValiant = 63405; + public const uint SpellDarnassusChapion = 63406; + public const uint SpellExodarAspirant = 63421; + public const uint SpellExodarValiant = 63422; + public const uint SpellExodarChapion = 63423; + public const uint SpellIronforgeAspirant = 63425; + public const uint SpellIronforgeValiant = 63426; + public const uint SpellIronforgeChapion = 63427; + public const uint SpellUndercityAspirant = 63428; + public const uint SpellUndercityValiant = 63429; + public const uint SpellUndercityChapion = 63430; + public const uint SpellOrgrimmarAspirant = 63431; + public const uint SpellOrgrimmarValiant = 63432; + public const uint SpellOrgrimmarChapion = 63433; + public const uint SpellThunderBluffAspirant = 63434; + public const uint SpellThunderBluffValiant = 63435; + public const uint SpellThunderBluffChapion = 63436; + public const uint SpellArgentCrusadeAspirant = 63606; + public const uint SpellArgentCrusadeValiant = 63500; + public const uint SpellArgentCrusadeChapion = 63501; + public const uint SpellEbonBladeAspirant = 63607; + public const uint SpellEbonBladeValiant = 63608; + public const uint SpellEbonBladeChapion = 63609; + + public const uint NpcStormwindSteed = 33217; + public const uint NpcIronforgeRam = 33316; + public const uint NpcGnomereganMechanostrider = 33317; + public const uint NpcExodarElekk = 33318; + public const uint NpcDarnassianNightsaber = 33319; + public const uint NpcOrgrimmarWolf = 33320; + public const uint NpcDarkSpearRaptor = 33321; + public const uint NpcThunderBluffKodo = 33322; + public const uint NpcSilvermoonHawkstrider = 33323; + public const uint NpcForsakenWarhorse = 33324; + public const uint NpcArgentWarhorse = 33782; + public const uint NpcArgentSteedAspirant = 33845; + public const uint NpcArgentHawkstriderAspirant = 33844; + + public const uint AchievementChapionStormwind = 2781; + public const uint AchievementChapionDarnassus = 2777; + public const uint AchievementChapionIronforge = 2780; + public const uint AchievementChapionGnomeregan = 2779; + public const uint AchievementChapionTheExodar = 2778; + public const uint AchievementChapionOrgrimmar = 2783; + public const uint AchievementChapionSenJin = 2784; + public const uint AchievementChapionThunderBluff = 2786; + public const uint AchievementChapionUndercity = 2787; + public const uint AchievementChapionSilvermoon = 2785; + public const uint AchievementArgentValor = 2758; + public const uint AchievementChapionAlliance = 2782; + public const uint AchievementChapionHorde = 2788; + + public const uint QuestValiantOfStormwind = 13593; + public const uint QuestAValiantOfStormwind = 13684; + public const uint QuestValiantOfDarnassus = 13706; + public const uint QuestAValiantOfDarnassus = 13689; + public const uint QuestValiantOfIronforge = 13703; + public const uint QuestAValiantOfIronforge = 13685; + public const uint QuestValiantOfGnomeregan = 13704; + public const uint QuestAValiantOfGnomeregan = 13688; + public const uint QuestValiantOfTheExodar = 13705; + public const uint QuestAValiantOfTheExodar = 13690; + public const uint QuestValiantOfOrgrimmar = 13707; + public const uint QuestAValiantOfOrgrimmar = 13691; + public const uint QuestValiantOfSenJin = 13708; + public const uint QuestAValiantOfSenJin = 13693; + public const uint QuestValiantOfThunderBluff = 13709; + public const uint QuestAValiantOfThunderBluff = 13694; + public const uint QuestValiantOfUndercity = 13710; + public const uint QuestAValiantOfUndercity = 13695; + public const uint QuestValiantOfSilvermoon = 13711; + public const uint QuestAValiantOfSilvermoon = 13696; + } + + [Script] + class npc_tournament_mount : VehicleAI + { + public npc_tournament_mount(Creature creature) : base(creature) + { + _pennantSpellId = 0; + } + + public override void PassengerBoarded(Unit passenger, sbyte seatId, bool apply) + { + Player player = passenger.ToPlayer(); + if (player == null) + return; + + if (apply) + { + _pennantSpellId = GetPennantSpellId(player); + player.CastSpell(null, _pennantSpellId, true); + } + else + player.RemoveAurasDueToSpell(_pennantSpellId); + } + + + uint _pennantSpellId; + + uint GetPennantSpellId(Player player) + { + switch (me.GetEntry()) + { + case TournamentPennantIds.NpcArgentSteedAspirant: + case TournamentPennantIds.NpcStormwindSteed: + { + if (player.HasAchieved(TournamentPennantIds.AchievementChapionStormwind)) + return TournamentPennantIds.SpellStormwindChapion; + else if (player.GetQuestRewardStatus(TournamentPennantIds.QuestValiantOfStormwind) || player.GetQuestRewardStatus(TournamentPennantIds.QuestAValiantOfStormwind)) + return TournamentPennantIds.SpellStormwindValiant; + else + return TournamentPennantIds.SpellStormwindAspirant; + } + case TournamentPennantIds.NpcGnomereganMechanostrider: + { + if (player.HasAchieved(TournamentPennantIds.AchievementChapionGnomeregan)) + return TournamentPennantIds.SpellGnomereganChapion; + else if (player.GetQuestRewardStatus(TournamentPennantIds.QuestValiantOfGnomeregan) || player.GetQuestRewardStatus(TournamentPennantIds.QuestAValiantOfGnomeregan)) + return TournamentPennantIds.SpellGnomereganValiant; + else + return TournamentPennantIds.SpellGnomereganAspirant; + } + case TournamentPennantIds.NpcDarkSpearRaptor: + { + if (player.HasAchieved(TournamentPennantIds.AchievementChapionSenJin)) + return TournamentPennantIds.SpellSenJinChapion; + else if (player.GetQuestRewardStatus(TournamentPennantIds.QuestValiantOfSenJin) || player.GetQuestRewardStatus(TournamentPennantIds.QuestAValiantOfSenJin)) + return TournamentPennantIds.SpellSenJinValiant; + else + return TournamentPennantIds.SpellSenJinAspirant; + } + case TournamentPennantIds.NpcArgentHawkstriderAspirant: + case TournamentPennantIds.NpcSilvermoonHawkstrider: + { + if (player.HasAchieved(TournamentPennantIds.AchievementChapionSilvermoon)) + return TournamentPennantIds.SpellSilvermoonChapion; + else if (player.GetQuestRewardStatus(TournamentPennantIds.QuestValiantOfSilvermoon) || player.GetQuestRewardStatus(TournamentPennantIds.QuestAValiantOfSilvermoon)) + return TournamentPennantIds.SpellSilvermoonValiant; + else + return TournamentPennantIds.SpellSilvermoonAspirant; + } + case TournamentPennantIds.NpcDarnassianNightsaber: + { + if (player.HasAchieved(TournamentPennantIds.AchievementChapionDarnassus)) + return TournamentPennantIds.SpellDarnassusChapion; + else if (player.GetQuestRewardStatus(TournamentPennantIds.QuestValiantOfDarnassus) || player.GetQuestRewardStatus(TournamentPennantIds.QuestAValiantOfDarnassus)) + return TournamentPennantIds.SpellDarnassusValiant; + else + return TournamentPennantIds.SpellDarnassusAspirant; + } + case TournamentPennantIds.NpcExodarElekk: + { + if (player.HasAchieved(TournamentPennantIds.AchievementChapionTheExodar)) + return TournamentPennantIds.SpellExodarChapion; + else if (player.GetQuestRewardStatus(TournamentPennantIds.QuestValiantOfTheExodar) || player.GetQuestRewardStatus(TournamentPennantIds.QuestAValiantOfTheExodar)) + return TournamentPennantIds.SpellExodarValiant; + else + return TournamentPennantIds.SpellExodarAspirant; + } + case TournamentPennantIds.NpcIronforgeRam: + { + if (player.HasAchieved(TournamentPennantIds.AchievementChapionIronforge)) + return TournamentPennantIds.SpellIronforgeChapion; + else if (player.GetQuestRewardStatus(TournamentPennantIds.QuestValiantOfIronforge) || player.GetQuestRewardStatus(TournamentPennantIds.QuestAValiantOfIronforge)) + return TournamentPennantIds.SpellIronforgeValiant; + else + return TournamentPennantIds.SpellIronforgeAspirant; + } + case TournamentPennantIds.NpcForsakenWarhorse: + { + if (player.HasAchieved(TournamentPennantIds.AchievementChapionUndercity)) + return TournamentPennantIds.SpellUndercityChapion; + else if (player.GetQuestRewardStatus(TournamentPennantIds.QuestValiantOfUndercity) || player.GetQuestRewardStatus(TournamentPennantIds.QuestAValiantOfUndercity)) + return TournamentPennantIds.SpellUndercityValiant; + else + return TournamentPennantIds.SpellUndercityAspirant; + } + case TournamentPennantIds.NpcOrgrimmarWolf: + { + if (player.HasAchieved(TournamentPennantIds.AchievementChapionOrgrimmar)) + return TournamentPennantIds.SpellOrgrimmarChapion; + else if (player.GetQuestRewardStatus(TournamentPennantIds.QuestValiantOfOrgrimmar) || player.GetQuestRewardStatus(TournamentPennantIds.QuestAValiantOfOrgrimmar)) + return TournamentPennantIds.SpellOrgrimmarValiant; + else + return TournamentPennantIds.SpellOrgrimmarAspirant; + } + case TournamentPennantIds.NpcThunderBluffKodo: + { + if (player.HasAchieved(TournamentPennantIds.AchievementChapionThunderBluff)) + return TournamentPennantIds.SpellThunderBluffChapion; + else if (player.GetQuestRewardStatus(TournamentPennantIds.QuestValiantOfThunderBluff) || player.GetQuestRewardStatus(TournamentPennantIds.QuestAValiantOfThunderBluff)) + return TournamentPennantIds.SpellThunderBluffValiant; + else + return TournamentPennantIds.SpellThunderBluffAspirant; + } + case TournamentPennantIds.NpcArgentWarhorse: + { + if (player.HasAchieved(TournamentPennantIds.AchievementChapionAlliance) || player.HasAchieved(TournamentPennantIds.AchievementChapionHorde)) + return player.GetClass() == Class.Deathknight ? TournamentPennantIds.SpellEbonBladeChapion : TournamentPennantIds.SpellArgentCrusadeChapion; + else if (player.HasAchieved(TournamentPennantIds.AchievementArgentValor)) + return player.GetClass() == Class.Deathknight ? TournamentPennantIds.SpellEbonBladeValiant : TournamentPennantIds.SpellArgentCrusadeValiant; + else + return player.GetClass() == Class.Deathknight ? TournamentPennantIds.SpellEbonBladeAspirant : TournamentPennantIds.SpellArgentCrusadeAspirant; + } + default: + return 0; + } + } + } + + [Script] + class npc_brewfest_reveler : ScriptedAI + { + const uint SpellBrewfestToast = 41586; + + public npc_brewfest_reveler(Creature creature) : base(creature) { } + + public override void ReceiveEmote(Player player, TextEmotes emote) + { + if (!GameEventMgr.IsHolidayActive(HolidayIds.Brewfest)) + return; + + if (emote == TextEmotes.Dance) + me.CastSpell(player, SpellBrewfestToast, false); + } + } + + [Script] + class npc_brewfest_reveler_2 : ScriptedAI + { + Emote[] BrewfestRandomEmote = + { + Emote.OneshotQuestion, + Emote.OneshotApplaud, + Emote.OneshotShout, + Emote.OneshotEatNoSheathe, + Emote.OneshotLaughNoSheathe + }; + + const uint SpellBrewfestToast = 41586; + const uint NpcBrewfestReveler = 24484; + + List _revelerGuids = new(); + + npc_brewfest_reveler_2(Creature creature) : base(creature) { } + + public override void Reset() + { + _scheduler.CancelAll(); + _scheduler.Schedule(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2), task => + { + List creatureList = me.GetCreatureListWithEntryInGrid(NpcBrewfestReveler, 5.0f); + foreach (Creature creature in creatureList) + if (creature != me) + _revelerGuids.Add(creature.GetGUID()); + + _scheduler.Schedule(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2), faceToTask => + { + // Turn to random brewfest reveler within set range + if (!_revelerGuids.Empty()) + { + Creature creature = ObjectAccessor.GetCreature(me, _revelerGuids.SelectRandom()); + if (creature != null) + me.SetFacingToObject(creature); + } + + _scheduler.Schedule(TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(6), emoteTask => + { + // Play random emote or dance + + Action taskContext = task => + { + // If dancing stop before next random state + if (me.GetEmoteState() == Emote.StateDance) + me.SetEmoteState(Emote.OneshotNone); + + // Random EventEmote or EventFaceto + if (RandomHelper.randChance(50)) + faceToTask.Repeat(TimeSpan.FromSeconds(1)); + else + emoteTask.Repeat(TimeSpan.FromSeconds(1)); + }; + + if (RandomHelper.randChance(50)) + { + me.HandleEmoteCommand(BrewfestRandomEmote.SelectRandom()); + _scheduler.Schedule(TimeSpan.FromSeconds(4), TimeSpan.FromSeconds(6), taskContext); + } + else + { + me.SetEmoteState(Emote.StateDance); + _scheduler.Schedule(TimeSpan.FromSeconds(8), TimeSpan.FromSeconds(12), taskContext); + } + }); + }); + }); + } + + // Copied from old script. I don't know if this is 100% correct. + public override void ReceiveEmote(Player player, TextEmotes emote) + { + if (!GameEventMgr.IsHolidayActive(HolidayIds.Brewfest)) + return; + + if (emote == TextEmotes.Dance) + me.CastSpell(player, SpellBrewfestToast, false); + } + + public override void UpdateAI(uint diff) + { + UpdateVictim(); + + _scheduler.Update(diff); + } + } + + [Script] + class npc_training_dummy : NullCreatureAI + { + Dictionary _combatTimer = new(); + + public npc_training_dummy(Creature creature) : base(creature) { } + + public override void JustEnteredCombat(Unit who) + { + _combatTimer[who.GetGUID()] = TimeSpan.FromSeconds(5); + } + + public override void DamageTaken(Unit attacker, ref uint damage, DamageEffectType damageType, SpellInfo spellInfo = null) + { + damage = 0; + + if (attacker == null || damageType == DamageEffectType.DOT) + return; + + _combatTimer[attacker.GetGUID()] = TimeSpan.FromSeconds(5); + } + + public override void UpdateAI(uint diff) + { + foreach (var key in _combatTimer.Keys.ToList()) + { + _combatTimer[key] -= TimeSpan.FromMilliseconds(diff); + if (_combatTimer[key] <= TimeSpan.FromSeconds(0)) + { + // The attacker has not dealt any damage to the dummy for over 5 seconds. End combat. + var pveRefs = me.GetCombatManager().GetPvECombatRefs(); + var it = pveRefs.LookupByKey(key); + if (it != null) + it.EndCombat(); + + _combatTimer.Remove(key); + } + } + } + } + + struct WormholeIds + { + public const uint MenuIdWormhole = 10668; // "This tear in the fabric of time and space looks ominous." + public const uint NpcTextWormhole = 14785; // (not 907 "What brings you to this part of the world, $n?") + public const uint GossipOption1 = 0; // "Borean Tundra" + public const uint GossipOption2 = 1; // "Howling Fjord" + public const uint GossipOption3 = 2; // "Sholazar BaMath.Sin" + public const uint GossipOption4 = 3; // "Icecrown" + public const uint GossipOption5 = 4; // "Storm Peaks" + public const uint GossipOption6 = 5; // "Underground..." + + public const uint SpellBoreanTundra = 67834; // 0 + public const uint SpellHowlingFjord = 67838; // 1 + public const uint SpellSholazarBasin = 67835; // 2 + public const uint SpellIcecrown = 67836; // 3 + public const uint SpellStormPeaks = 67837; // 4 + public const uint SpellUnderground = 68081; // 5 + } + + [Script] + class npc_wormhole : PassiveAI + { + bool _showUnderground; + + public npc_wormhole(Creature creature) : base(creature) + { + Initialize(); + } + + void Initialize() + { + _showUnderground = RandomHelper.URand(0, 100) == 0; // Guessed value, it is really rare though + } + + public override void InitializeAI() + { + Initialize(); + } + + public override bool OnGossipHello(Player player) + { + player.InitGossipMenu(WormholeIds.MenuIdWormhole); + if (me.IsSummon()) + { + if (player == me.ToTempSummon().GetSummoner()) + { + player.AddGossipItem(WormholeIds.MenuIdWormhole, WormholeIds.GossipOption1, eTradeskill.GossipSenderMain, eTradeskill.GossipActionInfoDef + 1); + player.AddGossipItem(WormholeIds.MenuIdWormhole, WormholeIds.GossipOption2, eTradeskill.GossipSenderMain, eTradeskill.GossipActionInfoDef + 2); + player.AddGossipItem(WormholeIds.MenuIdWormhole, WormholeIds.GossipOption3, eTradeskill.GossipSenderMain, eTradeskill.GossipActionInfoDef + 3); + player.AddGossipItem(WormholeIds.MenuIdWormhole, WormholeIds.GossipOption4, eTradeskill.GossipSenderMain, eTradeskill.GossipActionInfoDef + 4); + player.AddGossipItem(WormholeIds.MenuIdWormhole, WormholeIds.GossipOption5, eTradeskill.GossipSenderMain, eTradeskill.GossipActionInfoDef + 5); + + if (_showUnderground) + player.AddGossipItem(WormholeIds.MenuIdWormhole, WormholeIds.GossipOption6, eTradeskill.GossipSenderMain, eTradeskill.GossipActionInfoDef + 6); + + player.SendGossipMenu(WormholeIds.NpcTextWormhole, me.GetGUID()); + } + } + + return true; + } + + public override bool OnGossipSelect(Player player, uint menuId, uint gossipListId) + { + uint action = player.PlayerTalkClass.GetGossipOptionAction(gossipListId); + player.ClearGossipMenu(); + + switch (action) + { + case eTradeskill.GossipActionInfoDef + 1: // Borean Tundra + player.CloseGossipMenu(); + DoCast(player, WormholeIds.SpellBoreanTundra, false); + break; + case eTradeskill.GossipActionInfoDef + 2: // Howling Fjord + player.CloseGossipMenu(); + DoCast(player, WormholeIds.SpellHowlingFjord, false); + break; + case eTradeskill.GossipActionInfoDef + 3: // Sholazar BaMath.Sin + player.CloseGossipMenu(); + DoCast(player, WormholeIds.SpellSholazarBasin, false); + break; + case eTradeskill.GossipActionInfoDef + 4: // Icecrown + player.CloseGossipMenu(); + DoCast(player, WormholeIds.SpellIcecrown, false); + break; + case eTradeskill.GossipActionInfoDef + 5: // Storm peaks + player.CloseGossipMenu(); + DoCast(player, WormholeIds.SpellStormPeaks, false); + break; + case eTradeskill.GossipActionInfoDef + 6: // Underground + player.CloseGossipMenu(); + DoCast(player, WormholeIds.SpellUnderground, false); + break; + } + + return true; + } + } + + [Script] + class npc_spring_rabbit : ScriptedAI + { + const uint SpellSpringFling = 61875; + const uint SpellSpringRabbitJump = 61724; + const uint SpellSpringRabbitWander = 61726; + const uint SpellSummonBabyBunny = 61727; + const uint SpellSpringRabbitInLove = 61728; + const uint NpcSpringRabbit = 32791; + + ObjectGuid rabbitGUID; + + public npc_spring_rabbit(Creature creature) : base(creature) + { + Initialize(); + } + + void Initialize() + { + rabbitGUID.Clear(); + + _scheduler.Schedule(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(10), 1, task => + { + Creature rabbit = me.FindNearestCreature(NpcSpringRabbit, 10.0f); + if (rabbit != null) + { + if (rabbit == me || rabbit.HasAura(SpellSpringRabbitInLove)) + return; + + me.AddAura(SpellSpringRabbitInLove, me); + DoAction(1); + rabbit.AddAura(SpellSpringRabbitInLove, rabbit); + rabbit.GetAI().DoAction(1); + rabbit.CastSpell(rabbit, SpellSpringRabbitJump, true); + rabbitGUID = rabbit.GetGUID(); + task.CancelGroup(1); + } + task.Repeat(); + }); + + _scheduler.Schedule(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(10), task => + { + Unit rabbit = ObjAccessor.GetUnit(me, rabbitGUID); + if (rabbit != null) + DoCast(rabbit, SpellSpringRabbitJump); + task.Repeat(); + }); + + _scheduler.Schedule(TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(20), task => + { + DoCast(SpellSummonBabyBunny); + task.Repeat(TimeSpan.FromSeconds(20), TimeSpan.FromSeconds(40)); + }); + } + + public override void Reset() + { + _scheduler.CancelAll(); + Initialize(); + Unit owner = me.GetOwner(); + if (owner != null) + me.GetMotionMaster().MoveFollow(owner, SharedConst.PetFollowDist, SharedConst.PetFollowAngle); + } + + public override void JustEngagedWith(Unit who) { } + + public override void DoAction(int param) + { + Unit owner = me.GetOwner(); + if (owner != null) + owner.CastSpell(owner, SpellSpringFling, true); + } + + public override void UpdateAI(uint diff) + { + _scheduler.Update(diff); + } + } + + [Script] + class npc_imp_in_a_ball : ScriptedAI + { + const uint SayRandom = 0; + const uint EventTalk = 1; + + ObjectGuid summonerGUID; + + public npc_imp_in_a_ball(Creature creature) : base(creature) + { + summonerGUID.Clear(); + } + + public override void IsSummonedBy(WorldObject summoner) + { + if (summoner.IsPlayer()) + { + summonerGUID = summoner.GetGUID(); + _scheduler.Schedule(TimeSpan.FromSeconds(3), task => + { + Player owner = ObjAccessor.GetPlayer(me, summonerGUID); + if (owner != null) + { + CreatureTextMgr.SendChat(me, (byte)SayRandom, owner, owner.GetGroup() != null ? ChatMsg.MonsterParty : ChatMsg.MonsterWhisper, Language.Addon, CreatureTextRange.Normal); + } + }); + } + } + + public override void UpdateAI(uint diff) + { + _scheduler.Update(diff); + } + } + + [Script] + class npc_train_wrecker : NullCreatureAI + { + const uint GoToyTrain = 193963; + const uint SpellToyTrainPulse = 61551; + const uint SpellWreckTrain = 62943; + const uint MoveidChase = 1; + const uint MoveidJump = 2; + + const uint NpcExultingWindUpTrainWrecker = 81071; + + ObjectGuid _target; + + public npc_train_wrecker(Creature creature) : base(creature) + { + Initialize(); + } + + void Initialize() + { + _scheduler.Schedule(TimeSpan.FromSeconds(1), task => + { + GameObject target = me.FindNearestGameObject(GoToyTrain, 15.0f); + if (target != null) + { + _target = target.GetGUID(); + me.SetWalk(true); + me.GetMotionMaster().MovePoint(MoveidChase, target.GetNearPosition(3.0f, target.GetAbsoluteAngle(me))); + return; + } + + task.Repeat(TimeSpan.FromSeconds(3)); + }); + } + + public override void Reset() + { + _scheduler.CancelAll(); + Initialize(); + } + + GameObject VerifyTarget() + { + GameObject target = ObjectAccessor.GetGameObject(me, _target); + if (target != null) + return target; + + me.HandleEmoteCommand(Emote.OneshotRude); + me.DespawnOrUnsummon(TimeSpan.FromSeconds(3)); + return null; + } + + public override void UpdateAI(uint diff) + { + _scheduler.Update(diff); + } + + void MovementInform(uint type, uint id) + { + _scheduler.CancelAll(); + if (id == MoveidChase) + { + _scheduler.Async(() => + { + GameObject target = VerifyTarget(); + if (target != null) + me.GetMotionMaster().MoveJump(target, 5.0f, 10.0f, MoveidJump); + }); + } + else if (id == MoveidJump) + { + _scheduler.Async(() => + { + GameObject target = VerifyTarget(); + if (target != null) + { + me.SetFacingTo(target.GetOrientation()); + me.HandleEmoteCommand(Emote.OneshotAttack1h); + _scheduler.Schedule(TimeSpan.FromSeconds(1.5), task => + { + GameObject target = VerifyTarget(); + if (target != null) + { + me.CastSpell(target, SpellWreckTrain, false); + _scheduler.Schedule(TimeSpan.FromSeconds(2), danceTask => + { + me.UpdateEntry(NpcExultingWindUpTrainWrecker); + me.SetEmoteState(Emote.OneshotDance); + me.DespawnOrUnsummon(TimeSpan.FromSeconds(5)); + }); + } + }); + } + }); + } + } + } + + struct ArgentSquireIds + { + public const uint SpellDarnassusPennant = 63443; + public const uint SpellExodarPennant = 63439; + public const uint SpellGnomereganPennant = 63442; + public const uint SpellIronforgePennant = 63440; + public const uint SpellStormwindPennant = 62727; + public const uint SpellSenjinPennant = 63446; + public const uint SpellUndercityPennant = 63441; + public const uint SpellOrgrimmarPennant = 63444; + public const uint SpellSilvermoonPennant = 63438; + public const uint SpellThunderbluffPennant = 63445; + public const uint AuraPostmanS = 67376; + public const uint AuraShopS = 67377; + public const uint AuraBankS = 67368; + public const uint AuraTiredS = 67401; + public const uint AuraBankG = 68849; + public const uint AuraPostmanG = 68850; + public const uint AuraShopG = 68851; + public const uint AuraTiredG = 68852; + public const uint SpellTiredPlayer = 67334; + + public const uint GossipOptionBank = 0; + public const uint GossipOptionShop = 1; + public const uint GossipOptionMail = 2; + public const uint GossipOptionDarnassusSenjinPennant = 3; + public const uint GossipOptionExodarUndercityPennant = 4; + public const uint GossipOptionGnomereganOrgrimmarPennant = 5; + public const uint GossipOptionIronforgeSilvermoonPennant = 6; + public const uint GossipOptionStormwindThunderbluffPennant = 7; + + public const uint NpcArgentSquire = 33238; + public const uint AchievementPonyUp = 3736; + + public static (uint, uint)[] bannerSpells = + { + (SpellDarnassusPennant, SpellSenjinPennant), + (SpellExodarPennant, SpellUndercityPennant), + (SpellGnomereganPennant, SpellOrgrimmarPennant), + (SpellIronforgePennant, SpellSilvermoonPennant), + (SpellStormwindPennant, SpellThunderbluffPennant) + }; + } + + [Script] + class npc_argent_squire_gruntling : ScriptedAI + { + public npc_argent_squire_gruntling(Creature creature) : base(creature) { } + + public override void Reset() + { + Player owner = me.GetOwner()?.ToPlayer(); + if (owner != null) + { + Aura ownerTired = owner.GetAura(ArgentSquireIds.SpellTiredPlayer); + if (ownerTired != null) + { + Aura squireTired = me.AddAura(IsArgentSquire() ? ArgentSquireIds.AuraTiredS : ArgentSquireIds.AuraTiredG, me); + if (squireTired != null) + squireTired.SetDuration(ownerTired.GetDuration()); + } + + if (owner.HasAchieved(ArgentSquireIds.AchievementPonyUp) && !me.HasAura(ArgentSquireIds.AuraTiredS) && !me.HasAura(ArgentSquireIds.AuraTiredG)) + { + me.SetNpcFlag(NPCFlags.Banker | NPCFlags.Mailbox | NPCFlags.Vendor); + return; + } + } + + me.RemoveNpcFlag(NPCFlags.Banker | NPCFlags.Mailbox | NPCFlags.Vendor); + } + + public override bool OnGossipSelect(Player player, uint menuId, uint gossipListId) + { + switch (gossipListId) + { + case ArgentSquireIds.GossipOptionBank: + { + me.RemoveNpcFlag(NPCFlags.Mailbox | NPCFlags.Vendor); + uint _bankAura = IsArgentSquire() ? ArgentSquireIds.AuraBankS : ArgentSquireIds.AuraBankG; + if (!me.HasAura(_bankAura)) + DoCastSelf(_bankAura); + + if (!player.HasAura(ArgentSquireIds.SpellTiredPlayer)) + player.CastSpell(player, ArgentSquireIds.SpellTiredPlayer, true); + break; + } + case ArgentSquireIds.GossipOptionShop: + { + me.RemoveNpcFlag(NPCFlags.Banker | NPCFlags.Mailbox); + uint _shopAura = IsArgentSquire() ? ArgentSquireIds.AuraShopS : ArgentSquireIds.AuraShopG; + if (!me.HasAura(_shopAura)) + DoCastSelf(_shopAura); + + if (!player.HasAura(ArgentSquireIds.SpellTiredPlayer)) + player.CastSpell(player, ArgentSquireIds.SpellTiredPlayer, true); + break; + } + case ArgentSquireIds.GossipOptionMail: + { + me.RemoveNpcFlag(NPCFlags.Banker | NPCFlags.Vendor); + uint _mailAura = IsArgentSquire() ? ArgentSquireIds.AuraPostmanS : ArgentSquireIds.AuraPostmanG; + if (!me.HasAura(_mailAura)) + DoCastSelf(_mailAura); + + if (!player.HasAura(ArgentSquireIds.SpellTiredPlayer)) + player.CastSpell(player, ArgentSquireIds.SpellTiredPlayer, true); + break; + } + case ArgentSquireIds.GossipOptionDarnassusSenjinPennant: + case ArgentSquireIds.GossipOptionExodarUndercityPennant: + case ArgentSquireIds.GossipOptionGnomereganOrgrimmarPennant: + case ArgentSquireIds.GossipOptionIronforgeSilvermoonPennant: + case ArgentSquireIds.GossipOptionStormwindThunderbluffPennant: + if (IsArgentSquire()) + DoCastSelf(ArgentSquireIds.bannerSpells[gossipListId - 3].Item1, true); + else + DoCastSelf(ArgentSquireIds.bannerSpells[gossipListId - 3].Item2, true); + + player.PlayerTalkClass.SendCloseGossip(); + break; + default: + break; + } + + return false; + } + + bool IsArgentSquire() { return me.GetEntry() == ArgentSquireIds.NpcArgentSquire; } + } + + struct BountifulTableIds + { + public const sbyte SeatTurkeyChair = 0; + public const sbyte SeatCranberryChair = 1; + public const sbyte SeatStuffingChair = 2; + public const sbyte SeatSweetPotatoChair = 3; + public const sbyte SeatPieChair = 4; + public const sbyte SeatFoodHolder = 5; + public const sbyte SeatPlateHolder = 6; + public const uint NpcTheTurkeyChair = 34812; + public const uint NpcTheCranberryChair = 34823; + public const uint NpcTheStuffingChair = 34819; + public const uint NpcTheSweetPotatoChair = 34824; + public const uint NpcThePieChair = 34822; + public const uint SpellCranberryServer = 61793; + public const uint SpellPieServer = 61794; + public const uint SpellStuffingServer = 61795; + public const uint SpellTurkeyServer = 61796; + public const uint SpellSweetPotatoesServer = 61797; + + public static Dictionary ChairSpells = new() + { + { NpcTheCranberryChair, SpellCranberryServer }, + { NpcThePieChair, SpellPieServer }, + { NpcTheStuffingChair, SpellStuffingServer }, + { NpcTheTurkeyChair, SpellTurkeyServer }, + { NpcTheSweetPotatoChair, SpellSweetPotatoesServer }, + }; + } + + class CastFoodSpell : BasicEvent + { + Unit _owner; + uint _spellId; + + public CastFoodSpell(Unit owner, uint spellId) + { + _owner = owner; + _spellId = spellId; + } + + public override bool Execute(ulong execTime, uint diff) + { + _owner.CastSpell(_owner, _spellId, true); + return true; + } + } + + [Script] + class npc_bountiful_table : PassiveAI + { + public npc_bountiful_table(Creature creature) : base(creature) { } + + public override void PassengerBoarded(Unit who, sbyte seatId, bool apply) + { + float x = 0.0f; + float y = 0.0f; + float z = 0.0f; + float o = 0.0f; + + switch (seatId) + { + case BountifulTableIds.SeatTurkeyChair: + x = 3.87f; + y = 2.07f; + o = 3.700098f; + break; + case BountifulTableIds.SeatCranberryChair: + x = 3.87f; + y = -2.07f; + o = 2.460914f; + break; + case BountifulTableIds.SeatStuffingChair: + x = -2.52f; + break; + case BountifulTableIds.SeatSweetPotatoChair: + x = -0.09f; + y = -3.24f; + o = 1.186824f; + break; + case BountifulTableIds.SeatPieChair: + x = -0.18f; + y = 3.24f; + o = 5.009095f; + break; + case BountifulTableIds.SeatFoodHolder: + case BountifulTableIds.SeatPlateHolder: + Vehicle holders = who.GetVehicleKit(); + if (holders != null) + holders.InstallAllAccessories(true); + return; + default: + break; + } + + var initializer = (MoveSplineInit init) => + { + init.DisableTransportPathTransformations(); + init.MoveTo(x, y, z, false); + init.SetFacing(o); + }; + + who.GetMotionMaster().LaunchMoveSpline(initializer, EventId.VehicleBoard, MovementGeneratorPriority.Highest); + who.m_Events.AddEvent(new CastFoodSpell(who, BountifulTableIds.ChairSpells.LookupByKey(who.GetEntry())), who.m_Events.CalculateTime(TimeSpan.FromSeconds(1))); + Creature creature = who.ToCreature(); + if (creature != null) + creature.SetDisplayFromModel(0); + } + } + + [Script] + class npc_gen_void_zone : ScriptedAI + { + const uint SpellConsumption = 28874; + + public npc_gen_void_zone(Creature creature) : base(creature) { } + + public override void InitializeAI() + { + me.SetReactState(ReactStates.Passive); + } + + public override void JustAppeared() + { + _scheduler.Schedule(TimeSpan.FromSeconds(2), task => DoCastSelf(SpellConsumption)); + } + + public override void UpdateAI(uint diff) + { + _scheduler.Update(diff); + } + } +} + diff --git a/Source/Scripts/World/SceneScripts.cs b/Source/Scripts/World/Scene.cs similarity index 74% rename from Source/Scripts/World/SceneScripts.cs rename to Source/Scripts/World/Scene.cs index 50f9d2e72..76e932503 100644 --- a/Source/Scripts/World/SceneScripts.cs +++ b/Source/Scripts/World/Scene.cs @@ -5,23 +5,21 @@ using Game; using Game.Entities; using Game.Scripting; -namespace Scripts.World.SceneScripts +namespace Scripts.World.Achievements { - struct SpellIds - { - public const uint DeathwingSimulator = 201184; - } - [Script] class scene_deathwing_simulator : SceneScript { + const uint SpellDeathwingSimulator = 201184; + public scene_deathwing_simulator() : base("scene_deathwing_simulator") { } // Called when a player receive trigger from scene public override void OnSceneTriggerEvent(Player player, uint sceneInstanceID, SceneTemplate sceneTemplate, string triggerName) { if (triggerName == "Burn Player") - player.CastSpell(player, SpellIds.DeathwingSimulator, true); // Deathwing Simulator Burn player + player.CastSpell(player, SpellDeathwingSimulator, true); // Deathwing Simulator Burn player } } -} \ No newline at end of file +} +