Core/GuardAI: Guards don't evade if the target casts Vanish and also can detect players with Feign Death.

Core/Scripts: Moved scripts from ScriptedGuardAI to guards.cpp
Core/Creature: Removed custom npcflags UNIT_NPC_FLAG_GUARD (now moved to flag_extras) and UNIT_NPC_FLAG_OUTDOORPVP (not used)

Thanks to Kaelima, Xanadu, Shauren and linencloth.

Signed-off-by: Manuel Carrasco <[email protected]>
This commit is contained in:
Manuel Carrasco
2011-02-10 22:36:35 -03:00
parent 71849e0efe
commit 6b2ffc9039
16 changed files with 337 additions and 398 deletions
+28 -16
View File
@@ -23,7 +23,7 @@
#include "World.h"
#include "CreatureAIImpl.h"
int GuardAI::Permissible(const Creature *creature)
int GuardAI::Permissible(const Creature* creature)
{
if (creature->isGuard())
return PERMIT_BASE_SPECIAL;
@@ -31,25 +31,42 @@ int GuardAI::Permissible(const Creature *creature)
return PERMIT_BASE_NO;
}
GuardAI::GuardAI(Creature *c) : CreatureAI(c), i_victimGuid(0), i_state(STATE_NORMAL), i_tracker(TIME_INTERVAL_LOOK)
GuardAI::GuardAI(Creature* creature) : ScriptedAI(creature), i_victimGuid(0), i_state(STATE_NORMAL), i_tracker(TIME_INTERVAL_LOOK)
{
}
void GuardAI::MoveInLineOfSight(Unit *u)
bool GuardAI::CanSeeAlways(WorldObject const* obj)
{
if (!obj->isType(TYPEMASK_UNIT))
return false;
std::list<HostileReference *> t_list = me->getThreatManager().getThreatList();
for (std::list<HostileReference *>::const_iterator itr = t_list.begin(); itr!= t_list.end(); ++itr)
{
if (Unit* unit = Unit::GetUnit(*me, (*itr)->getUnitGuid()))
if (unit == obj)
return true;
}
return false;
}
void GuardAI::MoveInLineOfSight(Unit* unit)
{
// Ignore Z for flying creatures
if (!me->canFly() && me->GetDistanceZ(u) > CREATURE_Z_ATTACK_RANGE)
if (!me->canFly() && me->GetDistanceZ(unit) > CREATURE_Z_ATTACK_RANGE)
return;
if (!me->getVictim() && me->canAttack(u) &&
(u->IsHostileToPlayers() || me->IsHostileTo(u) /*|| u->getVictim() && me->IsFriendlyTo(u->getVictim())*/) &&
u->isInAccessiblePlaceFor(me))
if (!me->getVictim() && me->canAttack(unit) &&
(unit->IsHostileToPlayers() || me->IsHostileTo(unit) /*|| u->getVictim() && me->IsFriendlyTo(u->getVictim())*/) &&
unit->isInAccessiblePlaceFor(me))
{
float attackRadius = me->GetAttackDistance(u);
if (me->IsWithinDistInMap(u,attackRadius))
float attackRadius = me->GetAttackDistance(unit);
if (me->IsWithinDistInMap(unit,attackRadius))
{
//Need add code to let guard support player
AttackStart(u);
AttackStart(unit);
//u->RemoveAurasByType(SPELL_AURA_MOD_STEALTH);
}
}
@@ -122,13 +139,8 @@ void GuardAI::UpdateAI(const uint32 /*diff*/)
}
}
bool GuardAI::IsVisible(Unit *pl) const
{
return me->IsWithinDist(pl,sWorld->getFloatConfig(CONFIG_SIGHT_GUARDER))
&& me->canSeeOrDetect(pl);
}
void GuardAI::JustDied(Unit *killer)
void GuardAI::JustDied(Unit* killer)
{
if (Player* pkiller = killer->GetCharmerOrOwnerPlayerOrPlayerItself())
me->SendZoneUnderAttackMessage(pkiller);
+3 -3
View File
@@ -19,12 +19,12 @@
#ifndef TRINITY_GUARDAI_H
#define TRINITY_GUARDAI_H
#include "CreatureAI.h"
#include "ScriptedCreature.h"
#include "Timer.h"
class Creature;
class GuardAI : public CreatureAI
class GuardAI : public ScriptedAI
{
enum GuardState
{
@@ -39,7 +39,7 @@ class GuardAI : public CreatureAI
void MoveInLineOfSight(Unit *);
void EnterEvadeMode();
void JustDied(Unit *);
bool IsVisible(Unit *) const;
bool CanSeeAlways(WorldObject const* obj);
void UpdateAI(const uint32);
static int Permissible(const Creature *);
+1
View File
@@ -168,6 +168,7 @@ class CreatureAI : public UnitAI
virtual void PassengerBoarded(Unit * /*who*/, int8 /*seatId*/, bool /*apply*/) {}
virtual bool CanSeeAlways(WorldObject const* obj) {return false;}
protected:
virtual void MoveInLineOfSight(Unit *);
@@ -1,198 +0,0 @@
/*
* Copyright (C) 2008-2011 TrinityCore <http://www.trinitycore.org/>
* Copyright (C) 2006-2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* ScriptData
SDName: Guard_AI
SD%Complete: 90
SDComment:
SDCategory: Guards
EndScriptData */
#include "ScriptPCH.h"
#include "ScriptedGuardAI.h"
// **** This script is for use within every single guard to save coding time ****
#define GENERIC_CREATURE_COOLDOWN 5000
#define SAY_GUARD_SIL_AGGRO1 -1070001
#define SAY_GUARD_SIL_AGGRO2 -1070002
#define SAY_GUARD_SIL_AGGRO3 -1070003
guardAI::guardAI(Creature* pCreature) : ScriptedAI(pCreature),
GlobalCooldown(0),
BuffTimer(0)
{}
void guardAI::Reset()
{
GlobalCooldown = 0;
BuffTimer = 0; //Rebuff as soon as we can
}
void guardAI::EnterCombat(Unit *who)
{
if (me->GetEntry() == 15184)
DoScriptText(RAND(SAY_GUARD_SIL_AGGRO1,SAY_GUARD_SIL_AGGRO2,SAY_GUARD_SIL_AGGRO3), me, who);
if (SpellEntry const *spell = me->reachWithSpellAttack(who))
DoCastSpell(who, spell);
}
void guardAI::JustDied(Unit *Killer)
{
//Send Zone Under Attack message to the LocalDefense and WorldDefense Channels
if (Player* pKiller = Killer->GetCharmerOrOwnerPlayerOrPlayerItself())
me->SendZoneUnderAttackMessage(pKiller);
}
void guardAI::UpdateAI(const uint32 diff)
{
//Always decrease our global cooldown first
if (GlobalCooldown > diff)
GlobalCooldown -= diff;
else GlobalCooldown = 0;
//Buff timer (only buff when we are alive and not in combat
if (me->isAlive() && !me->isInCombat())
{
if (BuffTimer <= diff)
{
//Find a spell that targets friendly and applies an aura (these are generally buffs)
SpellEntry const *info = SelectSpell(me, 0, 0, SELECT_TARGET_ANY_FRIEND, 0, 0, 0, 0, SELECT_EFFECT_AURA);
if (info && !GlobalCooldown)
{
//Cast the buff spell
DoCastSpell(me, info);
//Set our global cooldown
GlobalCooldown = GENERIC_CREATURE_COOLDOWN;
//Set our timer to 10 minutes before rebuff
BuffTimer = 600000;
} //Try again in 30 seconds
else BuffTimer = 30000;
} else BuffTimer -= diff;
}
//Return since we have no target
if (!UpdateVictim())
return;
// Make sure our attack is ready and we arn't currently casting
if (me->isAttackReady() && !me->IsNonMeleeSpellCasted(false))
{
//If we are within range melee the target
if (me->IsWithinMeleeRange(me->getVictim()))
{
bool Healing = false;
SpellEntry const *info = NULL;
//Select a healing spell if less than 30% hp
if (HealthBelowPct(30))
info = SelectSpell(me, 0, 0, SELECT_TARGET_ANY_FRIEND, 0, 0, 0, 0, SELECT_EFFECT_HEALING);
//No healing spell available, select a hostile spell
if (info) Healing = true;
else info = SelectSpell(me->getVictim(), 0, 0, SELECT_TARGET_ANY_ENEMY, 0, 0, 0, 0, SELECT_EFFECT_DONTCARE);
//20% chance to replace our white hit with a spell
if (info && rand() % 5 == 0 && !GlobalCooldown)
{
//Cast the spell
if (Healing)DoCastSpell(me, info);
else DoCastSpell(me->getVictim(), info);
//Set our global cooldown
GlobalCooldown = GENERIC_CREATURE_COOLDOWN;
}
else me->AttackerStateUpdate(me->getVictim());
me->resetAttackTimer();
}
}
else
{
//Only run this code if we arn't already casting
if (!me->IsNonMeleeSpellCasted(false))
{
bool Healing = false;
SpellEntry const *info = NULL;
//Select a healing spell if less than 30% hp ONLY 33% of the time
if (HealthBelowPct(30) && rand() % 3 == 0)
info = SelectSpell(me, 0, 0, SELECT_TARGET_ANY_FRIEND, 0, 0, 0, 0, SELECT_EFFECT_HEALING);
//No healing spell available, See if we can cast a ranged spell (Range must be greater than ATTACK_DISTANCE)
if (info) Healing = true;
else info = SelectSpell(me->getVictim(), 0, 0, SELECT_TARGET_ANY_ENEMY, 0, 0, NOMINAL_MELEE_RANGE, 0, SELECT_EFFECT_DONTCARE);
//Found a spell, check if we arn't on cooldown
if (info && !GlobalCooldown)
{
//If we are currently moving stop us and set the movement generator
if ((*me).GetMotionMaster()->GetCurrentMovementGeneratorType() != IDLE_MOTION_TYPE)
{
(*me).GetMotionMaster()->Clear(false);
(*me).GetMotionMaster()->MoveIdle();
}
//Cast spell
if (Healing) DoCastSpell(me,info);
else DoCastSpell(me->getVictim(),info);
//Set our global cooldown
GlobalCooldown = GENERIC_CREATURE_COOLDOWN;
} //If no spells available and we arn't moving run to target
else if ((*me).GetMotionMaster()->GetCurrentMovementGeneratorType() != TARGETED_MOTION_TYPE)
{
//Cancel our current spell and then mutate new movement generator
me->InterruptNonMeleeSpells(false);
(*me).GetMotionMaster()->Clear(false);
(*me).GetMotionMaster()->MoveChase(me->getVictim());
}
}
}
}
void guardAI::DoReplyToTextEmote(uint32 em)
{
switch(em)
{
case TEXTEMOTE_KISS: me->HandleEmoteCommand(EMOTE_ONESHOT_BOW); break;
case TEXTEMOTE_WAVE: me->HandleEmoteCommand(EMOTE_ONESHOT_WAVE); break;
case TEXTEMOTE_SALUTE: me->HandleEmoteCommand(EMOTE_ONESHOT_SALUTE); break;
case TEXTEMOTE_SHY: me->HandleEmoteCommand(EMOTE_ONESHOT_FLEX); break;
case TEXTEMOTE_RUDE:
case TEXTEMOTE_CHICKEN: me->HandleEmoteCommand(EMOTE_ONESHOT_POINT); break;
}
}
void guardAI_orgrimmar::ReceiveEmote(Player* pPlayer, uint32 text_emote)
{
if (pPlayer->GetTeam() == HORDE)
DoReplyToTextEmote(text_emote);
}
void guardAI_stormwind::ReceiveEmote(Player* pPlayer, uint32 text_emote)
{
if (pPlayer->GetTeam() == ALLIANCE)
DoReplyToTextEmote(text_emote);
}
@@ -1,45 +0,0 @@
/* Copyright (C) 2006 - 2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
* This program is free software licensed under GPL version 2
* Please see the included DOCS/LICENSE.TXT for more information */
#ifndef SC_GUARDAI_H
#define SC_GUARDAI_H
#define GENERIC_CREATURE_COOLDOWN 5000
struct guardAI : public ScriptedAI
{
public:
explicit guardAI(Creature* pCreature);
~guardAI() {}
uint32 GlobalCooldown; //This variable acts like the global cooldown that players have (1.5 seconds)
uint32 BuffTimer; //This variable keeps track of buffs
void Reset();
void EnterCombat(Unit * /*who*/);
void JustDied(Unit *Killer);
void UpdateAI(const uint32 diff);
//common used for guards in main cities
void DoReplyToTextEmote(uint32 em);
};
struct guardAI_orgrimmar : public guardAI
{
guardAI_orgrimmar(Creature *c) : guardAI(c) {}
void ReceiveEmote(Player *player, uint32 text_emote);
};
struct guardAI_stormwind : public guardAI
{
guardAI_stormwind(Creature *c) : guardAI(c) {}
void ReceiveEmote(Player *player, uint32 text_emote);
};
#endif
+20 -1
View File
@@ -1421,6 +1421,17 @@ bool Creature::isVisibleForInState(WorldObject const* seer) const
return false;
}
bool Creature::canSeeAlways(WorldObject const* obj) const
{
if (Unit::canSeeAlways(obj))
return true;
if (IsAIEnabled && AI()->CanSeeAlways(obj))
return true;
return false;
}
bool Creature::canStartAttack(Unit const* who, bool force) const
{
if (isCivilian())
@@ -1960,10 +1971,18 @@ bool Creature::_IsTargetAcceptable(const Unit *target) const
// if the target cannot be attacked, the target is not acceptable
if (IsFriendlyTo(target)
|| !target->isAttackableByAOE()
|| target->HasUnitState(UNIT_STAT_DIED)
|| (m_vehicle && (IsOnVehicle(target) || m_vehicle->GetBase()->IsOnVehicle(target))))
return false;
if (target->HasUnitState(UNIT_STAT_DIED))
{
// guards can detect fake death
if (isGuard() && target->HasFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_FEIGN_DEATH))
return true;
else
return false;
}
const Unit *myVictim = getAttackerForHelper();
const Unit *targetVictim = target->getAttackerForHelper();
+5 -2
View File
@@ -49,7 +49,7 @@ enum CreatureFlagsExtra
CREATURE_FLAG_EXTRA_TRIGGER = 0x00000080, // trigger creature
CREATURE_FLAG_EXTRA_NO_TAUNT = 0x00000100, // creature is immune to taunt auras and effect attack me
CREATURE_FLAG_EXTRA_WORLDEVENT = 0x00004000, // custom flag for world event creatures (left room for merging)
//CREATURE_FLAG_EXTRA_CHARM_AI = 0x00008000, // use ai when charmed
CREATURE_FLAG_EXTRA_GUARD = 0x00008000, // Creature is guard
CREATURE_FLAG_EXTRA_NO_CRIT = 0x00020000, // creature can't do critical strikes
CREATURE_FLAG_EXTRA_NO_SKILLGAIN = 0x00040000, // creature won't increase weapon skills
CREATURE_FLAG_EXTRA_TAUNT_DIMINISH = 0x00080000, // Taunt is a subject to diminishing returns on this creautre
@@ -61,7 +61,8 @@ enum CreatureFlagsExtra
CREATURE_FLAG_EXTRA_NO_PARRY | CREATURE_FLAG_EXTRA_NO_PARRY_HASTEN | CREATURE_FLAG_EXTRA_NO_BLOCK | \
CREATURE_FLAG_EXTRA_NO_CRUSH | CREATURE_FLAG_EXTRA_NO_XP_AT_KILL | CREATURE_FLAG_EXTRA_TRIGGER | \
CREATURE_FLAG_EXTRA_NO_TAUNT | CREATURE_FLAG_EXTRA_WORLDEVENT | CREATURE_FLAG_EXTRA_NO_CRIT | \
CREATURE_FLAG_EXTRA_NO_SKILLGAIN | CREATURE_FLAG_EXTRA_TAUNT_DIMINISH | CREATURE_FLAG_EXTRA_ALL_DIMINISH)
CREATURE_FLAG_EXTRA_NO_SKILLGAIN | CREATURE_FLAG_EXTRA_TAUNT_DIMINISH | CREATURE_FLAG_EXTRA_ALL_DIMINISH | \
CREATURE_FLAG_EXTRA_GUARD)
// GCC have alternative #pragma pack(N) syntax and old gcc version not support pack(push,N), also any gcc version not support it at some platform
#if defined(__GNUC__)
@@ -435,6 +436,7 @@ class Creature : public Unit, public GridObject<Creature>
bool isRacialLeader() const { return GetCreatureInfo()->RacialLeader; }
bool isCivilian() const { return GetCreatureInfo()->flags_extra & CREATURE_FLAG_EXTRA_CIVILIAN; }
bool isTrigger() const { return GetCreatureInfo()->flags_extra & CREATURE_FLAG_EXTRA_TRIGGER; }
bool isGuard() const { return GetCreatureInfo()->flags_extra & CREATURE_FLAG_EXTRA_GUARD; }
bool canWalk() const { return GetCreatureInfo()->InhabitType & INHABIT_GROUND; }
bool canSwim() const { return GetCreatureInfo()->InhabitType & INHABIT_WATER; }
//bool canFly() const { return GetCreatureInfo()->InhabitType & INHABIT_AIR; }
@@ -729,6 +731,7 @@ class Creature : public Unit, public GridObject<Creature>
uint32 guid_transport;
bool isVisibleForInState(WorldObject const* seer) const;
bool canSeeAlways(WorldObject const* obj) const;
private:
//WaypointMovementGenerator vars
uint32 m_waypointID;
+1 -1
View File
@@ -529,7 +529,7 @@ void Object::_BuildValuesUpdate(uint8 updatetype, ByteBuffer * data, UpdateMask
if (index == UNIT_NPC_FLAGS)
{
// remove custom flag before sending
uint32 appendValue = m_uint32Values[ index ] & ~(UNIT_NPC_FLAG_GUARD + UNIT_NPC_FLAG_OUTDOORPVP);
uint32 appendValue = m_uint32Values[ index ];
if (GetTypeId() == TYPEID_UNIT)
{
+11 -1
View File
@@ -11879,9 +11879,19 @@ bool Unit::canAttack(Unit const* target, bool force) const
else if (!IsHostileTo(target))
return false;
if (!target->isAttackableByAOE() || target->HasUnitState(UNIT_STAT_DIED))
if (!target->isAttackableByAOE())
return false;
if (target->HasUnitState(UNIT_STAT_DIED))
{
if (!ToCreature() || !ToCreature()->isGuard())
return false;
// guards can detect fake death
if (!target->HasFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_FEIGN_DEATH))
return false;
}
if (m_vehicle)
if (IsOnVehicle(target) || m_vehicle->GetBase()->IsOnVehicle(target))
return false;
+1 -6
View File
@@ -624,8 +624,6 @@ enum NPCFlags
UNIT_NPC_FLAG_GUILD_BANKER = 0x00800000, // cause client to send 997 opcode
UNIT_NPC_FLAG_SPELLCLICK = 0x01000000, // cause client to send 1015 opcode (spell click), dynamic, set at loading and don't must be set in DB
UNIT_NPC_FLAG_PLAYER_VEHICLE = 0x02000000, // players with mounts that have vehicle data should have it set
UNIT_NPC_FLAG_GUARD = 0x10000000, // custom flag for guards
UNIT_NPC_FLAG_OUTDOORPVP = 0x20000000, // custom flag for outdoor pvp creatures
};
enum MovementFlags
@@ -1422,14 +1420,11 @@ class Unit : public WorldObject
return HasFlag(UNIT_NPC_FLAGS,
UNIT_NPC_FLAG_VENDOR | UNIT_NPC_FLAG_TRAINER | UNIT_NPC_FLAG_FLIGHTMASTER |
UNIT_NPC_FLAG_PETITIONER | UNIT_NPC_FLAG_BATTLEMASTER | UNIT_NPC_FLAG_BANKER |
UNIT_NPC_FLAG_INNKEEPER | UNIT_NPC_FLAG_GUARD | UNIT_NPC_FLAG_SPIRITHEALER |
UNIT_NPC_FLAG_INNKEEPER | UNIT_NPC_FLAG_SPIRITHEALER |
UNIT_NPC_FLAG_SPIRITGUIDE | UNIT_NPC_FLAG_TABARDDESIGNER | UNIT_NPC_FLAG_AUCTIONEER);
}
bool isSpiritService() const { return HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPIRITHEALER | UNIT_NPC_FLAG_SPIRITGUIDE); }
//Need fix or use this
bool isGuard() const { return HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GUARD); }
bool isInFlight() const { return HasUnitState(UNIT_STAT_IN_FLIGHT); }
bool isInCombat() const { return HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IN_COMBAT); }
@@ -321,7 +321,7 @@ void WorldSession::HandleGossipHelloOpcode(WorldPacket & recv_data)
//if (GetPlayer()->HasUnitState(UNIT_STAT_DIED))
// GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
if (unit->isArmorer() || unit->isCivilian() || unit->isQuestGiver() || unit->isServiceProvider())
if (unit->isArmorer() || unit->isCivilian() || unit->isQuestGiver() || unit->isServiceProvider() || unit->isGuard())
{
unit->StopMoving();
}