diff options
30 files changed, 278 insertions, 54 deletions
diff --git a/sql/FULL/world_scripts_full.sql b/sql/FULL/world_scripts_full.sql index 408a2117bea..c4cdeab90a7 100644 --- a/sql/FULL/world_scripts_full.sql +++ b/sql/FULL/world_scripts_full.sql @@ -1092,6 +1092,7 @@ UPDATE `creature_template` SET `ScriptName`='mob_spawn_of_marli' WHERE `entry`=1 UPDATE `creature_template` SET `ScriptName`='mob_batrider' WHERE `entry`=14965; UPDATE `creature_template` SET `ScriptName`='mob_shade_of_jindo' WHERE `entry`=14986; UPDATE `creature_template` SET `ScriptName`='mob_ohgan' WHERE `entry`=14988; +UPDATE `creature_template` SET `ScriptName`='npc_mirror_image' WHERE `entry`=31216; UPDATE `creature_template` SET `ScriptName`='EventAI', `modelid_A` = 16925,`modelid_H` = 16925, `minmana` = 1000000,`maxmana` = 1000000, `unit_flags` = 33554434 WHERE `entry` IN(29998, 33753, 33752, 33751, 33750); DELETE FROM `creature_ai_scripts` WHERE `creature_id` IN (29998, 33753, 33752, 33751, 33750); diff --git a/sql/FULL/world_spell_full.sql b/sql/FULL/world_spell_full.sql index 550ec360dd3..6d0ee52b9cd 100644 --- a/sql/FULL/world_spell_full.sql +++ b/sql/FULL/world_spell_full.sql @@ -140,7 +140,9 @@ INSERT INTO `spell_linked_spell` (`spell_trigger`, `spell_effect`, `type`, `comm -- -------- -- TARGET -- -------- - +-- Mirror Image +DELETE FROM `spell_script_target` WHERE `entry` IN (58836); +INSERT INTO `spell_script_target` VALUES (58836, 1, 31216); -- zulaman DELETE FROM `spell_script_target` WHERE `entry` IN (42577,42471,43734,42631); diff --git a/sql/FULL/world_tmp_full.sql b/sql/FULL/world_tmp_full.sql index c62373d9a90..9931dc50ee3 100644 --- a/sql/FULL/world_tmp_full.sql +++ b/sql/FULL/world_tmp_full.sql @@ -179,6 +179,8 @@ spell6 = VALUES(spell6), spell7 = VALUES(spell7), spell8 = VALUES(spell8); +UPDATE `creature_template` SET `spell1`=59638, `spell2` = 59637 WHERE `entry`=31216; # Mirror Image + # Spore UPDATE `creature_template` SET `minlevel`='80',`maxlevel`='80',`faction_A`='21',`faction_H`='21' WHERE entry IN (16286,30068); diff --git a/sql/updates/4527_world_spell_script_target.sql b/sql/updates/4527_world_spell_script_target.sql new file mode 100644 index 00000000000..f1bf84d509a --- /dev/null +++ b/sql/updates/4527_world_spell_script_target.sql @@ -0,0 +1,4 @@ +DELETE FROM `spell_script_target` WHERE `entry` IN (58836); +INSERT INTO `spell_script_target` VALUES (58836, 1, 31216); +UPDATE `creature_template` SET `ScriptName`='npc_mirror_image' WHERE `entry`=31216; +UPDATE `creature_template` SET `spell1`=59638, `spell2` = 59637 WHERE `entry`=31216; diff --git a/src/bindings/scripts/VC90/90ScriptDev2.vcproj b/src/bindings/scripts/VC90/90ScriptDev2.vcproj index d3f7fc71e79..4edd104a1c6 100644 --- a/src/bindings/scripts/VC90/90ScriptDev2.vcproj +++ b/src/bindings/scripts/VC90/90ScriptDev2.vcproj @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="windows-1250"?> <VisualStudioProject ProjectType="Visual C++" - Version="9.00" + Version="9,00" Name="TrinityScript" ProjectGUID="{4295C8A9-79B7-4354-8064-F05FB9CA0C96}" RootNamespace="ScriptDev2" @@ -555,14 +555,14 @@ <Filter Name="Blackfathom Depths" > - <File - RelativePath="..\scripts\zone\blackfathom_depths\instance_blackfathom_deeps.cpp" - > - </File> - <File - RelativePath="..\scripts\zone\blackfathom_depths\def_blackfathom_deeps.h" - > - </File> + <File + RelativePath="..\scripts\zone\blackfathom_depths\def_blackfathom_deeps.h" + > + </File> + <File + RelativePath="..\scripts\zone\blackfathom_depths\instance_blackfathom_deeps.cpp" + > + </File> </Filter> <Filter Name="Bloodmyst Isle" @@ -725,10 +725,12 @@ > </Filter> <Filter - Name="Desolace"> - <File - RelativePath="..\scripts\zone\desolace\desolace.cpp"> - </File> + Name="Desolace" + > + <File + RelativePath="..\scripts\zone\desolace\desolace.cpp" + > + </File> </Filter> <Filter Name="Dire Maul" @@ -838,7 +840,7 @@ Name="Razorfen Kraul" > <File - RelativePath="..\scripts\zone\razorfen_kraul\razorfen_kraul.cpp" + RelativePath="..\scripts\zone\razorfen_kraul\def_razorfen_kraul.h" > </File> <File @@ -846,7 +848,7 @@ > </File> <File - RelativePath="..\scripts\zone\razorfen_kraul\def_razorfen_kraul.h" + RelativePath="..\scripts\zone\razorfen_kraul\razorfen_kraul.cpp" > </File> </Filter> diff --git a/src/bindings/scripts/scripts/npc/npcs_special.cpp b/src/bindings/scripts/scripts/npc/npcs_special.cpp index 67105b192f4..6c44b5b9ae3 100644 --- a/src/bindings/scripts/scripts/npc/npcs_special.cpp +++ b/src/bindings/scripts/scripts/npc/npcs_special.cpp @@ -1636,6 +1636,32 @@ CreatureAI* GetAI_mob_mojo(Creature *_Creature) return new mob_mojoAI (_Creature); } +struct TRINITY_DLL_DECL npc_mirror_image : public SpellAI +{ + npc_mirror_image(Creature *c) : SpellAI(c) {} + Unit * owner; + void Reset() + { + if (m_creature->isSummon()) + owner = ((TempSummon*)me)->GetOwner(); + if (!owner) + return; + me->SetDisplayId(owner->GetDisplayId()); + owner->SetLevel(owner->getLevel()); + // Inherit Master's Threat List (not yet implemented) + owner->CastSpell((Unit*)NULL, 58838, true); + // here mirror image casts on summoner spell (not present in client dbc) 49866 + // here should be auras (not present in client dbc): 35657, 35658, 35659, 35660 selfcasted by mirror images (stats related?) + // Clone Me! + owner->CastSpell(me, 45204, false); + } +}; + +CreatureAI* GetAI_npc_mirror_image(Creature *_Creature) +{ + return new npc_mirror_image (_Creature); +} + void AddSC_npcs_special() { Script *newscript; @@ -1728,6 +1754,11 @@ void AddSC_npcs_special() newscript->RegisterSelf(); newscript = new Script; + newscript->Name="npc_mirror_image"; + newscript->GetAI = &GetAI_npc_mirror_image; + newscript->RegisterSelf(); + + newscript = new Script; newscript->Name="mob_mojo"; newscript->GetAI = &GetAI_mob_mojo; newscript->RegisterSelf(); diff --git a/src/bindings/scripts/scripts/zone/darkshore/darkshore.cpp b/src/bindings/scripts/scripts/zone/darkshore/darkshore.cpp index bb4009956f9..d266e3721ae 100644 --- a/src/bindings/scripts/scripts/zone/darkshore/darkshore.cpp +++ b/src/bindings/scripts/scripts/zone/darkshore/darkshore.cpp @@ -227,7 +227,7 @@ struct TRINITY_DLL_DECL npc_threshwackonatorAI : public ScriptedAI if(me->isAlive()) { if(Player* pPlayer = Unit::GetPlayer(PlayerGUID)) - me->GetMotionMaster()->MoveFollow(pPlayer, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE); + me->GetMotionMaster()->MoveFollow(pPlayer, PET_FOLLOW_DIST, m_creature->GetFollowAngle()); else { me->GetMotionMaster()->MovementExpired(); @@ -246,7 +246,7 @@ struct TRINITY_DLL_DECL npc_threshwackonatorAI : public ScriptedAI me->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE); if(Player* pPlayer = Unit::GetPlayer(PlayerGUID)) - me->GetMotionMaster()->MoveFollow(pPlayer, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE); + me->GetMotionMaster()->MoveFollow(pPlayer, PET_FOLLOW_DIST, m_creature->GetFollowAngle()); DoScriptText(EMOTE_START, me); } diff --git a/src/bindings/scripts/scripts/zone/desolace/desolace.cpp b/src/bindings/scripts/scripts/zone/desolace/desolace.cpp index e1366d51727..1f0e2e71286 100644 --- a/src/bindings/scripts/scripts/zone/desolace/desolace.cpp +++ b/src/bindings/scripts/scripts/zone/desolace/desolace.cpp @@ -137,7 +137,7 @@ bool EffectDummyCreature_npc_aged_dying_ancient_kodo(Unit *pCaster, uint32 spell if (pCreatureTarget->GetMotionMaster()->GetCurrentMovementGeneratorType() == WAYPOINT_MOTION_TYPE) pCreatureTarget->GetMotionMaster()->MoveIdle(); - pCreatureTarget->GetMotionMaster()->MoveFollow(pCaster, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE); + pCreatureTarget->GetMotionMaster()->MoveFollow(pCaster, PET_FOLLOW_DIST, pCreatureTarget->GetFollowAngle()); } //always return true when we are handling this spell and effect diff --git a/src/bindings/scripts/scripts/zone/teldrassil/teldrassil.cpp b/src/bindings/scripts/scripts/zone/teldrassil/teldrassil.cpp index eff160843de..9efc1e890b6 100644 --- a/src/bindings/scripts/scripts/zone/teldrassil/teldrassil.cpp +++ b/src/bindings/scripts/scripts/zone/teldrassil/teldrassil.cpp @@ -81,7 +81,7 @@ struct TRINITY_DLL_DECL npc_mistAI : public ScriptedAI if (m_creature->isAlive()) { if (Player* pPlayer = Unit::GetPlayer(uiPlayerGUID)) - m_creature->GetMotionMaster()->MoveFollow(pPlayer, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE); + m_creature->GetMotionMaster()->MoveFollow(pPlayer, PET_FOLLOW_DIST, m_creature->GetFollowAngle()); else { m_creature->GetMotionMaster()->MovementExpired(); @@ -100,7 +100,7 @@ struct TRINITY_DLL_DECL npc_mistAI : public ScriptedAI m_creature->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE); if (Player* pPlayer = Unit::GetPlayer(uiPlayer)) - m_creature->GetMotionMaster()->MoveFollow(pPlayer, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE); + m_creature->GetMotionMaster()->MoveFollow(pPlayer, PET_FOLLOW_DIST, m_creature->GetFollowAngle()); } void DoComplete() diff --git a/src/game/Creature.cpp b/src/game/Creature.cpp index 8a15cb88167..80fb7e43fec 100644 --- a/src/game/Creature.cpp +++ b/src/game/Creature.cpp @@ -2577,3 +2577,4 @@ time_t Creature::GetLinkedCreatureRespawnTime() const return 0; } + diff --git a/src/game/Creature.h b/src/game/Creature.h index ef4e99da8c0..61cb390b02f 100644 --- a/src/game/Creature.h +++ b/src/game/Creature.h @@ -163,6 +163,7 @@ enum SummonMask SUMMON_MASK_VEHICLE = 0x00000020, SUMMON_MASK_PUPPET = 0x00000040, SUMMON_MASK_HUNTER_PET = 0x00000080, + SUMMON_MASK_CONTROLABLE_GUARDIAN = 0x00000100, }; // GCC have alternative #pragma pack(N) syntax and old gcc version not support pack(push,N), also any gcc version not support it at some platform diff --git a/src/game/CreatureAI.cpp b/src/game/CreatureAI.cpp index 654c6450b02..6c6df7ac09e 100644 --- a/src/game/CreatureAI.cpp +++ b/src/game/CreatureAI.cpp @@ -148,7 +148,6 @@ bool CreatureAI::UpdateVictim() { if(!me->isInCombat()) return false; - if(Unit *victim = me->SelectVictim()) AttackStart(victim); return me->getVictim(); @@ -195,7 +194,10 @@ void CreatureAI::EnterEvadeMode() return; if(Unit *owner = me->GetCharmerOrOwner()) - me->GetMotionMaster()->MoveFollow(owner, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE, MOTION_SLOT_ACTIVE); + { + me->GetMotionMaster()->Clear(false); + me->GetMotionMaster()->MoveFollow(owner, PET_FOLLOW_DIST, m_creature->GetFollowAngle(), MOTION_SLOT_ACTIVE); + } else me->GetMotionMaster()->MoveTargetedHome(); diff --git a/src/game/CreatureAISelector.cpp b/src/game/CreatureAISelector.cpp index 65e50a2e41b..d507a82dabb 100644 --- a/src/game/CreatureAISelector.cpp +++ b/src/game/CreatureAISelector.cpp @@ -54,13 +54,13 @@ namespace FactorySelector // select by NPC flags if(!ai_factory) { - if(creature->isGuardian() && ((Guardian*)creature)->GetOwner()->GetTypeId() == TYPEID_PLAYER) + if(creature->HasSummonMask(SUMMON_MASK_CONTROLABLE_GUARDIAN) && ((Guardian*)creature)->GetOwner()->GetTypeId() == TYPEID_PLAYER) ai_factory = ai_registry.GetRegistryItem("PetAI"); else if(creature->isVehicle() || creature->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK)) ai_factory = ai_registry.GetRegistryItem("NullCreatureAI"); else if(creature->isGuard()) ai_factory = ai_registry.GetRegistryItem("GuardAI"); - else if(creature->isGuardian()) + else if(creature->HasSummonMask(SUMMON_MASK_CONTROLABLE_GUARDIAN)) ai_factory = ai_registry.GetRegistryItem("PetAI"); else if(creature->isTotem()) ai_factory = ai_registry.GetRegistryItem("TotemAI"); diff --git a/src/game/Level2.cpp b/src/game/Level2.cpp index 374de8d6997..7c506c7d212 100644 --- a/src/game/Level2.cpp +++ b/src/game/Level2.cpp @@ -1661,7 +1661,7 @@ bool ChatHandler::HandleNpcFollowCommand(const char* /*args*/) } // Follow player - Using pet's default dist and angle - creature->GetMotionMaster()->MoveFollow(player, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE); + creature->GetMotionMaster()->MoveFollow(player, PET_FOLLOW_DIST, creature->GetFollowAngle()); PSendSysMessage(LANG_CREATURE_FOLLOW_YOU_NOW, creature->GetName()); return true; diff --git a/src/game/Object.cpp b/src/game/Object.cpp index 5b5ffc350a9..76041d8792c 100644 --- a/src/game/Object.cpp +++ b/src/game/Object.cpp @@ -1718,6 +1718,8 @@ TempSummon *Map::SummonCreature(uint32 entry, float x, float y, float z, float a mask = SUMMON_MASK_PUPPET; else if(properties->Type == SUMMON_TYPE_MINIPET) mask = SUMMON_MASK_MINION; + else if (properties->Flags & 512) // Mirror Image, Summon Gargoyle + mask = SUMMON_MASK_GUARDIAN; } uint32 phase = PHASEMASK_NORMAL, team = 0; diff --git a/src/game/Opcodes.cpp b/src/game/Opcodes.cpp index b66786f9d91..ddf7c74146a 100644 --- a/src/game/Opcodes.cpp +++ b/src/game/Opcodes.cpp @@ -1053,7 +1053,7 @@ OpcodeHandler opcodeTable[NUM_MSG_TYPES] = /*0x3FE*/ { "MSG_GUILD_BANK_MONEY_WITHDRAWN", STATUS_LOGGEDIN, &WorldSession::HandleGuildBankMoneyWithdrawn }, /*0x3FF*/ { "MSG_GUILD_EVENT_LOG_QUERY", STATUS_LOGGEDIN, &WorldSession::HandleGuildEventLogQueryOpcode }, /*0x400*/ { "CMSG_MAELSTROM_RENAME_GUILD", STATUS_NEVER, &WorldSession::Handle_NULL }, - /*0x401*/ { "CMSG_GET_MIRRORIMAGE_DATA", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x401*/ { "CMSG_GET_MIRRORIMAGE_DATA", STATUS_LOGGEDIN, &WorldSession::HandleMirrrorImageDataRequest }, /*0x402*/ { "SMSG_MIRRORIMAGE_DATA", STATUS_NEVER, &WorldSession::Handle_ServerSide }, /*0x403*/ { "SMSG_FORCE_DISPLAY_UPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, /*0x404*/ { "SMSG_SPELL_CHANCE_RESIST_PUSHBACK", STATUS_NEVER, &WorldSession::Handle_ServerSide }, diff --git a/src/game/Pet.cpp b/src/game/Pet.cpp index 8145b4a5f45..8d8b667eb7f 100644 --- a/src/game/Pet.cpp +++ b/src/game/Pet.cpp @@ -166,7 +166,7 @@ bool Pet::LoadPetFromDB( Player* owner, uint32 petentry, uint32 petnumber, bool } float px, py, pz; - owner->GetClosePoint(px, py, pz, GetObjectSize(), PET_FOLLOW_DIST, PET_FOLLOW_ANGLE); + owner->GetClosePoint(px, py, pz, GetObjectSize(), PET_FOLLOW_DIST, GetFollowAngle()); Relocate(px, py, pz, owner->GetOrientation()); if (!IsPositionValid()) diff --git a/src/game/PetAI.cpp b/src/game/PetAI.cpp index 7851ee6773f..7c2889d1ef1 100644 --- a/src/game/PetAI.cpp +++ b/src/game/PetAI.cpp @@ -74,7 +74,7 @@ void PetAI::_stopAttack() if(owner && m_creature->GetCharmInfo() && m_creature->GetCharmInfo()->HasCommandState(COMMAND_FOLLOW)) { - m_creature->GetMotionMaster()->MoveFollow(owner,PET_FOLLOW_DIST,PET_FOLLOW_ANGLE); + m_creature->GetMotionMaster()->MoveFollow(owner,PET_FOLLOW_DIST, m_creature->GetFollowAngle()); } else { @@ -112,8 +112,10 @@ void PetAI::UpdateAI(const uint32 diff) if(owner->isInCombat() && !(m_creature->HasReactState(REACT_PASSIVE) || m_creature->GetCharmInfo()->HasCommandState(COMMAND_STAY))) AttackStart(owner->getAttackerForHelper()); else if(m_creature->GetCharmInfo()->HasCommandState(COMMAND_FOLLOW) && !m_creature->hasUnitState(UNIT_STAT_FOLLOW)) - m_creature->GetMotionMaster()->MoveFollow(owner,PET_FOLLOW_DIST,PET_FOLLOW_ANGLE); + m_creature->GetMotionMaster()->MoveFollow(owner,PET_FOLLOW_DIST, m_creature->GetFollowAngle()); } + else if (owner && !m_creature->hasUnitState(UNIT_STAT_FOLLOW)) // no charm info and no victim + m_creature->GetMotionMaster()->MoveFollow(owner,PET_FOLLOW_DIST, m_creature->GetFollowAngle()); if(!me->GetCharmInfo()) return; diff --git a/src/game/PetHandler.cpp b/src/game/PetHandler.cpp index 55f7fa42055..3de8ee028d3 100644 --- a/src/game/PetHandler.cpp +++ b/src/game/PetHandler.cpp @@ -102,7 +102,7 @@ void WorldSession::HandlePetActionHelper(Unit *pet, uint64 guid1, uint16 spellid case COMMAND_FOLLOW: //spellid=1792 //FOLLOW pet->AttackStop(); pet->InterruptNonMeleeSpells(false); - pet->GetMotionMaster()->MoveFollow(_player,PET_FOLLOW_DIST,PET_FOLLOW_ANGLE); + pet->GetMotionMaster()->MoveFollow(_player,PET_FOLLOW_DIST,pet->GetFollowAngle()); charmInfo->SetCommandState( COMMAND_FOLLOW ); break; case COMMAND_ATTACK: //spellid=1792 //ATTACK diff --git a/src/game/SpellAuraDefines.h b/src/game/SpellAuraDefines.h index 55d5e619128..1a116173135 100644 --- a/src/game/SpellAuraDefines.h +++ b/src/game/SpellAuraDefines.h @@ -292,7 +292,7 @@ enum AuraType SPELL_AURA_COMPREHEND_LANGUAGE = 244, SPELL_AURA_MOD_AURA_DURATION_BY_DISPEL = 245, SPELL_AURA_MOD_AURA_DURATION_BY_DISPEL_NOT_STACK = 246, - SPELL_AURA_247 = 247, + SPELL_AURA_CLONE_CASTER = 247, SPELL_AURA_MOD_COMBAT_RESULT_CHANCE = 248, SPELL_AURA_CONVERT_RUNE = 249, SPELL_AURA_MOD_INCREASE_HEALTH_2 = 250, @@ -324,7 +324,7 @@ enum AuraType SPELL_AURA_276 = 276, // Only "Test Mod Damage % Mechanic" spell, possible mod damage done SPELL_AURA_MOD_MAX_AFFECTED_TARGETS = 277, SPELL_AURA_MOD_DISARM_RANGED = 278, - SPELL_AURA_279 = 279, + SPELL_AURA_INITIALIZE_IMAGES = 279, SPELL_AURA_MOD_ARMOR_PENETRATION_PCT = 280, SPELL_AURA_MOD_HONOR_GAIN_PCT = 281, SPELL_AURA_MOD_BASE_HEALTH_PCT = 282, diff --git a/src/game/SpellAuras.cpp b/src/game/SpellAuras.cpp index 3e9324366a4..6be082b4f6f 100644 --- a/src/game/SpellAuras.cpp +++ b/src/game/SpellAuras.cpp @@ -300,7 +300,7 @@ pAuraHandler AuraHandler[TOTAL_AURAS]= &Aura::HandleComprehendLanguage, //244 SPELL_AURA_COMPREHEND_LANGUAGE &Aura::HandleNoImmediateEffect, //245 SPELL_AURA_MOD_AURA_DURATION_BY_DISPEL &Aura::HandleNoImmediateEffect, //246 SPELL_AURA_MOD_AURA_DURATION_BY_DISPEL_NOT_STACK implemented in Spell::EffectApplyAura - &Aura::HandleNULL, //247 target to become a clone of the caster + &Aura::HandleAuraCloneCaster, //247 SPELL_AURA_CLONE_CASTER &Aura::HandleNoImmediateEffect, //248 SPELL_AURA_MOD_COMBAT_RESULT_CHANCE implemented in Unit::RollMeleeOutcomeAgainst &Aura::HandleAuraConvertRune, //249 SPELL_AURA_CONVERT_RUNE &Aura::HandleAuraModIncreaseHealth, //250 SPELL_AURA_MOD_INCREASE_HEALTH_2 @@ -332,7 +332,7 @@ pAuraHandler AuraHandler[TOTAL_AURAS]= &Aura::HandleNULL, //276 mod damage % mechanic? &Aura::HandleNoImmediateEffect, //277 SPELL_AURA_MOD_ABILITY_AFFECTED_TARGETS implemented in spell::settargetmap &Aura::HandleAuraModDisarm, //278 SPELL_AURA_MOD_DISARM_RANGED disarm ranged weapon - &Aura::HandleNULL, //279 visual effects? 58836 and 57507 - makes summon to have name of caster + &Aura::HandleAuraInitializeImages, //279 SPELL_AURA_INITIALIZE_IMAGES &Aura::HandleModArmorPenetrationPct, //280 SPELL_AURA_MOD_ARMOR_PENETRATION_PCT &Aura::HandleNoImmediateEffect, //281 SPELL_AURA_MOD_HONOR_GAIN_PCT implemented in Player::RewardHonor &Aura::HandleAuraIncreaseBaseHealthPercent, //282 SPELL_AURA_INCREASE_BASE_HEALTH_PERCENT @@ -6966,7 +6966,7 @@ void AuraEffect::PeriodicDummyTick() if (spell->Id == 55342) { // Set name of summons to name of caster - m_target->CastSpell(m_target, m_spellProto->EffectTriggerSpell[m_effIndex], true); + m_target->CastSpell((Unit *)NULL, m_spellProto->EffectTriggerSpell[m_effIndex], true); m_isPeriodic = false; } break; @@ -7357,7 +7357,7 @@ void AuraEffect::HandleModPossessPet(bool apply, bool Real, bool /*changeAmount* ((Player*)caster)->PetSpellInitialize(); if(!m_target->getVictim()) { - m_target->GetMotionMaster()->MoveFollow(caster, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE); + m_target->GetMotionMaster()->MoveFollow(caster, PET_FOLLOW_DIST, m_target->GetFollowAngle()); m_target->GetCharmInfo()->SetCommandState(COMMAND_FOLLOW); } } @@ -7489,7 +7489,40 @@ void AuraEffect::HandleReflectSpells( bool Apply, bool Real , bool /*changeAmoun } } } +void AuraEffect::HandleAuraInitializeImages( bool Apply, bool Real , bool /*changeAmount*/) +{ + if (!Real || !Apply) + return; + Unit * caster = GetCaster(); + if (!caster) + return; + // Set item visual + if (caster->GetTypeId()== TYPEID_PLAYER) + { + if (Item const * item = ((Player *)caster)->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND)) + m_target->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID, item->GetProto()->ItemId); + if (Item const * item = ((Player *)caster)->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND)) + m_target->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 1, item->GetProto()->ItemId); + } + else + { + m_target->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID, caster->GetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID)); + m_target->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 1, caster->GetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 1)); + m_target->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 2, caster->GetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 2)); + } +} +void AuraEffect::HandleAuraCloneCaster( bool Apply, bool Real , bool /*changeAmount*/) +{ + if (!Real || !Apply) + return; + Unit * caster = GetCaster(); + if (!caster) + return; + // Set item visual + m_target->SetDisplayId(caster->GetDisplayId()); + m_target->SetUInt32Value(UNIT_FIELD_FLAGS_2, 2064); +} int32 AuraEffect::CalculateCrowdControlAuraAmount(Unit * caster) { // Damage cap for CC effects diff --git a/src/game/SpellAuras.h b/src/game/SpellAuras.h index 942d0c574b3..8b780520849 100644 --- a/src/game/SpellAuras.h +++ b/src/game/SpellAuras.h @@ -335,6 +335,8 @@ class TRINITY_DLL_SPEC AuraEffect void HandleCharmConvert(bool apply, bool Real, bool changeAmount); void HandleReflectSpells( bool Apply, bool Real , bool changeAmount); void HandleModArmorPenetrationPct(bool Apply, bool Real, bool changeAmount); + void HandleAuraInitializeImages(bool Apply, bool Real, bool changeAmount); + void HandleAuraCloneCaster(bool Apply, bool Real, bool changeAmount); int32 CalculateCrowdControlAuraAmount(Unit * caster); diff --git a/src/game/SpellEffects.cpp b/src/game/SpellEffects.cpp index e750f4fc7b0..f8821cc8217 100644 --- a/src/game/SpellEffects.cpp +++ b/src/game/SpellEffects.cpp @@ -3280,6 +3280,9 @@ void Spell::EffectSummonType(uint32 i) switch(properties->Category) { default: + if (properties->Flags & 512) + SummonGuardian(entry, properties); + break; switch(properties->Type) { case SUMMON_TYPE_PET: @@ -3363,7 +3366,7 @@ void Spell::EffectSummonType(uint32 i) TempSummonType summonType = (duration == 0) ? TEMPSUMMON_DEAD_DESPAWN : TEMPSUMMON_TIMED_DESPAWN; TempSummon * summon = m_originalCaster->SummonCreature(entry,px,py,pz,m_caster->GetOrientation(),summonType,duration); - summon->SetUInt64Value(UNIT_FIELD_SUMMONEDBY, m_originalCaster->GetGUID()); + summon->SetUInt64Value(UNIT_FIELD_SUMONEDBY, m_originalCaster->GetGUID()); if (properties->Category == SUMMON_CATEGORY_ALLY) summon->setFaction(m_originalCaster->getFaction()); } @@ -4556,6 +4559,15 @@ void Spell::EffectScriptEffect(uint32 effIndex) { switch(m_spellInfo->Id) { + case 45204: // Clone Me! + case 41055: // Copy Weapon + case 45206: // Copy Off-hand Weapon + unitTarget->CastSpell(m_caster, damage, false); + break; + case 45205: // Copy Offhand Weapon + case 41054: // Copy Weapon + m_caster->CastSpell(unitTarget, damage, false); + break; // Despawn Horse case 52267: { @@ -6695,6 +6707,8 @@ void Spell::SummonGuardian(uint32 entry, SummonPropertiesEntry const *properties ((Guardian*)summon)->InitStatsForLevel(level); summon->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id); + if(summon->HasSummonMask(SUMMON_MASK_MINION) && m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION) + ((Minion*)summon)->SetFollowAngle(m_caster->GetAngle(summon)); summon->AI()->EnterEvadeMode(); } diff --git a/src/game/SpellHandler.cpp b/src/game/SpellHandler.cpp index 74313eaa38b..dd88ef8e50a 100644 --- a/src/game/SpellHandler.cpp +++ b/src/game/SpellHandler.cpp @@ -537,3 +537,79 @@ void WorldSession::HandleSpellClick( WorldPacket & recv_data ) if(unit->isVehicle()) _player->EnterVehicle((Vehicle*)unit); } + +void WorldSession::HandleMirrrorImageDataRequest( WorldPacket & recv_data ) +{ + sLog.outDebug("WORLD: CMSG_GET_MIRRORIMAGE_DATA"); + CHECK_PACKET_SIZE(recv_data, 8); + uint64 guid; + recv_data >> guid; + + // Get unit for which data is needed by client + Unit *unit = ObjectAccessor::GetObjectInWorld(guid, (Unit*)NULL); + if(!unit) + return; + // Get creator of the unit + Unit *creator = ObjectAccessor::GetObjectInWorld(unit->GetCreatorGUID(),(Unit*)NULL); + if (!creator) + return; + WorldPacket data(SMSG_MIRRORIMAGE_DATA, 68); + data << (uint64)guid; + data << (uint32)creator->GetDisplayId(); + if (creator->GetTypeId()==TYPEID_PLAYER) + { + Player * pCreator = (Player *)creator; + data << (uint8)pCreator->getRace(); + data << (uint8)pCreator->getGender(); + data << (uint8)pCreator->getClass(); + data << (uint8)pCreator->GetByteValue(PLAYER_BYTES, 0); // skin + + data << (uint8)pCreator->GetByteValue(PLAYER_BYTES, 1); // face + data << (uint8)pCreator->GetByteValue(PLAYER_BYTES, 2); // hair + data << (uint8)pCreator->GetByteValue(PLAYER_BYTES, 3); // haircolor + data << (uint8)pCreator->GetByteValue(PLAYER_BYTES_2, 0); // facialhair + + data << (uint32)0; // unk + static const EquipmentSlots ItemSlots[] = + { + EQUIPMENT_SLOT_HEAD, + EQUIPMENT_SLOT_SHOULDERS, + EQUIPMENT_SLOT_BODY, + EQUIPMENT_SLOT_CHEST, + EQUIPMENT_SLOT_WAIST, + EQUIPMENT_SLOT_LEGS, + EQUIPMENT_SLOT_FEET, + EQUIPMENT_SLOT_WRISTS, + EQUIPMENT_SLOT_HANDS, + EQUIPMENT_SLOT_BACK, + EQUIPMENT_SLOT_TABARD, + EQUIPMENT_SLOT_END + }; + // Display items in visible slots + for (EquipmentSlots const* itr = &ItemSlots[0];*itr!=EQUIPMENT_SLOT_END;++itr) + if (Item const *item = pCreator->GetItemByPos(INVENTORY_SLOT_BAG_0, *itr)) + data << (uint32)item->GetProto()->DisplayInfoID; + else + data << (uint32)0; + } + else + { + // Skip player data for creatures + data << (uint32)0; + data << (uint32)0; + data << (uint32)0; + data << (uint32)0; + data << (uint32)0; + data << (uint32)0; + data << (uint32)0; + data << (uint32)0; + data << (uint32)0; + data << (uint32)0; + data << (uint32)0; + data << (uint32)0; + data << (uint32)0; + data << (uint32)0; + } + SendPacket( &data ); +} + diff --git a/src/game/TemporarySummon.cpp b/src/game/TemporarySummon.cpp index 3382c794622..8e04bc22c68 100644 --- a/src/game/TemporarySummon.cpp +++ b/src/game/TemporarySummon.cpp @@ -265,6 +265,7 @@ Minion::Minion(SummonPropertiesEntry const *properties, Unit *owner) : TempSummo { assert(m_owner); m_summonMask |= SUMMON_MASK_MINION; + m_followAngle = PET_FOLLOW_ANGLE; } void Minion::InitStats(uint32 duration) @@ -286,7 +287,7 @@ void Minion::InitSummon() if(m_owner->GetTypeId() == TYPEID_PLAYER && m_owner->GetMinionGUID() == GetGUID() && !m_owner->GetCharmGUID()) - ((Player*)m_owner)->CharmSpellInitialize(); + ((Player*)m_owner)->CharmSpellInitialize(); } void Minion::RemoveFromWorld() @@ -302,7 +303,11 @@ Guardian::Guardian(SummonPropertiesEntry const *properties, Unit *owner) : Minio , m_bonusdamage(0) { m_summonMask |= SUMMON_MASK_GUARDIAN; - InitCharmInfo(); + if (properties && properties->Type == SUMMON_TYPE_PET) + { + m_summonMask |= SUMMON_MASK_CONTROLABLE_GUARDIAN; + InitCharmInfo(); + } } void Guardian::InitStats(uint32 duration) @@ -311,7 +316,7 @@ void Guardian::InitStats(uint32 duration) InitStatsForLevel(m_owner->getLevel()); - if(m_owner->GetTypeId() == TYPEID_PLAYER) + if(m_owner->GetTypeId() == TYPEID_PLAYER && HasSummonMask(SUMMON_MASK_CONTROLABLE_GUARDIAN)) m_charmInfo->InitCharmCreateSpells(); SetReactState(REACT_AGGRESSIVE); diff --git a/src/game/TemporarySummon.h b/src/game/TemporarySummon.h index 47961d714ec..dc721bac25d 100644 --- a/src/game/TemporarySummon.h +++ b/src/game/TemporarySummon.h @@ -54,9 +54,12 @@ class Minion : public TempSummon void InitSummon(); void RemoveFromWorld(); Unit *GetOwner() { return m_owner; } + float GetFollowAngle() { return m_followAngle; } + void SetFollowAngle(float angle) { m_followAngle = angle; } bool IsPetGhoul() const {return GetEntry() == 26125;} // Ghoul may be guardian or pet protected: Unit * const m_owner; + float m_followAngle; }; class Guardian : public Minion diff --git a/src/game/Unit.cpp b/src/game/Unit.cpp index 24128fc85c5..18a5beb11c8 100644 --- a/src/game/Unit.cpp +++ b/src/game/Unit.cpp @@ -218,13 +218,13 @@ void Unit::Update( uint32 p_time ) if (CanHaveThreatList() && getThreatManager().isNeedUpdateToClient(p_time)) SendThreatListUpdate(); - // update combat timer only for players and pets - if (isInCombat() && IsControlledByPlayer()) + // update combat timer only for players and pets (only pets with PetAI) + if (isInCombat() && (GetTypeId() == TYPEID_PLAYER || (((Creature *)this)->isPet()) && IsControlledByPlayer())) { // Check UNIT_STAT_MELEE_ATTACKING or UNIT_STAT_CHASE (without UNIT_STAT_FOLLOW in this case) so pets can reach far away // targets without stopping half way there and running off. // These flags are reset after target dies or another command is given. - if( m_HostilRefManager.isEmpty() ) + if( m_HostilRefManager.isEmpty()) { // m_CombatTimer set at aura start and it will be freeze until aura removing if ( m_CombatTimer <= p_time ) @@ -8359,7 +8359,7 @@ bool Unit::Attack(Unit *victim, bool meleeAttack) //if(GetTypeId()==TYPEID_UNIT) // ((Creature*)this)->SetCombatStartPosition(GetPositionX(), GetPositionY(), GetPositionZ()); - if(GetTypeId()==TYPEID_UNIT && !IsControlledByPlayer()) + if(GetTypeId()==TYPEID_UNIT) { // should not let player enter combat by right clicking target SetInCombatWith(victim); @@ -8704,13 +8704,13 @@ void Unit::SetMinion(Minion *minion, bool apply) assert((*itr)->GetOwnerGUID() == GetGUID()); assert((*itr)->GetTypeId() == TYPEID_UNIT); - if(!((Creature*)(*itr))->HasSummonMask(SUMMON_MASK_GUARDIAN)) + if(!((Creature*)(*itr))->HasSummonMask(SUMMON_MASK_CONTROLABLE_GUARDIAN)) continue; if(AddUInt64Value(UNIT_FIELD_SUMMON, (*itr)->GetGUID())) { //show another pet bar if there is no charm bar - if(GetTypeId() == TYPEID_PLAYER && !GetCharmGUID() && ((Creature*)(*itr))->HasSummonMask(SUMMON_MASK_GUARDIAN)) + if(GetTypeId() == TYPEID_PLAYER && !GetCharmGUID() && ((Creature*)(*itr))->HasSummonMask(SUMMON_MASK_CONTROLABLE_GUARDIAN)) { if(((Creature*)(*itr))->isPet()) ((Player*)this)->PetSpellInitialize(); @@ -11140,8 +11140,8 @@ bool Unit::CanHaveThreatList() const //if( ((Creature*)this)->isVehicle() ) // return false; - // pets can not have a threat list, unless they are controlled by a creature - if( ((Creature*)this)->isPet() && IS_PLAYER_GUID(((Pet*)this)->GetOwnerGUID()) ) + // summons can not have a threat list, unless they are controlled by a creature + if( ((Creature*)this)->HasSummonMask(SUMMON_MASK_MINION | SUMMON_MASK_GUARDIAN | SUMMON_MASK_CONTROLABLE_GUARDIAN) && IS_PLAYER_GUID(((Pet*)this)->GetOwnerGUID()) ) return false; return true; @@ -11284,9 +11284,39 @@ Unit* Creature::SelectVictim() target = getVictim(); } - if ( !target && !m_ThreatManager.isThreatListEmpty() ) - // No taunt aura or taunt aura caster is dead standart target selection - target = m_ThreatManager.getHostilTarget(); + if (CanHaveThreatList()) + { + if ( !target && !m_ThreatManager.isThreatListEmpty() ) + // No taunt aura or taunt aura caster is dead standart target selection + target = m_ThreatManager.getHostilTarget(); + } + else + { + // We have player pet probably + target = getAttackerForHelper(); + if (!target && isSummon()) + { + if (Unit * owner = ((TempSummon*)this)->GetOwner()) + { + if (HasReactState(REACT_AGGRESSIVE) || HasReactState(REACT_DEFENSIVE)) + { + if (owner->isInCombat()) + target = owner->getAttackerForHelper(); + if (!target) + { + for(ControlList::const_iterator itr = owner->m_Controlled.begin(); itr != owner->m_Controlled.end(); ++itr) + { + if ((*itr)->isInCombat()) + { + target = (*itr)->getAttackerForHelper(); + if (target) break; + } + } + } + } + } + } + } if(target) { @@ -11299,7 +11329,7 @@ Unit* Creature::SelectVictim() // it in combat but attacker not make any damage and not enter to aggro radius to have record in threat list // for example at owner command to pet attack some far away creature // Note: creature not have targeted movement generator but have attacker in this case - if(m_attackers.size() && m_ThreatManager.isThreatListEmpty()) //there are some cases null target are always returned,so creature evade can not be called at all. such as pull creature at a distance beyond the attackdist to the attacker + if(m_attackers.size() && CanHaveThreatList() && m_ThreatManager.isThreatListEmpty()) //there are some cases null target are always returned,so creature evade can not be called at all. such as pull creature at a distance beyond the attackdist to the attacker return NULL; /*if( GetMotionMaster()->GetCurrentMovementGeneratorType() != TARGETED_MOTION_TYPE ) { @@ -14846,6 +14876,15 @@ void Unit::SetFlying(bool apply) } } +float Unit::GetFollowAngle() const +{ + if (GetTypeId()!=TYPEID_UNIT) + return PET_FOLLOW_ANGLE; + if (!((Creature*)this)->HasSummonMask(SUMMON_MASK_MINION)) + return PET_FOLLOW_ANGLE; + return ((Minion*)this)->GetFollowAngle(); +} + void Unit::NearTeleportTo( float x, float y, float z, float orientation, bool casting /*= false*/ ) { if(GetTypeId() == TYPEID_PLAYER) diff --git a/src/game/Unit.h b/src/game/Unit.h index 5416f3befd4..7e455dfadea 100644 --- a/src/game/Unit.h +++ b/src/game/Unit.h @@ -1803,6 +1803,7 @@ class TRINITY_DLL_SPEC Unit : public WorldObject bool canFly() const { return m_movementInfo.HasMovementFlag(MOVEMENTFLAG_FLY_MODE); } bool IsFlying() const { return m_movementInfo.HasMovementFlag(MOVEMENTFLAG_FLYING); } void SetFlying(bool apply); + float GetFollowAngle() const; protected: explicit Unit (); diff --git a/src/game/UnitAI.cpp b/src/game/UnitAI.cpp index c159861697c..9118fc075b6 100644 --- a/src/game/UnitAI.cpp +++ b/src/game/UnitAI.cpp @@ -348,7 +348,7 @@ void SimpleCharmedAI::UpdateAI(const uint32 /*diff*/) } if(!charmer->isInCombat()) - me->GetMotionMaster()->MoveFollow(charmer, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE); + me->GetMotionMaster()->MoveFollow(charmer, PET_FOLLOW_DIST, me->GetFollowAngle()); Unit *target = me->getVictim(); if(!target || !charmer->canAttack(target)) diff --git a/src/game/WorldSession.h b/src/game/WorldSession.h index f4842c0f0bc..22ab100197c 100644 --- a/src/game/WorldSession.h +++ b/src/game/WorldSession.h @@ -711,6 +711,7 @@ class TRINITY_DLL_SPEC WorldSession void HandleCalendarGetNumPending(WorldPacket& recv_data); void HandleSpellClick(WorldPacket& recv_data); + void HandleMirrrorImageDataRequest( WorldPacket & recv_data ); void HandleAlterAppearance(WorldPacket& recv_data); void HandleRemoveGlyph(WorldPacket& recv_data); void HandleCharCustomize(WorldPacket& recv_data); |