From 0ae2afe307595463df8f4a16fdd1ee9740a11c9e Mon Sep 17 00:00:00 2001 From: Treeston Date: Tue, 9 Jul 2019 18:24:43 +0200 Subject: [PATCH] Scripts/World: Rewrite air force trigger bots script, add hostility check along the way. Closes #23307. --- src/server/scripts/World/npcs_special.cpp | 272 ++++++++-------------- 1 file changed, 101 insertions(+), 171 deletions(-) diff --git a/src/server/scripts/World/npcs_special.cpp b/src/server/scripts/World/npcs_special.cpp index dade8cb14be3b..f15429b0e9c64 100644 --- a/src/server/scripts/World/npcs_special.cpp +++ b/src/server/scripts/World/npcs_special.cpp @@ -43,57 +43,53 @@ # npc_air_force_bots #########*/ -enum SpawnType -{ - SPAWNTYPE_TRIPWIRE_ROOFTOP, // no warning, summon Creature at smaller range - SPAWNTYPE_ALARMBOT, // cast guards mark and summon npc - if player shows up with that buff duration < 5 seconds attack -}; - -struct SpawnAssociation -{ - uint32 thisCreatureEntry; - uint32 spawnedCreatureEntry; - SpawnType spawnType; -}; - -enum AirFoceBots -{ - SPELL_GUARDS_MARK = 38067, - AURA_DURATION_TIME_LEFT = 30000 -}; - -float const RANGE_TRIPWIRE = 15.0f; -float const RANGE_GUARDS_MARK = 100.0f; - -SpawnAssociation spawnAssociations[] = -{ - {2614, 15241, SPAWNTYPE_ALARMBOT}, //Air Force Alarm Bot (Alliance) - {2615, 15242, SPAWNTYPE_ALARMBOT}, //Air Force Alarm Bot (Horde) - {21974, 21976, SPAWNTYPE_ALARMBOT}, //Air Force Alarm Bot (Area 52) - {21993, 15242, SPAWNTYPE_ALARMBOT}, //Air Force Guard Post (Horde - Bat Rider) - {21996, 15241, SPAWNTYPE_ALARMBOT}, //Air Force Guard Post (Alliance - Gryphon) - {21997, 21976, SPAWNTYPE_ALARMBOT}, //Air Force Guard Post (Goblin - Area 52 - Zeppelin) - {21999, 15241, SPAWNTYPE_TRIPWIRE_ROOFTOP}, //Air Force Trip Wire - Rooftop (Alliance) - {22001, 15242, SPAWNTYPE_TRIPWIRE_ROOFTOP}, //Air Force Trip Wire - Rooftop (Horde) - {22002, 15242, SPAWNTYPE_TRIPWIRE_ROOFTOP}, //Air Force Trip Wire - Ground (Horde) - {22003, 15241, SPAWNTYPE_TRIPWIRE_ROOFTOP}, //Air Force Trip Wire - Ground (Alliance) - {22063, 21976, SPAWNTYPE_TRIPWIRE_ROOFTOP}, //Air Force Trip Wire - Rooftop (Goblin - Area 52) - {22065, 22064, SPAWNTYPE_ALARMBOT}, //Air Force Guard Post (Ethereal - Stormspire) - {22066, 22067, SPAWNTYPE_ALARMBOT}, //Air Force Guard Post (Scryer - Dragonhawk) - {22068, 22064, SPAWNTYPE_TRIPWIRE_ROOFTOP}, //Air Force Trip Wire - Rooftop (Ethereal - Stormspire) - {22069, 22064, SPAWNTYPE_ALARMBOT}, //Air Force Alarm Bot (Stormspire) - {22070, 22067, SPAWNTYPE_TRIPWIRE_ROOFTOP}, //Air Force Trip Wire - Rooftop (Scryer) - {22071, 22067, SPAWNTYPE_ALARMBOT}, //Air Force Alarm Bot (Scryer) - {22078, 22077, SPAWNTYPE_ALARMBOT}, //Air Force Alarm Bot (Aldor) - {22079, 22077, SPAWNTYPE_ALARMBOT}, //Air Force Guard Post (Aldor - Gryphon) - {22080, 22077, SPAWNTYPE_TRIPWIRE_ROOFTOP}, //Air Force Trip Wire - Rooftop (Aldor) - {22086, 22085, SPAWNTYPE_ALARMBOT}, //Air Force Alarm Bot (Sporeggar) - {22087, 22085, SPAWNTYPE_ALARMBOT}, //Air Force Guard Post (Sporeggar - Spore Bat) - {22088, 22085, SPAWNTYPE_TRIPWIRE_ROOFTOP}, //Air Force Trip Wire - Rooftop (Sporeggar) - {22090, 22089, SPAWNTYPE_ALARMBOT}, //Air Force Guard Post (Toshley's Station - Flying Machine) - {22124, 22122, SPAWNTYPE_ALARMBOT}, //Air Force Alarm Bot (Cenarion) - {22125, 22122, SPAWNTYPE_ALARMBOT}, //Air Force Guard Post (Cenarion - Stormcrow) - {22126, 22122, SPAWNTYPE_ALARMBOT} //Air Force Trip Wire - Rooftop (Cenarion Expedition) +enum AirForceBots +{ + TRIPWIRE, // do not attack flying players, smaller range + ALARMBOT, // attack flying players, casts guard's mark + + SPELL_GUARDS_MARK = 38067 +}; + +float constexpr RANGE_TRIPWIRE = 15.0f; +float constexpr RANGE_ALARMBOT = 100.0f; + +struct AirForceSpawn +{ + uint32 myEntry; + uint32 otherEntry; + AirForceBots type; +}; + +AirForceSpawn constexpr airforceSpawns[] = +{ + {2614, 15241, ALARMBOT}, // Air Force Alarm Bot (Alliance) + {2615, 15242, ALARMBOT}, // Air Force Alarm Bot (Horde) + {21974, 21976, ALARMBOT}, // Air Force Alarm Bot (Area 52) + {21993, 15242, ALARMBOT}, // Air Force Guard Post (Horde - Bat Rider) + {21996, 15241, ALARMBOT}, // Air Force Guard Post (Alliance - Gryphon) + {21997, 21976, ALARMBOT}, // Air Force Guard Post (Goblin - Area 52 - Zeppelin) + {21999, 15241, TRIPWIRE}, // Air Force Trip Wire - Rooftop (Alliance) + {22001, 15242, TRIPWIRE}, // Air Force Trip Wire - Rooftop (Horde) + {22002, 15242, TRIPWIRE}, // Air Force Trip Wire - Ground (Horde) + {22003, 15241, TRIPWIRE}, // Air Force Trip Wire - Ground (Alliance) + {22063, 21976, TRIPWIRE}, // Air Force Trip Wire - Rooftop (Goblin - Area 52) + {22065, 22064, ALARMBOT}, // Air Force Guard Post (Ethereal - Stormspire) + {22066, 22067, ALARMBOT}, // Air Force Guard Post (Scryer - Dragonhawk) + {22068, 22064, TRIPWIRE}, // Air Force Trip Wire - Rooftop (Ethereal - Stormspire) + {22069, 22064, ALARMBOT}, // Air Force Alarm Bot (Stormspire) + {22070, 22067, TRIPWIRE}, // Air Force Trip Wire - Rooftop (Scryer) + {22071, 22067, ALARMBOT}, // Air Force Alarm Bot (Scryer) + {22078, 22077, ALARMBOT}, // Air Force Alarm Bot (Aldor) + {22079, 22077, ALARMBOT}, // Air Force Guard Post (Aldor - Gryphon) + {22080, 22077, TRIPWIRE}, // Air Force Trip Wire - Rooftop (Aldor) + {22086, 22085, ALARMBOT}, // Air Force Alarm Bot (Sporeggar) + {22087, 22085, ALARMBOT}, // Air Force Guard Post (Sporeggar - Spore Bat) + {22088, 22085, TRIPWIRE}, // Air Force Trip Wire - Rooftop (Sporeggar) + {22090, 22089, ALARMBOT}, // Air Force Guard Post (Toshley's Station - Flying Machine) + {22124, 22122, ALARMBOT}, // Air Force Alarm Bot (Cenarion) + {22125, 22122, ALARMBOT}, // Air Force Guard Post (Cenarion - Stormcrow) + {22126, 22122, ALARMBOT} // Air Force Trip Wire - Rooftop (Cenarion Expedition) }; class npc_air_force_bots : public CreatureScript @@ -101,157 +97,91 @@ class npc_air_force_bots : public CreatureScript public: npc_air_force_bots() : CreatureScript("npc_air_force_bots") { } - struct npc_air_force_botsAI : public ScriptedAI + struct npc_air_force_botsAI : public NullCreatureAI { - npc_air_force_botsAI(Creature* creature) : ScriptedAI(creature) + static AirForceSpawn const& FindSpawnFor(uint32 entry) { - SpawnAssoc = nullptr; - SpawnedGUID.Clear(); - - // find the correct spawnhandling - static uint8 constexpr const EntryCount = uint8(std::extent::value); - - for (uint8 i = 0; i < EntryCount; ++i) - { - if (spawnAssociations[i].thisCreatureEntry == creature->GetEntry()) - { - SpawnAssoc = &spawnAssociations[i]; - break; - } - } - - if (!SpawnAssoc) - TC_LOG_ERROR("sql.sql", "TCSR: Creature template entry %u has ScriptName npc_air_force_bots, but it's not handled by that script", creature->GetEntry()); - else + for (AirForceSpawn const& spawn : airforceSpawns) { - CreatureTemplate const* spawnedTemplate = sObjectMgr->GetCreatureTemplate(SpawnAssoc->spawnedCreatureEntry); - - if (!spawnedTemplate) + if (spawn.myEntry == entry) { - TC_LOG_ERROR("sql.sql", "TCSR: Creature template entry %u does not exist in DB, which is required by npc_air_force_bots", SpawnAssoc->spawnedCreatureEntry); - SpawnAssoc = nullptr; - return; + ASSERT_NODEBUGINFO(sObjectMgr->GetCreatureTemplate(spawn.otherEntry), "Invalid creature entry %u in 'npc_air_force_bots' script", spawn.otherEntry); + return spawn; } } + ASSERT_NODEBUGINFO(false, "Unhandled creature with entry %u is assigned 'npc_air_force_bots' script", entry); } - SpawnAssociation* SpawnAssoc; - ObjectGuid SpawnedGUID; + npc_air_force_botsAI(Creature* creature) : NullCreatureAI(creature), _spawn(FindSpawnFor(creature->GetEntry())) {} - void Reset() override { } - - Creature* SummonGuard() + Creature* GetOrSummonGuard() { - Creature* summoned = me->SummonCreature(SpawnAssoc->spawnedCreatureEntry, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 300000); + Creature* guard = ObjectAccessor::GetCreature(*me, _myGuard); - if (summoned) - SpawnedGUID = summoned->GetGUID(); - else - { - TC_LOG_ERROR("sql.sql", "TCSR: npc_air_force_bots: wasn't able to spawn Creature %u", SpawnAssoc->spawnedCreatureEntry); - SpawnAssoc = nullptr; - } + if (!guard && (guard = me->SummonCreature(_spawn.otherEntry, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 300000))) + _myGuard = guard->GetGUID(); - return summoned; + return guard; } - Creature* GetSummonedGuard() + void UpdateAI(uint32 /*diff*/) override { - Creature* creature = ObjectAccessor::GetCreature(*me, SpawnedGUID); - - if (creature && creature->IsAlive()) - return creature; + if (_toAttack.empty()) + return; - return nullptr; - } + Creature* guard = GetOrSummonGuard(); + if (!guard) + return; - void UpdateAI(uint32 diff) override - { - ScriptedAI::UpdateAI(diff); + for (ObjectGuid guid : _toAttack) + { + Unit* target = ObjectAccessor::GetUnit(*me, guid); + if (!target) + continue; + if (guard->IsEngagedBy(target)) + continue; + + guard->EngageWithTarget(target); + if (_spawn.type == ALARMBOT) + guard->CastSpell(target, SPELL_GUARDS_MARK, true); + } - inLineOfSightSinceLastUpdate.clear(); + _toAttack.clear(); } void MoveInLineOfSight(Unit* who) override { - if (!SpawnAssoc) + // guards are only spawned against players + if (who->GetTypeId() != TYPEID_PLAYER) return; - if (me->IsValidAttackTarget(who)) - { - Player* playerTarget = who->ToPlayer(); - - // airforce guards only spawn for players - if (!playerTarget) - return; - - Creature* lastSpawnedGuard = SpawnedGUID.IsEmpty() ? nullptr : GetSummonedGuard(); - - // prevent calling Unit::GetUnit at next MoveInLineOfSight call - speedup - if (!lastSpawnedGuard) - SpawnedGUID.Clear(); - - switch (SpawnAssoc->spawnType) - { - case SPAWNTYPE_ALARMBOT: - { - // handle only 1 change for world update for each target - if (!inLineOfSightSinceLastUpdate.insert(who->GetGUID()).second) - return; - - if (!who->IsWithinDistInMap(me, RANGE_GUARDS_MARK)) - return; - - Aura* markAura = who->GetAura(SPELL_GUARDS_MARK); - if (markAura) - { - // the target wasn't able to move out of our range within 25 seconds - if (!lastSpawnedGuard) - { - lastSpawnedGuard = SummonGuard(); - - if (!lastSpawnedGuard) - return; - } - - if (markAura->GetDuration() < AURA_DURATION_TIME_LEFT) - if (!lastSpawnedGuard->GetVictim()) - lastSpawnedGuard->AI()->AttackStart(who); - } - else - { - if (!lastSpawnedGuard) - lastSpawnedGuard = SummonGuard(); - - if (!lastSpawnedGuard) - return; + // we're already scheduled to attack this player on our next tick, don't bother checking + if (_toAttack.find(who->GetGUID()) != _toAttack.end()) + return; - lastSpawnedGuard->CastSpell(who, SPELL_GUARDS_MARK, true); - } - break; - } - case SPAWNTYPE_TRIPWIRE_ROOFTOP: - { - if (!who->IsWithinDistInMap(me, RANGE_TRIPWIRE)) - return; + // check if they're in range + if (!who->IsWithinDistInMap(me, (_spawn.type == ALARMBOT) ? RANGE_ALARMBOT : RANGE_TRIPWIRE)) + return; - if (!lastSpawnedGuard) - lastSpawnedGuard = SummonGuard(); + // check if they're hostile + if (!(me->IsHostileTo(who) || who->IsHostileTo(me))) + return; - if (!lastSpawnedGuard) - return; + // check if they're a valid attack target + if (!me->IsValidAttackTarget(who)) + return; - // ROOFTOP only triggers if the player is on the ground - if (!playerTarget->IsFlying() && !lastSpawnedGuard->GetVictim()) - lastSpawnedGuard->AI()->AttackStart(who); + if ((_spawn.type == TRIPWIRE) && who->IsFlying()) + return; - break; - } - } - } + _toAttack.insert(who->GetGUID()); } - GuidSet inLineOfSightSinceLastUpdate; + private: + AirForceSpawn const& _spawn; + ObjectGuid _myGuard; + std::unordered_set _toAttack; + }; CreatureAI* GetAI(Creature* creature) const override