[7847] Replace paramter unions by uniton of event ai action type structures. Add/fix some loading checks.

* Also send to process function reference to strucutre instead large amount fields.
* Specially note that fixed check for first arg of action type 27 that before wrongly test as quest id,
when by using code expected creature id. Possible some used data wrongly can be quest ids....
This commit is contained in:
VladimirMangos 2009-05-18 22:39:35 +04:00
parent 2d4edd18f6
commit 07d00c3927
4 changed files with 537 additions and 427 deletions

View file

@ -461,37 +461,35 @@ bool CreatureEventAI::ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pAction
//Process actions
for (uint32 j = 0; j < MAX_ACTIONS; j++)
ProcessAction(pHolder.Event.action[j].type, pHolder.Event.action[j].param1, pHolder.Event.action[j].param2, pHolder.Event.action[j].param3, rnd, pHolder.Event.event_id, pActionInvoker);
ProcessAction(pHolder.Event.action[j], rnd, pHolder.Event.event_id, pActionInvoker);
return true;
}
void CreatureEventAI::ProcessAction(uint16 type, uint32 param1, uint32 param2, uint32 param3, uint32 rnd, uint32 EventId, Unit* pActionInvoker)
void CreatureEventAI::ProcessAction(CreatureEventAI_Action const& action, uint32 rnd, uint32 EventId, Unit* pActionInvoker)
{
switch (type)
switch (action.type)
{
case ACTION_T_TEXT:
{
if (!param1)
if (!action.text.TextId1)
return;
uint32 temp = 0;
int32 temp = 0;
if (param2 && param3)
if (action.text.TextId2 && action.text.TextId3)
{
switch( rand()%3 )
{
case 0: temp = param1; break;
case 2: temp = param2; break;
case 3: temp = param3; break;
case 0: temp = action.text.TextId1; break;
case 2: temp = action.text.TextId2; break;
case 3: temp = action.text.TextId3; break;
}
}else if ( param2 && urand(0,1) )
{
temp = param2;
}else
{
temp = param1;
}
else if (action.text.TextId2 && urand(0,1))
temp = action.text.TextId2;
else
temp = action.text.TextId1;
if (temp)
{
@ -517,12 +515,12 @@ void CreatureEventAI::ProcessAction(uint16 type, uint32 param1, uint32 param2, u
DoScriptText(temp, m_creature, target);
}
break;
}
break;
case ACTION_T_SET_FACTION:
{
if (param1)
m_creature->setFaction(param1);
if (action.set_faction.factionId)
m_creature->setFaction(action.set_faction.factionId);
else
{
if (CreatureInfo const* ci = GetCreatureTemplateStore(m_creature->GetEntry()))
@ -532,16 +530,16 @@ void CreatureEventAI::ProcessAction(uint16 type, uint32 param1, uint32 param2, u
m_creature->setFaction(ci->faction_A);
}
}
break;
}
break;
case ACTION_T_MORPH_TO_ENTRY_OR_MODEL:
{
if (param1 || param2)
if (action.morph.creatireId || action.morph.modelId)
{
//set model based on entry from creature_template
if (param1)
if (action.morph.creatireId)
{
if (CreatureInfo const* ci = GetCreatureTemplateStore(param1))
if (CreatureInfo const* ci = GetCreatureTemplateStore(action.morph.creatireId))
{
//use default display
if (ci->DisplayID_A)
@ -550,73 +548,67 @@ void CreatureEventAI::ProcessAction(uint16 type, uint32 param1, uint32 param2, u
}
//if no param1, then use value from param2 (modelId)
else
m_creature->SetDisplayId(param2);
m_creature->SetDisplayId(action.morph.modelId);
}
else
m_creature->DeMorph();
break;
}
break;
case ACTION_T_SOUND:
m_creature->PlayDirectSound(param1);
m_creature->PlayDirectSound(action.sound.soundId);
break;
case ACTION_T_EMOTE:
m_creature->HandleEmoteCommand(param1);
m_creature->HandleEmoteCommand(action.emote.emoteId);
break;
case ACTION_T_RANDOM_SOUND:
{
uint32 temp = GetRandActionParam(rnd, param1, param2, param3);
if (temp != uint32(0xffffffff))
m_creature->PlayDirectSound( temp );
int32 temp = GetRandActionParam(rnd, action.random_sound.soundId1, action.random_sound.soundId2, action.random_sound.soundId3);
if (temp >= 0)
m_creature->PlayDirectSound(temp);
break;
}
break;
case ACTION_T_RANDOM_EMOTE:
{
uint32 temp = GetRandActionParam(rnd, param1, param2, param3);
if (temp != uint32(0xffffffff))
int32 temp = GetRandActionParam(rnd, action.random_emote.emoteId1, action.random_emote.emoteId2, action.random_emote.emoteId3);
if (temp >= 0)
m_creature->HandleEmoteCommand(temp);
break;
}
break;
case ACTION_T_CAST:
{
Unit* target = GetTargetByType(param2, pActionInvoker);
Unit* target = GetTargetByType(action.cast.target, pActionInvoker);
Unit* caster = m_creature;
if (!target)
return;
//Cast is always triggered if target is forced to cast on self
if (param3 & CAST_FORCE_TARGET_SELF)
{
param3 |= CAST_TRIGGERED;
if (action.cast.castFlags & CAST_FORCE_TARGET_SELF)
caster = target;
}
//Allowed to cast only if not casting (unless we interrupt ourself) or if spell is triggered
bool canCast = !caster->IsNonMeleeSpellCasted(false) || (param3 & (CAST_TRIGGERED | CAST_INTURRUPT_PREVIOUS));
bool canCast = !caster->IsNonMeleeSpellCasted(false) || (action.cast.castFlags & (CAST_TRIGGERED | CAST_INTURRUPT_PREVIOUS));
// If cast flag CAST_AURA_NOT_PRESENT is active, check if target already has aura on them
if(param3 & CAST_AURA_NOT_PRESENT)
if(action.cast.castFlags & CAST_AURA_NOT_PRESENT)
{
for(uint8 i = 0; i < 3; ++i)
if(target->HasAura(param1, i))
if(target->HasAura(action.cast.spellId, i))
return;
}
if (canCast)
{
const SpellEntry* tSpell = GetSpellStore()->LookupEntry(param1);
const SpellEntry* tSpell = GetSpellStore()->LookupEntry(action.cast.spellId);
//Verify that spell exists
if (tSpell)
{
//Check if cannot cast spell
if (!(param3 & (CAST_FORCE_TARGET_SELF | CAST_FORCE_CAST)) &&
!CanCast(target, tSpell, (param3 & CAST_TRIGGERED)))
if (!(action.cast.castFlags & (CAST_FORCE_TARGET_SELF | CAST_FORCE_CAST)) &&
!CanCast(target, tSpell, (action.cast.castFlags & CAST_TRIGGERED)))
{
//Melee current victim if flag not set
if (!(param3 & CAST_NO_MELEE_IF_OOM))
if (!(action.cast.castFlags & CAST_NO_MELEE_IF_OOM))
{
if (m_creature->GetMotionMaster()->GetCurrentMovementGeneratorType() == TARGETED_MOTION_TYPE)
{
@ -632,111 +624,83 @@ void CreatureEventAI::ProcessAction(uint16 type, uint32 param1, uint32 param2, u
else
{
//Interrupt any previous spell
if (caster->IsNonMeleeSpellCasted(false) && param3 & CAST_INTURRUPT_PREVIOUS)
if (caster->IsNonMeleeSpellCasted(false) && action.cast.castFlags & CAST_INTURRUPT_PREVIOUS)
caster->InterruptNonMeleeSpells(false);
caster->CastSpell(target, param1, (param3 & CAST_TRIGGERED));
caster->CastSpell(target, action.cast.spellId, (action.cast.castFlags & CAST_TRIGGERED));
}
}else
sLog.outErrorDb("CreatureEventAI: event %d creature %d attempt to cast spell that doesn't exist %d", EventId, m_creature->GetEntry(), param1);
}
else
sLog.outErrorDb("CreatureEventAI: event %d creature %d attempt to cast spell that doesn't exist %d", EventId, m_creature->GetEntry(), action.cast.spellId);
}
break;
}
break;
case ACTION_T_SUMMON:
{
Unit* target = GetTargetByType(param2, pActionInvoker);
Unit* target = GetTargetByType(action.summon.target, pActionInvoker);
Creature* pCreature = NULL;
if (param3)
pCreature = m_creature->SummonCreature(param1, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, param3);
if (action.summon.duration)
pCreature = m_creature->SummonCreature(action.summon.creatured, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, action.summon.duration);
else
pCreature = m_creature->SummonCreature(param1, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 0);
pCreature = m_creature->SummonCreature(action.summon.creatured, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 0);
if (!pCreature)
{
sLog.outErrorDb( "CreatureEventAI: failed to spawn creature %u. Spawn event %d is on creature %d", param1, EventId, m_creature->GetEntry());
}
else if (param2 != TARGET_T_SELF && target)
sLog.outErrorDb( "CreatureEventAI: failed to spawn creature %u. Spawn event %d is on creature %d", action.summon.creatured, EventId, m_creature->GetEntry());
else if (action.summon.target != TARGET_T_SELF && target)
pCreature->AI()->AttackStart(target);
break;
}
break;
case ACTION_T_THREAT_SINGLE_PCT:
{
Unit* target = GetTargetByType(param2, pActionInvoker);
if (target)
m_creature->getThreatManager().modifyThreatPercent(target, param1);
}
break;
if (Unit* target = GetTargetByType(action.threat_single_pct.target, pActionInvoker))
m_creature->getThreatManager().modifyThreatPercent(target, action.threat_single_pct.percent);
break;
case ACTION_T_THREAT_ALL_PCT:
{
Unit* Temp = NULL;
std::list<HostilReference*>::iterator i = m_creature->getThreatManager().getThreatList().begin();
for (; i != m_creature->getThreatManager().getThreatList().end(); ++i)
{
Temp = Unit::GetUnit(*m_creature,(*i)->getUnitGuid());
if (Temp)
m_creature->getThreatManager().modifyThreatPercent(Temp, param1);
}
std::list<HostilReference*>& threatList = m_creature->getThreatManager().getThreatList();
for (std::list<HostilReference*>::iterator i = threatList.begin(); i != threatList.end(); ++i)
if(Unit* Temp = Unit::GetUnit(*m_creature,(*i)->getUnitGuid()))
m_creature->getThreatManager().modifyThreatPercent(Temp, action.threat_all_pct.percent);
break;
}
break;
case ACTION_T_QUEST_EVENT:
{
Unit* target = GetTargetByType(param2, pActionInvoker);
if (target && target->GetTypeId() == TYPEID_PLAYER)
((Player*)target)->AreaExploredOrEventHappens(param1);
}
break;
case ACTION_T_CASTCREATUREGO:
{
Unit* target = GetTargetByType(param3, pActionInvoker);
if (target && target->GetTypeId() == TYPEID_PLAYER)
((Player*)target)->CastedCreatureOrGO(param1, m_creature->GetGUID(), param2);
}
break;
if (Unit* target = GetTargetByType(action.quest_event.target, pActionInvoker))
if (target->GetTypeId() == TYPEID_PLAYER)
((Player*)target)->AreaExploredOrEventHappens(action.quest_event.questId);
break;
case ACTION_T_CAST_EVENT:
if (Unit* target = GetTargetByType(action.cast_event.target, pActionInvoker))
if (target->GetTypeId() == TYPEID_PLAYER)
((Player*)target)->CastedCreatureOrGO(action.cast_event.creatureId, m_creature->GetGUID(), action.cast_event.spellId);
break;
case ACTION_T_SET_UNIT_FIELD:
{
Unit* target = GetTargetByType(param3, pActionInvoker);
Unit* target = GetTargetByType(action.set_unit_field.target, pActionInvoker);
if (param1 < OBJECT_END || param1 >= UNIT_END)
// not allow modify important for integrity object fields
if (action.set_unit_field.field < OBJECT_END || action.set_unit_field.field >= UNIT_END)
return;
if (target)
target->SetUInt32Value(param1, param2);
target->SetUInt32Value(action.set_unit_field.field, action.set_unit_field.value);
break;
}
break;
case ACTION_T_SET_UNIT_FLAG:
{
Unit* target = GetTargetByType(param2, pActionInvoker);
if (target)
target->SetFlag(UNIT_FIELD_FLAGS, param1);
}
break;
if (Unit* target = GetTargetByType(action.unit_flag.target, pActionInvoker))
target->SetFlag(UNIT_FIELD_FLAGS, action.unit_flag.value);
break;
case ACTION_T_REMOVE_UNIT_FLAG:
{
Unit* target = GetTargetByType(param2, pActionInvoker);
if (target)
target->RemoveFlag(UNIT_FIELD_FLAGS, param1);
}
break;
if (Unit* target = GetTargetByType(action.unit_flag.target, pActionInvoker))
target->RemoveFlag(UNIT_FIELD_FLAGS, action.unit_flag.value);
break;
case ACTION_T_AUTO_ATTACK:
{
if (param1)
MeleeEnabled = true;
else MeleeEnabled = false;
}
break;
MeleeEnabled = action.auto_attack.state != 0;
break;
case ACTION_T_COMBAT_MOVEMENT:
{
CombatMovementEnabled = param1;
CombatMovementEnabled = action.combat_movement.state != 0;
//Allow movement (create new targeted movement gen only if idle)
if (CombatMovementEnabled)
@ -754,69 +718,59 @@ void CreatureEventAI::ProcessAction(uint16 type, uint32 param1, uint32 param2, u
m_creature->GetMotionMaster()->MoveIdle();
m_creature->StopMoving();
}
}
break;
break;
case ACTION_T_SET_PHASE:
{
Phase = param1;
}
break;
Phase = action.set_phase.phase;
break;
case ACTION_T_INC_PHASE:
{
Phase += param1;
int32 new_phase = int32(Phase)+action.set_inc_phase.step;
if (new_phase < 0)
{
sLog.outErrorDb( "CreatureEventAI: Event %d decrease Phase under 0. CreatureEntry = %d", EventId, m_creature->GetEntry());
Phase = 0;
}
else if (new_phase >= MAX_PHASE)
{
sLog.outErrorDb( "CreatureEventAI: Event %d incremented Phase above %u. Phase mask cannot be used with phases past %u. CreatureEntry = %d", EventId, MAX_PHASE-1, MAX_PHASE-1, m_creature->GetEntry());
Phase = MAX_PHASE-1;
}
else
Phase = new_phase;
if (Phase > 31)
sLog.outErrorDb( "CreatureEventAI: Event %d incremented Phase above 31. Phase mask cannot be used with phases past 31. CreatureEntry = %d", EventId, m_creature->GetEntry());
break;
}
break;
case ACTION_T_EVADE:
{
EnterEvadeMode();
}
break;
break;
case ACTION_T_FLEE:
{
//TODO: Replace with Flee movement generator
m_creature->CastSpell(m_creature, SPELL_RUN_AWAY, true);
}
break;
break;
case ACTION_T_QUEST_EVENT_ALL:
{
Unit* Temp = NULL;
if( pActionInvoker && pActionInvoker->GetTypeId() == TYPEID_PLAYER )
if (pActionInvoker && pActionInvoker->GetTypeId() == TYPEID_PLAYER)
{
Temp = Unit::GetUnit(*m_creature,pActionInvoker->GetGUID());
if( Temp )
((Player*)Temp)->GroupEventHappens(param1,m_creature);
if (Unit* Temp = Unit::GetUnit(*m_creature,pActionInvoker->GetGUID()))
if (Temp->GetTypeId() == TYPEID_PLAYER)
((Player*)Temp)->GroupEventHappens(action.quest_event_all.questId,m_creature);
}
}
break;
case ACTION_T_CASTCREATUREGO_ALL:
break;
case ACTION_T_CAST_EVENT_ALL:
{
Unit* Temp = NULL;
std::list<HostilReference*>::iterator i = m_creature->getThreatManager().getThreatList().begin();
for (; i != m_creature->getThreatManager().getThreatList().end(); ++i)
{
Temp = Unit::GetUnit(*m_creature,(*i)->getUnitGuid());
if (Temp && Temp->GetTypeId() == TYPEID_PLAYER)
((Player*)Temp)->CastedCreatureOrGO(param1, m_creature->GetGUID(), param2);
}
std::list<HostilReference*>& threatList = m_creature->getThreatManager().getThreatList();
for (std::list<HostilReference*>::iterator i = threatList.begin(); i != threatList.end(); ++i)
if (Unit* Temp = Unit::GetUnit(*m_creature,(*i)->getUnitGuid()))
if (Temp->GetTypeId() == TYPEID_PLAYER)
((Player*)Temp)->CastedCreatureOrGO(action.cast_event_all.creatureId, m_creature->GetGUID(), action.cast_event_all.spellId);
break;
}
break;
case ACTION_T_REMOVEAURASFROMSPELL:
{
Unit* target = GetTargetByType(param1, pActionInvoker);
if (target)
target->RemoveAurasDueToSpell(param2);
}
break;
if (Unit* target = GetTargetByType(action.remove_aura.target, pActionInvoker))
target->RemoveAurasDueToSpell(action.remove_aura.spellId);
break;
case ACTION_T_RANGED_MOVEMENT:
{
AttackDistance = param1;
AttackAngle = ((float)param2/180)*M_PI;
AttackDistance = action.ranged_movement.distance;
AttackAngle = ((float)action.ranged_movement.angle/180)*M_PI;
if (CombatMovementEnabled)
{
@ -827,69 +781,52 @@ void CreatureEventAI::ProcessAction(uint16 type, uint32 param1, uint32 param2, u
m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim(), AttackDistance, AttackAngle);
}
}
}
break;
break;
case ACTION_T_RANDOM_PHASE:
{
uint32 temp = GetRandActionParam(rnd, param1, param2, param3);
Phase = temp;
}
break;
Phase = GetRandActionParam(rnd, action.random_phase.phase1, action.random_phase.phase2, action.random_phase.phase3);
break;
case ACTION_T_RANDOM_PHASE_RANGE:
{
if (param2 > param1)
{
Phase = param1 + (rnd % (param2 - param1));
}
if (action.random_phase_range.phaseMax > action.random_phase_range.phaseMin)
Phase = action.random_phase_range.phaseMin + (rnd % (action.random_phase_range.phaseMax - action.random_phase_range.phaseMin));
else
sLog.outErrorDb( "CreatureEventAI: ACTION_T_RANDOM_PHASE_RANGE cannot have Param2 <= Param1. Divide by Zero. Event = %d. CreatureEntry = %d", EventId, m_creature->GetEntry());
}
break;
break;
case ACTION_T_SUMMON_ID:
{
Unit* target = GetTargetByType(param2, pActionInvoker);
Unit* target = GetTargetByType(action.summon_id.target, pActionInvoker);
//Duration
Creature* pCreature = NULL;
CreatureEventAI_Summon_Map::const_iterator i = CreatureEAI_Mgr.GetCreatureEventAISummonMap().find(param3);
CreatureEventAI_Summon_Map::const_iterator i = CreatureEAI_Mgr.GetCreatureEventAISummonMap().find(action.summon_id.spawnId);
if (i == CreatureEAI_Mgr.GetCreatureEventAISummonMap().end())
{
sLog.outErrorDb( "CreatureEventAI: failed to spawn creature %u. Summon map index %u does not exist. EventID %d. CreatureID %d", param1, param3, EventId, m_creature->GetEntry());
sLog.outErrorDb( "CreatureEventAI: failed to spawn creature %u. Summon map index %u does not exist. EventID %d. CreatureID %d", action.summon_id.creatureId, action.summon_id.spawnId, EventId, m_creature->GetEntry());
return;
}
Creature* pCreature = NULL;
if ((*i).second.SpawnTimeSecs)
pCreature = m_creature->SummonCreature(param1, (*i).second.position_x, (*i).second.position_y, (*i).second.position_z, (*i).second.orientation, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, (*i).second.SpawnTimeSecs);
else pCreature = m_creature->SummonCreature(param1, (*i).second.position_x, (*i).second.position_y, (*i).second.position_z, (*i).second.orientation, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 0);
pCreature = m_creature->SummonCreature(action.summon_id.creatureId, (*i).second.position_x, (*i).second.position_y, (*i).second.position_z, (*i).second.orientation, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, (*i).second.SpawnTimeSecs);
else
pCreature = m_creature->SummonCreature(action.summon_id.creatureId, (*i).second.position_x, (*i).second.position_y, (*i).second.position_z, (*i).second.orientation, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 0);
if (!pCreature)
{
sLog.outErrorDb( "CreatureEventAI: failed to spawn creature %u. EventId %d.Creature %d", param1, EventId, m_creature->GetEntry());
}
else if (param2 != TARGET_T_SELF && target)
sLog.outErrorDb( "CreatureEventAI: failed to spawn creature %u. EventId %d.Creature %d", action.summon_id.creatureId, EventId, m_creature->GetEntry());
else if (action.summon_id.target != TARGET_T_SELF && target)
pCreature->AI()->AttackStart(target);
break;
}
break;
case ACTION_T_KILLED_MONSTER:
{
//first attempt player who tapped creature
if (Player* pPlayer = m_creature->GetLootRecipient())
pPlayer->RewardPlayerAndGroupAtEvent(param1, m_creature);
pPlayer->RewardPlayerAndGroupAtEvent(action.killed_monster.creatureId, m_creature);
else
{
//if not available, use pActionInvoker
if (Unit* pTarget = GetTargetByType(param2, pActionInvoker))
{
if (Unit* pTarget = GetTargetByType(action.killed_monster.target, pActionInvoker))
if (Player* pPlayer2 = pTarget->GetCharmerOrOwnerPlayerOrPlayerItself())
pPlayer2->RewardPlayerAndGroupAtEvent(param1, m_creature);
}
pPlayer2->RewardPlayerAndGroupAtEvent(action.killed_monster.creatureId, m_creature);
}
}
break;
break;
case ACTION_T_SET_INST_DATA:
{
InstanceData* pInst = (InstanceData*)m_creature->GetInstanceData();
@ -899,12 +836,12 @@ void CreatureEventAI::ProcessAction(uint16 type, uint32 param1, uint32 param2, u
return;
}
pInst->SetData(param1, param2);
pInst->SetData(action.set_inst_data.field, action.set_inst_data.value);
break;
}
break;
case ACTION_T_SET_INST_DATA64:
{
Unit* target = GetTargetByType(param2, pActionInvoker);
Unit* target = GetTargetByType(action.set_inst_data64.target, pActionInvoker);
if (!target)
{
sLog.outErrorDb("CreatureEventAI: Event %d attempt to set instance data64 but Target == NULL. Creature %d", EventId, m_creature->GetEntry());
@ -918,19 +855,19 @@ void CreatureEventAI::ProcessAction(uint16 type, uint32 param1, uint32 param2, u
return;
}
pInst->SetData64(param1, target->GetGUID());
pInst->SetData64(action.set_inst_data64.field, target->GetGUID());
break;
}
break;
case ACTION_T_UPDATE_TEMPLATE:
{
if (m_creature->GetEntry() == param1)
if (m_creature->GetEntry() == action.update_template.creatureId)
{
sLog.outErrorDb("CreatureEventAI: Event %d ACTION_T_UPDATE_TEMPLATE call with param1 == current entry. Creature %d", EventId, m_creature->GetEntry());
return;
}
m_creature->UpdateEntry(param1, param2 ? HORDE : ALLIANCE);
m_creature->UpdateEntry(action.update_template.creatureId, action.update_template.team ? HORDE : ALLIANCE);
}
break;
case ACTION_T_DIE:
@ -1342,15 +1279,20 @@ inline uint32 CreatureEventAI::GetRandActionParam(uint32 rnd, uint32 param1, uin
{
switch (rnd % 3)
{
case 0:
return param1;
break;
case 1:
return param2;
break;
case 2:
return param3;
break;
case 0: return param1;
case 1: return param2;
case 2: return param3;
}
return 0;
}
inline int32 CreatureEventAI::GetRandActionParam(uint32 rnd, int32 param1, int32 param2, int32 param3)
{
switch (rnd % 3)
{
case 0: return param1;
case 1: return param2;
case 2: return param3;
}
return 0;
}
@ -1361,28 +1303,20 @@ inline Unit* CreatureEventAI::GetTargetByType(uint32 Target, Unit* pActionInvoke
{
case TARGET_T_SELF:
return m_creature;
break;
case TARGET_T_HOSTILE:
return m_creature->getVictim();
break;
case TARGET_T_HOSTILE_SECOND_AGGRO:
return SelectUnit(ATTACKING_TARGET_TOPAGGRO,1);
break;
case TARGET_T_HOSTILE_LAST_AGGRO:
return SelectUnit(ATTACKING_TARGET_BOTTOMAGGRO,0);
break;
case TARGET_T_HOSTILE_RANDOM:
return SelectUnit(ATTACKING_TARGET_RANDOM,0);
break;
case TARGET_T_HOSTILE_RANDOM_NOT_TOP:
return SelectUnit(ATTACKING_TARGET_RANDOM,1);
break;
case TARGET_T_ACTION_INVOKER:
return pActionInvoker;
break;
default:
return NULL;
break;
};
}

View file

@ -30,6 +30,7 @@ class WorldObject;
#define EVENT_UPDATE_TIME 500
#define SPELL_RUN_AWAY 8225
#define MAX_ACTIONS 3
#define MAX_PHASE 32
enum EventAI_Type
{
@ -78,7 +79,7 @@ enum EventAI_ActionType
ACTION_T_THREAT_SINGLE_PCT = 13, //*Threat%, Target
ACTION_T_THREAT_ALL_PCT = 14, //Threat%
ACTION_T_QUEST_EVENT = 15, //*QuestID, Target
ACTION_T_CASTCREATUREGO = 16, //*QuestID, SpellId, Target
ACTION_T_CAST_EVENT = 16, //*QuestID, SpellId, Target - must be removed as hack?
ACTION_T_SET_UNIT_FIELD = 17, //*Field_Number, Value, Target
ACTION_T_SET_UNIT_FLAG = 18, //*Flags (may be more than one field OR'd together), Target
ACTION_T_REMOVE_UNIT_FLAG = 19, //*Flags (may be more than one field OR'd together), Target
@ -89,7 +90,7 @@ enum EventAI_ActionType
ACTION_T_EVADE = 24, //No Params
ACTION_T_FLEE = 25, //No Params
ACTION_T_QUEST_EVENT_ALL = 26, //*QuestID
ACTION_T_CASTCREATUREGO_ALL = 27, //*QuestId, SpellId
ACTION_T_CAST_EVENT_ALL = 27, //*QuestId, SpellId
ACTION_T_REMOVEAURASFROMSPELL = 28, //*Target, Spellid
ACTION_T_RANGED_MOVEMENT = 29, //Distance, Angle
ACTION_T_RANDOM_PHASE = 30, //PhaseId1, PhaseId2, PhaseId3
@ -165,6 +166,202 @@ struct StringTextData
// Text Maps
typedef UNORDERED_MAP<int32, StringTextData> CreatureEventAI_TextMap;
struct CreatureEventAI_Action
{
EventAI_ActionType type: 16;
union
{
// ACTION_T_TEXT = 1
struct
{
int32 TextId1;
int32 TextId2;
int32 TextId3;
} text;
// ACTION_T_SET_FACTION = 2
struct
{
uint32 factionId; // faction or 0 for default)
} set_faction;
// ACTION_T_MORPH_TO_ENTRY_OR_MODEL = 3
struct
{
uint32 creatireId; // set one from fields (or 0 for both to demorph)
uint32 modelId;
} morph;
// ACTION_T_SOUND = 4
struct
{
uint32 soundId;
} sound;
// ACTION_T_EMOTE = 5
struct
{
uint32 emoteId;
} emote;
// ACTION_T_RANDOM_SOUND = 9
struct
{
int32 soundId1; // (-1 in any field means no output if randomed that field)
int32 soundId2;
int32 soundId3;
} random_sound;
// ACTION_T_RANDOM_EMOTE = 10
struct
{
int32 emoteId1; // (-1 in any field means no output if randomed that field)
int32 emoteId2;
int32 emoteId3;
} random_emote;
// ACTION_T_CAST = 11
struct
{
uint32 spellId;
uint32 target;
uint32 castFlags;
} cast;
// ACTION_T_SUMMON = 12
struct
{
uint32 creatured;
uint32 target;
uint32 duration;
} summon;
// ACTION_T_THREAT_SINGLE_PCT = 13
struct
{
int32 percent;
uint32 target;
} threat_single_pct;
// ACTION_T_THREAT_ALL_PCT = 14
struct
{
int32 percent;
} threat_all_pct;
// ACTION_T_QUEST_EVENT = 15
struct
{
uint32 questId;
uint32 target;
} quest_event;
// ACTION_T_CAST_EVENT = 16
struct
{
uint32 creatureId;
uint32 spellId;
uint32 target;
} cast_event;
// ACTION_T_SET_UNIT_FIELD = 17
struct
{
uint32 field;
uint32 value;
uint32 target;
} set_unit_field;
// ACTION_T_SET_UNIT_FLAG = 18, // value provided mask bits that will be set
// ACTION_T_REMOVE_UNIT_FLAG = 19, // value provided mask bits that will be clear
struct
{
uint32 value;
uint32 target;
} unit_flag;
// ACTION_T_AUTO_ATTACK = 20
struct
{
uint32 state; // 0 = stop attack, anything else means continue attacking
} auto_attack;
// ACTION_T_COMBAT_MOVEMENT = 21
struct
{
uint32 state; // 0 = stop combat based movement, anything else continue attacking
} combat_movement;
// ACTION_T_SET_PHASE = 22
struct
{
uint32 phase;
} set_phase;
// ACTION_T_INC_PHASE = 23
struct
{
int32 step;
} set_inc_phase;
// ACTION_T_QUEST_EVENT_ALL = 26
struct
{
uint32 questId;
} quest_event_all;
// ACTION_T_CAST_EVENT_ALL = 27
struct
{
uint32 creatureId;
uint32 spellId;
} cast_event_all;
// ACTION_T_REMOVEAURASFROMSPELL = 28
struct
{
uint32 target;
uint32 spellId;
} remove_aura;
// ACTION_T_RANGED_MOVEMENT = 29
struct
{
uint32 distance;
int32 angle;
} ranged_movement;
// ACTION_T_RANDOM_PHASE = 30
struct
{
uint32 phase1;
uint32 phase2;
uint32 phase3;
} random_phase;
// ACTION_T_RANDOM_PHASE_RANGE = 31
struct
{
uint32 phaseMin;
uint32 phaseMax;
} random_phase_range;
// ACTION_T_SUMMON_ID = 32
struct
{
uint32 creatureId;
uint32 target;
uint32 spawnId;
} summon_id;
// ACTION_T_KILLED_MONSTER = 33
struct
{
uint32 creatureId;
uint32 target;
} killed_monster;
// ACTION_T_SET_INST_DATA = 34
struct
{
uint32 field;
uint32 value;
} set_inst_data;
// ACTION_T_SET_INST_DATA64 = 35
struct
{
uint32 field;
uint32 target;
} set_inst_data64;
// ACTION_T_UPDATE_TEMPLATE = 36, //*Entry, Team
struct
{
uint32 creatureId;
uint32 team;
} update_template;
// RAW
struct
{
uint32 param1;
uint32 param2;
uint32 param3;
} raw;
};
};
struct CreatureEventAI_Event
{
uint32 event_id;
@ -198,25 +395,7 @@ struct CreatureEventAI_Event
int32 event_param4_s;
};
struct _action
{
EventAI_ActionType type: 16;
union
{
uint32 param1;
int32 param1_s;
};
union
{
uint32 param2;
int32 param2_s;
};
union
{
uint32 param3;
int32 param3_s;
};
}action[MAX_ACTIONS];
CreatureEventAI_Action action[MAX_ACTIONS];
};
//Event_Map
typedef UNORDERED_MAP<uint32, std::vector<CreatureEventAI_Event> > CreatureEventAI_Event_Map;
@ -270,8 +449,9 @@ class MANGOS_DLL_SPEC CreatureEventAI : public CreatureAI
static int Permissible(const Creature *);
bool ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pActionInvoker = NULL);
void ProcessAction(uint16 type, uint32 param1, uint32 param2, uint32 param3, uint32 rnd, uint32 EventId, Unit* pActionInvoker);
void ProcessAction(CreatureEventAI_Action const& action, uint32 rnd, uint32 EventId, Unit* pActionInvoker);
inline uint32 GetRandActionParam(uint32 rnd, uint32 param1, uint32 param2, uint32 param3);
inline int32 GetRandActionParam(uint32 rnd, int32 param1, int32 param2, int32 param3);
inline Unit* GetTargetByType(uint32 Target, Unit* pActionInvoker);
inline Unit* SelectUnit(AttackingTarget target, uint32 position);

View file

@ -388,251 +388,252 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Scripts()
continue;
}
temp.action[j].type = EventAI_ActionType(action_type);
temp.action[j].param1 = fields[11+(j*4)].GetUInt32();
temp.action[j].param2 = fields[12+(j*4)].GetUInt32();
temp.action[j].param3 = fields[13+(j*4)].GetUInt32();
CreatureEventAI_Action& action = temp.action[j];
action.type = EventAI_ActionType(action_type);
action.raw.param1 = fields[11+(j*4)].GetUInt32();
action.raw.param2 = fields[12+(j*4)].GetUInt32();
action.raw.param3 = fields[13+(j*4)].GetUInt32();
//Report any errors in actions
switch (temp.action[j].type)
switch (action.type)
{
case ACTION_T_NONE:
break;
case ACTION_T_TEXT:
{
if (temp.action[j].param1_s < 0)
if (action.text.TextId1 < 0)
{
if (m_CreatureEventAI_TextMap.find(temp.action[j].param1_s) == m_CreatureEventAI_TextMap.end())
if (m_CreatureEventAI_TextMap.find(action.text.TextId1) == m_CreatureEventAI_TextMap.end())
sLog.outErrorDb("CreatureEventAI: Event %u Action %u param1 refrences non-existing entry in texts table.", i, j+1);
}
if (temp.action[j].param2_s < 0)
if (action.text.TextId2 < 0)
{
if (m_CreatureEventAI_TextMap.find(temp.action[j].param2_s) == m_CreatureEventAI_TextMap.end())
if (m_CreatureEventAI_TextMap.find(action.text.TextId2) == m_CreatureEventAI_TextMap.end())
sLog.outErrorDb("CreatureEventAI: Event %u Action %u param2 refrences non-existing entry in texts table.", i, j+1);
if (!temp.action[j].param1_s)
if (!action.text.TextId1)
sLog.outErrorDb("CreatureEventAI: Event %u Action %u has param2, but param1 is not set. Required for randomized text.", i, j+1);
}
if (temp.action[j].param3_s < 0)
if (action.text.TextId3 < 0)
{
if (m_CreatureEventAI_TextMap.find(temp.action[j].param3_s) == m_CreatureEventAI_TextMap.end())
if (m_CreatureEventAI_TextMap.find(action.text.TextId3) == m_CreatureEventAI_TextMap.end())
sLog.outErrorDb("CreatureEventAI: Event %u Action %u param3 refrences non-existing entry in texts table.", i, j+1);
if (!temp.action[j].param1_s || !temp.action[j].param2_s)
if (!action.text.TextId1 || !action.text.TextId2)
sLog.outErrorDb("CreatureEventAI: Event %u Action %u has param3, but param1 and/or param2 is not set. Required for randomized text.", i, j+1);
}
break;
}
case ACTION_T_SET_FACTION:
if (temp.action[j].param1 !=0 && !sFactionStore.LookupEntry(temp.action[j].param1))
if (action.set_faction.factionId !=0 && !sFactionStore.LookupEntry(action.set_faction.factionId))
{
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant FactionId %u.", i, j+1, temp.action[j].param1);
temp.action[j].param1 = 0;
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent FactionId %u.", i, j+1, action.set_faction.factionId);
action.set_faction.factionId = 0;
}
break;
case ACTION_T_MORPH_TO_ENTRY_OR_MODEL:
if (temp.action[j].param1 !=0 || temp.action[j].param2 !=0)
if (action.morph.creatireId !=0 || action.morph.modelId !=0)
{
if (temp.action[j].param1 && !sCreatureStorage.LookupEntry<CreatureInfo>(temp.action[j].param1))
if (action.morph.creatireId && !sCreatureStorage.LookupEntry<CreatureInfo>(action.morph.creatireId))
{
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant Creature entry %u.", i, j+1, temp.action[j].param1);
temp.action[j].param1 = 0;
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant Creature entry %u.", i, j+1, action.morph.creatireId);
action.morph.creatireId = 0;
}
if (temp.action[j].param2 && !sCreatureDisplayInfoStore.LookupEntry(temp.action[j].param2))
if (action.morph.modelId)
{
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant ModelId %u.", i, j+1, temp.action[j].param2);
temp.action[j].param2 = 0;
if (action.morph.creatireId)
{
sLog.outErrorDb("CreatureEventAI: Event %u Action %u have unused ModelId %u with also set creature id %u.", i, j+1, action.morph.modelId,action.morph.creatireId);
action.morph.modelId = 0;
}
else if (!sCreatureDisplayInfoStore.LookupEntry(action.morph.modelId))
{
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant ModelId %u.", i, j+1, action.morph.modelId);
action.morph.modelId = 0;
}
}
break;
}
break;
case ACTION_T_SOUND:
if (!sSoundEntriesStore.LookupEntry(temp.action[j].param1))
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant SoundID %u.", i, j+1, temp.action[j].param1);
break;
case ACTION_T_RANDOM_SOUND:
if (!sSoundEntriesStore.LookupEntry(temp.action[j].param1))
sLog.outErrorDb("CreatureEventAI: Event %u Action %u param1 uses non-existant SoundID %u.", i, j+1, temp.action[j].param1);
if (temp.action[j].param2_s >= 0 && !sSoundEntriesStore.LookupEntry(temp.action[j].param2))
sLog.outErrorDb("CreatureEventAI: Event %u Action %u param2 uses non-existant SoundID %u.", i, j+1, temp.action[j].param2);
if (temp.action[j].param3_s >= 0 && !sSoundEntriesStore.LookupEntry(temp.action[j].param3))
sLog.outErrorDb("CreatureEventAI: Event %u Action %u param3 uses non-existant SoundID %u.", i, j+1, temp.action[j].param3);
if (!sSoundEntriesStore.LookupEntry(action.sound.soundId))
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant SoundID %u.", i, j+1, action.sound.soundId);
break;
case ACTION_T_EMOTE:
if (!sEmotesStore.LookupEntry(temp.action[j].param1))
sLog.outErrorDb("CreatureEventAI: Event %u Action %u param1 (EmoteId: %u) are not valid.", i, j+1, temp.action[j].param1);
if (!sEmotesStore.LookupEntry(action.emote.emoteId))
sLog.outErrorDb("CreatureEventAI: Event %u Action %u param1 (EmoteId: %u) are not valid.", i, j+1, action.emote.emoteId);
break;
case ACTION_T_RANDOM_SOUND:
if (!sSoundEntriesStore.LookupEntry(action.random_sound.soundId1))
sLog.outErrorDb("CreatureEventAI: Event %u Action %u param1 uses non-existant SoundID %u.", i, j+1, action.random_sound.soundId1);
if (action.random_sound.soundId2 >= 0 && !sSoundEntriesStore.LookupEntry(action.random_sound.soundId2))
sLog.outErrorDb("CreatureEventAI: Event %u Action %u param2 uses non-existant SoundID %u.", i, j+1, action.random_sound.soundId2);
if (action.random_sound.soundId3 >= 0 && !sSoundEntriesStore.LookupEntry(action.random_sound.soundId3))
sLog.outErrorDb("CreatureEventAI: Event %u Action %u param3 uses non-existant SoundID %u.", i, j+1, action.random_sound.soundId3);
break;
case ACTION_T_RANDOM_EMOTE:
if (!sEmotesStore.LookupEntry(temp.action[j].param1))
sLog.outErrorDb("CreatureEventAI: Event %u Action %u param1 (EmoteId: %u) are not valid.", i, j+1, temp.action[j].param1);
if (temp.action[j].param2_s >= 0 && !sEmotesStore.LookupEntry(temp.action[j].param2))
sLog.outErrorDb("CreatureEventAI: Event %u Action %u param2 (EmoteId: %u) are not valid.", i, j+1, temp.action[j].param2);
if (temp.action[j].param3_s >= 0 && !sEmotesStore.LookupEntry(temp.action[j].param3))
sLog.outErrorDb("CreatureEventAI: Event %u Action %u param3 (EmoteId: %u) are not valid.", i, j+1, temp.action[j].param3);
if (!sEmotesStore.LookupEntry(action.random_emote.emoteId1))
sLog.outErrorDb("CreatureEventAI: Event %u Action %u param1 (EmoteId: %u) are not valid.", i, j+1, action.random_emote.emoteId1);
if (action.random_emote.emoteId2 >= 0 && !sEmotesStore.LookupEntry(action.random_emote.emoteId2))
sLog.outErrorDb("CreatureEventAI: Event %u Action %u param2 (EmoteId: %u) are not valid.", i, j+1, action.random_emote.emoteId2);
if (action.random_emote.emoteId3 >= 0 && !sEmotesStore.LookupEntry(action.random_emote.emoteId3))
sLog.outErrorDb("CreatureEventAI: Event %u Action %u param3 (EmoteId: %u) are not valid.", i, j+1, action.random_emote.emoteId3);
break;
case ACTION_T_CAST:
{
const SpellEntry *spell = sSpellStore.LookupEntry(temp.action[j].param1);
const SpellEntry *spell = sSpellStore.LookupEntry(action.cast.spellId);
if (!spell)
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant SpellID %u.", i, j+1, temp.action[j].param1);
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent SpellID %u.", i, j+1, action.cast.spellId);
else
{
if (spell->RecoveryTime > 0 && temp.event_flags & EFLAG_REPEATABLE)
{
//output as debug for now, also because there's no general rule all spells have RecoveryTime
if (temp.event_param3 < spell->RecoveryTime)
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,temp.action[j].param1, spell->RecoveryTime, temp.event_param3);
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);
}
}
if (temp.action[j].param2 >= TARGET_T_END)
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1);
break;
}
case ACTION_T_REMOVEAURASFROMSPELL:
{
if (!sSpellStore.LookupEntry(temp.action[j].param2))
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant SpellID %u.", i, j+1, temp.action[j].param2);
//Cast is always triggered if target is forced to cast on self
if (action.cast.castFlags & CAST_FORCE_TARGET_SELF)
action.cast.castFlags |= CAST_TRIGGERED;
if (temp.action[j].param1 >= TARGET_T_END)
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1);
break;
}
case ACTION_T_QUEST_EVENT:
{
if (Quest const* qid = objmgr.GetQuestTemplate(temp.action[j].param1))
{
if (!qid->HasFlag(QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT))
sLog.outErrorDb("CreatureEventAI: Event %u Action %u. SpecialFlags for quest entry %u does not include |2, Action will not have any effect.", i, j+1, temp.action[j].param1);
}
else
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant Quest entry %u.", i, j+1, temp.action[j].param1);
if (temp.action[j].param2 >= TARGET_T_END)
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1);
break;
}
case ACTION_T_QUEST_EVENT_ALL:
{
if (Quest const* qid = objmgr.GetQuestTemplate(temp.action[j].param1))
{
if (!qid->HasFlag(QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT))
sLog.outErrorDb("CreatureEventAI: Event %u Action %u. SpecialFlags for quest entry %u does not include |2, Action will not have any effect.", i, j+1, temp.action[j].param1);
}
else
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant Quest entry %u.", i, j+1, temp.action[j].param1);
break;
}
case ACTION_T_CASTCREATUREGO:
{
if (!sCreatureStorage.LookupEntry<CreatureInfo>(temp.action[j].param1))
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant creature entry %u.", i, j+1, temp.action[j].param1);
if (!sSpellStore.LookupEntry(temp.action[j].param2))
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant SpellID %u.", i, j+1, temp.action[j].param2);
if (temp.action[j].param3 >= TARGET_T_END)
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1);
break;
}
case ACTION_T_CASTCREATUREGO_ALL:
{
if (!objmgr.GetQuestTemplate(temp.action[j].param1))
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant Quest entry %u.", i, j+1, temp.action[j].param1);
if (!sSpellStore.LookupEntry(temp.action[j].param2))
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant SpellID %u.", i, j+1, temp.action[j].param2);
break;
}
//2nd param target
case ACTION_T_SUMMON_ID:
{
if (!sCreatureStorage.LookupEntry<CreatureInfo>(temp.action[j].param1))
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant creature entry %u.", i, j+1, temp.action[j].param1);
if (m_CreatureEventAI_Summon_Map.find(temp.action[j].param3) == m_CreatureEventAI_Summon_Map.end())
sLog.outErrorDb("CreatureEventAI: Event %u Action %u summons missing CreatureEventAI_Summon %u", i, j+1, temp.action[j].param3);
if (temp.action[j].param2 >= TARGET_T_END)
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1);
break;
}
case ACTION_T_KILLED_MONSTER:
{
if (!sCreatureStorage.LookupEntry<CreatureInfo>(temp.action[j].param1))
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant creature entry %u.", i, j+1, temp.action[j].param1);
if (temp.action[j].param2 >= TARGET_T_END)
if (action.cast.target >= TARGET_T_END)
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1);
break;
}
case ACTION_T_SUMMON:
{
if (!sCreatureStorage.LookupEntry<CreatureInfo>(temp.action[j].param1))
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant creature entry %u.", i, j+1, temp.action[j].param1);
if (!sCreatureStorage.LookupEntry<CreatureInfo>(action.summon.creatured))
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent creature entry %u.", i, j+1, action.summon.creatured);
if (temp.action[j].param2 >= TARGET_T_END)
if (action.summon.target >= TARGET_T_END)
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1);
break;
}
case ACTION_T_THREAT_SINGLE_PCT:
if (std::abs(action.threat_single_pct.percent) > 100)
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses invalid percent value %u.", i, j+1, action.threat_single_pct.percent);
if (action.threat_single_pct.target >= TARGET_T_END)
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1);
break;
case ACTION_T_THREAT_ALL_PCT:
if (std::abs(action.threat_all_pct.percent) > 100)
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses invalid percent value %u.", i, j+1, action.threat_all_pct.percent);
break;
case ACTION_T_QUEST_EVENT:
if (Quest const* qid = objmgr.GetQuestTemplate(action.quest_event.questId))
{
if (!qid->HasFlag(QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT))
sLog.outErrorDb("CreatureEventAI: Event %u Action %u. SpecialFlags for quest entry %u does not include |2, Action will not have any effect.", i, j+1, action.quest_event.questId);
}
else
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent Quest entry %u.", i, j+1, action.quest_event.questId);
if (action.quest_event.target >= TARGET_T_END)
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1);
break;
case ACTION_T_CAST_EVENT:
if (!sCreatureStorage.LookupEntry<CreatureInfo>(action.cast_event.creatureId))
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent creature entry %u.", i, j+1, action.cast_event.creatureId);
if (!sSpellStore.LookupEntry(action.cast_event.spellId))
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent SpellID %u.", i, j+1, action.cast_event.spellId);
if (action.cast_event.target >= TARGET_T_END)
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1);
break;
case ACTION_T_SET_UNIT_FIELD:
if (action.set_unit_field.field < OBJECT_END || action.set_unit_field.field >= UNIT_END)
sLog.outErrorDb("CreatureEventAI: Event %u Action %u param1 (UNIT_FIELD*). Index out of range for intended use.", i, j+1);
if (action.set_unit_field.target >= TARGET_T_END)
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1);
break;
case ACTION_T_SET_UNIT_FLAG:
case ACTION_T_REMOVE_UNIT_FLAG:
if (temp.action[j].param2 >= TARGET_T_END)
if (action.unit_flag.target >= TARGET_T_END)
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1);
break;
//3rd param target
case ACTION_T_SET_UNIT_FIELD:
if (temp.action[j].param1 < OBJECT_END || temp.action[j].param1 >= UNIT_END)
sLog.outErrorDb("CreatureEventAI: Event %u Action %u param1 (UNIT_FIELD*). Index out of range for intended use.", i, j+1);
if (temp.action[j].param3 >= TARGET_T_END)
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1);
break;
case ACTION_T_SET_PHASE:
if (temp.action[j].param1 > 31)
sLog.outErrorDb("CreatureEventAI: Event %u Action %u attempts to set phase > 31. Phase mask cannot be used past phase 31.", i, j+1);
if (action.set_phase.phase >= MAX_PHASE)
sLog.outErrorDb("CreatureEventAI: Event %u Action %u attempts to set phase >= %u. Phase mask cannot be used past phase %u.", i, j+1, MAX_PHASE, MAX_PHASE-1);
break;
case ACTION_T_INC_PHASE:
if (!temp.action[j].param1)
if (action.set_inc_phase.step == 0)
sLog.outErrorDb("CreatureEventAI: Event %u Action %u is incrementing phase by 0. Was this intended?", i, j+1);
else if (std::abs(action.set_inc_phase.step) > MAX_PHASE-1)
sLog.outErrorDb("CreatureEventAI: Event %u Action %u is change phase by too large for any use %i.", i, j+1, action.set_inc_phase.step);
break;
case ACTION_T_SET_INST_DATA:
{
if (!(temp.event_flags & EFLAG_NORMAL) && !(temp.event_flags & EFLAG_HEROIC))
sLog.outErrorDb("CreatureEventAI: Event %u Action %u. Cannot set instance data without event flags (normal/heroic).", i, j+1);
if (temp.action[j].param2 > 4/*SPECIAL*/)
sLog.outErrorDb("CreatureEventAI: Event %u Action %u attempts to set instance data above encounter state 4. Custom case?", i, j+1);
case ACTION_T_QUEST_EVENT_ALL:
if (Quest const* qid = objmgr.GetQuestTemplate(action.quest_event_all.questId))
{
if (!qid->HasFlag(QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT))
sLog.outErrorDb("CreatureEventAI: Event %u Action %u. SpecialFlags for quest entry %u does not include |2, Action will not have any effect.", i, j+1, action.quest_event_all.questId);
}
else
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent Quest entry %u.", i, j+1, action.quest_event_all.questId);
break;
}
case ACTION_T_SET_INST_DATA64:
{
if (!(temp.event_flags & EFLAG_NORMAL) && !(temp.event_flags & EFLAG_HEROIC))
sLog.outErrorDb("CreatureEventAI: Event %u Action %u. Cannot set instance data without event flags (normal/heroic).", i, j+1);
if (temp.action[j].param2 >= TARGET_T_END)
case ACTION_T_CAST_EVENT_ALL:
if (!sCreatureStorage.LookupEntry<CreatureInfo>(action.cast_event_all.creatureId))
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent creature entry %u.", i, j+1, action.cast_event_all.creatureId);
if (!sSpellStore.LookupEntry(action.cast_event_all.spellId))
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent SpellID %u.", i, j+1, action.cast_event_all.spellId);
break;
case ACTION_T_REMOVEAURASFROMSPELL:
if (!sSpellStore.LookupEntry(action.remove_aura.spellId))
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent SpellID %u.", i, j+1, action.remove_aura.spellId);
if (action.remove_aura.target >= TARGET_T_END)
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1);
break;
case ACTION_T_RANDOM_PHASE: //PhaseId1, PhaseId2, PhaseId3
if (action.random_phase.phase1 >= MAX_PHASE)
sLog.outErrorDb("CreatureEventAI: Event %u Action %u attempts to set phase1 >= %u. Phase mask cannot be used past phase %u.", i, j+1, MAX_PHASE, MAX_PHASE-1);
if (action.random_phase.phase2 >= MAX_PHASE)
sLog.outErrorDb("CreatureEventAI: Event %u Action %u attempts to set phase2 >= %u. Phase mask cannot be used past phase %u.", i, j+1, MAX_PHASE, MAX_PHASE-1);
if (action.random_phase.phase3 >= MAX_PHASE)
sLog.outErrorDb("CreatureEventAI: Event %u Action %u attempts to set phase3 >= %u. Phase mask cannot be used past phase %u.", i, j+1, MAX_PHASE, MAX_PHASE-1);
break;
case ACTION_T_RANDOM_PHASE_RANGE: //PhaseMin, PhaseMax
if (action.random_phase_range.phaseMin >= MAX_PHASE)
sLog.outErrorDb("CreatureEventAI: Event %u Action %u attempts to set phaseMin >= %u. Phase mask cannot be used past phase %u.", i, j+1, MAX_PHASE, MAX_PHASE-1);
if (action.random_phase_range.phaseMin >= MAX_PHASE)
sLog.outErrorDb("CreatureEventAI: Event %u Action %u attempts to set phaseMax >= %u. Phase mask cannot be used past phase %u.", i, j+1, MAX_PHASE, MAX_PHASE-1);
if (action.random_phase_range.phaseMin >= action.random_phase_range.phaseMax)
{
sLog.outErrorDb("CreatureEventAI: Event %u Action %u attempts to set phaseMax <= phaseMin.", i, j+1);
std::swap(action.random_phase_range.phaseMin,action.random_phase_range.phaseMax);
// equal case processed at call
}
break;
case ACTION_T_SUMMON_ID:
if (!sCreatureStorage.LookupEntry<CreatureInfo>(action.summon_id.creatureId))
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant creature entry %u.", i, j+1, action.summon_id.creatureId);
if (action.summon_id.target >= TARGET_T_END)
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1);
if (m_CreatureEventAI_Summon_Map.find(action.summon_id.spawnId) == m_CreatureEventAI_Summon_Map.end())
sLog.outErrorDb("CreatureEventAI: Event %u Action %u summons missing CreatureEventAI_Summon %u", i, j+1, action.summon_id.spawnId);
break;
case ACTION_T_KILLED_MONSTER:
if (!sCreatureStorage.LookupEntry<CreatureInfo>(action.killed_monster.creatureId))
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant creature entry %u.", i, j+1, action.killed_monster.creatureId);
if (action.killed_monster.target >= TARGET_T_END)
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1);
break;
case ACTION_T_SET_INST_DATA:
if (!(temp.event_flags & EFLAG_NORMAL) && !(temp.event_flags & EFLAG_HEROIC))
sLog.outErrorDb("CreatureEventAI: Event %u Action %u. Cannot set instance data without event flags (normal/heroic).", i, j+1);
if (action.set_inst_data.value > 4/*SPECIAL*/)
sLog.outErrorDb("CreatureEventAI: Event %u Action %u attempts to set instance data above encounter state 4. Custom case?", i, j+1);
break;
case ACTION_T_SET_INST_DATA64:
if (!(temp.event_flags & EFLAG_NORMAL) && !(temp.event_flags & EFLAG_HEROIC))
sLog.outErrorDb("CreatureEventAI: Event %u Action %u. Cannot set instance data without event flags (normal/heroic).", i, j+1);
if (action.set_inst_data64.target >= TARGET_T_END)
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1);
break;
}
case ACTION_T_UPDATE_TEMPLATE:
{
if (!sCreatureStorage.LookupEntry<CreatureInfo>(temp.action[j].param1))
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant creature entry %u.", i, j+1, temp.action[j].param1);
if (!sCreatureStorage.LookupEntry<CreatureInfo>(action.update_template.creatureId))
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant creature entry %u.", i, j+1, action.update_template.creatureId);
break;
}
case ACTION_T_THREAT_ALL_PCT:
if (abs(temp.action[j].param1_s) > 100)
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses invalid percent value %u.", i, j+1, temp.action[j].param1);
break;
case ACTION_T_EVADE: //No Params
case ACTION_T_FLEE: //No Params
case ACTION_T_DIE: //No Params
@ -642,11 +643,6 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Scripts()
case ACTION_T_RANGED_MOVEMENT: //Distance, Angle
break;
case ACTION_T_RANDOM_PHASE: //PhaseId1, PhaseId2, PhaseId3
case ACTION_T_RANDOM_PHASE_RANGE: //PhaseMin, PhaseMax
// check not implemented
break;
case ACTION_T_RANDOM_SAY:
case ACTION_T_RANDOM_YELL:
case ACTION_T_RANDOM_TEXTEMOTE:

View file

@ -1,4 +1,4 @@
#ifndef __REVISION_NR_H__
#define __REVISION_NR_H__
#define REVISION_NR "7846"
#define REVISION_NR "7847"
#endif // __REVISION_NR_H__