diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/game/CreatureEventAI.cpp | 33 | ||||
-rw-r--r-- | src/game/CreatureEventAI.h | 11 | ||||
-rw-r--r-- | src/game/CreatureEventAIMgr.cpp | 14 | ||||
-rw-r--r-- | src/game/ObjectMgr.cpp | 62 | ||||
-rw-r--r-- | src/game/ObjectMgr.h | 17 | ||||
-rw-r--r-- | src/game/Player.cpp | 25 | ||||
-rw-r--r-- | src/game/SpellAuras.cpp | 2 | ||||
-rw-r--r-- | src/game/SpellHandler.cpp | 6 | ||||
-rw-r--r-- | src/game/Unit.cpp | 14 | ||||
-rw-r--r-- | src/game/Unit.h | 2 |
10 files changed, 156 insertions, 30 deletions
diff --git a/src/game/CreatureEventAI.cpp b/src/game/CreatureEventAI.cpp index 000d59fac28..3d83b2c4fb4 100644 --- a/src/game/CreatureEventAI.cpp +++ b/src/game/CreatureEventAI.cpp @@ -275,8 +275,8 @@ bool CreatureEventAI::ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pAction //Repeat Timers pHolder.UpdateRepeatTimer(m_creature,event.summon_unit.repeatMin,event.summon_unit.repeatMax); + break; } - break; case EVENT_T_TARGET_MANA: { if (!m_creature->isInCombat() || !m_creature->getVictim() || !m_creature->getVictim()->GetMaxPower(POWER_MANA)) @@ -294,6 +294,34 @@ bool CreatureEventAI::ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pAction case EVENT_T_REACHED_HOME: case EVENT_T_RECEIVE_EMOTE: break; + case EVENT_T_BUFFED: + { + //Note: checked only aura for effect 0, if need check aura for effect 1/2 then + // possible way: pack in event.buffed.amount 2 uint16 (ammount+effectIdx) + Aura* aura = m_creature->GetAura(event.buffed.spellId,0); + if(!aura || aura->GetStackAmount() < event.buffed.amount) + return false; + + //Repeat Timers + pHolder.UpdateRepeatTimer(m_creature,event.buffed.repeatMin,event.buffed.repeatMax); + break; + } + case EVENT_T_TARGET_BUFFED: + { + //Prevent event from occuring on no unit + if (!pActionInvoker) + return false; + + //Note: checked only aura for effect 0, if need check aura for effect 1/2 then + // possible way: pack in event.buffed.amount 2 uint16 (ammount+effectIdx) + Aura* aura = pActionInvoker->GetAura(event.buffed.spellId,0); + if(!aura || aura->GetStackAmount() < event.buffed.amount) + return false; + + //Repeat Timers + pHolder.UpdateRepeatTimer(m_creature,event.buffed.repeatMin,event.buffed.repeatMax); + break; + } default: sLog.outErrorDb("CreatureEventAI: Creature %u using Event %u has invalid Event Type(%u), missing from ProcessEvent() Switch.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type); break; @@ -853,6 +881,9 @@ void CreatureEventAI::JustDied(Unit* killer) if ((*i).Event.event_type == EVENT_T_DEATH) ProcessEvent(*i, killer); } + + // reset phase after any death state events + Phase = 0; } void CreatureEventAI::KilledUnit(Unit* victim) diff --git a/src/game/CreatureEventAI.h b/src/game/CreatureEventAI.h index d94ceeeac18..db9cb680670 100644 --- a/src/game/CreatureEventAI.h +++ b/src/game/CreatureEventAI.h @@ -56,6 +56,8 @@ enum EventAI_Type EVENT_T_QUEST_COMPLETE = 20, // EVENT_T_REACHED_HOME = 21, // NONE EVENT_T_RECEIVE_EMOTE = 22, // EmoteId, Condition, CondValue1, CondValue2 + EVENT_T_BUFFED = 23, // Param1 = SpellID, Param2 = Number of Time STacked, Param3/4 Repeat Min/Max + EVENT_T_TARGET_BUFFED = 24, // Param1 = SpellID, Param2 = Number of Time STacked, Param3/4 Repeat Min/Max EVENT_T_END, }; @@ -507,6 +509,15 @@ struct CreatureEventAI_Event uint32 conditionValue1; uint32 conditionValue2; } receive_emote; + // EVENT_T_BUFFED = 23 + // EVENT_T_TARGET_BUFFED = 24 + struct + { + uint32 spellId; + uint32 amount; + uint32 repeatMin; + uint32 repeatMax; + } buffed; // RAW struct diff --git a/src/game/CreatureEventAIMgr.cpp b/src/game/CreatureEventAIMgr.cpp index 0a6fb8bdb59..643a5a3212a 100644 --- a/src/game/CreatureEventAIMgr.cpp +++ b/src/game/CreatureEventAIMgr.cpp @@ -378,6 +378,20 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Scripts() break; } + case EVENT_T_BUFFED: + case EVENT_T_TARGET_BUFFED: + { + SpellEntry const* pSpell = sSpellStore.LookupEntry(temp.buffed.spellId); + if (!pSpell) + { + sLog.outErrorDb("CreatureEventAI: Creature %u has non-existant SpellID(%u) defined in event %u.", temp.creature_id, temp.spell_hit.spellId, i); + continue; + } + if (temp.buffed.repeatMax < temp.buffed.repeatMin) + sLog.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i); + break; + } + default: sLog.outErrorDb("CreatureEventAI: Creature %u using not checked at load event (%u) in event %u. Need check code update?", temp.creature_id, temp.event_id, i); break; diff --git a/src/game/ObjectMgr.cpp b/src/game/ObjectMgr.cpp index 20ace65d79b..867da4401d3 100644 --- a/src/game/ObjectMgr.cpp +++ b/src/game/ObjectMgr.cpp @@ -110,6 +110,25 @@ LanguageDesc const* GetLanguageDescByID(uint32 lang) return NULL; } +bool SpellClickInfo::IsFitToRequirements(Player const* player) const +{ + if(questStart) + { + // not in expected required quest state + if(!player || (!questStartCanActive || !player->IsActiveQuest(questStart)) && !player->GetQuestRewardStatus(questStart)) + return false; + } + + if(questEnd) + { + // not in expected forbidden quest state + if(!player || player->GetQuestRewardStatus(questEnd)) + return false; + } + + return true; +} + ObjectMgr::ObjectMgr() { m_hiCharGuid = 1; @@ -699,6 +718,12 @@ void ObjectMgr::LoadCreatureTemplates() if(cInfo->rangeattacktime == 0) const_cast<CreatureInfo*>(cInfo)->rangeattacktime = BASE_ATTACK_TIME; + if(cInfo->npcflag & UNIT_NPC_FLAG_SPELLCLICK) + { + sLog.outErrorDb("Creature (Entry: %u) has dynamic flag UNIT_NPC_FLAG_SPELLCLICK (%u) set, it expect to be set by code base at `npc_spellclick_spells` content.",cInfo->Entry,UNIT_NPC_FLAG_SPELLCLICK); + const_cast<CreatureInfo*>(cInfo)->npcflag &= ~UNIT_NPC_FLAG_SPELLCLICK; + } + if((cInfo->npcflag & UNIT_NPC_FLAG_TRAINER) && cInfo->trainer_type >= MAX_TRAINER_TYPE) sLog.outErrorDb("Creature (Entry: %u) has wrong trainer type %u",cInfo->Entry,cInfo->trainer_type); @@ -6567,8 +6592,8 @@ void ObjectMgr::LoadNPCSpellClickSpells() uint32 count = 0; mSpellClickInfoMap.clear(); - - QueryResult *result = WorldDatabase.Query("SELECT npc_entry, spell_id, quest_id, quest_status, cast_flags FROM npc_spellclick_spells"); + // 0 1 2 3 4 5 + QueryResult *result = WorldDatabase.Query("SELECT npc_entry, spell_id, quest_start, quest_start_active, quest_end, cast_flags FROM npc_spellclick_spells"); if(!result) { @@ -6607,28 +6632,45 @@ void ObjectMgr::LoadNPCSpellClickSpells() continue; } - uint32 quest = fields[2].GetUInt32(); + uint32 quest_start = fields[2].GetUInt32(); // quest might be 0 to enable spellclick independent of any quest - if (quest) + if (quest_start) { - if(mQuestTemplates.find(quest) == mQuestTemplates.end()) + if(mQuestTemplates.find(quest_start) == mQuestTemplates.end()) { - sLog.outErrorDb("Table npc_spellclick_spells references unknown quest %u. Skipping entry.", spellid); + sLog.outErrorDb("Table npc_spellclick_spells references unknown start quest %u. Skipping entry.", quest_start); continue; } } - uint32 queststatus = fields[3].GetUInt32(); + bool quest_start_active = fields[3].GetBool(); + + uint32 quest_end = fields[4].GetUInt32(); + // quest might be 0 to enable spellclick active infinity after start quest + if (quest_end) + { + if(mQuestTemplates.find(quest_end) == mQuestTemplates.end()) + { + sLog.outErrorDb("Table npc_spellclick_spells references unknown end quest %u. Skipping entry.", quest_end); + continue; + } + + } - uint8 castFlags = fields[4].GetUInt8(); + uint8 castFlags = fields[5].GetUInt8(); SpellClickInfo info; info.spellId = spellid; - info.questId = quest; - info.questStatus = queststatus; + info.questStart = quest_start; + info.questStartCanActive = quest_start_active; + info.questEnd = quest_end; info.castFlags = castFlags; mSpellClickInfoMap.insert(SpellClickInfoMap::value_type(npc_entry, info)); + + // mark creature template as spell clickable + const_cast<CreatureInfo*>(cInfo)->npcflag |= UNIT_NPC_FLAG_SPELLCLICK; + ++count; } while (result->NextRow()); diff --git a/src/game/ObjectMgr.h b/src/game/ObjectMgr.h index a508c573ef6..0c032bc8ec3 100644 --- a/src/game/ObjectMgr.h +++ b/src/game/ObjectMgr.h @@ -102,12 +102,17 @@ extern ScriptMapMap sWaypointScripts; struct SpellClickInfo { uint32 spellId; - uint32 questId; - uint32 questStatus; + uint32 questStart; // quest start (quest must be active or rewarded for spell apply) + uint32 questEnd; // quest end (quest don't must be rewarded for spell apply) + bool questStartCanActive; // if true then quest start can be active (not only rewarded) uint8 castFlags; + + // helpers + bool IsFitToRequirements(Player const* player) const; }; typedef std::multimap<uint32, SpellClickInfo> SpellClickInfoMap; +typedef std::pair<SpellClickInfoMap::const_iterator,SpellClickInfoMap::const_iterator> SpellClickInfoMapBounds; struct AreaTrigger { @@ -585,7 +590,6 @@ class ObjectMgr void LoadReputationOnKill(); void LoadPointsOfInterest(); - SpellClickInfoMap mSpellClickInfoMap; void LoadNPCSpellClickSpells(); void LoadWeatherZoneChances(); @@ -828,6 +832,11 @@ class ObjectMgr int GetOrNewIndexForLocale(LocaleConstant loc); + SpellClickInfoMapBounds GetSpellClickInfoMapBounds(uint32 creature_id) const + { + return SpellClickInfoMapBounds(mSpellClickInfoMap.lower_bound(creature_id),mSpellClickInfoMap.upper_bound(creature_id)); + } + ItemRequiredTargetMapBounds GetItemRequiredTargetMapBounds(uint32 uiItemEntry) const { return ItemRequiredTargetMapBounds(m_ItemRequiredTarget.lower_bound(uiItemEntry),m_ItemRequiredTarget.upper_bound(uiItemEntry)); @@ -921,6 +930,8 @@ class ObjectMgr ScriptNameMap m_scriptNames; + SpellClickInfoMap mSpellClickInfoMap; + ItemRequiredTargetMap m_ItemRequiredTarget; typedef std::vector<LocaleConstant> LocalForIndex; diff --git a/src/game/Player.cpp b/src/game/Player.cpp index 19e5c7e02d2..af7057518fb 100644 --- a/src/game/Player.cpp +++ b/src/game/Player.cpp @@ -19186,18 +19186,20 @@ void Player::UpdateForQuestWorldObjects() Creature *obj = ObjectAccessor::GetCreatureOrPetOrVehicle(*this, *itr); if(!obj) continue; + // check if this unit requires quest specific flags + if(!obj->HasFlag(UNIT_NPC_FLAGS,UNIT_NPC_FLAG_SPELLCLICK)) + continue; - SpellClickInfoMap const& map = objmgr.mSpellClickInfoMap; - for(SpellClickInfoMap::const_iterator itr = map.lower_bound(obj->GetEntry()); itr != map.upper_bound(obj->GetEntry()); ++itr) + SpellClickInfoMapBounds clickPair = objmgr.GetSpellClickInfoMapBounds(obj->GetEntry()); + for(SpellClickInfoMap::const_iterator itr = clickPair.first; itr != clickPair.second; ++itr) { - if(itr->second.questId != 0) + if(itr->second.questStart || itr->second.questEnd) { obj->BuildCreateUpdateBlockForPlayer(&udata,this); break; } } - } } udata.BuildPacket(&packet); @@ -20754,16 +20756,17 @@ void Player::ResummonPetTemporaryUnSummonedIfAny() bool Player::canSeeSpellClickOn(Creature const *c) const { - SpellClickInfoMap::const_iterator lower = objmgr.mSpellClickInfoMap.lower_bound(c->GetEntry()); - SpellClickInfoMap::const_iterator upper = objmgr.mSpellClickInfoMap.upper_bound(c->GetEntry()); - if(lower == upper) + if(!c->HasFlag(UNIT_NPC_FLAGS,UNIT_NPC_FLAG_SPELLCLICK)) + return false; + + SpellClickInfoMapBounds clickPair = objmgr.GetSpellClickInfoMapBounds(c->GetEntry()); + if(clickPair.first == clickPair.second) return true; - for(SpellClickInfoMap::const_iterator itr = lower; itr != upper; ++itr) - { - if(itr->second.questId == 0 || GetQuestStatus(itr->second.questId) == itr->second.questStatus) + for(SpellClickInfoMap::const_iterator itr = clickPair.first; itr != clickPair.second; ++itr) + if(itr->second.IsFitToRequirements(this)) return true; - } + return false; } diff --git a/src/game/SpellAuras.cpp b/src/game/SpellAuras.cpp index 471209e8f77..c83134335ea 100644 --- a/src/game/SpellAuras.cpp +++ b/src/game/SpellAuras.cpp @@ -4573,7 +4573,7 @@ void AuraEffect::HandleAuraPeriodicDummy(bool apply, bool Real, bool changeAmoun { // Explosive Shot if (apply && !loading && caster) - m_amount += int32(caster->GetTotalAttackPowerValue(RANGED_ATTACK) * 16 / 100); + m_amount += int32(caster->GetTotalAttackPowerValue(RANGED_ATTACK) * 14 / 100); break; } } diff --git a/src/game/SpellHandler.cpp b/src/game/SpellHandler.cpp index 1a9bc13eab8..74313eaa38b 100644 --- a/src/game/SpellHandler.cpp +++ b/src/game/SpellHandler.cpp @@ -522,10 +522,10 @@ void WorldSession::HandleSpellClick( WorldPacket & recv_data ) if(!unit) return; - SpellClickInfoMap const& map = objmgr.mSpellClickInfoMap; - for(SpellClickInfoMap::const_iterator itr = map.lower_bound(unit->GetEntry()); itr != map.upper_bound(unit->GetEntry()); ++itr) + SpellClickInfoMapBounds clickPair = objmgr.GetSpellClickInfoMapBounds(unit->GetEntry()); + for(SpellClickInfoMap::const_iterator itr = clickPair.first; itr != clickPair.second; ++itr) { - if(itr->second.questId == 0 || _player->GetQuestStatus(itr->second.questId) == itr->second.questStatus) + if(itr->second.IsFitToRequirements(_player)) { Unit *caster = (itr->second.castFlags & 0x1) ? (Unit*)_player : (Unit*)unit; Unit *target = (itr->second.castFlags & 0x2) ? (Unit*)_player : (Unit*)unit; diff --git a/src/game/Unit.cpp b/src/game/Unit.cpp index dd1307e7ef4..190aea1b923 100644 --- a/src/game/Unit.cpp +++ b/src/game/Unit.cpp @@ -6575,6 +6575,20 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger break; } } + // Storm, Earth and Fire + if (dummySpell->SpellIconID == 3063) + { + // Earthbind Totem summon only + if(procSpell->Id != 2484) + return false; + + float chance = triggerAmount; + if (!roll_chance_f(chance)) + return false; + + triggered_spell_id = 64695; + break; + } // Ancestral Awakening if (dummySpell->SpellIconID == 3065) { diff --git a/src/game/Unit.h b/src/game/Unit.h index 00478a74acd..ce1c0265268 100644 --- a/src/game/Unit.h +++ b/src/game/Unit.h @@ -614,7 +614,7 @@ enum NPCFlags UNIT_NPC_FLAG_AUCTIONEER = 0x00200000, // 100% UNIT_NPC_FLAG_STABLEMASTER = 0x00400000, // 100% UNIT_NPC_FLAG_GUILD_BANKER = 0x00800000, // cause client to send 997 opcode - UNIT_NPC_FLAG_SPELLCLICK = 0x01000000, // cause client to send 1015 opcode (spell click) + UNIT_NPC_FLAG_SPELLCLICK = 0x01000000, // cause client to send 1015 opcode (spell click), dynamic, set at loading and don't must be set in DB UNIT_NPC_FLAG_GUARD = 0x10000000, // custom flag for guards UNIT_NPC_FLAG_OUTDOORPVP = 0x20000000, // custom flag for outdoor pvp creatures }; |