Skip to content

Commit

Permalink
Scripts/World: Rewrite air force trigger bots script, add hostility c…
Browse files Browse the repository at this point in the history
…heck along the way. Closes #23307.
  • Loading branch information
Treeston committed Jul 9, 2019
1 parent b9d1e66 commit 0ae2afe
Showing 1 changed file with 101 additions and 171 deletions.
272 changes: 101 additions & 171 deletions src/server/scripts/World/npcs_special.cpp
Expand Up @@ -43,215 +43,145 @@
# 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
{
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<decltype(spawnAssociations)>::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<ObjectGuid> _toAttack;

};

CreatureAI* GetAI(Creature* creature) const override
Expand Down

0 comments on commit 0ae2afe

Please sign in to comment.