aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/game/CreatureEventAI.cpp33
-rw-r--r--src/game/CreatureEventAI.h11
-rw-r--r--src/game/CreatureEventAIMgr.cpp14
-rw-r--r--src/game/ObjectMgr.cpp62
-rw-r--r--src/game/ObjectMgr.h17
-rw-r--r--src/game/Player.cpp25
-rw-r--r--src/game/SpellAuras.cpp2
-rw-r--r--src/game/SpellHandler.cpp6
-rw-r--r--src/game/Unit.cpp14
-rw-r--r--src/game/Unit.h2
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
};