diff --git a/src/bindings/scripts/scripts/npc/npcs_special.cpp b/src/bindings/scripts/scripts/npc/npcs_special.cpp index 00116746a68..d762c8803e6 100644 --- a/src/bindings/scripts/scripts/npc/npcs_special.cpp +++ b/src/bindings/scripts/scripts/npc/npcs_special.cpp @@ -825,6 +825,71 @@ bool GossipSelect_npc_sayge(Player *player, Creature *_Creature, uint32 sender, return true; } +struct TRINITY_DLL_DECL npc_steam_tonkAI : public ScriptedAI +{ + npc_steam_tonkAI(Creature *c) : ScriptedAI(c) {Reset();} + + void Reset() {} + void Aggro(Unit *who) {} + + void OnPossess(bool apply) + { + if (apply) + { + // Initialize the action bar without the melee attack command + m_creature->InitCharmInfo(m_creature); + m_creature->GetCharmInfo()->InitEmptyActionBar(false); + + m_creature->SetAggressive(false); + } + else + m_creature->SetAggressive(true); + } + +}; + +CreatureAI* GetAI_npc_steam_tonk(Creature *_Creature) +{ + return new npc_steam_tonkAI(_Creature); +} + +#define SPELL_TONK_MINE_DETONATE 25099 + +struct TRINITY_DLL_DECL npc_tonk_mineAI : public ScriptedAI +{ + npc_tonk_mineAI(Creature *c) : ScriptedAI(c) + { + m_creature->SetAggressive(false); + Reset(); + } + + uint32 ExplosionTimer; + + void Reset() + { + ExplosionTimer = 3000; + } + + void Aggro(Unit *who) {} + void AttackStart(Unit *who) {} + void MoveInLineOfSight(Unit *who) {} + + void UpdateAI(const uint32 diff) + { + if (ExplosionTimer < diff) + { + m_creature->CastSpell(m_creature, SPELL_TONK_MINE_DETONATE, true); + m_creature->setDeathState(DEAD); // unsummon it + } else + ExplosionTimer -= diff; + } +}; + +CreatureAI* GetAI_npc_tonk_mine(Creature *_Creature) +{ + return new npc_tonk_mineAI(_Creature); +} + void AddSC_npcs_special() { Script *newscript; @@ -875,4 +940,14 @@ void AddSC_npcs_special() newscript->pGossipHello = &GossipHello_npc_sayge; newscript->pGossipSelect = &GossipSelect_npc_sayge; newscript->RegisterSelf(); + + newscript = new Script; + newscript->Name="npc_steam_tonk"; + newscript->GetAI = &GetAI_npc_steam_tonk; + newscript->RegisterSelf(); + + newscript = new Script; + newscript->Name="npc_tonk_mine"; + newscript->GetAI = &GetAI_npc_tonk_mine; + newscript->RegisterSelf(); } diff --git a/src/game/Creature.cpp b/src/game/Creature.cpp index 24b4f4e141b..b2b6639148e 100644 --- a/src/game/Creature.cpp +++ b/src/game/Creature.cpp @@ -147,7 +147,11 @@ Creature::~Creature() delete i_AI; i_AI = NULL; - DeletePossessedAI(); + if (i_AI_possessed) + { + delete i_AI_possessed; + i_AI_possessed = NULL; + } } void Creature::AddToWorld() @@ -562,13 +566,10 @@ void Creature::InitPossessedAI() i_AI->OnPossess(true); } -void Creature::DeletePossessedAI() +void Creature::DisablePossessedAI() { if (!i_AI_possessed) return; - delete i_AI_possessed; - i_AI_possessed = NULL; - // Signal the old AI that it's been re-enabled i_AI->OnPossess(false); } diff --git a/src/game/Creature.h b/src/game/Creature.h index 5f2d6e249f7..5b5b1efffc5 100644 --- a/src/game/Creature.h +++ b/src/game/Creature.h @@ -460,7 +460,7 @@ class TRINITY_DLL_SPEC Creature : public Unit bool AIM_Initialize(); void InitPossessedAI(); - void DeletePossessedAI(); + void DisablePossessedAI(); void AI_SendMoveToPacket(float x, float y, float z, uint32 time, uint32 MovementFlags, uint8 type); CreatureAI* AI() { return isPossessed() && i_AI_possessed ? i_AI_possessed : i_AI; } diff --git a/src/game/PetHandler.cpp b/src/game/PetHandler.cpp index f95be3a9352..d912f42c22b 100644 --- a/src/game/PetHandler.cpp +++ b/src/game/PetHandler.cpp @@ -97,6 +97,13 @@ void WorldSession::HandlePetAction( WorldPacket & recv_data ) break; case COMMAND_ATTACK: //spellid=1792 //ATTACK { + // Can't attack if owner is pacified + if (_player->HasAuraType(SPELL_AURA_MOD_PACIFY)) + { + //pet->SendPetCastFail(spellid, SPELL_FAILED_PACIFIED); + //TODO: Send proper error message to client + return; + } // only place where pet can be player pet->clearUnitState(UNIT_STAT_FOLLOW); uint64 selguid = _player->GetSelection(); diff --git a/src/game/Player.cpp b/src/game/Player.cpp index 13f37a50b05..8fe5baed6f4 100644 --- a/src/game/Player.cpp +++ b/src/game/Player.cpp @@ -18956,8 +18956,8 @@ void Player::RemovePossess(bool attack) if(attack) target->AddThreat(this, 1000000.0f); } - // Delete the assigned possessed AI - ((Creature*)target)->DeletePossessedAI(); + // Disable the assigned possessed AI + ((Creature*)target)->DisablePossessedAI(); } } diff --git a/src/game/PossessedAI.cpp b/src/game/PossessedAI.cpp index 6b803303185..17dd28f0b0f 100644 --- a/src/game/PossessedAI.cpp +++ b/src/game/PossessedAI.cpp @@ -1,127 +1,127 @@ -/* - * Copyright (C) 2008 Trinity - * - * Thanks to the original authors: MaNGOS - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "PossessedAI.h" -#include "Creature.h" -#include "World.h" - -void PossessedAI::AttackStart(Unit *u) -{ - if( !u ) - return; - - if (i_pet.getVictim() && u != i_pet.getVictim()) - i_pet.AttackStop(); - - if(i_pet.Attack(u, true)) - i_victimGuid = u->GetGUID(); - - // Do not autochase our target, and also make sure our current movement generator - // is removed since the motion master is reset before this function is called - i_pet.GetMotionMaster()->Clear(false); - i_pet.GetMotionMaster()->MoveIdle(); -} - -bool PossessedAI::_needToStop() const -{ - if(!i_pet.getVictim() || !i_pet.isAlive()) - return true; - - // This is needed for charmed creatures, as once their target was reset other effects can trigger threat - if(i_pet.getVictim() == i_pet.GetCharmer()) - return true; - - return !i_pet.canAttack(i_pet.getVictim()); -} - -void PossessedAI::_stopAttack() -{ - if( !i_victimGuid ) - return; - - Unit* victim = Unit::GetUnit(i_pet, i_victimGuid ); - - if ( !victim ) - return; - - assert(!i_pet.getVictim() || i_pet.getVictim() == victim); - - if( !i_pet.isAlive() ) - { - i_pet.StopMoving(); - i_pet.GetMotionMaster()->Clear(false); - i_pet.GetMotionMaster()->MoveIdle(); - i_victimGuid = 0; - i_pet.CombatStop(); - i_pet.getHostilRefManager().deleteReferences(); - - return; - } - - i_pet.GetMotionMaster()->Clear(false); - i_pet.GetMotionMaster()->MoveIdle(); - i_victimGuid = 0; - i_pet.AttackStop(); -} - -void PossessedAI::UpdateAI(const uint32 diff) -{ - // update i_victimGuid if i_pet.getVictim() !=0 and changed - if(i_pet.getVictim()) - i_victimGuid = i_pet.getVictim()->GetGUID(); - - // i_pet.getVictim() can't be used for check in case stop fighting, i_pet.getVictim() clear at Unit death etc. - if( i_victimGuid ) - { - if( _needToStop() ) - { - _stopAttack(); // i_victimGuid == 0 && i_pet.getVictim() == NULL now - return; - } - else if(i_pet.IsWithinCombatDist(i_pet.getVictim(), ATTACK_DISTANCE) && i_pet.isAttackReady()) - { - i_pet.AttackerStateUpdate(i_pet.getVictim()); - - i_pet.resetAttackTimer(); - - if( _needToStop() ) - _stopAttack(); - } - } -} - -bool PossessedAI::_isVisible(Unit *u) const -{ - return i_pet.GetDistance(u) < sWorld.getConfig(CONFIG_SIGHT_MONSTER) - && u->isVisibleForOrDetect(&i_pet,true); -} - -void PossessedAI::JustDied(Unit *u) -{ - // We died while possessed, disable our loot - i_pet.RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE); -} - -void PossessedAI::KilledUnit(Unit* victim) -{ - // We killed a creature, disable victim's loot - if (victim->GetTypeId() == TYPEID_UNIT) - victim->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE); -} +/* + * Copyright (C) 2008 Trinity + * + * Thanks to the original authors: MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "PossessedAI.h" +#include "Creature.h" +#include "World.h" + +void PossessedAI::AttackStart(Unit *u) +{ + if( !u || i_pet.GetCharmer()->HasAuraType(SPELL_AURA_MOD_PACIFY)) + return; + + if (i_pet.getVictim() && u != i_pet.getVictim()) + i_pet.AttackStop(); + + if(i_pet.Attack(u, true)) + i_victimGuid = u->GetGUID(); + + // Do not autochase our target, and also make sure our current movement generator + // is removed since the motion master is reset before this function is called + i_pet.GetMotionMaster()->Clear(false); + i_pet.GetMotionMaster()->MoveIdle(); +} + +bool PossessedAI::_needToStop() const +{ + if(!i_pet.getVictim() || !i_pet.isAlive()) + return true; + + // This is needed for charmed creatures, as once their target was reset other effects can trigger threat + if(i_pet.getVictim() == i_pet.GetCharmer()) + return true; + + return !i_pet.canAttack(i_pet.getVictim()); +} + +void PossessedAI::_stopAttack() +{ + if( !i_victimGuid ) + return; + + Unit* victim = Unit::GetUnit(i_pet, i_victimGuid ); + + if ( !victim ) + return; + + assert(!i_pet.getVictim() || i_pet.getVictim() == victim); + + if( !i_pet.isAlive() ) + { + i_pet.StopMoving(); + i_pet.GetMotionMaster()->Clear(false); + i_pet.GetMotionMaster()->MoveIdle(); + i_victimGuid = 0; + i_pet.CombatStop(); + i_pet.getHostilRefManager().deleteReferences(); + + return; + } + + i_pet.GetMotionMaster()->Clear(false); + i_pet.GetMotionMaster()->MoveIdle(); + i_victimGuid = 0; + i_pet.AttackStop(); +} + +void PossessedAI::UpdateAI(const uint32 diff) +{ + // update i_victimGuid if i_pet.getVictim() !=0 and changed + if(i_pet.getVictim()) + i_victimGuid = i_pet.getVictim()->GetGUID(); + + // i_pet.getVictim() can't be used for check in case stop fighting, i_pet.getVictim() clear at Unit death etc. + if( i_victimGuid ) + { + if( _needToStop() ) + { + _stopAttack(); // i_victimGuid == 0 && i_pet.getVictim() == NULL now + return; + } + else if(i_pet.IsWithinCombatDist(i_pet.getVictim(), ATTACK_DISTANCE) && i_pet.isAttackReady() && !i_pet.GetCharmer()->HasAuraType(SPELL_AURA_MOD_PACIFY)) + { + i_pet.AttackerStateUpdate(i_pet.getVictim()); + + i_pet.resetAttackTimer(); + + if( _needToStop() ) + _stopAttack(); + } + } +} + +bool PossessedAI::_isVisible(Unit *u) const +{ + return i_pet.GetDistance(u) < sWorld.getConfig(CONFIG_SIGHT_MONSTER) + && u->isVisibleForOrDetect(&i_pet,true); +} + +void PossessedAI::JustDied(Unit *u) +{ + // We died while possessed, disable our loot + i_pet.RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE); +} + +void PossessedAI::KilledUnit(Unit* victim) +{ + // We killed a creature, disable victim's loot + if (victim->GetTypeId() == TYPEID_UNIT) + victim->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE); +} diff --git a/src/game/SharedDefines.h b/src/game/SharedDefines.h index 37dca1aa656..dc22b3affbc 100644 --- a/src/game/SharedDefines.h +++ b/src/game/SharedDefines.h @@ -2069,7 +2069,7 @@ enum SummonType SUMMON_TYPE_CRITTER2 = 407, SUMMON_TYPE_CRITTER3 = 307, SUMMON_TYPE_UNKNOWN5 = 409, - SUMMON_TYPE_UNKNOWN2 = 427, + SUMMON_TYPE_POSESSED3 = 427, SUMMON_TYPE_POSESSED2 = 428 }; diff --git a/src/game/Spell.cpp b/src/game/Spell.cpp index da6d67a3c6c..59d019800ed 100644 --- a/src/game/Spell.cpp +++ b/src/game/Spell.cpp @@ -1533,6 +1533,7 @@ void Spell::SetTargetMap(uint32 i,uint32 cur,std::list &TagUnitMap) }break; case TARGET_SCRIPT: case TARGET_SCRIPT_COORDINATES: + case TARGET_UNIT_AREA_SCRIPT: { SpellScriptTarget::const_iterator lower = spellmgr.GetBeginSpellScriptTarget(m_spellInfo->Id); SpellScriptTarget::const_iterator upper = spellmgr.GetEndSpellScriptTarget(m_spellInfo->Id); @@ -3865,6 +3866,7 @@ uint8 Spell::CanCast(bool strict) { case SUMMON_TYPE_POSESSED: case SUMMON_TYPE_POSESSED2: + case SUMMON_TYPE_POSESSED3: case SUMMON_TYPE_DEMON: case SUMMON_TYPE_SUMMON: { diff --git a/src/game/SpellAuras.cpp b/src/game/SpellAuras.cpp index 55203554665..35d39a22b27 100644 --- a/src/game/SpellAuras.cpp +++ b/src/game/SpellAuras.cpp @@ -49,7 +49,6 @@ #include "GridNotifiers.h" #include "GridNotifiersImpl.h" #include "CellImpl.h" -#include "TemporarySummon.h" #define NULL_AURA_SLOT 0xFF @@ -2024,13 +2023,6 @@ void Aura::HandleAuraDummy(bool apply, bool Real) m_target->CastSpell(m_target,47287,true,NULL,this); return; } - - // Eye of Kilrogg, unsummon eye when aura is gone - if(GetId() == 126 && caster->GetTypeId() == TYPEID_PLAYER && caster->GetCharm()) - { - ((TemporarySummon*)caster->GetCharm())->UnSummon(); - return; - } } // AT APPLY & REMOVE diff --git a/src/game/SpellEffects.cpp b/src/game/SpellEffects.cpp index 0cbd727ac9d..6984fc5789c 100644 --- a/src/game/SpellEffects.cpp +++ b/src/game/SpellEffects.cpp @@ -3150,6 +3150,7 @@ void Spell::EffectSummonType(uint32 i) break; case SUMMON_TYPE_POSESSED: case SUMMON_TYPE_POSESSED2: + case SUMMON_TYPE_POSESSED3: EffectSummonPossessed(i); break; case SUMMON_TYPE_WILD: @@ -3174,7 +3175,6 @@ void Spell::EffectSummonType(uint32 i) EffectSummonTotem(i); break; case SUMMON_TYPE_UNKNOWN1: - case SUMMON_TYPE_UNKNOWN2: case SUMMON_TYPE_UNKNOWN3: case SUMMON_TYPE_UNKNOWN4: case SUMMON_TYPE_UNKNOWN5: diff --git a/src/game/SpellHandler.cpp b/src/game/SpellHandler.cpp index 07d8b18b543..6caab76e061 100644 --- a/src/game/SpellHandler.cpp +++ b/src/game/SpellHandler.cpp @@ -33,6 +33,7 @@ #include "MapManager.h" #include "ScriptCalls.h" #include "Totem.h" +#include "TemporarySummon.h" void WorldSession::HandleUseItemOpcode(WorldPacket& recvPacket) { @@ -376,6 +377,14 @@ void WorldSession::HandleCancelAuraOpcode( WorldPacket& recvPacket) ((Unit*)_player->GetFarsightTarget())->RemoveAurasDueToSpellByCancel(spellId); return; } + else if (spellInfo->Effect[i] == SPELL_EFFECT_SUMMON && + (spellInfo->EffectMiscValueB[i] == SUMMON_TYPE_POSESSED || + spellInfo->EffectMiscValueB[i] == SUMMON_TYPE_POSESSED2 || + spellInfo->EffectMiscValueB[i] == SUMMON_TYPE_POSESSED3)) + { + // Possession is removed in the UnSummon function + ((TemporarySummon*)_player->GetCharm())->UnSummon(); + } } } diff --git a/src/game/Unit.cpp b/src/game/Unit.cpp index 73dce3e043a..e0fa1b0fc89 100644 --- a/src/game/Unit.cpp +++ b/src/game/Unit.cpp @@ -46,6 +46,7 @@ #include "GridNotifiersImpl.h" #include "CellImpl.h" #include "Path.h" +#include "TemporarySummon.h" #include @@ -3411,6 +3412,21 @@ void Unit::InterruptNonMeleeSpells(bool withDelayed, uint32 spell_id) // channeled spells are interrupted if they are not finished, even if they are delayed if (m_currentSpells[CURRENT_CHANNELED_SPELL] && (!spell_id || m_currentSpells[CURRENT_CHANNELED_SPELL]->m_spellInfo->Id==spell_id)) { + // Unsummon any summoned as possessed creatures on channel interrupt + SpellEntry const *spellInfo = m_currentSpells[CURRENT_CHANNELED_SPELL]->m_spellInfo; + for (int i = 0; i < 3; i++) + { + if (spellInfo->Effect[i] == SPELL_EFFECT_SUMMON && + (spellInfo->EffectMiscValueB[i] == SUMMON_TYPE_POSESSED || + spellInfo->EffectMiscValueB[i] == SUMMON_TYPE_POSESSED2 || + spellInfo->EffectMiscValueB[i] == SUMMON_TYPE_POSESSED3)) + { + // Possession is removed in the UnSummon function + if (GetCharm()) + ((TemporarySummon*)GetCharm())->UnSummon(); + } + } + if (m_currentSpells[CURRENT_CHANNELED_SPELL]->getState() != SPELL_STATE_FINISHED) m_currentSpells[CURRENT_CHANNELED_SPELL]->cancel(); m_currentSpells[CURRENT_CHANNELED_SPELL]->SetReferencedFromCurrent(false); @@ -9790,7 +9806,7 @@ CharmInfo* Unit::InitCharmInfo(Unit *charm) } CharmInfo::CharmInfo(Unit* unit) -: m_unit(unit), m_CommandState(COMMAND_FOLLOW), m_reactState(REACT_PASSIVE), m_petnumber(0) +: m_unit(unit), m_CommandState(COMMAND_FOLLOW), m_reactState(REACT_PASSIVE), m_petnumber(0), m_barInit(false) { for(int i =0; i<4; ++i) { @@ -9801,6 +9817,9 @@ CharmInfo::CharmInfo(Unit* unit) void CharmInfo::InitPetActionBar() { + if (m_barInit) + return; + // the first 3 SpellOrActions are attack, follow and stay for(uint32 i = 0; i < 3; i++) { @@ -9815,17 +9834,25 @@ void CharmInfo::InitPetActionBar() PetActionBar[i + 3].Type = ACT_DISABLED; PetActionBar[i + 3].SpellOrAction = 0; } + m_barInit = true; } -void CharmInfo::InitEmptyActionBar() +void CharmInfo::InitEmptyActionBar(bool withAttack) { - for(uint32 x = 1; x < 10; ++x) + if (m_barInit) + return; + + for(uint32 x = 0; x < 10; ++x) { PetActionBar[x].Type = ACT_CAST; PetActionBar[x].SpellOrAction = 0; } - PetActionBar[0].Type = ACT_COMMAND; - PetActionBar[0].SpellOrAction = COMMAND_ATTACK; + if (withAttack) + { + PetActionBar[0].Type = ACT_COMMAND; + PetActionBar[0].SpellOrAction = COMMAND_ATTACK; + } + m_barInit = true; } void CharmInfo::InitPossessCreateSpells() diff --git a/src/game/Unit.h b/src/game/Unit.h index cd5770cccd6..5ae34624c96 100644 --- a/src/game/Unit.h +++ b/src/game/Unit.h @@ -640,7 +640,7 @@ struct CharmSpellEntry typedef std::list SharedVisionList; -struct CharmInfo +struct TRINITY_DLL_SPEC CharmInfo { public: explicit CharmInfo(Unit* unit); @@ -657,7 +657,7 @@ struct CharmInfo void InitPossessCreateSpells(); void InitCharmCreateSpells(); void InitPetActionBar(); - void InitEmptyActionBar(); + void InitEmptyActionBar(bool withAttack = true); //return true if successful bool AddSpellToAB(uint32 oldid, uint32 newid, ActiveStates newstate = ACT_DECIDE); void ToggleCreatureAutocast(uint32 spellid, bool apply); @@ -671,6 +671,7 @@ struct CharmInfo CommandStates m_CommandState; ReactStates m_reactState; uint32 m_petnumber; + bool m_barInit; }; // for clearing special attacks