Skip to content

Instantly share code, notes, and snippets.

@rsa
Created November 20, 2010 22:44
Show Gist options
  • Save rsa/708243 to your computer and use it in GitHub Desktop.
Save rsa/708243 to your computer and use it in GitHub Desktop.
Simplify PetActions for use with multiple controlled units. Original author Laise
diff --git a/src/game/Pet.h b/src/game/Pet.h
index 620791d..9cdc2ad 100644
--- a/src/game/Pet.h
+++ b/src/game/Pet.h
@@ -269,4 +269,30 @@ class Pet : public Creature
MANGOS_ASSERT(false);
}
};
+
+struct DoPetActionWithHelper
+{
+ explicit DoPetActionWithHelper( Player* _owner, uint8 _flag, uint32 _spellid, ObjectGuid _petGuid, ObjectGuid _targetGuid) :
+ owner(_owner), flag(_flag), spellid(_spellid), petGuid(_petGuid), targetGuid(_targetGuid)
+ {}
+ void operator()(Unit* unit) const { unit->DoPetAction(owner, flag, spellid, petGuid, targetGuid); }
+ Player* owner;
+ uint8 flag;
+ uint32 spellid;
+ ObjectGuid petGuid;
+ ObjectGuid targetGuid;
+};
+
+struct DoPetCastWithHelper
+{
+ explicit DoPetCastWithHelper( Player* _owner, uint8 _cast_count, SpellCastTargets* _targets, SpellEntry const* _spellInfo ) :
+ owner(_owner), cast_count(_cast_count), targets(_targets), spellInfo(_spellInfo)
+ {}
+ void operator()(Unit* unit) const { unit->DoPetCastSpell(owner,cast_count,targets,spellInfo ); }
+ Player* owner;
+ uint8 cast_count;
+ SpellCastTargets* targets;
+ SpellEntry const* spellInfo;
+};
+
#endif
diff --git a/src/game/PetHandler.cpp b/src/game/PetHandler.cpp
index f1ce925..2402087 100644
--- a/src/game/PetHandler.cpp
+++ b/src/game/PetHandler.cpp
@@ -80,196 +80,8 @@ void WorldSession::HandlePetAction( WorldPacket & recv_data )
return;
}
- switch(flag)
- {
- case ACT_COMMAND: //0x07
- switch(spellid)
- {
- case COMMAND_STAY: //flat=1792 //STAY
- pet->StopMoving();
- pet->GetMotionMaster()->Clear(false);
- pet->GetMotionMaster()->MoveIdle();
- charmInfo->SetCommandState( COMMAND_STAY );
- break;
- case COMMAND_FOLLOW: //spellid=1792 //FOLLOW
- pet->AttackStop();
- pet->GetMotionMaster()->MoveFollow(_player,PET_FOLLOW_DIST,PET_FOLLOW_ANGLE);
- charmInfo->SetCommandState( COMMAND_FOLLOW );
- break;
- case COMMAND_ATTACK: //spellid=1792 //ATTACK
- {
- Unit *TargetUnit = _player->GetMap()->GetUnit(targetGuid);
- if(!TargetUnit)
- return;
-
- // not let attack friendly units.
- if(GetPlayer()->IsFriendlyTo(TargetUnit))
- return;
- // Not let attack through obstructions
- if(!pet->IsWithinLOSInMap(TargetUnit))
- return;
-
- // This is true if pet has no target or has target but targets differs.
- if(pet->getVictim() != TargetUnit)
- {
- if (pet->getVictim())
- pet->AttackStop();
-
- if (pet->hasUnitState(UNIT_STAT_CONTROLLED))
- {
- pet->Attack(TargetUnit, true);
- pet->SendPetAIReaction();
- }
- else
- {
- pet->GetMotionMaster()->Clear();
-
- if (((Creature*)pet)->AI())
- ((Creature*)pet)->AI()->AttackStart(TargetUnit);
-
- // 10% chance to play special pet attack talk, else growl
- if(((Creature*)pet)->IsPet() && ((Pet*)pet)->getPetType() == SUMMON_PET && pet != TargetUnit && roll_chance_i(10))
- pet->SendPetTalk((uint32)PET_TALK_ATTACK);
- else
- {
- // 90% chance for pet and 100% chance for charmed creature
- pet->SendPetAIReaction();
- }
- }
- }
- break;
- }
- case COMMAND_ABANDON: // abandon (hunter pet) or dismiss (summoned pet)
- if(((Creature*)pet)->IsPet())
- {
- Pet* p = (Pet*)pet;
- if(p->getPetType() == HUNTER_PET)
- p->Unsummon(PET_SAVE_AS_DELETED, _player);
- else
- //dismissing a summoned pet is like killing them (this prevents returning a soulshard...)
- p->SetDeathState(CORPSE);
- }
- else // charmed
- _player->Uncharm();
- break;
- default:
- sLog.outError("WORLD: unknown PET flag Action %i and spellid %i.", uint32(flag), spellid);
- }
- break;
- case ACT_REACTION: // 0x6
- switch(spellid)
- {
- case REACT_PASSIVE: //passive
- case REACT_DEFENSIVE: //recovery
- case REACT_AGGRESSIVE: //activete
- charmInfo->SetReactState( ReactStates(spellid) );
- break;
- }
- break;
- case ACT_DISABLED: // 0x81 spell (disabled), ignore
- case ACT_PASSIVE: // 0x01
- case ACT_ENABLED: // 0xC1 spell
- {
- Unit* unit_target = NULL;
- if (!targetGuid.IsEmpty())
- unit_target = _player->GetMap()->GetUnit(targetGuid);
-
- // do not cast unknown spells
- SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellid );
- if (!spellInfo)
- {
- sLog.outError("WORLD: unknown PET spell id %i", spellid);
- return;
- }
-
- if (pet->GetCharmInfo() && pet->GetCharmInfo()->GetGlobalCooldownMgr().HasGlobalCooldown(spellInfo))
- return;
-
- for(int i = 0; i < MAX_EFFECT_INDEX;++i)
- {
- if(spellInfo->EffectImplicitTargetA[i] == TARGET_ALL_ENEMY_IN_AREA || spellInfo->EffectImplicitTargetA[i] == TARGET_ALL_ENEMY_IN_AREA_INSTANT || spellInfo->EffectImplicitTargetA[i] == TARGET_ALL_ENEMY_IN_AREA_CHANNELED)
- return;
- }
-
- // do not cast not learned spells
- if(!pet->HasSpell(spellid) || IsPassiveSpell(spellInfo))
- return;
-
- pet->clearUnitState(UNIT_STAT_MOVING);
-
- Spell *spell = new Spell(pet, spellInfo, false);
-
- SpellCastResult result = spell->CheckPetCast(unit_target);
-
- //auto turn to target unless possessed
- if(result == SPELL_FAILED_UNIT_NOT_INFRONT && !pet->HasAuraType(SPELL_AURA_MOD_POSSESS))
- {
- if(unit_target)
- {
- pet->SetInFront(unit_target);
- if (unit_target->GetTypeId() == TYPEID_PLAYER)
- pet->SendCreateUpdateToPlayer( (Player*)unit_target );
- }
- else if(Unit *unit_target2 = spell->m_targets.getUnitTarget())
- {
- pet->SetInFront(unit_target2);
- if (unit_target2->GetTypeId() == TYPEID_PLAYER)
- pet->SendCreateUpdateToPlayer( (Player*)unit_target2 );
- }
- if (Unit* powner = pet->GetCharmerOrOwner())
- if(powner->GetTypeId() == TYPEID_PLAYER)
- pet->SendCreateUpdateToPlayer((Player*)powner);
- result = SPELL_CAST_OK;
- }
-
- if(result == SPELL_CAST_OK)
- {
- ((Creature*)pet)->AddCreatureSpellCooldown(spellid);
-
- unit_target = spell->m_targets.getUnitTarget();
-
- //10% chance to play special pet attack talk, else growl
- //actually this only seems to happen on special spells, fire shield for imp, torment for voidwalker, but it's stupid to check every spell
- if(((Creature*)pet)->IsPet() && (((Pet*)pet)->getPetType() == SUMMON_PET) && (pet != unit_target) && (urand(0, 100) < 10))
- pet->SendPetTalk((uint32)PET_TALK_SPECIAL_SPELL);
- else
- {
- pet->SendPetAIReaction();
- }
-
- if( unit_target && !GetPlayer()->IsFriendlyTo(unit_target) && !pet->HasAuraType(SPELL_AURA_MOD_POSSESS))
- {
- // This is true if pet has no target or has target but targets differs.
- if (pet->getVictim() != unit_target)
- {
- if (pet->getVictim())
- pet->AttackStop();
- pet->GetMotionMaster()->Clear();
- if (((Creature*)pet)->AI())
- ((Creature*)pet)->AI()->AttackStart(unit_target);
- }
- }
-
- spell->prepare(&(spell->m_targets));
- }
- else
- {
- if(pet->HasAuraType(SPELL_AURA_MOD_POSSESS))
- Spell::SendCastResult(GetPlayer(),spellInfo,0,result);
- else
- pet->SendPetCastFail(spellid, result);
-
- if (!((Creature*)pet)->HasSpellCooldown(spellid))
- GetPlayer()->SendClearCooldown(spellid, pet);
-
- spell->finish(false);
- delete spell;
- }
- break;
- }
- default:
- sLog.outError("WORLD: unknown PET flag Action %i and spellid %i.", uint32(flag), spellid);
- }
+ if (((Creature*)pet)->IsPet() || pet->isCharmed())
+ GetPlayer()->CallForAllControlledUnits(DoPetActionWithHelper(GetPlayer(), flag, spellid, petGuid, targetGuid),CONTROLLED_PET|CONTROLLED_GUARDIANS|CONTROLLED_CHARM);
}
void WorldSession::HandlePetStopAttack(WorldPacket& recv_data)
@@ -636,7 +448,7 @@ void WorldSession::HandlePetCastSpellOpcode( WorldPacket& recvPacket )
DEBUG_LOG("WORLD: CMSG_PET_CAST_SPELL, %s, cast_count: %u, spellid %u, unk_flags %u", guid.GetString().c_str(), cast_count, spellid, unk_flags);
- Creature* pet = _player->GetMap()->GetAnyTypeCreature(guid);
+ Creature* pet = GetPlayer()->GetMap()->GetAnyTypeCreature(guid);
if (!pet || (guid != _player->GetPetGuid() && guid != _player->GetCharmGuid()))
{
@@ -659,41 +471,15 @@ void WorldSession::HandlePetCastSpellOpcode( WorldPacket& recvPacket )
if (!pet->HasSpell(spellid) || IsPassiveSpell(spellInfo))
return;
- SpellCastTargets targets;
-
- recvPacket >> targets.ReadForCaster(pet);
-
- pet->clearUnitState(UNIT_STAT_MOVING);
+ SpellCastTargets* targets = new SpellCastTargets;
- Spell *spell = new Spell(pet, spellInfo, false);
- spell->m_cast_count = cast_count; // probably pending spell cast
- spell->m_targets = targets;
-
- SpellCastResult result = spell->CheckPetCast(NULL);
- if (result == SPELL_CAST_OK)
- {
- pet->AddCreatureSpellCooldown(spellid);
- if (pet->IsPet())
- {
- //10% chance to play special pet attack talk, else growl
- //actually this only seems to happen on special spells, fire shield for imp, torment for voidwalker, but it's stupid to check every spell
- if(((Pet*)pet)->getPetType() == SUMMON_PET && (urand(0, 100) < 10))
- pet->SendPetTalk((uint32)PET_TALK_SPECIAL_SPELL);
- else
- pet->SendPetAIReaction();
- }
+ recvPacket >> targets->ReadForCaster(pet);
- spell->prepare(&(spell->m_targets));
- }
- else
- {
- pet->SendPetCastFail(spellid, result);
- if (!pet->HasSpellCooldown(spellid))
- GetPlayer()->SendClearCooldown(spellid, pet);
+ if (pet->IsPet() || pet->isCharmed())
+ GetPlayer()->CallForAllControlledUnits(DoPetCastWithHelper(GetPlayer(), cast_count, targets, spellInfo ),CONTROLLED_PET|CONTROLLED_GUARDIANS|CONTROLLED_CHARM);
- spell->finish(false);
- delete spell;
- }
+ if (targets)
+ delete targets;
}
void WorldSession::SendPetNameInvalid(uint32 error, const std::string& name, DeclinedName *declinedName)
diff --git a/src/game/Unit.cpp b/src/game/Unit.cpp
index 50f3f88..58200bc 100644
--- a/src/game/Unit.cpp
+++ b/src/game/Unit.cpp
@@ -9554,6 +9554,245 @@ void CharmInfo::SetSpellAutocast( uint32 spell_id, bool state )
}
}
+void Unit::DoPetAction( Player* owner, uint8 flag, uint32 spellid, ObjectGuid petGuid, ObjectGuid targetGuid)
+{
+ if ((((Creature*)this)->IsPet() && !((Pet*)this)->IsInWorld()) || !GetCharmInfo())
+ return;
+
+ switch(flag)
+ {
+ case ACT_COMMAND: //0x07
+ // Maybe exists some flag that disable it at client side
+ if (petGuid.IsVehicle())
+ return;
+
+ switch(spellid)
+ {
+ case COMMAND_STAY: //flat=1792 //STAY
+ StopMoving();
+ GetMotionMaster()->Clear(false);
+ GetMotionMaster()->MoveIdle();
+ GetCharmInfo()->SetCommandState( COMMAND_STAY );
+ break;
+ case COMMAND_FOLLOW: //spellid=1792 //FOLLOW
+ AttackStop();
+ GetMotionMaster()->MoveFollow(owner,PET_FOLLOW_DIST,PET_FOLLOW_ANGLE);
+ GetCharmInfo()->SetCommandState( COMMAND_FOLLOW );
+ break;
+ case COMMAND_ATTACK: //spellid=1792 //ATTACK
+ {
+ Unit *TargetUnit = owner->GetMap()->GetUnit(targetGuid);
+ if(!TargetUnit)
+ return;
+
+ // not let attack friendly units.
+ if(owner->IsFriendlyTo(TargetUnit))
+ return;
+ // Not let attack through obstructions
+ if(!IsWithinLOSInMap(TargetUnit))
+ return;
+
+ // This is true if pet has no target or has target but targets differs.
+ if(getVictim() != TargetUnit)
+ {
+ if (getVictim())
+ AttackStop();
+
+ if (hasUnitState(UNIT_STAT_CONTROLLED))
+ {
+ Attack(TargetUnit, true);
+ SendPetAIReaction();
+ }
+ else
+ {
+ GetMotionMaster()->Clear();
+
+ if (((Creature*)this)->AI())
+ ((Creature*)this)->AI()->AttackStart(TargetUnit);
+
+ // 10% chance to play special pet attack talk, else growl
+ if(((Creature*)this)->IsPet() && ((Pet*)this)->getPetType() == SUMMON_PET && this != TargetUnit && roll_chance_i(10))
+ SendPetTalk((uint32)PET_TALK_ATTACK);
+ else
+ {
+ // 90% chance for pet and 100% chance for charmed creature
+ SendPetAIReaction();
+ }
+ }
+
+ }
+ break;
+ }
+ case COMMAND_ABANDON: // abandon (hunter pet) or dismiss (summoned pet)
+ if(((Creature*)this)->IsPet())
+ {
+ Pet* p = (Pet*)this;
+ if(p->getPetType() == HUNTER_PET)
+ p->Unsummon(PET_SAVE_AS_DELETED, owner);
+ else
+ //dismissing a summoned pet is like killing them (this prevents returning a soulshard...)
+ p->SetDeathState(CORPSE);
+ }
+ else // charmed
+ owner->Uncharm();
+ break;
+ default:
+ sLog.outError("WORLD: unknown PET flag Action %i and spellid %i.", uint32(flag), spellid);
+ }
+ break;
+ case ACT_REACTION: // 0x6
+ switch(spellid)
+ {
+ case REACT_PASSIVE: //passive
+ case REACT_DEFENSIVE: //recovery
+ case REACT_AGGRESSIVE: //activete
+ GetCharmInfo()->SetReactState( ReactStates(spellid) );
+ break;
+ }
+ break;
+ case ACT_DISABLED: // 0x81 spell (disabled), ignore
+ case ACT_PASSIVE: // 0x01
+ case ACT_ENABLED: // 0xC1 spell
+ {
+ Unit* unit_target = NULL;
+
+ if (!targetGuid.IsEmpty())
+ unit_target = owner->GetMap()->GetUnit(targetGuid);
+
+ // do not cast unknown spells
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellid );
+ if(!spellInfo)
+ {
+ sLog.outError("WORLD: unknown PET spell id %i", spellid);
+ return;
+ }
+
+ if (GetCharmInfo() && GetCharmInfo()->GetGlobalCooldownMgr().HasGlobalCooldown(spellInfo))
+ return;
+
+ for(int i = 0; i < MAX_EFFECT_INDEX;++i)
+ {
+ if(spellInfo->EffectImplicitTargetA[i] == TARGET_ALL_ENEMY_IN_AREA || spellInfo->EffectImplicitTargetA[i] == TARGET_ALL_ENEMY_IN_AREA_INSTANT || spellInfo->EffectImplicitTargetA[i] == TARGET_ALL_ENEMY_IN_AREA_CHANNELED)
+ return;
+ }
+
+ // do not cast not learned spells
+ if(!HasSpell(spellid) || IsPassiveSpell(spellid))
+ return;
+
+ clearUnitState(UNIT_STAT_MOVING);
+
+ Spell *spell = new Spell(this, spellInfo, false);
+
+ SpellCastResult result = spell->CheckPetCast(unit_target);
+
+ //auto turn to target unless possessed
+ if(result == SPELL_FAILED_UNIT_NOT_INFRONT && !HasAuraType(SPELL_AURA_MOD_POSSESS))
+ {
+ if(unit_target)
+ {
+ SetInFront(unit_target);
+ if (unit_target->GetTypeId() == TYPEID_PLAYER)
+ SendCreateUpdateToPlayer( (Player*)unit_target );
+ }
+ else if(Unit *unit_target2 = spell->m_targets.getUnitTarget())
+ {
+ SetInFront(unit_target2);
+ if (unit_target2->GetTypeId() == TYPEID_PLAYER)
+ SendCreateUpdateToPlayer( (Player*)unit_target2 );
+ }
+ if (Unit* powner = GetCharmerOrOwner())
+ if(powner->GetTypeId() == TYPEID_PLAYER)
+ SendCreateUpdateToPlayer((Player*)powner);
+ result = SPELL_CAST_OK;
+ }
+
+ if(result == SPELL_CAST_OK)
+ {
+ ((Creature*)this)->AddCreatureSpellCooldown(spellid);
+
+ unit_target = spell->m_targets.getUnitTarget();
+
+ //10% chance to play special pet attack talk, else growl
+ //actually this only seems to happen on special spells, fire shield for imp, torment for voidwalker, but it's stupid to check every spell
+ if(((Creature*)this)->IsPet() && (((Pet*)this)->getPetType() == SUMMON_PET) && (this != unit_target) && (urand(0, 100) < 10))
+ SendPetTalk((uint32)PET_TALK_SPECIAL_SPELL);
+ else
+ SendPetAIReaction();
+
+ if( unit_target && !owner->IsFriendlyTo(unit_target) && !HasAuraType(SPELL_AURA_MOD_POSSESS))
+ {
+ // This is true if pet has no target or has target but targets differs.
+ if (getVictim() != unit_target)
+ {
+ if (getVictim())
+ AttackStop();
+ GetMotionMaster()->Clear();
+ if (((Creature*)this)->AI())
+ ((Creature*)this)->AI()->AttackStart(unit_target);
+ }
+ }
+
+ spell->prepare(&(spell->m_targets));
+ }
+ else
+ {
+ if(HasAuraType(SPELL_AURA_MOD_POSSESS))
+ Spell::SendCastResult(owner,spellInfo,0,result);
+ else
+ SendPetCastFail(spellid, result);
+
+ if (!((Creature*)this)->HasSpellCooldown(spellid))
+ owner->SendClearCooldown(spellid, this);
+
+ spell->finish(false);
+ delete spell;
+ }
+ break;
+ }
+ default:
+ sLog.outError("WORLD: unknown PET flag Action %i and spellid %i.", uint32(flag), spellid);
+ }
+
+}
+
+void Unit::DoPetCastSpell( Player *owner, uint8 cast_count, SpellCastTargets* targets, SpellEntry const* spellInfo )
+{
+ Creature* pet = dynamic_cast<Creature*>(this);
+
+ clearUnitState(UNIT_STAT_MOVING);
+
+ Spell *spell = new Spell(pet, spellInfo, false);
+ spell->m_cast_count = cast_count; // probably pending spell cast
+ spell->m_targets = *targets;
+
+ SpellCastResult result = spell->CheckPetCast(NULL);
+ if (result == SPELL_CAST_OK)
+ {
+ pet->AddCreatureSpellCooldown(spellInfo->Id);
+ if (pet->IsPet())
+ {
+ //10% chance to play special pet attack talk, else growl
+ //actually this only seems to happen on special spells, fire shield for imp, torment for voidwalker, but it's stupid to check every spell
+ if(((Pet*)pet)->getPetType() == SUMMON_PET && (urand(0, 100) < 10))
+ pet->SendPetTalk((uint32)PET_TALK_SPECIAL_SPELL);
+ else
+ pet->SendPetAIReaction();
+ }
+
+ spell->prepare(&(spell->m_targets));
+ }
+ else
+ {
+ pet->SendPetCastFail(spellInfo->Id, result);
+ if (!pet->HasSpellCooldown(spellInfo->Id))
+ owner->SendClearCooldown(spellInfo->Id, pet);
+
+ spell->finish(false);
+ delete spell;
+ }
+}
+
bool Unit::isFrozen() const
{
return HasAuraState(AURA_STATE_FROZEN);
diff --git a/src/game/Unit.h b/src/game/Unit.h
index b8f5597..cde4a3d 100644
--- a/src/game/Unit.h
+++ b/src/game/Unit.h
@@ -299,6 +299,7 @@ class Creature;
class Spell;
class DynamicObject;
class GameObject;
+class SpellCastTargets;
class Item;
class Pet;
class PetAura;
@@ -1925,6 +1926,8 @@ class MANGOS_DLL_SPEC Unit : public WorldObject
void SendPetTalk (uint32 pettalk);
void SendPetAIReaction();
///----------End of Pet responses methods----------
+ void DoPetAction (Player* owner, uint8 flag, uint32 spellid, ObjectGuid petGuid, ObjectGuid targetGuid);
+ void DoPetCastSpell (Player *owner, uint8 cast_count, SpellCastTargets* targets, SpellEntry const* spellInfo);
void propagateSpeedChange() { GetMotionMaster()->propagateSpeedChange(); }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment