diff --git a/src/game/Spell.h b/src/game/Spell.h index a5411153e8b..b1cca52bcd2 100644 --- a/src/game/Spell.h +++ b/src/game/Spell.h @@ -231,8 +231,8 @@ class Spell friend struct MaNGOS::SpellNotifierPlayer; friend struct MaNGOS::SpellNotifierCreatureAndPlayer; friend void Unit::SetCurrentCastedSpell( Spell * pSpell ); - public: + public: void EffectEmpty(SpellEffectIndex eff_idx); void EffectNULL(SpellEffectIndex eff_idx); void EffectUnused(SpellEffectIndex eff_idx); @@ -393,11 +393,6 @@ class Spell void setState(uint32 state) { m_spellState = state; } void DoCreateItem(SpellEffectIndex eff_idx, uint32 itemtype); - void DoSummonPet(SpellEffectIndex eff_idx); - void DoSummonWild(SpellEffectIndex eff_idx, uint32 forceFaction = 0); - void DoSummonGuardian(SpellEffectIndex eff_idx, uint32 forceFaction = 0); - void DoSummonTotem(SpellEffectIndex eff_idx, uint8 slot_dbc = 0); - void DoSummonCritter(SpellEffectIndex eff_idx, uint32 forceFaction = 0); void WriteSpellGoTargets(WorldPacket* data); void WriteAmmoToPacket(WorldPacket* data); @@ -648,6 +643,27 @@ class Spell // we can't store original aura link to prevent access to deleted auras // and in same time need aura data and after aura deleting. SpellEntry const* m_triggeredByAuraSpell; + + private: + // NPC Summonings + struct CreaturePosition + { + CreaturePosition() : + x(0.0f), y(0.0f), z(0.0f), + creature(NULL) + {} + + float x, y, z; + Creature* creature; + }; + typedef std::vector CreatureSummonPositions; + + // return true IFF further processing required + bool DoSummonPet(SpellEffectIndex eff_idx); + bool DoSummonTotem(SpellEffectIndex eff_idx, uint8 slot_dbc = 0); + bool DoSummonWild(CreatureSummonPositions& list, SummonPropertiesEntry const* prop, SpellEffectIndex effIdx, uint32 level); + bool DoSummonCritter(CreatureSummonPositions& list, SummonPropertiesEntry const* prop, SpellEffectIndex effIdx, uint32 level); + bool DoSummonGuardian(CreatureSummonPositions& list, SummonPropertiesEntry const* prop, SpellEffectIndex effIdx, uint32 level); }; enum ReplenishType diff --git a/src/game/SpellEffects.cpp b/src/game/SpellEffects.cpp index 6d1b4a95ef2..90c0d55093e 100644 --- a/src/game/SpellEffects.cpp +++ b/src/game/SpellEffects.cpp @@ -4747,13 +4747,72 @@ void Spell::EffectApplyAreaAura(SpellEffectIndex eff_idx) void Spell::EffectSummonType(SpellEffectIndex eff_idx) { uint32 prop_id = m_spellInfo->EffectMiscValueB[eff_idx]; - SummonPropertiesEntry const *summon_prop = sSummonPropertiesStore.LookupEntry(prop_id); - if(!summon_prop) + SummonPropertiesEntry const* summon_prop = sSummonPropertiesStore.LookupEntry(prop_id); + if (!summon_prop) { sLog.outError("EffectSummonType: Unhandled summon type %u", prop_id); return; } + // Pet's are atm handled differently + if (summon_prop->Group == SUMMON_PROP_GROUP_PETS && prop_id != 1562) + { + DoSummonPet(eff_idx); + return; + } + + // Expected Amount: TODO - there are quite some exceptions (like totems, engineering dragonlings..) + uint32 amount = damage > 0 ? damage : 1; + + // Expected Level (Totem, Pet and Critter may not use this) + uint32 level = m_caster->getLevel(); + // level of creature summoned using engineering item based at engineering skill level + if (m_caster->GetTypeId() == TYPEID_PLAYER && m_CastItem) + { + ItemPrototype const* proto = m_CastItem->GetProto(); + if (proto && proto->RequiredSkill == SKILL_ENGINEERING) + if (uint16 engineeringSkill = ((Player*)m_caster)->GetSkillValue(SKILL_ENGINEERING)) + level = engineeringSkill / 5; + } + + CreatureSummonPositions summonPositions; + summonPositions.resize(amount, CreaturePosition()); + + // Set middle position + if (m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION) + { + summonPositions[0].x = m_targets.m_destX; + summonPositions[0].y = m_targets.m_destY; + summonPositions[0].z = m_targets.m_destZ; + } + else + { + m_caster->GetPosition(summonPositions[0].x, summonPositions[0].y, summonPositions[0].z); + + // TODO - Is this really an error? + sLog.outDebug("Spell Effect EFFECT_SUMMON (%u) - summon without destination (spell id %u, effIndex %u)", m_spellInfo->Effect[eff_idx], m_spellInfo->Id, eff_idx); + } + + // Set summon positions + float radius = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[eff_idx])); + CreatureSummonPositions::iterator itr = summonPositions.begin(); + for (++itr; itr != summonPositions.end(); ++itr) // In case of multiple summons around position for not-fist positions + { + if (m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION || radius > 1.0f) + { + m_caster->GetRandomPoint(summonPositions[0].x, summonPositions[0].y, summonPositions[0].z, radius, itr->x, itr->y, itr->z); + if (m_caster->GetMap()->GetObjectHitPos(summonPositions[0].x, summonPositions[0].y, summonPositions[0].z, itr->x, itr->y, itr->z, itr->x, itr->y, itr->z, 0.5f)) + m_caster->UpdateAllowedPositionZ(itr->x, itr->y, itr->z); + } + else // Get a point near the caster + { + m_caster->GetClosePoint(itr->x, itr->y, itr->z, 0.0f, radius, frand(0.0f, 2*M_PI_F)); + if (m_caster->GetMap()->GetObjectHitPos(summonPositions[0].x, summonPositions[0].y, summonPositions[0].z, itr->x, itr->y, itr->z, itr->x, itr->y, itr->z, 0.5f)) + m_caster->UpdateAllowedPositionZ(itr->x, itr->y, itr->z); + } + } + + bool summonResult = false; switch(summon_prop->Group) { // faction handled later on, or loaded from template @@ -4768,15 +4827,15 @@ void Spell::EffectSummonType(SpellEffectIndex eff_idx) //121: 23035, battlestands //647: 52893, Anti-Magic Zone (npc used) if (prop_id == 121 || prop_id == 647) - DoSummonTotem(eff_idx); + summonResult = DoSummonTotem(eff_idx); else - DoSummonWild(eff_idx, summon_prop->FactionId); + summonResult = DoSummonWild(summonPositions, summon_prop, eff_idx, level); break; } case UNITNAME_SUMMON_TITLE_PET: case UNITNAME_SUMMON_TITLE_MINION: case UNITNAME_SUMMON_TITLE_RUNEBLADE: - DoSummonGuardian(eff_idx, summon_prop->FactionId); + summonResult = DoSummonGuardian(summonPositions, summon_prop, eff_idx, level); break; case UNITNAME_SUMMON_TITLE_GUARDIAN: { @@ -4784,7 +4843,7 @@ void Spell::EffectSummonType(SpellEffectIndex eff_idx) { // * Stone Statue, etc -- fits much better totem AI if (m_spellInfo->SpellIconID == 2056) - DoSummonTotem(eff_idx); + summonResult = DoSummonTotem(eff_idx); else { // possible sort totems/guardians only by summon creature type @@ -4795,37 +4854,37 @@ void Spell::EffectSummonType(SpellEffectIndex eff_idx) // FIXME: not all totems and similar cases seelcted by this check... if (cInfo->type == CREATURE_TYPE_TOTEM) - DoSummonTotem(eff_idx); + summonResult = DoSummonTotem(eff_idx); else - DoSummonGuardian(eff_idx, summon_prop->FactionId); + summonResult = DoSummonGuardian(summonPositions, summon_prop, eff_idx, level); } } else - DoSummonGuardian(eff_idx, summon_prop->FactionId); + summonResult = DoSummonGuardian(summonPositions, summon_prop, eff_idx, level); break; } case UNITNAME_SUMMON_TITLE_CONSTRUCT: { if (prop_id == 2913) // Scrapbot - DoSummonWild(eff_idx, summon_prop->FactionId); + summonResult = DoSummonWild(summonPositions, summon_prop, eff_idx, level); else - DoSummonGuardian(eff_idx, summon_prop->FactionId); + summonResult = DoSummonGuardian(summonPositions, summon_prop, eff_idx, level); break; } case UNITNAME_SUMMON_TITLE_TOTEM: - DoSummonTotem(eff_idx, summon_prop->Slot); + summonResult = DoSummonTotem(eff_idx, summon_prop->Slot); break; case UNITNAME_SUMMON_TITLE_COMPANION: // slot 6 set for critters that can help to player in fighting if (summon_prop->Slot == 6) - DoSummonGuardian(eff_idx, summon_prop->FactionId); + summonResult = DoSummonGuardian(summonPositions, summon_prop, eff_idx, level); else - DoSummonCritter(eff_idx, summon_prop->FactionId); + summonResult = DoSummonCritter(summonPositions, summon_prop, eff_idx, level); break; case UNITNAME_SUMMON_TITLE_OPPONENT: case UNITNAME_SUMMON_TITLE_LIGHTWELL: case UNITNAME_SUMMON_TITLE_BUTLER: - DoSummonWild(eff_idx, summon_prop->FactionId); + summonResult = DoSummonWild(summonPositions, summon_prop, eff_idx, level); break; case UNITNAME_SUMMON_TITLE_VEHICLE: case UNITNAME_SUMMON_TITLE_MOUNT: @@ -4841,19 +4900,17 @@ void Spell::EffectSummonType(SpellEffectIndex eff_idx) case SUMMON_PROP_GROUP_PETS: { // FIXME : multiple summons - not yet supported as pet - //1562 - force of nature - sid 33831 - //1161 - feral spirit - sid 51533 - if(prop_id == 1562) // 3 uncontrolable instead of one controllable :/ - DoSummonGuardian(eff_idx, summon_prop->FactionId); - else - DoSummonPet(eff_idx); + // 1562 - force of nature - sid 33831 + // 1161 - feral spirit - sid 51533 + if (prop_id == 1562) // 3 uncontrolable instead of one controllable :/ + summonResult = DoSummonGuardian(summonPositions, summon_prop, eff_idx, level); break; } case SUMMON_PROP_GROUP_CONTROLLABLE: { // no type here // maybe wrong - but thats the handler currently used for those - DoSummonGuardian(eff_idx, summon_prop->FactionId); + summonResult = DoSummonGuardian(summonPositions, summon_prop, eff_idx, level); break; } case SUMMON_PROP_GROUP_VEHICLE: @@ -4867,31 +4924,307 @@ void Spell::EffectSummonType(SpellEffectIndex eff_idx) sLog.outError("EffectSummonType: Unhandled summon group type %u", summon_prop->Group); break; } + + if (!summonResult) + return; // No further handling required + + for (CreatureSummonPositions::iterator itr = summonPositions.begin(); itr != summonPositions.end(); ++itr) + { + MANGOS_ASSERT(itr->creature || itr != summonPositions.begin()); + if (!itr->creature) + { + sLog.outError("EffectSummonType: Expected to have %u NPCs summoned, but some failed (Spell id %u)", amount, m_spellInfo->Id); + continue; + } + + if (summon_prop->FactionId) + itr->creature->setFaction(summon_prop->FactionId); + + if (!itr->creature->IsTemporarySummon()) + { + itr->creature->AIM_Initialize(); + + m_caster->GetMap()->Add(itr->creature); + + // Notify Summoner + if (m_caster->GetTypeId() == TYPEID_UNIT && ((Creature*)m_caster)->AI()) + ((Creature*)m_caster)->AI()->JustSummoned(itr->creature); + if (m_originalCaster && m_originalCaster != m_caster && m_originalCaster->GetTypeId() == TYPEID_UNIT && ((Creature*)m_originalCaster)->AI()) + ((Creature*)m_originalCaster)->AI()->JustSummoned(itr->creature); + } + } +} + +bool Spell::DoSummonWild(CreatureSummonPositions& list, SummonPropertiesEntry const* prop, SpellEffectIndex effIdx, uint32 level) +{ + MANGOS_ASSERT(!list.empty() && prop); + + uint32 creature_entry = m_spellInfo->EffectMiscValue[effIdx]; + CreatureInfo const* cInfo = sCreatureStorage.LookupEntry(creature_entry); + if (!cInfo) + { + sLog.outErrorDb("Spell::DoSummonWild: creature entry %u not found for spell %u.", creature_entry, m_spellInfo->Id); + return false; + } + + TempSummonType summonType = (m_duration == 0) ? TEMPSUMMON_DEAD_DESPAWN : TEMPSUMMON_TIMED_OR_DEAD_DESPAWN; + + for (CreatureSummonPositions::iterator itr = list.begin(); itr != list.end(); ++itr) + if (Creature* summon = m_caster->SummonCreature(creature_entry, itr->x, itr->y, itr->z, m_caster->GetOrientation(), summonType, m_duration)) + { + itr->creature = summon; + + summon->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id); + + // UNIT_FIELD_CREATEDBY are not set for these kind of spells. + // Does exceptions exist? If so, what are they? + // summon->SetCreatorGuid(m_caster->GetObjectGuid()); + + // Notify original caster if not done already + if (m_originalCaster && m_originalCaster != m_caster && m_originalCaster->GetTypeId() == TYPEID_UNIT && ((Creature*)m_originalCaster)->AI()) + ((Creature*)m_originalCaster)->AI()->JustSummoned(summon); + } + else + return false; + + return true; +} + +bool Spell::DoSummonCritter(CreatureSummonPositions& list, SummonPropertiesEntry const* prop, SpellEffectIndex effIdx, uint32 /*level*/) +{ + MANGOS_ASSERT(!list.empty() && prop); + + // ATM only first position is supported for summoning + uint32 pet_entry = m_spellInfo->EffectMiscValue[effIdx]; + CreatureInfo const* cInfo = sCreatureStorage.LookupEntry(pet_entry); + if (!cInfo) + { + sLog.outErrorDb("Spell::DoSummonCritter: creature entry %u not found for spell %u.", pet_entry, m_spellInfo->Id); + return false; + } + + Pet* old_critter = m_caster->GetMiniPet(); + + // for same pet just despawn (player unsummon command) + if (m_caster->GetTypeId() == TYPEID_PLAYER && old_critter && old_critter->GetEntry() == pet_entry) + { + m_caster->RemoveMiniPet(); + return false; + } + + // despawn old pet before summon new + if (old_critter) + m_caster->RemoveMiniPet(); + + // for (CreatureSummonPositions::iterator itr = list.begin(); itr != list.end(); ++itr) + CreatureCreatePos pos(m_caster->GetMap(), list[0].x, list[0].y, list[0].z, m_caster->GetOrientation(), m_caster->GetPhaseMask()); + + // summon new pet + Pet* critter = new Pet(MINI_PET); + + uint32 pet_number = sObjectMgr.GeneratePetNumber(); + if (!critter->Create(m_caster->GetMap()->GenerateLocalLowGuid(HIGHGUID_PET), pos, cInfo, pet_number)) + { + sLog.outError("Spell::EffectSummonCritter, spellid %u: no such creature entry %u", m_spellInfo->Id, pet_entry); + delete critter; + return false; + } + + // itr! + list[0].creature = critter; + + critter->SetSummonPoint(pos); + + //critter->SetName(""); // generated by client + critter->SetOwnerGuid(m_caster->GetObjectGuid()); + critter->SetCreatorGuid(m_caster->GetObjectGuid()); + + critter->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id); + + + critter->InitPetCreateSpells(); // e.g. disgusting oozeling has a create spell as critter... + //critter->InitLevelupSpellsForLevel(); // none? + critter->SelectLevel(critter->GetCreatureInfo()); // some summoned creaters have different from 1 DB data for level/hp + critter->SetUInt32Value(UNIT_NPC_FLAGS, critter->GetCreatureInfo()->npcflag); + // some mini-pets have quests + // set timer for unsummon + if (m_duration > 0) + critter->SetDuration(m_duration); + + m_caster->SetMiniPet(critter); + + return true; +} + +bool Spell::DoSummonGuardian(CreatureSummonPositions& list, SummonPropertiesEntry const* prop, SpellEffectIndex effIdx, uint32 level) +{ + MANGOS_ASSERT(!list.empty() && prop); + + uint32 pet_entry = m_spellInfo->EffectMiscValue[effIdx]; + CreatureInfo const* cInfo = sCreatureStorage.LookupEntry(pet_entry); + if (!cInfo) + { + sLog.outErrorDb("Spell::DoSummonGuardian: creature entry %u not found for spell %u.", pet_entry, m_spellInfo->Id); + return false; + } + + PetType petType = prop->Title == UNITNAME_SUMMON_TITLE_COMPANION ? PROTECTOR_PET : GUARDIAN_PET; + + // second direct cast unsummon guardian(s) (guardians without like functionality have cooldown > spawn time) + if (!m_IsTriggeredSpell && m_caster->GetTypeId() == TYPEID_PLAYER) + { + bool found = false; + // including protector + while (Pet* old_summon = m_caster->FindGuardianWithEntry(pet_entry)) + { + old_summon->Unsummon(PET_SAVE_AS_DELETED, m_caster); + found = true; + } + + if (found) + return false; + } + + // protectors allowed only in single amount + if (petType == PROTECTOR_PET) + if (Pet* old_protector = m_caster->GetProtectorPet()) + old_protector->Unsummon(PET_SAVE_AS_DELETED, m_caster); + + // in another case summon new + for (CreatureSummonPositions::iterator itr = list.begin(); itr != list.end(); ++itr) + { + Pet* spawnCreature = new Pet(petType); + + CreatureCreatePos pos(m_caster->GetMap(), itr->x, itr->y, itr->z, -m_caster->GetOrientation(), m_caster->GetPhaseMask()); + + uint32 pet_number = sObjectMgr.GeneratePetNumber(); + if (!spawnCreature->Create(m_caster->GetMap()->GenerateLocalLowGuid(HIGHGUID_PET), pos, cInfo, pet_number)) + { + sLog.outError("Spell::DoSummonGuardian: can't create creature entry %u for spell %u.", pet_entry, m_spellInfo->Id); + delete spawnCreature; + return false; + } + + itr->creature = spawnCreature; + + spawnCreature->SetSummonPoint(pos); + + if (m_duration > 0) + spawnCreature->SetDuration(m_duration); + + //spawnCreature->SetName(""); // generated by client + spawnCreature->SetOwnerGuid(m_caster->GetObjectGuid()); + spawnCreature->setPowerType(POWER_MANA); + spawnCreature->SetUInt32Value(UNIT_NPC_FLAGS, spawnCreature->GetCreatureInfo()->npcflag); + + spawnCreature->SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, 0); + spawnCreature->SetCreatorGuid(m_caster->GetObjectGuid()); + spawnCreature->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id); + + spawnCreature->InitStatsForLevel(level, m_caster); + spawnCreature->GetCharmInfo()->SetPetNumber(pet_number, false); + + m_caster->AddGuardian(spawnCreature); + } + + return true; } -void Spell::DoSummonPet(SpellEffectIndex eff_idx) +bool Spell::DoSummonTotem(SpellEffectIndex eff_idx, uint8 slot_dbc) +{ + // DBC store slots starting from 1, with no slot 0 value) + int slot = slot_dbc ? slot_dbc - 1 : TOTEM_SLOT_NONE; + + // unsummon old totem + if (slot < MAX_TOTEM_SLOT) + if (Totem *OldTotem = m_caster->GetTotem(TotemSlot(slot))) + OldTotem->UnSummon(); + + // FIXME: Setup near to finish point because GetObjectBoundingRadius set in Create but some Create calls can be dependent from proper position + // if totem have creature_template_addon.auras with persistent point for example or script call + float angle = slot < MAX_TOTEM_SLOT ? M_PI_F/MAX_TOTEM_SLOT - (slot*2*M_PI_F/MAX_TOTEM_SLOT) : 0; + + CreatureCreatePos pos(m_caster, m_caster->GetOrientation(), 2.0f, angle); + + CreatureInfo const* cinfo = sCreatureStorage.LookupEntry(m_spellInfo->EffectMiscValue[eff_idx]); + if (!cinfo) + { + sLog.outErrorDb("Creature entry %u does not exist but used in spell %u totem summon.", m_spellInfo->Id, m_spellInfo->EffectMiscValue[eff_idx]); + return false; + } + + Totem* pTotem = new Totem; + + if (!pTotem->Create(m_caster->GetMap()->GenerateLocalLowGuid(HIGHGUID_UNIT), pos, cinfo, m_caster)) + { + delete pTotem; + return false; + } + + pTotem->SetSummonPoint(pos); + + if (slot < MAX_TOTEM_SLOT) + m_caster->_AddTotem(TotemSlot(slot),pTotem); + + //pTotem->SetName(""); // generated by client + pTotem->SetOwner(m_caster); + pTotem->SetTypeBySummonSpell(m_spellInfo); // must be after Create call where m_spells initialized + + pTotem->SetDuration(m_duration); + + if (damage) // if not spell info, DB values used + { + pTotem->SetMaxHealth(damage); + pTotem->SetHealth(damage); + } + + pTotem->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id); + + if (m_caster->GetTypeId() == TYPEID_PLAYER) + pTotem->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE); + + if (m_caster->IsPvP()) + pTotem->SetPvP(true); + + if (m_caster->IsFFAPvP()) + pTotem->SetFFAPvP(true); + + // sending SMSG_TOTEM_CREATED before add to map (done in Summon) + if (slot < MAX_TOTEM_SLOT && m_caster->GetTypeId() == TYPEID_PLAYER) + { + WorldPacket data(SMSG_TOTEM_CREATED, 1 + 8 + 4 + 4); + data << uint8(slot); + data << pTotem->GetObjectGuid(); + data << uint32(m_duration); + data << uint32(m_spellInfo->Id); + ((Player*)m_caster)->SendDirectMessage(&data); + } + + pTotem->Summon(m_caster); + + return false; +} + +bool Spell::DoSummonPet(SpellEffectIndex eff_idx) { if (m_caster->GetPetGuid()) - return; + return false; if (!unitTarget) - return; + return false; uint32 pet_entry = m_spellInfo->EffectMiscValue[eff_idx]; - if (!pet_entry) - return; - CreatureInfo const* cInfo = sCreatureStorage.LookupEntry(pet_entry); if (!cInfo) { sLog.outErrorDb("Spell::DoSummonPet: creature entry %u not found for spell %u.", pet_entry, m_spellInfo->Id); - return; + return false; } - uint32 level = m_caster->getLevel(); + uint32 level = m_caster->getLevel(); // TODO Engineering Pets have also caster-level? (if they exist) Pet* spawnCreature = new Pet(SUMMON_PET); - if (m_caster->GetTypeId()==TYPEID_PLAYER && spawnCreature->LoadPetFromDB((Player*)m_caster,pet_entry)) + if (m_caster->GetTypeId()==TYPEID_PLAYER && spawnCreature->LoadPetFromDB((Player*)m_caster, pet_entry)) { // Summon in dest location if (m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION) @@ -4901,7 +5234,7 @@ void Spell::DoSummonPet(SpellEffectIndex eff_idx) if (m_duration > 0) spawnCreature->SetDuration(m_duration); - return; + return false; } // Summon in dest location @@ -4916,7 +5249,7 @@ void Spell::DoSummonPet(SpellEffectIndex eff_idx) { sLog.outErrorDb("Spell::EffectSummon: can't create creature with entry %u for spell %u", cInfo->Entry, m_spellInfo->Id); delete spawnCreature; - return; + return false; } spawnCreature->SetSummonPoint(pos); @@ -4962,6 +5295,8 @@ void Spell::DoSummonPet(SpellEffectIndex eff_idx) ((Creature*)m_caster)->AI()->JustSummoned((Creature*)spawnCreature); if (m_originalCaster && m_originalCaster != m_caster && m_originalCaster->GetTypeId() == TYPEID_UNIT && ((Creature*)m_originalCaster)->AI()) ((Creature*)m_originalCaster)->AI()->JustSummoned((Creature*)spawnCreature); + + return false; } void Spell::EffectLearnSpell(SpellEffectIndex eff_idx) @@ -5112,299 +5447,90 @@ void Spell::EffectDispel(SpellEffectIndex eff_idx) data << m_caster->GetObjectGuid(); // Caster GUID data << unitTarget->GetObjectGuid(); // Victim GUID data << uint32(m_spellInfo->Id); // Dispel spell id - for (std::list< uint32 >::iterator j = fail_list.begin(); j != fail_list.end(); ++j) - data << uint32(*j); // Spell Id - m_caster->SendMessageToSet(&data, true); - } - } -} - -void Spell::EffectDualWield(SpellEffectIndex /*eff_idx*/) -{ - if (unitTarget && unitTarget->GetTypeId() == TYPEID_PLAYER) - ((Player*)unitTarget)->SetCanDualWield(true); -} - -void Spell::EffectPull(SpellEffectIndex /*eff_idx*/) -{ - // TODO: create a proper pull towards distract spell center for distract - DEBUG_LOG("WORLD: Spell Effect DUMMY"); -} - -void Spell::EffectDistract(SpellEffectIndex /*eff_idx*/) -{ - // Check for possible target - if (!unitTarget || unitTarget->isInCombat()) - return; - - // target must be OK to do this - if (unitTarget->hasUnitState(UNIT_STAT_CAN_NOT_REACT)) - return; - - unitTarget->SetFacingTo(unitTarget->GetAngle(m_targets.m_destX, m_targets.m_destY)); - unitTarget->clearUnitState(UNIT_STAT_MOVING); - - if (unitTarget->GetTypeId() == TYPEID_UNIT) - unitTarget->GetMotionMaster()->MoveDistract(damage * IN_MILLISECONDS); -} - -void Spell::EffectPickPocket(SpellEffectIndex /*eff_idx*/) -{ - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - // victim must be creature and attackable - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT || m_caster->IsFriendlyTo(unitTarget)) - return; - - // victim have to be alive and humanoid or undead - if (unitTarget->isAlive() && (unitTarget->GetCreatureTypeMask() & CREATURE_TYPEMASK_HUMANOID_OR_UNDEAD) != 0) - { - int32 chance = 10 + int32(m_caster->getLevel()) - int32(unitTarget->getLevel()); - - if (chance > irand(0, 19)) - { - // Stealing successful - //DEBUG_LOG("Sending loot from pickpocket"); - ((Player*)m_caster)->SendLoot(unitTarget->GetObjectGuid(),LOOT_PICKPOCKETING); - } - else - { - // Reveal action + get attack - m_caster->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); - unitTarget->AttackedBy(m_caster); - } - } -} - -void Spell::EffectAddFarsight(SpellEffectIndex eff_idx) -{ - if(m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - DynamicObject* dynObj = new DynamicObject; - - // set radius to 0: spell not expected to work as persistent aura - if(!dynObj->Create(m_caster->GetMap()->GenerateLocalLowGuid(HIGHGUID_DYNAMICOBJECT), m_caster, - m_spellInfo->Id, eff_idx, m_targets.m_destX, m_targets.m_destY, m_targets.m_destZ, m_duration, 0, DYNAMIC_OBJECT_FARSIGHT_FOCUS)) - { - delete dynObj; - return; - } - - m_caster->AddDynObject(dynObj); - m_caster->GetMap()->Add(dynObj); - - ((Player*)m_caster)->GetCamera().SetView(dynObj); -} - -void Spell::DoSummonWild(SpellEffectIndex eff_idx, uint32 forceFaction) -{ - uint32 creature_entry = m_spellInfo->EffectMiscValue[eff_idx]; - if (!creature_entry) - return; - - uint32 level = m_caster->getLevel(); - - // level of creature summoned using engineering item based at engineering skill level - if (m_caster->GetTypeId()==TYPEID_PLAYER && m_CastItem) - { - ItemPrototype const *proto = m_CastItem->GetProto(); - if (proto && proto->RequiredSkill == SKILL_ENGINEERING) - { - uint16 skill202 = ((Player*)m_caster)->GetSkillValue(SKILL_ENGINEERING); - if (skill202) - level = skill202/5; - } - } - - // select center of summon position - float center_x = m_targets.m_destX; - float center_y = m_targets.m_destY; - float center_z = m_targets.m_destZ; - - float radius = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[eff_idx])); - TempSummonType summonType = (m_duration == 0) ? TEMPSUMMON_DEAD_DESPAWN : TEMPSUMMON_TIMED_OR_DEAD_DESPAWN; - - int32 amount = damage > 0 ? damage : 1; - - for(int32 count = 0; count < amount; ++count) - { - float px, py, pz; - // If dest location if present - if (m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION) - { - // Summon 1 unit in dest location - if (count == 0) - { - px = m_targets.m_destX; - py = m_targets.m_destY; - pz = m_targets.m_destZ; - } - // Summon in random point all other units if location present - else - m_caster->GetRandomPoint(center_x, center_y, center_z, radius, px, py, pz); - } - // Summon if dest location not present near caster - else - { - if (radius > 0.0f) - { - // not using bounding radius of caster here - m_caster->GetClosePoint(px, py, pz, 0.0f, radius); - } - else - { - // EffectRadiusIndex 0 or 36 - px = m_caster->GetPositionX(); - py = m_caster->GetPositionY(); - pz = m_caster->GetPositionZ(); - } - } - - if (Creature* summon = m_caster->SummonCreature(creature_entry, px, py, pz, m_caster->GetOrientation(), summonType, m_duration)) - { - summon->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id); - - // UNIT_FIELD_CREATEDBY are not set for these kind of spells. - // Does exceptions exist? If so, what are they? - // summon->SetCreatorGuid(m_caster->GetObjectGuid()); - - if(forceFaction) - summon->setFaction(forceFaction); - - // Notify original caster if not done already - if (m_originalCaster && m_originalCaster != m_caster && m_originalCaster->GetTypeId() == TYPEID_UNIT && ((Creature*)m_originalCaster)->AI()) - ((Creature*)m_originalCaster)->AI()->JustSummoned(summon); + for (std::list< uint32 >::iterator j = fail_list.begin(); j != fail_list.end(); ++j) + data << uint32(*j); // Spell Id + m_caster->SendMessageToSet(&data, true); } } } -void Spell::DoSummonGuardian(SpellEffectIndex eff_idx, uint32 forceFaction) +void Spell::EffectDualWield(SpellEffectIndex /*eff_idx*/) { - uint32 pet_entry = m_spellInfo->EffectMiscValue[eff_idx]; - if (!pet_entry) - return; + if (unitTarget && unitTarget->GetTypeId() == TYPEID_PLAYER) + ((Player*)unitTarget)->SetCanDualWield(true); +} - SummonPropertiesEntry const* propEntry = sSummonPropertiesStore.LookupEntry(m_spellInfo->EffectMiscValueB[eff_idx]); - if (!propEntry) - return; +void Spell::EffectPull(SpellEffectIndex /*eff_idx*/) +{ + // TODO: create a proper pull towards distract spell center for distract + DEBUG_LOG("WORLD: Spell Effect DUMMY"); +} - CreatureInfo const* cInfo = sCreatureStorage.LookupEntry(pet_entry); - if (!cInfo) - { - sLog.outErrorDb("Spell::DoSummonGuardian: creature entry %u not found for spell %u.", pet_entry, m_spellInfo->Id); +void Spell::EffectDistract(SpellEffectIndex /*eff_idx*/) +{ + // Check for possible target + if (!unitTarget || unitTarget->isInCombat()) return; - } - - PetType petType = propEntry->Title == UNITNAME_SUMMON_TITLE_COMPANION ? PROTECTOR_PET : GUARDIAN_PET; - - // second direct cast unsummon guardian(s) (guardians without like functionality have cooldown > spawn time) - if (!m_IsTriggeredSpell && m_caster->GetTypeId() == TYPEID_PLAYER) - { - bool found = false; - // including protector - while (Pet* old_summon = m_caster->FindGuardianWithEntry(pet_entry)) - { - old_summon->Unsummon(PET_SAVE_AS_DELETED, m_caster); - found = true; - } - - if (found) - return; - } - // protectors allowed only in single amount - if (petType == PROTECTOR_PET) - if (Pet* old_protector = m_caster->GetProtectorPet()) - old_protector->Unsummon(PET_SAVE_AS_DELETED, m_caster); - - // in another case summon new - uint32 level = m_caster->getLevel(); + // target must be OK to do this + if (unitTarget->hasUnitState(UNIT_STAT_CAN_NOT_REACT)) + return; - // level of pet summoned using engineering item based at engineering skill level - if (m_caster->GetTypeId() == TYPEID_PLAYER && m_CastItem) - { - ItemPrototype const *proto = m_CastItem->GetProto(); - if (proto && proto->RequiredSkill == SKILL_ENGINEERING) - { - uint16 skill202 = ((Player*)m_caster)->GetSkillValue(SKILL_ENGINEERING); - if (skill202) - { - level = skill202 / 5; - } - } - } + unitTarget->SetFacingTo(unitTarget->GetAngle(m_targets.m_destX, m_targets.m_destY)); + unitTarget->clearUnitState(UNIT_STAT_MOVING); - // select center of summon position - float center_x = m_targets.m_destX; - float center_y = m_targets.m_destY; - float center_z = m_targets.m_destZ; + if (unitTarget->GetTypeId() == TYPEID_UNIT) + unitTarget->GetMotionMaster()->MoveDistract(damage * IN_MILLISECONDS); +} - float radius = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[eff_idx])); +void Spell::EffectPickPocket(SpellEffectIndex /*eff_idx*/) +{ + if (m_caster->GetTypeId() != TYPEID_PLAYER) + return; - int32 amount = damage > 0 ? damage : 1; + // victim must be creature and attackable + if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT || m_caster->IsFriendlyTo(unitTarget)) + return; - for (int32 count = 0; count < amount; ++count) + // victim have to be alive and humanoid or undead + if (unitTarget->isAlive() && (unitTarget->GetCreatureTypeMask() & CREATURE_TYPEMASK_HUMANOID_OR_UNDEAD) != 0) { - Pet* spawnCreature = new Pet(petType); - - // If dest location if present - // Summon 1 unit in dest location - CreatureCreatePos pos(m_caster->GetMap(), m_targets.m_destX, m_targets.m_destY, m_targets.m_destZ, -m_caster->GetOrientation(), m_caster->GetPhaseMask()); + int32 chance = 10 + int32(m_caster->getLevel()) - int32(unitTarget->getLevel()); - if (m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION) + if (chance > irand(0, 19)) { - // Summon in random point all other units if location present - if (count > 0) - { - float x, y, z; - m_caster->GetRandomPoint(center_x, center_y, center_z, radius, x, y, z); - pos = CreatureCreatePos(m_caster->GetMap(), x, y, z, m_caster->GetOrientation(), m_caster->GetPhaseMask()); - } + // Stealing successful + //DEBUG_LOG("Sending loot from pickpocket"); + ((Player*)m_caster)->SendLoot(unitTarget->GetObjectGuid(),LOOT_PICKPOCKETING); } - // Summon if dest location not present near caster else - pos = CreatureCreatePos(m_caster, m_caster->GetOrientation()); - - Map *map = m_caster->GetMap(); - uint32 pet_number = sObjectMgr.GeneratePetNumber(); - if (!spawnCreature->Create(map->GenerateLocalLowGuid(HIGHGUID_PET), pos, cInfo, pet_number)) { - sLog.outError("Spell::DoSummonGuardian: can't create creature entry %u for spell %u.", pet_entry, m_spellInfo->Id); - delete spawnCreature; - return; + // Reveal action + get attack + m_caster->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); + unitTarget->AttackedBy(m_caster); } + } +} - spawnCreature->SetSummonPoint(pos); - - if (m_duration > 0) - spawnCreature->SetDuration(m_duration); - - //spawnCreature->SetName(""); // generated by client - spawnCreature->SetOwnerGuid(m_caster->GetObjectGuid()); - spawnCreature->setPowerType(POWER_MANA); - spawnCreature->SetUInt32Value(UNIT_NPC_FLAGS, spawnCreature->GetCreatureInfo()->npcflag); - spawnCreature->setFaction(forceFaction ? forceFaction : m_caster->getFaction()); - spawnCreature->SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, 0); - spawnCreature->SetCreatorGuid(m_caster->GetObjectGuid()); - spawnCreature->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id); - - spawnCreature->InitStatsForLevel(level, m_caster); - spawnCreature->GetCharmInfo()->SetPetNumber(pet_number, false); +void Spell::EffectAddFarsight(SpellEffectIndex eff_idx) +{ + if(m_caster->GetTypeId() != TYPEID_PLAYER) + return; - spawnCreature->AIM_Initialize(); + DynamicObject* dynObj = new DynamicObject; - m_caster->AddGuardian(spawnCreature); + // set radius to 0: spell not expected to work as persistent aura + if(!dynObj->Create(m_caster->GetMap()->GenerateLocalLowGuid(HIGHGUID_DYNAMICOBJECT), m_caster, + m_spellInfo->Id, eff_idx, m_targets.m_destX, m_targets.m_destY, m_targets.m_destZ, m_duration, 0, DYNAMIC_OBJECT_FARSIGHT_FOCUS)) + { + delete dynObj; + return; + } - map->Add((Creature*)spawnCreature); + m_caster->AddDynObject(dynObj); + m_caster->GetMap()->Add(dynObj); - // Notify Summoner - if (m_caster->GetTypeId() == TYPEID_UNIT && ((Creature*)m_caster)->AI()) - ((Creature*)m_caster)->AI()->JustSummoned(spawnCreature); - if (m_originalCaster && m_originalCaster != m_caster && m_originalCaster->GetTypeId() == TYPEID_UNIT && ((Creature*)m_originalCaster)->AI()) - ((Creature*)m_originalCaster)->AI()->JustSummoned(spawnCreature); - } + ((Player*)m_caster)->GetCamera().SetView(dynObj); } void Spell::EffectTeleUnitsFaceCaster(SpellEffectIndex eff_idx) @@ -8528,79 +8654,6 @@ void Spell::EffectApplyGlyph(SpellEffectIndex eff_idx) } } -void Spell::DoSummonTotem(SpellEffectIndex eff_idx, uint8 slot_dbc) -{ - // DBC store slots starting from 1, with no slot 0 value) - int slot = slot_dbc ? slot_dbc - 1 : TOTEM_SLOT_NONE; - - // unsummon old totem - if (slot < MAX_TOTEM_SLOT) - if (Totem *OldTotem = m_caster->GetTotem(TotemSlot(slot))) - OldTotem->UnSummon(); - - // FIXME: Setup near to finish point because GetObjectBoundingRadius set in Create but some Create calls can be dependent from proper position - // if totem have creature_template_addon.auras with persistent point for example or script call - float angle = slot < MAX_TOTEM_SLOT ? M_PI_F/MAX_TOTEM_SLOT - (slot*2*M_PI_F/MAX_TOTEM_SLOT) : 0; - - CreatureCreatePos pos(m_caster, m_caster->GetOrientation(), 2.0f, angle); - - CreatureInfo const *cinfo = ObjectMgr::GetCreatureTemplate(m_spellInfo->EffectMiscValue[eff_idx]); - if (!cinfo) - { - sLog.outErrorDb("Creature entry %u does not exist but used in spell %u totem summon.", m_spellInfo->Id, m_spellInfo->EffectMiscValue[eff_idx]); - return; - } - - Totem* pTotem = new Totem; - - if (!pTotem->Create(m_caster->GetMap()->GenerateLocalLowGuid(HIGHGUID_UNIT), pos, cinfo, m_caster)) - { - delete pTotem; - return; - } - - pTotem->SetSummonPoint(pos); - - if (slot < MAX_TOTEM_SLOT) - m_caster->_AddTotem(TotemSlot(slot),pTotem); - - //pTotem->SetName(""); // generated by client - pTotem->SetOwner(m_caster); - pTotem->SetTypeBySummonSpell(m_spellInfo); // must be after Create call where m_spells initialized - - pTotem->SetDuration(m_duration); - - if (damage) // if not spell info, DB values used - { - pTotem->SetMaxHealth(damage); - pTotem->SetHealth(damage); - } - - pTotem->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id); - - if (m_caster->GetTypeId() == TYPEID_PLAYER) - pTotem->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE); - - if (m_caster->IsPvP()) - pTotem->SetPvP(true); - - if (m_caster->IsFFAPvP()) - pTotem->SetFFAPvP(true); - - // sending SMSG_TOTEM_CREATED before add to map (done in Summon) - if (slot < MAX_TOTEM_SLOT && m_caster->GetTypeId() == TYPEID_PLAYER) - { - WorldPacket data(SMSG_TOTEM_CREATED, 1 + 8 + 4 + 4); - data << uint8(slot); - data << pTotem->GetObjectGuid(); - data << uint32(m_duration); - data << uint32(m_spellInfo->Id); - ((Player*)m_caster)->SendDirectMessage(&data); - } - - pTotem->Summon(m_caster); -} - void Spell::EffectEnchantHeldItem(SpellEffectIndex eff_idx) { // this is only item spell effect applied to main-hand weapon of target player (players in area) @@ -9031,78 +9084,6 @@ void Spell::EffectCharge2(SpellEffectIndex /*eff_idx*/) m_caster->Attack(unitTarget, true); } -void Spell::DoSummonCritter(SpellEffectIndex eff_idx, uint32 forceFaction) -{ - uint32 pet_entry = m_spellInfo->EffectMiscValue[eff_idx]; - if(!pet_entry) - return; - - CreatureInfo const* cInfo = sCreatureStorage.LookupEntry(pet_entry); - if (!cInfo) - { - sLog.outErrorDb("Spell::DoSummonCritter: creature entry %u not found for spell %u.", pet_entry, m_spellInfo->Id); - return; - } - - Pet* old_critter = m_caster->GetMiniPet(); - - // for same pet just despawn (player unsummon command) - if (m_caster->GetTypeId() == TYPEID_PLAYER && old_critter && old_critter->GetEntry() == pet_entry) - { - m_caster->RemoveMiniPet(); - return; - } - - // despawn old pet before summon new - if (old_critter) - m_caster->RemoveMiniPet(); - - CreatureCreatePos pos(m_caster->GetMap(), m_targets.m_destX, m_targets.m_destY, m_targets.m_destZ, m_caster->GetOrientation(), m_caster->GetPhaseMask()); - if (!(m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION)) - pos = CreatureCreatePos(m_caster, m_caster->GetOrientation()); - - // summon new pet - Pet* critter = new Pet(MINI_PET); - - Map *map = m_caster->GetMap(); - uint32 pet_number = sObjectMgr.GeneratePetNumber(); - if (!critter->Create(map->GenerateLocalLowGuid(HIGHGUID_PET), pos, cInfo, pet_number)) - { - sLog.outError("Spell::EffectSummonCritter, spellid %u: no such creature entry %u", m_spellInfo->Id, pet_entry); - delete critter; - return; - } - - critter->SetSummonPoint(pos); - - //critter->SetName(""); // generated by client - critter->SetOwnerGuid(m_caster->GetObjectGuid()); - critter->SetCreatorGuid(m_caster->GetObjectGuid()); - - critter->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id); - critter->setFaction(forceFaction ? forceFaction : m_caster->getFaction()); - critter->AIM_Initialize(); - critter->InitPetCreateSpells(); // e.g. disgusting oozeling has a create spell as critter... - //critter->InitLevelupSpellsForLevel(); // none? - critter->SelectLevel(critter->GetCreatureInfo()); // some summoned creaters have different from 1 DB data for level/hp - critter->SetUInt32Value(UNIT_NPC_FLAGS, critter->GetCreatureInfo()->npcflag); - // some mini-pets have quests - - // set timer for unsummon - if(m_duration > 0) - critter->SetDuration(m_duration); - - m_caster->SetMiniPet(critter); - - map->Add((Creature*)critter); - - // Notify Summoner - if (m_caster->GetTypeId() == TYPEID_UNIT && ((Creature*)m_caster)->AI()) - ((Creature*)m_caster)->AI()->JustSummoned(critter); - if (m_originalCaster && m_originalCaster != m_caster && m_originalCaster->GetTypeId() == TYPEID_UNIT && ((Creature*)m_originalCaster)->AI()) - ((Creature*)m_originalCaster)->AI()->JustSummoned(critter); -} - void Spell::EffectKnockBack(SpellEffectIndex eff_idx) { if (!unitTarget) diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index 54ce7df3a83..e475aa5c7d6 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 "12040" + #define REVISION_NR "12041" #endif // __REVISION_NR_H__