[7852] Use union of per-event type structures, more checks at creature event ai loading.

Including:
* Fixed check for EVENT_T_SPELLHIT school field. Add check at loading. Note: this event expect before and now schol _mask_ in param2.
* In EVENT_T_RECEIVE_EMOTE use original player condition check code instead copy of related code.
This commit is contained in:
VladimirMangos 2009-05-19 22:10:30 +04:00
parent e712baa58c
commit 72b4ff2fac
4 changed files with 262 additions and 403 deletions

View file

@ -30,6 +30,22 @@
#include "WorldPacket.h"
#include "InstanceData.h"
bool CreatureEventAIHolder::UpdateRepeatTimer( Creature* creature, uint32 repeatMin, uint32 repeatMax )
{
if (repeatMin == repeatMax)
Time = repeatMin;
else if (repeatMax > repeatMin)
Time = urand(repeatMin, repeatMax);
else
{
sLog.outErrorDb("CreatureEventAI: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", creature->GetEntry(), Event.event_id, Event.event_type);
Enabled = false;
return false;
}
return true;
}
int CreatureEventAI::Permissible(const Creature *creature)
{
if( creature->GetAIName() == "EventAI" )
@ -106,53 +122,25 @@ bool CreatureEventAI::ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pAction
if (pHolder.Event.event_chance <= rnd % 100)
return false;
uint32 param1 = pHolder.Event.event_param1;
uint32 param2 = pHolder.Event.event_param2;
uint32 param3 = pHolder.Event.event_param3;
uint32 param4 = pHolder.Event.event_param4;
CreatureEventAI_Event const& event = pHolder.Event;
//Check event conditions based on the event type, also reset events
switch (pHolder.Event.event_type)
switch (event.event_type)
{
case EVENT_T_TIMER:
{
if (!m_creature->isInCombat())
return false;
//Repeat Timers
if (param3 == param4)
{
pHolder.Time = param3;
}else if (param4 > param3)
pHolder.Time = urand(param3, param4);
else
{
sLog.outErrorDb("CreatureEventAI: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type);
pHolder.Enabled = false;
}
}
break;
pHolder.UpdateRepeatTimer(m_creature,event.timer.repeatMin,event.timer.repeatMax);
break;
case EVENT_T_TIMER_OOC:
{
if (m_creature->isInCombat())
return false;
//Repeat Timers
if (param3 == param4)
{
pHolder.Time = param3;
}else if (param4 > param3)
pHolder.Time = urand(param3, param4);
else
{
sLog.outErrorDb("CreatureEventAI: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type);
pHolder.Enabled = false;
}
}
break;
pHolder.UpdateRepeatTimer(m_creature,event.timer.repeatMin,event.timer.repeatMax);
break;
case EVENT_T_HP:
{
if (!m_creature->isInCombat() || !m_creature->GetMaxHealth())
@ -160,24 +148,13 @@ bool CreatureEventAI::ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pAction
uint32 perc = (m_creature->GetHealth()*100) / m_creature->GetMaxHealth();
if (perc > param1 || perc < param2)
if (perc > event.percent_range.percentMax || perc < event.percent_range.percentMin)
return false;
//Repeat Timers
if (param3 == param4)
{
pHolder.Time = param3;
}else if (param4 > param3)
pHolder.Time = urand(param3, param4);
else
{
sLog.outErrorDb("CreatureEventAI: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type);
pHolder.Enabled = false;
}
pHolder.UpdateRepeatTimer(m_creature,event.percent_range.repeatMin,event.percent_range.repeatMax);
break;
}
break;
case EVENT_T_MANA:
{
if (!m_creature->isInCombat() || !m_creature->GetMaxPower(POWER_MANA))
@ -185,109 +162,38 @@ bool CreatureEventAI::ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pAction
uint32 perc = (m_creature->GetPower(POWER_MANA)*100) / m_creature->GetMaxPower(POWER_MANA);
if (perc > param1 || perc < param2)
if (perc > event.percent_range.percentMax || perc < event.percent_range.percentMin)
return false;
//Repeat Timers
if (param3 == param4)
{
pHolder.Time = param3;
}else if (param4 > param3)
pHolder.Time = urand(param3, param4);
else
{
sLog.outErrorDb("CreatureEventAI: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type);
pHolder.Enabled = false;
}
pHolder.UpdateRepeatTimer(m_creature,event.percent_range.repeatMin,event.percent_range.repeatMax);
break;
}
break;
case EVENT_T_AGGRO:
{
}
break;
break;
case EVENT_T_KILL:
{
//Repeat Timers
if (param1 == param2)
{
pHolder.Time = param1;
}else if (param2 > param1)
pHolder.Time = urand(param1, param2);
else
{
sLog.outErrorDb("CreatureEventAI: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type);
pHolder.Enabled = false;
}
}
pHolder.UpdateRepeatTimer(m_creature,event.kill.repeatMin,event.kill.repeatMax);
break;
case EVENT_T_DEATH:
{
}
break;
case EVENT_T_EVADE:
{
}
break;
break;
case EVENT_T_SPELLHIT:
{
//Spell hit is special case, param1 and param2 handled within CreatureEventAI::SpellHit
//Repeat Timers
if (param3 == param4)
{
pHolder.Time = param3;
}else if (param4 > param3)
pHolder.Time = urand(param3, param4);
else
{
sLog.outErrorDb("CreatureEventAI: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type);
pHolder.Enabled = false;
}
}
break;
pHolder.UpdateRepeatTimer(m_creature,event.spell_hit.repeatMin,event.spell_hit.repeatMax);
break;
case EVENT_T_RANGE:
{
//Repeat Timers
if (param3 == param4)
{
pHolder.Time = param3;
}else if (param4 > param3)
pHolder.Time = urand(param3, param4);
else
{
sLog.outErrorDb("CreatureEventAI: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type);
pHolder.Enabled = false;
}
}
break;
pHolder.UpdateRepeatTimer(m_creature,event.range.repeatMin,event.range.repeatMax);
break;
case EVENT_T_OOC_LOS:
{
//Repeat Timers
if (param3 == param4)
{
pHolder.Time = param3;
}else if (param4 > param3)
pHolder.Time = urand(param3, param4);
else
{
sLog.outErrorDb("CreatureEventAI: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type);
pHolder.Enabled = false;
}
}
break;
pHolder.UpdateRepeatTimer(m_creature,event.ooc_los.repeatMin,event.ooc_los.repeatMax);
break;
case EVENT_T_SPAWNED:
{
}
break;
break;
case EVENT_T_TARGET_HP:
{
if (!m_creature->isInCombat() || !m_creature->getVictim() || !m_creature->getVictim()->GetMaxHealth())
@ -295,78 +201,42 @@ bool CreatureEventAI::ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pAction
uint32 perc = (m_creature->getVictim()->GetHealth()*100) / m_creature->getVictim()->GetMaxHealth();
if (perc > param1 || perc < param2)
if (perc > event.percent_range.percentMax || perc < event.percent_range.percentMin)
return false;
//Repeat Timers
if (param3 == param4)
{
pHolder.Time = param3;
}else if (param4 > param3)
pHolder.Time = urand(param3, param4);
else
{
sLog.outErrorDb("CreatureEventAI: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type);
pHolder.Enabled = false;
}
pHolder.UpdateRepeatTimer(m_creature,event.percent_range.repeatMin,event.percent_range.repeatMax);
break;
}
break;
case EVENT_T_TARGET_CASTING:
{
if (!m_creature->isInCombat() || !m_creature->getVictim() || !m_creature->getVictim()->IsNonMeleeSpellCasted(false, false, true))
return false;
//Repeat Timers
if (param1 == param2)
{
pHolder.Time = param1;
}else if (param2 > param1)
pHolder.Time = urand(param1, param2);
else
{
sLog.outErrorDb("CreatureEventAI: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type);
pHolder.Enabled = false;
}
}
break;
pHolder.UpdateRepeatTimer(m_creature,event.target_casting.repeatMin,event.target_casting.repeatMax);
break;
case EVENT_T_FRIENDLY_HP:
{
if (!m_creature->isInCombat())
return false;
Unit* pUnit = DoSelectLowestHpFriendly(param2, param1);
Unit* pUnit = DoSelectLowestHpFriendly(event.friendly_hp.radius, event.friendly_hp.hpDeficit);
if (!pUnit)
return false;
pActionInvoker = pUnit;
//Repeat Timers
if (param3 == param4)
{
pHolder.Time = param3;
}else if (param4 > param3)
pHolder.Time = urand(param3, param4);
else
{
sLog.outErrorDb("CreatureEventAI: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type);
pHolder.Enabled = false;
}
pHolder.UpdateRepeatTimer(m_creature,event.friendly_hp.repeatMin,event.friendly_hp.repeatMax);
break;
}
break;
case EVENT_T_FRIENDLY_IS_CC:
{
if (!m_creature->isInCombat())
return false;
std::list<Creature*> pList;
DoFindFriendlyCC(pList, param2);
DoFindFriendlyCC(pList, event.friendly_is_cc.radius);
//List is empty
if (pList.empty())
@ -376,23 +246,13 @@ bool CreatureEventAI::ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pAction
pActionInvoker = *(pList.begin());
//Repeat Timers
if (param3 == param4)
{
pHolder.Time = param3;
}else if (param4 > param3)
pHolder.Time = urand(param3, param4);
else
{
sLog.outErrorDb("CreatureEventAI: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type);
pHolder.Enabled = false;
}
pHolder.UpdateRepeatTimer(m_creature,event.friendly_is_cc.repeatMin,event.friendly_is_cc.repeatMax);
break;
}
break;
case EVENT_T_FRIENDLY_MISSING_BUFF:
{
std::list<Creature*> pList;
DoFindFriendlyMissingBuff(pList, param2, param1);
DoFindFriendlyMissingBuff(pList, event.friendly_buff.radius, event.friendly_buff.spellId);
//List is empty
if (pList.empty())
@ -402,20 +262,9 @@ bool CreatureEventAI::ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pAction
pActionInvoker = *(pList.begin());
//Repeat Timers
if (param3 == param4)
{
pHolder.Time = param3;
}else if (param4 > param3)
pHolder.Time = urand(param3, param4);
else
{
sLog.outErrorDb("CreatureEventAI: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type);
pHolder.Enabled = false;
}
pHolder.UpdateRepeatTimer(m_creature,event.friendly_buff.repeatMin,event.friendly_buff.repeatMax);
break;
}
break;
case EVENT_T_SUMMONED_UNIT:
{
//Prevent event from occuring on no unit or non creatures
@ -423,34 +272,31 @@ bool CreatureEventAI::ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pAction
return false;
//Creature id doesn't match up
if (param1 && ((Creature*)pActionInvoker)->GetEntry() != param1)
if (((Creature*)pActionInvoker)->GetEntry() != event.summon_unit.creatureId)
return false;
//Repeat Timers
if (param2 == param3)
{
pHolder.Time = param2;
}else if (param3 > param2)
pHolder.Time = urand(param2, param3);
else
{
sLog.outErrorDb("CreatureEventAI: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type);
pHolder.Enabled = false;
}
pHolder.UpdateRepeatTimer(m_creature,event.summon_unit.repeatMin,event.summon_unit.repeatMax);
}
break;
case EVENT_T_TARGET_MANA:
{
if (!m_creature->isInCombat() || !m_creature->getVictim() || !m_creature->getVictim()->GetMaxPower(POWER_MANA))
return false;
uint32 perc = (m_creature->getVictim()->GetPower(POWER_MANA)*100) / m_creature->getVictim()->GetMaxPower(POWER_MANA);
if (perc > event.percent_range.percentMax || perc < event.percent_range.percentMin)
return false;
//Repeat Timers
pHolder.UpdateRepeatTimer(m_creature,event.percent_range.repeatMin,event.percent_range.repeatMax);
break;
}
case EVENT_T_REACHED_HOME:
{
}
break;
case EVENT_T_RECEIVE_EMOTE:
{
}
break;
break;
default:
sLog.outErrorDb("CreatureEventAI: Creature %u using Event %u has invalid Event Type(%u), missing from ProcessEvent() Switch.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type);
break;
}
@ -922,25 +768,16 @@ void CreatureEventAI::Reset()
//Reset all events to enabled
for (std::list<CreatureEventAIHolder>::iterator i = CreatureEventAIList.begin(); i != CreatureEventAIList.end(); ++i)
{
switch ((*i).Event.event_type)
CreatureEventAI_Event const& event = (*i).Event;
switch (event.event_type)
{
//Reset all out of combat timers
case EVENT_T_TIMER_OOC:
{
if ((*i).Event.event_param2 == (*i).Event.event_param1)
{
(*i).Time = (*i).Event.event_param1;
if ((*i).UpdateRepeatTimer(m_creature,event.timer.initialMin,event.timer.initialMax))
(*i).Enabled = true;
}
else if ((*i).Event.event_param2 > (*i).Event.event_param1)
{
(*i).Time = urand((*i).Event.event_param1, (*i).Event.event_param2);
(*i).Enabled = true;
}
else
sLog.outErrorDb("CreatureEventAI: Creature %u using Event %u (Type = %u) has InitialMax < InitialMin. Event disabled.", m_creature->GetEntry(), (*i).Event.event_id, (*i).Event.event_type);
break;
}
break;
//default:
//TODO: enable below code line / verify this is correct to enable events previously disabled (ex. aggro yell), instead of enable this in void Aggro()
//(*i).Enabled = true;
@ -1034,7 +871,8 @@ void CreatureEventAI::EnterCombat(Unit *enemy)
{
for (std::list<CreatureEventAIHolder>::iterator i = CreatureEventAIList.begin(); i != CreatureEventAIList.end(); ++i)
{
switch ((*i).Event.event_type)
CreatureEventAI_Event const& event = (*i).Event;
switch (event.event_type)
{
case EVENT_T_AGGRO:
(*i).Enabled = true;
@ -1042,18 +880,8 @@ void CreatureEventAI::EnterCombat(Unit *enemy)
break;
//Reset all in combat timers
case EVENT_T_TIMER:
if ((*i).Event.event_param2 == (*i).Event.event_param1)
{
(*i).Time = (*i).Event.event_param1;
if ((*i).UpdateRepeatTimer(m_creature,event.timer.initialMin,event.timer.initialMax))
(*i).Enabled = true;
}
else if ((*i).Event.event_param2 > (*i).Event.event_param1)
{
(*i).Time = urand((*i).Event.event_param1, (*i).Event.event_param2);
(*i).Enabled = true;
}
else
sLog.outErrorDb("CreatureEventAI: Creature %u using Event %u (Type = %u) has InitialMax < InitialMin. Event disabled.", m_creature->GetEntry(), (*i).Event.event_id, (*i).Event.event_type);
break;
//All normal events need to be re-enabled and their time set to 0
default:
@ -1104,14 +932,14 @@ void CreatureEventAI::MoveInLineOfSight(Unit *who)
if ((*itr).Event.event_type == EVENT_T_OOC_LOS)
{
//can trigger if closer than fMaxAllowedRange
float fMaxAllowedRange = (*itr).Event.event_param2;
float fMaxAllowedRange = (*itr).Event.ooc_los.maxRange;
//if range is ok and we are actually in LOS
if (m_creature->IsWithinDistInMap(who, fMaxAllowedRange) && m_creature->IsWithinLOSInMap(who))
{
//if friendly event&&who is not hostile OR hostile event&&who is hostile
if (((*itr).Event.event_param1 && !m_creature->IsHostileTo(who)) ||
((!(*itr).Event.event_param1) && m_creature->IsHostileTo(who)))
if (((*itr).Event.ooc_los.noHostile && !m_creature->IsHostileTo(who)) ||
((!(*itr).Event.ooc_los.noHostile) && m_creature->IsHostileTo(who)))
ProcessEvent(*itr, who);
}
}
@ -1151,17 +979,11 @@ void CreatureEventAI::SpellHit(Unit* pUnit, const SpellEntry* pSpell)
return;
for (std::list<CreatureEventAIHolder>::iterator i = CreatureEventAIList.begin(); i != CreatureEventAIList.end(); ++i)
{
if ((*i).Event.event_type == EVENT_T_SPELLHIT)
{
//If spell id matches (or no spell id) & if spell school matches (or no spell school)
if (!(*i).Event.event_param1 || pSpell->Id == (*i).Event.event_param1)
{
if ((*i).Event.event_param2_s == -1 || pSpell->SchoolMask == (*i).Event.event_param2)
if (!(*i).Event.spell_hit.spellId || pSpell->Id == (*i).Event.spell_hit.spellId)
if (pSpell->SchoolMask & (*i).Event.spell_hit.schoolMask)
ProcessEvent(*i, pUnit);
}
}
}
}
void CreatureEventAI::UpdateAI(const uint32 diff)
@ -1216,8 +1038,7 @@ void CreatureEventAI::UpdateAI(const uint32 diff)
case EVENT_T_RANGE:
if (Combat)
if (m_creature->IsInMap(m_creature->getVictim()))
if (m_creature->IsInRange(m_creature->getVictim(),
(float)(*i).Event.event_param1,(float)(*i).Event.event_param2))
if (m_creature->IsInRange(m_creature->getVictim(),(float)(*i).Event.range.minDist,(float)(*i).Event.range.maxDist))
ProcessEvent(*i);
break;
}
@ -1531,60 +1352,11 @@ void CreatureEventAI::ReceiveEmote(Player* pPlayer, uint32 text_emote)
{
if ((*itr).Event.event_type == EVENT_T_RECEIVE_EMOTE)
{
if ((*itr).Event.event_param1 != text_emote)
if ((*itr).Event.receive_emote.emoteId != text_emote)
return;
bool bProcess = false;
switch((*itr).Event.event_param2)
{
//enum ConditionType
case CONDITION_NONE: // 0 0
bProcess = true;
break;
case CONDITION_AURA: // spell_id effindex
if (pPlayer->HasAura((*itr).Event.event_param3,(*itr).Event.event_param4))
bProcess = true;
break;
case CONDITION_ITEM: // item_id count
if (pPlayer->HasItemCount((*itr).Event.event_param3,(*itr).Event.event_param4))
bProcess = true;
break;
case CONDITION_ITEM_EQUIPPED: // item_id count
if (pPlayer->HasItemOrGemWithIdEquipped((*itr).Event.event_param3,(*itr).Event.event_param4))
bProcess = true;
break;
case CONDITION_ZONEID: // zone_id 0
if (pPlayer->GetZoneId() == (*itr).Event.event_param3)
bProcess = true;
break;
case CONDITION_REPUTATION_RANK: // faction_id min_rank
if (pPlayer->GetReputationRank((*itr).Event.event_param3) >= (*itr).Event.event_param4)
bProcess = true;
break;
case CONDITION_TEAM: // player_team 0, (469 - Alliance 67 - Horde)
if (pPlayer->GetTeam() == (*itr).Event.event_param3)
bProcess = true;
break;
case CONDITION_SKILL: // skill_id min skill_value
if (pPlayer->HasSkill((*itr).Event.event_param3) && pPlayer->GetSkillValue((*itr).Event.event_param3) >= (*itr).Event.event_param4)
bProcess = true;
break;
case CONDITION_QUESTREWARDED: // quest_id 0
if (pPlayer->GetQuestRewardStatus((*itr).Event.event_param3))
bProcess = true;
break;
case CONDITION_QUESTTAKEN: // quest_id 0, for condition true while quest active.
if (pPlayer->GetQuestStatus((*itr).Event.event_param3) == QUEST_STATUS_INCOMPLETE)
bProcess = true;
break;
case CONDITION_ACTIVE_EVENT: // event_id 0
if (IsHolidayActive(HolidayIds((*itr).Event.event_param3)))
bProcess = true;
break;
}
if (bProcess)
PlayerCondition pcon((*itr).Event.receive_emote.condition,(*itr).Event.receive_emote.conditionValue1,(*itr).Event.receive_emote.conditionValue2);
if (pcon.Meets(pPlayer))
{
sLog.outDebug("CreatureEventAI: ReceiveEmote CreatureEventAI: Condition ok, processing");
ProcessEvent(*itr, pPlayer);

View file

@ -44,7 +44,7 @@ enum EventAI_Type
EVENT_T_EVADE = 7, // NONE
EVENT_T_SPELLHIT = 8, // SpellID, School, RepeatMin, RepeatMax
EVENT_T_RANGE = 9, // MinDist, MaxDist, RepeatMin, RepeatMax
EVENT_T_OOC_LOS = 10, // NoHostile, NoFriendly, RepeatMin, RepeatMax
EVENT_T_OOC_LOS = 10, // NoHostile, MaxRnage, RepeatMin, RepeatMax
EVENT_T_SPAWNED = 11, // NONE
EVENT_T_TARGET_HP = 12, // HPMax%, HPMin%, RepeatMin, RepeatMax
EVENT_T_TARGET_CASTING = 13, // RepeatMin, RepeatMax
@ -376,23 +376,116 @@ struct CreatureEventAI_Event
union
{
uint32 event_param1;
int32 event_param1_s;
};
union
{
uint32 event_param2;
int32 event_param2_s;
};
union
{
uint32 event_param3;
int32 event_param3_s;
};
union
{
uint32 event_param4;
int32 event_param4_s;
// EVENT_T_TIMER = 0
// EVENT_T_TIMER_OOC = 1
struct
{
uint32 initialMin;
uint32 initialMax;
uint32 repeatMin;
uint32 repeatMax;
} timer;
// EVENT_T_HP = 2
// EVENT_T_MANA = 3
// EVENT_T_TARGET_HP = 12
// EVENT_T_TARGET_MANA = 18
struct
{
uint32 percentMax;
uint32 percentMin;
uint32 repeatMin;
uint32 repeatMax;
} percent_range;
// EVENT_T_KILL = 5
struct
{
uint32 repeatMin;
uint32 repeatMax;
} kill;
// EVENT_T_SPELLHIT = 8
struct
{
uint32 spellId;
uint32 schoolMask; // -1 (==0xffffffff) is ok value for full mask, or must be more limited mask like (0 < 1) = 1 for normal/physical school
uint32 repeatMin;
uint32 repeatMax;
} spell_hit;
// EVENT_T_RANGE = 9
struct
{
uint32 minDist;
uint32 maxDist;
uint32 repeatMin;
uint32 repeatMax;
} range;
// EVENT_T_OOC_LOS = 10
struct
{
uint32 noHostile;
uint32 maxRange;
uint32 repeatMin;
uint32 repeatMax;
} ooc_los;
// EVENT_T_TARGET_CASTING = 13
struct
{
uint32 repeatMin;
uint32 repeatMax;
} target_casting;
// EVENT_T_FRIENDLY_HP = 14
struct
{
uint32 hpDeficit;
uint32 radius;
uint32 repeatMin;
uint32 repeatMax;
} friendly_hp;
// EVENT_T_FRIENDLY_IS_CC = 15
struct
{
uint32 dispelType; // unused ?
uint32 radius;
uint32 repeatMin;
uint32 repeatMax;
} friendly_is_cc;
// EVENT_T_FRIENDLY_MISSING_BUFF = 16
struct
{
uint32 spellId;
uint32 radius;
uint32 repeatMin;
uint32 repeatMax;
} friendly_buff;
// EVENT_T_SUMMONED_UNIT = 17
struct
{
uint32 creatureId;
uint32 repeatMin;
uint32 repeatMax;
} summon_unit;
// EVENT_T_QUEST_ACCEPT = 19
// EVENT_T_QUEST_COMPLETE = 20
struct
{
uint32 questId;
} quest;
// EVENT_T_RECEIVE_EMOTE = 22
struct
{
uint32 emoteId;
uint32 condition;
uint32 conditionValue1;
uint32 conditionValue2;
} receive_emote;
// RAW
struct
{
uint32 param1;
uint32 param2;
uint32 param3;
uint32 param4;
} raw;
};
CreatureEventAI_Action action[MAX_ACTIONS];
@ -421,6 +514,9 @@ struct CreatureEventAIHolder
CreatureEventAI_Event Event;
uint32 Time;
bool Enabled;
// helper
bool UpdateRepeatTimer(Creature* creature, uint32 repeatMin, uint32 repeatMax);
};
class MANGOS_DLL_SPEC CreatureEventAI : public CreatureAI

View file

@ -205,10 +205,10 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Scripts()
temp.event_inverse_phase_mask = fields[3].GetUInt32();
temp.event_chance = fields[4].GetUInt8();
temp.event_flags = fields[5].GetUInt8();
temp.event_param1 = fields[6].GetUInt32();
temp.event_param2 = fields[7].GetUInt32();
temp.event_param3 = fields[8].GetUInt32();
temp.event_param4 = fields[9].GetUInt32();
temp.raw.param1 = fields[6].GetUInt32();
temp.raw.param2 = fields[7].GetUInt32();
temp.raw.param3 = fields[8].GetUInt32();
temp.raw.param4 = fields[9].GetUInt32();
//Creature does not exist in database
if (!sCreatureStorage.LookupEntry<CreatureInfo>(temp.creature_id))
@ -232,87 +232,97 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Scripts()
{
case EVENT_T_TIMER:
case EVENT_T_TIMER_OOC:
{
if (temp.event_param2 < temp.event_param1)
if (temp.timer.initialMax < temp.timer.initialMin)
sLog.outErrorDb("CreatureEventAI: Creature %u are using timed event(%u) with param2 < param1 (InitialMax < InitialMin). Event will never repeat.", temp.creature_id, i);
if (temp.event_param4 < temp.event_param3)
if (temp.timer.repeatMax < temp.timer.repeatMin)
sLog.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
break;
}
case EVENT_T_HP:
case EVENT_T_MANA:
case EVENT_T_TARGET_HP:
{
if (temp.event_param2 > 100)
case EVENT_T_TARGET_MANA:
if (temp.percent_range.percentMax > 100)
sLog.outErrorDb("CreatureEventAI: Creature %u are using percentage event(%u) with param2 (MinPercent) > 100. Event will never trigger! ", temp.creature_id, i);
if (temp.event_param1 <= temp.event_param2)
if (temp.percent_range.percentMax <= temp.percent_range.percentMin)
sLog.outErrorDb("CreatureEventAI: Creature %u are using percentage event(%u) with param1 <= param2 (MaxPercent <= MinPercent). Event will never trigger! ", temp.creature_id, i);
if (temp.event_flags & EFLAG_REPEATABLE && !temp.event_param3 && !temp.event_param4)
if (temp.event_flags & EFLAG_REPEATABLE && !temp.percent_range.repeatMin && !temp.percent_range.repeatMax)
{
sLog.outErrorDb("CreatureEventAI: Creature %u has param3 and param4=0 (RepeatMin/RepeatMax) but cannot be repeatable without timers. Removing EFLAG_REPEATABLE for event %u.", temp.creature_id, i);
temp.event_flags &= ~EFLAG_REPEATABLE;
}
break;
}
case EVENT_T_SPELLHIT:
{
if (temp.event_param1)
if (temp.spell_hit.spellId)
{
SpellEntry const* pSpell = sSpellStore.LookupEntry(temp.event_param1);
SpellEntry const* pSpell = sSpellStore.LookupEntry(temp.spell_hit.spellId);
if (!pSpell)
{
sLog.outErrorDb("CreatureEventAI: Creature %u has non-existant SpellID(%u) defined in event %u.", temp.creature_id, temp.event_param1, i);
sLog.outErrorDb("CreatureEventAI: Creature %u has non-existant SpellID(%u) defined in event %u.", temp.creature_id, temp.spell_hit.spellId, i);
continue;
}
if (temp.event_param2_s != -1 && temp.event_param2 != pSpell->SchoolMask)
sLog.outErrorDb("CreatureEventAI: Creature %u has param1(spellId %u) but param2 is not -1 and not equal to spell's school mask. Event %u can never trigger.", temp.creature_id, temp.event_param1, i);
if ((temp.spell_hit.schoolMask & pSpell->SchoolMask) != pSpell->SchoolMask)
sLog.outErrorDb("CreatureEventAI: Creature %u has param1(spellId %u) but param2 is not -1 and not equal to spell's school mask. Event %u can never trigger.", temp.creature_id, temp.spell_hit.schoolMask, i);
}
//TODO: fix this system with SPELL_SCHOOL_MASK. Current complicate things, using int32(-1) instead of just 0
//SPELL_SCHOOL_MASK_NONE = 0 and does not exist, thus it can not ever trigger or be used in SpellHit()
if (temp.event_param2_s != -1 && temp.event_param2_s > SPELL_SCHOOL_MASK_ALL)
sLog.outErrorDb("CreatureEventAI: Creature %u is using invalid SpellSchoolMask(%u) defined in event %u.", temp.creature_id, temp.event_param2, i);
if (!temp.spell_hit.schoolMask)
sLog.outErrorDb("CreatureEventAI: Creature %u is using invalid SpellSchoolMask(%u) defined in event %u.", temp.creature_id, temp.spell_hit.schoolMask, i);
if (temp.event_param4 < temp.event_param3)
if (temp.spell_hit.repeatMax < temp.spell_hit.repeatMin)
sLog.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
break;
}
case EVENT_T_RANGE:
if (temp.range.maxDist < temp.range.minDist)
sLog.outErrorDb("CreatureEventAI: Creature %u are using event(%u) with param2 < param1 (MaxDist < MinDist). Event will never repeat.", temp.creature_id, i);
if (temp.range.repeatMax < temp.range.repeatMin)
sLog.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
break;
case EVENT_T_OOC_LOS:
if (temp.ooc_los.repeatMax < temp.ooc_los.repeatMin)
sLog.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
break;
case EVENT_T_FRIENDLY_HP:
if (temp.friendly_hp.repeatMax < temp.friendly_hp.repeatMin)
sLog.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
break;
case EVENT_T_FRIENDLY_IS_CC:
if (temp.friendly_is_cc.repeatMax < temp.friendly_is_cc.repeatMin)
sLog.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
break;
case EVENT_T_FRIENDLY_MISSING_BUFF:
{
//Disabled check for now. Check code related to events and adjust accordingly before enable.
//Events should have min/max or alternative set to a static value.
/*if (!temp.event_param3 && !temp.event_param4)
SpellEntry const* pSpell = sSpellStore.LookupEntry(temp.spell_hit.spellId);
if (!pSpell)
{
sLog.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) without param3/param4 (RepeatMin/RepeatMax). Using minimum values.", temp.creature_id, i);
temp.event_param3 = 2500;
temp.event_param4 = 2500;
}*/
if (temp.event_param4 < temp.event_param3)
sLog.outErrorDb("CreatureEventAI: Creature %u has non-existant SpellID(%u) defined in event %u.", temp.creature_id, temp.spell_hit.spellId, i);
continue;
}
if (temp.friendly_buff.repeatMax < temp.friendly_buff.repeatMin)
sLog.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
break;
}
case EVENT_T_KILL:
case EVENT_T_TARGET_CASTING:
{
if (temp.event_param2 < temp.event_param1)
if (temp.kill.repeatMax < temp.kill.repeatMin)
sLog.outErrorDb("CreatureEventAI: Creature %u are using event(%u) with param2 < param1 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
break;
}
case EVENT_T_TARGET_CASTING:
if (temp.target_casting.repeatMax < temp.target_casting.repeatMin)
sLog.outErrorDb("CreatureEventAI: Creature %u are using event(%u) with param2 < param1 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
break;
case EVENT_T_SUMMONED_UNIT:
if (!sCreatureStorage.LookupEntry<CreatureInfo>(temp.summon_unit.creatureId))
sLog.outErrorDb("CreatureEventAI: Creature %u are using event(%u) with not existed creature template id (%u) in param1, skipped.", temp.creature_id, i, temp.summon_unit.creatureId);
if (temp.summon_unit.repeatMax < temp.summon_unit.repeatMin)
sLog.outErrorDb("CreatureEventAI: Creature %u are using event(%u) with param2 < param1 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
break;
case EVENT_T_QUEST_ACCEPT:
case EVENT_T_QUEST_COMPLETE:
if (!objmgr.GetQuestTemplate(temp.quest.questId))
sLog.outErrorDb("CreatureEventAI: Creature %u are using event(%u) with not existed qyest id (%u) in param1, skipped.", temp.creature_id, i, temp.quest.questId);
sLog.outErrorDb("CreatureEventAI: Creature %u using not implemented event (%u) in event %u.", temp.creature_id, temp.event_id, i);
continue;
case EVENT_T_AGGRO:
case EVENT_T_DEATH:
@ -331,21 +341,15 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Scripts()
case EVENT_T_RECEIVE_EMOTE:
{
if (!sEmotesTextStore.LookupEntry(temp.event_param1))
if (!sEmotesTextStore.LookupEntry(temp.receive_emote.emoteId))
{
sLog.outErrorDb("CreatureEventAI: Creature %u using event %u: param1 (EmoteTextId: %u) are not valid.",temp.creature_id, i, temp.event_param1);
sLog.outErrorDb("CreatureEventAI: Creature %u using event %u: param1 (EmoteTextId: %u) are not valid.",temp.creature_id, i, temp.receive_emote.emoteId);
continue;
}
if (temp.event_param2 == CONDITION_AD_COMMISSION_AURA || temp.event_param2 == CONDITION_NO_AURA)
if (!PlayerCondition::IsValid(ConditionType(temp.receive_emote.condition), temp.receive_emote.conditionValue1, temp.receive_emote.conditionValue2))
{
sLog.outErrorDb("CreatureEventAI: Creature %u using event %u: param2 (Condition: %u) are not implemented for EventAI.",temp.creature_id, i, temp.event_param2);
continue;
}
if (!PlayerCondition::IsValid(ConditionType(temp.event_param2), temp.event_param3, temp.event_param4))
{
sLog.outErrorDb("CreatureEventAI: Creature %u using event %u: param2 (Condition: %u) are not valid.",temp.creature_id, i, temp.event_param2);
sLog.outErrorDb("CreatureEventAI: Creature %u using event %u: param2 (Condition: %u) are not valid.",temp.creature_id, i, temp.receive_emote.condition);
continue;
}
@ -358,21 +362,6 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Scripts()
break;
}
case EVENT_T_SUMMONED_UNIT:
{
if (!sCreatureStorage.LookupEntry<CreatureInfo>(temp.event_param1))
sLog.outErrorDb("CreatureEventAI: Creature %u are using event(%u) with not existed creature template id (%u) in param1, skipped.", temp.creature_id, i, temp.event_param1);
if (temp.event_param3 < temp.event_param2)
sLog.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param3 < param2 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
break;
}
case EVENT_T_QUEST_ACCEPT:
case EVENT_T_QUEST_COMPLETE:
sLog.outErrorDb("CreatureEventAI: Creature %u using not implemented event (%u) in event %u.", temp.creature_id, temp.event_id, i);
continue;
default:
sLog.outErrorDb("CreatureEventAI: Creature %u using not checked at load event (%u) in event %u. Need check code update?", temp.creature_id, temp.event_id, i);
break;
@ -486,6 +475,7 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Scripts()
const SpellEntry *spell = sSpellStore.LookupEntry(action.cast.spellId);
if (!spell)
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent SpellID %u.", i, j+1, action.cast.spellId);
/* FIXME: temp.raw.param3 not have event tipes with recovery time in it....
else
{
if (spell->RecoveryTime > 0 && temp.event_flags & EFLAG_REPEATABLE)
@ -495,6 +485,7 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Scripts()
sLog.outDebug("CreatureEventAI: Event %u Action %u uses SpellID %u but cooldown is longer(%u) than minumum defined in event param3(%u).", i, j+1,action.cast.spellId, spell->RecoveryTime, temp.event_param3);
}
}
*/
//Cast is always triggered if target is forced to cast on self
if (action.cast.castFlags & CAST_FORCE_TARGET_SELF)

View file

@ -1,4 +1,4 @@
#ifndef __REVISION_NR_H__
#define __REVISION_NR_H__
#define REVISION_NR "7851"
#define REVISION_NR "7852"
#endif // __REVISION_NR_H__