diff --git a/doc/EventAI.txt b/doc/EventAI.txt index 7a2087dec..a22b94eea 100644 --- a/doc/EventAI.txt +++ b/doc/EventAI.txt @@ -128,6 +128,7 @@ Params are always read from Param1, then Param2, then Param3. 36 ACTION_T_UPDATE_TEMPLATE TemplateId, Team Changes the creature to a new creature template of (param1) with team = Alliance if (param2) = false or Horde if (param2) = true 37 ACTION_T_DIE No Params Kills the creature 38 ACTION_T_ZONE_COMBAT_PULSE No Params Places all players within the instance into combat with the creature. Only works in combat and only works inside of instances. +39 ACTION_T_CALL_FOR_HELP Radius Call any friendly creatures (if its not in combat/etc) in radius attack creature target. * = Use -1 to specify that if this param is picked to do nothing. Random is constant between actions within an event. So if you have a random Yell and a random Sound they will match up (ex: param2 with param2) diff --git a/src/game/Creature.cpp b/src/game/Creature.cpp index 0680a14e8..9ce8aede1 100644 --- a/src/game/Creature.cpp +++ b/src/game/Creature.cpp @@ -85,8 +85,7 @@ VendorItem const* VendorItemData::FindItem(uint32 item_id) const bool AssistDelayEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/) { - Unit* victim = Unit::GetUnit(m_owner, m_victim); - if (victim) + if(Unit* victim = Unit::GetUnit(m_owner, m_victim)) { while (!m_assistants.empty()) { @@ -1725,26 +1724,34 @@ void Creature::CallAssistance() } } -bool Creature::CanAssistTo(const Unit* u, const Unit* enemy) const +bool Creature::CanAssistTo(const Unit* u, const Unit* enemy, bool checkfaction /*= true*/) const { // we don't need help from zombies :) - if( !isAlive() ) + if (!isAlive()) return false; // skip fighting creature - if( isInCombat() ) - return false; - - // only from same creature faction - if(getFaction() != u->getFaction() ) + if (isInCombat()) return false; // only free creature - if( GetCharmerOrOwnerGUID() ) + if (GetCharmerOrOwnerGUID()) return false; + // only from same creature faction + if (checkfaction) + { + if (getFaction() != u->getFaction()) + return false; + } + else + { + if (!IsFriendlyTo(u)) + return false; + } + // skip non hostile to caster enemy creatures - if( !IsHostileTo(enemy) ) + if (!IsHostileTo(enemy)) return false; return true; diff --git a/src/game/Creature.h b/src/game/Creature.h index c71106a8d..2fa5d3784 100644 --- a/src/game/Creature.h +++ b/src/game/Creature.h @@ -603,7 +603,7 @@ class MANGOS_DLL_SPEC Creature : public Unit void CallAssistance(); void SetNoCallAssistance(bool val) { m_AlreadyCallAssistance = val; } - bool CanAssistTo(const Unit* u, const Unit* enemy) const; + bool CanAssistTo(const Unit* u, const Unit* enemy, bool checkfaction = true) const; MovementGeneratorType GetDefaultMovementType() const { return m_defaultMovementType; } void SetDefaultMovementType(MovementGeneratorType mgt) { m_defaultMovementType = mgt; } diff --git a/src/game/CreatureEventAI.cpp b/src/game/CreatureEventAI.cpp index 9e663107f..490ed7e63 100644 --- a/src/game/CreatureEventAI.cpp +++ b/src/game/CreatureEventAI.cpp @@ -30,6 +30,40 @@ #include "WorldPacket.h" #include "InstanceData.h" +namespace MaNGOS +{ + class CallOfHelpCreatureInRangeDo // do attack at call of help to friendly crearture + { + public: + CallOfHelpCreatureInRangeDo(Unit* funit, Unit* enemy, float range) + : i_funit(funit), i_enemy(enemy), i_range(range) + {} + void operator()(Creature* u) + { + if (u == i_funit) + return; + + if (!u->CanAssistTo(i_funit, i_enemy, false)) + return; + + // too far + if( !i_funit->IsWithinDistInMap(u, i_range) ) + return; + + // only if see assisted creature + if( !i_funit->IsWithinLOSInMap(u) ) + return; + + if(u->AI()) + u->AI()->AttackStart(i_enemy); + } + private: + Unit* const i_funit; + Unit* const i_enemy; + float i_range; + }; +} + bool CreatureEventAIHolder::UpdateRepeatTimer( Creature* creature, uint32 repeatMin, uint32 repeatMax ) { if (repeatMin == repeatMax) @@ -705,7 +739,6 @@ void CreatureEventAI::ProcessAction(CreatureEventAI_Action const& action, uint32 break; } case ACTION_T_UPDATE_TEMPLATE: - { if (m_creature->GetEntry() == action.update_template.creatureId) { @@ -714,10 +747,8 @@ void CreatureEventAI::ProcessAction(CreatureEventAI_Action const& action, uint32 } m_creature->UpdateEntry(action.update_template.creatureId, action.update_template.team ? HORDE : ALLIANCE); - } - break; + break; case ACTION_T_DIE: - { if (m_creature->isDead()) { @@ -725,10 +756,8 @@ void CreatureEventAI::ProcessAction(CreatureEventAI_Action const& action, uint32 return; } m_creature->DealDamage(m_creature, m_creature->GetMaxHealth(),NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); - } - break; + break; case ACTION_T_ZONE_COMBAT_PULSE: - { if (!m_creature->isInCombat() || !m_creature->GetMap()->IsDungeon()) { @@ -737,8 +766,26 @@ void CreatureEventAI::ProcessAction(CreatureEventAI_Action const& action, uint32 } DoZoneInCombat(m_creature); + break; + case ACTION_T_CALL_FOR_HELP: + { + if (!m_creature->getVictim()) + return; + + CellPair p(MaNGOS::ComputeCellPair(m_creature->GetPositionX(), m_creature->GetPositionY())); + Cell cell(p); + cell.data.Part.reserved = ALL_DISTRICT; + cell.SetNoCreate(); + + MaNGOS::CallOfHelpCreatureInRangeDo u_do(m_creature, m_creature->getVictim(), action.call_for_help.radius); + MaNGOS::CreatureWorker worker(m_creature, u_do); + + TypeContainerVisitor, GridTypeMapContainer > grid_creature_searcher(worker); + + CellLock cell_lock(cell, p); + cell_lock->Visit(cell_lock, grid_creature_searcher, *m_creature->GetMap()); + break; } - break; } } diff --git a/src/game/CreatureEventAI.h b/src/game/CreatureEventAI.h index d357e5829..fd3ca906f 100644 --- a/src/game/CreatureEventAI.h +++ b/src/game/CreatureEventAI.h @@ -63,45 +63,46 @@ enum EventAI_Type enum EventAI_ActionType { - ACTION_T_NONE = 0, //*No action - ACTION_T_TEXT = 1, //*-TextId1, optionally -TextId2, optionally -TextId3(if -TextId2 exist). If more than just -TextId1 is defined, randomize. Negative values. - ACTION_T_SET_FACTION = 2, //*FactionId (or 0 for default) - ACTION_T_MORPH_TO_ENTRY_OR_MODEL = 3, //*Creature_template entry(param1) OR ModelId (param2) (or 0 for both to demorph) - ACTION_T_SOUND = 4, //*SoundId - ACTION_T_EMOTE = 5, //*EmoteId - ACTION_T_RANDOM_SAY = 6, //*UNUSED - ACTION_T_RANDOM_YELL = 7, //*UNUSED - ACTION_T_RANDOM_TEXTEMOTE = 8, //*UNUSED - ACTION_T_RANDOM_SOUND = 9, //SoundId1, SoundId2, SoundId3 (-1 in any field means no output if randomed that field) - ACTION_T_RANDOM_EMOTE = 10, //*EmoteId1, EmoteId2, EmoteId3 (-1 in any field means no output if randomed that field) - ACTION_T_CAST = 11, //*SpellId, Target, CastFlags - ACTION_T_SUMMON = 12, //*CreatureID, Target, Duration in ms - ACTION_T_THREAT_SINGLE_PCT = 13, //*Threat%, Target - ACTION_T_THREAT_ALL_PCT = 14, //Threat% - ACTION_T_QUEST_EVENT = 15, //*QuestID, 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 - ACTION_T_AUTO_ATTACK = 20, //AllowAttackState (0 = stop attack, anything else means continue attacking) - ACTION_T_COMBAT_MOVEMENT = 21, //AllowCombatMovement (0 = stop combat based movement, anything else continue attacking) - ACTION_T_SET_PHASE = 22, //*Phase - ACTION_T_INC_PHASE = 23, //*Value (may be negative to decrement phase, should not be 0) - ACTION_T_EVADE = 24, //No Params - ACTION_T_FLEE = 25, //No Params - ACTION_T_QUEST_EVENT_ALL = 26, //*QuestID - 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 - ACTION_T_RANDOM_PHASE_RANGE = 31, //PhaseMin, PhaseMax - ACTION_T_SUMMON_ID = 32, //*CreatureId, Target, SpawnId - ACTION_T_KILLED_MONSTER = 33, //*CreatureId, Target - ACTION_T_SET_INST_DATA = 34, //*Field, Data - ACTION_T_SET_INST_DATA64 = 35, //*Field, Target - ACTION_T_UPDATE_TEMPLATE = 36, //*Entry, Team - ACTION_T_DIE = 37, //No Params - ACTION_T_ZONE_COMBAT_PULSE = 38, //No Params + ACTION_T_NONE = 0, // No action + ACTION_T_TEXT = 1, // TextId1, optionally -TextId2, optionally -TextId3(if -TextId2 exist). If more than just -TextId1 is defined, randomize. Negative values. + ACTION_T_SET_FACTION = 2, // FactionId (or 0 for default) + ACTION_T_MORPH_TO_ENTRY_OR_MODEL = 3, // Creature_template entry(param1) OR ModelId (param2) (or 0 for both to demorph) + ACTION_T_SOUND = 4, // SoundId + ACTION_T_EMOTE = 5, // EmoteId + ACTION_T_RANDOM_SAY = 6, // UNUSED + ACTION_T_RANDOM_YELL = 7, // UNUSED + ACTION_T_RANDOM_TEXTEMOTE = 8, // UNUSED + ACTION_T_RANDOM_SOUND = 9, // SoundId1, SoundId2, SoundId3 (-1 in any field means no output if randomed that field) + ACTION_T_RANDOM_EMOTE = 10, // EmoteId1, EmoteId2, EmoteId3 (-1 in any field means no output if randomed that field) + ACTION_T_CAST = 11, // SpellId, Target, CastFlags + ACTION_T_SUMMON = 12, // CreatureID, Target, Duration in ms + ACTION_T_THREAT_SINGLE_PCT = 13, // Threat%, Target + ACTION_T_THREAT_ALL_PCT = 14, // Threat% + ACTION_T_QUEST_EVENT = 15, // QuestID, 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 + ACTION_T_AUTO_ATTACK = 20, // AllowAttackState (0 = stop attack, anything else means continue attacking) + ACTION_T_COMBAT_MOVEMENT = 21, // AllowCombatMovement (0 = stop combat based movement, anything else continue attacking) + ACTION_T_SET_PHASE = 22, // Phase + ACTION_T_INC_PHASE = 23, // Value (may be negative to decrement phase, should not be 0) + ACTION_T_EVADE = 24, // No Params + ACTION_T_FLEE = 25, // No Params + ACTION_T_QUEST_EVENT_ALL = 26, // QuestID + ACTION_T_CAST_EVENT_ALL = 27, // CreatureId, SpellId + ACTION_T_REMOVEAURASFROMSPELL = 28, // Target, Spellid + ACTION_T_RANGED_MOVEMENT = 29, // Distance, Angle + ACTION_T_RANDOM_PHASE = 30, // PhaseId1, PhaseId2, PhaseId3 + ACTION_T_RANDOM_PHASE_RANGE = 31, // PhaseMin, PhaseMax + ACTION_T_SUMMON_ID = 32, // CreatureId, Target, SpawnId + ACTION_T_KILLED_MONSTER = 33, // CreatureId, Target + ACTION_T_SET_INST_DATA = 34, // Field, Data + ACTION_T_SET_INST_DATA64 = 35, // Field, Target + ACTION_T_UPDATE_TEMPLATE = 36, // Entry, Team + ACTION_T_DIE = 37, // No Params + ACTION_T_ZONE_COMBAT_PULSE = 38, // No Params + ACTION_T_CALL_FOR_HELP = 39, // Radius ACTION_T_END, }; @@ -346,12 +347,17 @@ struct CreatureEventAI_Action uint32 field; uint32 target; } set_inst_data64; - // ACTION_T_UPDATE_TEMPLATE = 36, //*Entry, Team + // ACTION_T_UPDATE_TEMPLATE = 36 struct { uint32 creatureId; uint32 team; } update_template; + // ACTION_T_CALL_FOR_HELP = 39 + struct + { + uint32 radius; + } call_for_help; // RAW struct { diff --git a/src/game/CreatureEventAIMgr.cpp b/src/game/CreatureEventAIMgr.cpp index b870ff4e4..1b705c8a3 100644 --- a/src/game/CreatureEventAIMgr.cpp +++ b/src/game/CreatureEventAIMgr.cpp @@ -632,6 +632,7 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Scripts() case ACTION_T_AUTO_ATTACK: //AllowAttackState (0 = stop attack, anything else means continue attacking) case ACTION_T_COMBAT_MOVEMENT: //AllowCombatMovement (0 = stop combat based movement, anything else continue attacking) case ACTION_T_RANGED_MOVEMENT: //Distance, Angle + case ACTION_T_CALL_FOR_HELP: //Distance break; case ACTION_T_RANDOM_SAY: diff --git a/src/game/GridNotifiers.h b/src/game/GridNotifiers.h index d8798d58b..272f6fbc1 100644 --- a/src/game/GridNotifiers.h +++ b/src/game/GridNotifiers.h @@ -445,6 +445,25 @@ namespace MaNGOS template void Visit(GridRefManager &) {} }; + template + struct MANGOS_DLL_DECL CreatureWorker + { + uint32 i_phaseMask; + Do& i_do; + + CreatureWorker(WorldObject const* searcher, Do& _do) + : i_phaseMask(searcher->GetPhaseMask()), i_do(_do) {} + + void Visit(CreatureMapType &m) + { + for(CreatureMapType::iterator itr=m.begin(); itr != m.end(); ++itr) + if(itr->getSource()->InSamePhase(i_phaseMask)) + i_do(itr->getSource()); + } + + template void Visit(GridRefManager &) {} + }; + // Player searchers template diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index 5e1e30960..2340b9655 100644 --- a/src/shared/revision_nr.h +++ b/src/shared/revision_nr.h @@ -1,4 +1,4 @@ #ifndef __REVISION_NR_H__ #define __REVISION_NR_H__ - #define REVISION_NR "7859" + #define REVISION_NR "7860" #endif // __REVISION_NR_H__