// 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; } UpdateVictim(); } 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 (TargetSingleFriend) 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.SetSpawnHealth(); } 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.SetVendor(NPCFlags.Vendor, true); me.SetNpcFlag(NPCFlags.Banker | NPCFlags.Mailbox); return; } } me.SetVendor(NPCFlags.VendorMask, false); me.RemoveNpcFlag(NPCFlags.Banker | NPCFlags.Mailbox); } public override bool OnGossipSelect(Player player, uint menuId, uint gossipListId) { switch (gossipListId) { case ArgentSquireIds.GossipOptionBank: { me.SetVendor(NPCFlags.VendorMask, false); me.RemoveNpcFlag(NPCFlags.Mailbox); 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.SetVendor(NPCFlags.VendorMask, false); me.RemoveNpcFlag(NPCFlags.Banker); 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); } } }