aboutsummaryrefslogtreecommitdiff
path: root/src/server/game
diff options
context:
space:
mode:
authorVincent-Michael <Vincent_Michael@gmx.de>2014-04-05 20:43:05 +0200
committerVincent-Michael <Vincent_Michael@gmx.de>2014-04-05 20:43:05 +0200
commite4b14b943d3a2aa5d7b41107adc8c5236b86d1af (patch)
tree2ef54a0bc7d4f5385b9266afe528265ed9f35f8d /src/server/game
parenta903c335024d0ef4a3d9899587fcd0b10a92b31e (diff)
parent29610b250dd5017f068264d9b1a37748c9f30feb (diff)
Merge branch 'master' of github.com:TrinityCore/TrinityCore into 4.3.4
Conflicts: sql/old/3.3.5a/TDB52_to_TDB53_updates/world/2013_07_24_00_world_spell_script_names.sql sql/updates/world/2013_07_24_00_world_spell_script_names.sql sql/updates/world/2013_07_24_00_world_spell_script_names_335.sql src/server/game/Achievements/AchievementMgr.cpp src/server/game/Achievements/AchievementMgr.h src/server/game/Battlegrounds/ArenaTeamMgr.cpp src/server/game/Chat/ChatLink.cpp src/server/game/DataStores/DBCStores.cpp src/server/game/DataStores/DBCStructure.h src/server/game/DataStores/DBCfmt.h src/server/game/Entities/DynamicObject/DynamicObject.h src/server/game/Entities/Object/Object.cpp src/server/game/Entities/Player/Player.cpp src/server/game/Entities/Unit/StatSystem.cpp src/server/game/Entities/Unit/Unit.cpp src/server/game/Entities/Unit/Unit.h src/server/game/Groups/Group.cpp src/server/game/Handlers/QuestHandler.cpp src/server/game/Miscellaneous/SharedDefines.h src/server/game/Spells/Auras/SpellAuraEffects.cpp src/server/game/Spells/Auras/SpellAuras.cpp src/server/game/World/World.h src/server/scripts/Commands/cs_character.cpp src/server/scripts/Commands/cs_lookup.cpp src/server/scripts/Commands/cs_titles.cpp src/server/scripts/EasternKingdoms/ZulAman/boss_halazzi.cpp src/server/scripts/EasternKingdoms/ZulAman/boss_zuljin.cpp src/server/scripts/EasternKingdoms/ZulGurub/boss_jeklik.cpp src/server/scripts/Kalimdor/zone_darkshore.cpp src/server/scripts/Kalimdor/zone_mulgore.cpp src/server/scripts/Kalimdor/zone_tanaris.cpp src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp src/server/scripts/Spells/spell_dk.cpp src/server/scripts/Spells/spell_mage.cpp src/server/scripts/Spells/spell_rogue.cpp src/server/shared/Database/Implementation/CharacterDatabase.cpp src/server/shared/Database/Implementation/CharacterDatabase.h src/tools/vmap4_extractor/mpq_libmpq04.h
Diffstat (limited to 'src/server/game')
-rw-r--r--src/server/game/AI/SmartScripts/SmartAI.cpp16
-rw-r--r--src/server/game/AI/SmartScripts/SmartAI.h2
-rw-r--r--src/server/game/AI/SmartScripts/SmartScript.cpp82
-rw-r--r--src/server/game/AI/SmartScripts/SmartScript.h5
-rw-r--r--src/server/game/AI/SmartScripts/SmartScriptMgr.cpp76
-rw-r--r--src/server/game/AI/SmartScripts/SmartScriptMgr.h16
-rw-r--r--src/server/game/Achievements/AchievementMgr.cpp126
-rw-r--r--src/server/game/Achievements/AchievementMgr.h27
-rw-r--r--src/server/game/Battlegrounds/Battleground.cpp24
-rw-r--r--src/server/game/Battlegrounds/Battleground.h4
-rw-r--r--src/server/game/Battlegrounds/BattlegroundMgr.cpp26
-rw-r--r--src/server/game/Battlegrounds/BattlegroundMgr.h3
-rw-r--r--src/server/game/Battlegrounds/BattlegroundQueue.h4
-rw-r--r--src/server/game/Battlegrounds/Zones/BattlegroundAV.cpp2
-rw-r--r--src/server/game/Battlegrounds/Zones/BattlegroundEY.cpp2
-rw-r--r--src/server/game/Battlegrounds/Zones/BattlegroundIC.cpp16
-rw-r--r--src/server/game/Chat/Chat.cpp30
-rw-r--r--src/server/game/Chat/ChatLink.cpp2
-rw-r--r--src/server/game/Conditions/ConditionMgr.cpp5
-rw-r--r--src/server/game/DataStores/DBCEnums.h7
-rw-r--r--src/server/game/DataStores/DBCStores.cpp17
-rw-r--r--src/server/game/DataStores/DBCStores.h2
-rw-r--r--src/server/game/DataStores/DBCStructure.h30
-rw-r--r--src/server/game/DataStores/DBCfmt.h5
-rw-r--r--src/server/game/DungeonFinding/LFGMgr.cpp7
-rw-r--r--src/server/game/Entities/Creature/Creature.h8
-rw-r--r--src/server/game/Entities/Creature/GossipDef.cpp9
-rw-r--r--src/server/game/Entities/Creature/GossipDef.h9
-rw-r--r--src/server/game/Entities/DynamicObject/DynamicObject.cpp33
-rw-r--r--src/server/game/Entities/DynamicObject/DynamicObject.h4
-rw-r--r--src/server/game/Entities/GameObject/GameObject.cpp37
-rw-r--r--src/server/game/Entities/GameObject/GameObject.h2
-rw-r--r--src/server/game/Entities/Object/Object.cpp31
-rw-r--r--src/server/game/Entities/Object/Object.h7
-rw-r--r--src/server/game/Entities/Pet/Pet.cpp42
-rw-r--r--src/server/game/Entities/Player/Player.cpp330
-rw-r--r--src/server/game/Entities/Player/Player.h12
-rw-r--r--src/server/game/Entities/Transport/Transport.cpp150
-rw-r--r--src/server/game/Entities/Transport/Transport.h28
-rw-r--r--src/server/game/Entities/Unit/Unit.cpp62
-rw-r--r--src/server/game/Entities/Unit/Unit.h18
-rw-r--r--src/server/game/Globals/ObjectMgr.cpp11
-rw-r--r--src/server/game/Groups/Group.cpp67
-rw-r--r--src/server/game/Groups/Group.h3
-rw-r--r--src/server/game/Groups/GroupMgr.cpp4
-rw-r--r--src/server/game/Handlers/AddonHandler.cpp2
-rw-r--r--src/server/game/Handlers/CharacterHandler.cpp11
-rw-r--r--src/server/game/Handlers/GroupHandler.cpp22
-rw-r--r--src/server/game/Handlers/ItemHandler.cpp10
-rw-r--r--src/server/game/Handlers/LootHandler.cpp31
-rw-r--r--src/server/game/Handlers/MiscHandler.cpp13
-rw-r--r--src/server/game/Handlers/PetHandler.cpp2
-rw-r--r--src/server/game/Handlers/QuestHandler.cpp193
-rw-r--r--src/server/game/Loot/LootMgr.cpp48
-rw-r--r--src/server/game/Loot/LootMgr.h7
-rw-r--r--src/server/game/Mails/Mail.cpp2
-rw-r--r--src/server/game/Maps/Map.cpp288
-rw-r--r--src/server/game/Maps/Map.h30
-rw-r--r--src/server/game/Maps/MapManager.cpp8
-rw-r--r--src/server/game/Miscellaneous/SharedDefines.h30
-rwxr-xr-xsrc/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp4
-rw-r--r--src/server/game/Movement/PathGenerator.cpp130
-rw-r--r--src/server/game/Movement/PathGenerator.h3
-rw-r--r--src/server/game/Movement/Spline/MovementTypedefs.h6
-rw-r--r--src/server/game/Movement/Spline/Spline.h2
-rw-r--r--src/server/game/Scripting/ScriptLoader.cpp2
-rw-r--r--src/server/game/Scripting/ScriptMgr.cpp9
-rw-r--r--src/server/game/Scripting/ScriptMgr.h4
-rw-r--r--src/server/game/Server/WorldSession.h1
-rw-r--r--src/server/game/Spells/Auras/SpellAuraEffects.cpp9
-rw-r--r--src/server/game/Spells/Spell.cpp66
-rw-r--r--src/server/game/Spells/Spell.h6
-rw-r--r--src/server/game/Spells/SpellEffects.cpp7
-rw-r--r--src/server/game/Spells/SpellMgr.cpp11
-rw-r--r--src/server/game/Spells/SpellMgr.h8
-rw-r--r--src/server/game/Spells/SpellScript.h1
-rw-r--r--src/server/game/Texts/CreatureTextMgr.cpp4
-rw-r--r--src/server/game/Tickets/TicketMgr.cpp4
-rw-r--r--src/server/game/Tools/PlayerDump.cpp12
-rw-r--r--src/server/game/Warden/Warden.cpp3
-rw-r--r--src/server/game/World/World.cpp14
-rw-r--r--src/server/game/World/World.h1
82 files changed, 1790 insertions, 617 deletions
diff --git a/src/server/game/AI/SmartScripts/SmartAI.cpp b/src/server/game/AI/SmartScripts/SmartAI.cpp
index a203aeb6b97..23dea877776 100644
--- a/src/server/game/AI/SmartScripts/SmartAI.cpp
+++ b/src/server/game/AI/SmartScripts/SmartAI.cpp
@@ -412,26 +412,12 @@ void SmartAI::MovementInform(uint32 MovementType, uint32 Data)
MovepointReached(Data);
}
-void SmartAI::RemoveAuras()
-{
- /// @fixme: duplicated logic in CreatureAI::_EnterEvadeMode (could use RemoveAllAurasExceptType)
- Unit::AuraApplicationMap& appliedAuras = me->GetAppliedAuras();
- for (Unit::AuraApplicationMap::iterator iter = appliedAuras.begin(); iter != appliedAuras.end();)
- {
- Aura const* aura = iter->second->GetBase();
- if (!aura->IsPassive() && !aura->HasEffectType(SPELL_AURA_CONTROL_VEHICLE) && !aura->HasEffectType(SPELL_AURA_CLONE_CASTER) && aura->GetCasterGUID() != me->GetGUID())
- me->RemoveAura(iter);
- else
- ++iter;
- }
-}
-
void SmartAI::EnterEvadeMode()
{
if (!me->IsAlive() || me->IsInEvadeMode())
return;
- RemoveAuras();
+ me->RemoveAllAurasExceptType(SPELL_AURA_CONTROL_VEHICLE, SPELL_AURA_CLONE_CASTER);
me->AddUnitState(UNIT_STATE_EVADE);
me->DeleteThreatList();
diff --git a/src/server/game/AI/SmartScripts/SmartAI.h b/src/server/game/AI/SmartScripts/SmartAI.h
index dcf64b657f8..4d66b976746 100644
--- a/src/server/game/AI/SmartScripts/SmartAI.h
+++ b/src/server/game/AI/SmartScripts/SmartAI.h
@@ -193,8 +193,6 @@ class SmartAI : public CreatureAI
}
void StartDespawn() { mDespawnState = 2; }
- void RemoveAuras();
-
void OnSpellClick(Unit* clicker, bool& result);
private:
diff --git a/src/server/game/AI/SmartScripts/SmartScript.cpp b/src/server/game/AI/SmartScripts/SmartScript.cpp
index 99ac2ca6f25..006ee4b69ad 100644
--- a/src/server/game/AI/SmartScripts/SmartScript.cpp
+++ b/src/server/game/AI/SmartScripts/SmartScript.cpp
@@ -76,6 +76,7 @@ SmartScript::SmartScript()
goOrigGUID = 0;
mLastInvoker = 0;
mScriptType = SMART_SCRIPT_TYPE_CREATURE;
+ isProcessingTimedActionList = false;
}
SmartScript::~SmartScript()
@@ -511,7 +512,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
// unless target is outside spell range, out of mana, or LOS.
bool _allowMove = false;
- SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(e.action.cast.spell);
+ SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(e.action.cast.spell);
int32 mana = me->GetPower(POWER_MANA);
if (me->GetDistance(*itr) > spellInfo->GetMaxRange(true) ||
@@ -3116,6 +3117,68 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
ProcessTimedAction(e, e.event.friendlyHealthPct.repeatMin, e.event.friendlyHealthPct.repeatMax, target);
break;
}
+ case SMART_EVENT_DISTANCE_CREATURE:
+ {
+ if (!me)
+ return;
+
+ WorldObject* creature = NULL;
+
+ if (e.event.distance.guid != 0)
+ {
+ creature = FindCreatureNear(me, e.event.distance.guid);
+
+ if (!creature)
+ return;
+
+ if (!me->IsInRange(creature, 0, (float)e.event.distance.dist))
+ return;
+ }
+ else if (e.event.distance.entry != 0)
+ {
+ std::list<Creature*> list;
+ me->GetCreatureListWithEntryInGrid(list, e.event.distance.entry, (float)e.event.distance.dist);
+
+ if (list.size() > 0)
+ creature = list.front();
+ }
+
+ if (creature)
+ ProcessTimedAction(e, e.event.distance.repeat, e.event.distance.repeat);
+
+ break;
+ }
+ case SMART_EVENT_DISTANCE_GAMEOBJECT:
+ {
+ if (!me)
+ return;
+
+ WorldObject* gameobject = NULL;
+
+ if (e.event.distance.guid != 0)
+ {
+ gameobject = FindGameObjectNear(me, e.event.distance.guid);
+
+ if (!gameobject)
+ return;
+
+ if (!me->IsInRange(gameobject, 0, (float)e.event.distance.dist))
+ return;
+ }
+ else if (e.event.distance.entry != 0)
+ {
+ std::list<GameObject*> list;
+ me->GetGameObjectListWithEntryInGrid(list, e.event.distance.entry, (float)e.event.distance.dist);
+
+ if (list.size() > 0)
+ gameobject = list.front();
+ }
+
+ if (gameobject)
+ ProcessTimedAction(e, e.event.distance.repeat, e.event.distance.repeat);
+
+ break;
+ }
default:
TC_LOG_ERROR("sql.sql", "SmartScript::ProcessEvent: Unhandled Event type %u", e.GetEventType());
break;
@@ -3136,6 +3199,10 @@ void SmartScript::InitTimer(SmartScriptHolder& e)
case SMART_EVENT_OOC_LOS:
RecalcTimer(e, e.event.los.cooldownMin, e.event.los.cooldownMax);
break;
+ case SMART_EVENT_DISTANCE_CREATURE:
+ case SMART_EVENT_DISTANCE_GAMEOBJECT:
+ RecalcTimer(e, e.event.distance.repeat, e.event.distance.repeat);
+ break;
default:
e.active = true;
break;
@@ -3196,6 +3263,8 @@ void SmartScript::UpdateTimer(SmartScriptHolder& e, uint32 const diff)
case SMART_EVENT_TARGET_BUFFED:
case SMART_EVENT_IS_BEHIND_TARGET:
case SMART_EVENT_FRIENDLY_HEALTH_PCT:
+ case SMART_EVENT_DISTANCE_CREATURE:
+ case SMART_EVENT_DISTANCE_GAMEOBJECT:
{
ProcessEvent(e);
if (e.GetScriptType() == SMART_SCRIPT_TYPE_TIMED_ACTIONLIST)
@@ -3252,6 +3321,7 @@ void SmartScript::OnUpdate(uint32 const diff)
bool needCleanup = true;
if (!mTimedActionList.empty())
{
+ isProcessingTimedActionList = true;
for (SmartAIEventList::iterator i = mTimedActionList.begin(); i != mTimedActionList.end(); ++i)
{
if ((*i).enableTimed)
@@ -3260,6 +3330,8 @@ void SmartScript::OnUpdate(uint32 const diff)
needCleanup = false;
}
}
+
+ isProcessingTimedActionList = false;
}
if (needCleanup)
mTimedActionList.clear();
@@ -3501,6 +3573,14 @@ Unit* SmartScript::DoFindClosestFriendlyInRange(float range, bool playerOnly)
void SmartScript::SetScript9(SmartScriptHolder& e, uint32 entry)
{
+ //do NOT clear mTimedActionList if it's being iterated because it will invalidate the iterator and delete
+ // any SmartScriptHolder contained like the "e" parameter passed to this function
+ if (isProcessingTimedActionList)
+ {
+ TC_LOG_ERROR("scripts.ai", "Entry %d SourceType %u Event %u Action %u is trying to overwrite timed action list from a timed action, this is not allowed!.", e.entryOrGuid, e.GetScriptType(), e.GetEventType(), e.GetActionType());
+ return;
+ }
+
mTimedActionList.clear();
mTimedActionList = sSmartScriptMgr->GetScript(entry, SMART_SCRIPT_TYPE_TIMED_ACTIONLIST);
if (mTimedActionList.empty())
diff --git a/src/server/game/AI/SmartScripts/SmartScript.h b/src/server/game/AI/SmartScripts/SmartScript.h
index 2e1068d1bff..244728a3037 100644
--- a/src/server/game/AI/SmartScripts/SmartScript.h
+++ b/src/server/game/AI/SmartScripts/SmartScript.h
@@ -118,7 +118,7 @@ class SmartScript
smart = false;
if (!smart)
- TC_LOG_ERROR("sql.sql", "SmartScript: Action target Creature (GUID: %u Entry: %u) is not using SmartAI, action skipped to prevent crash.", c ? c->GetDBTableGUIDLow() : (me ? me->GetDBTableGUIDLow() : 0), c ? c->GetEntry() : (me ? me->GetEntry() : 0));
+ TC_LOG_ERROR("sql.sql", "SmartScript: Action target Creature (GUID: %u Entry: %u) is not using SmartAI, action called by Creature (GUID: %u Entry: %u) skipped to prevent crash.", c ? c->GetDBTableGUIDLow() : 0, c ? c->GetEntry() : 0, me ? me->GetDBTableGUIDLow() : 0, me ? me->GetEntry() : 0);
return smart;
}
@@ -132,7 +132,7 @@ class SmartScript
if (!go || go->GetAIName() != "SmartGameObjectAI")
smart = false;
if (!smart)
- TC_LOG_ERROR("sql.sql", "SmartScript: Action target GameObject (GUID: %u Entry: %u) is not using SmartGameObjectAI, action skipped to prevent crash.", g ? g->GetDBTableGUIDLow() : (go ? go->GetDBTableGUIDLow() : 0), g ? g->GetEntry() : (go ? go->GetEntry() : 0));
+ TC_LOG_ERROR("sql.sql", "SmartScript: Action target GameObject (GUID: %u Entry: %u) is not using SmartGameObjectAI, action called by GameObject (GUID: %u Entry: %u) skipped to prevent crash.", g ? g->GetDBTableGUIDLow() : 0, g ? g->GetEntry() : 0, go ? go->GetDBTableGUIDLow() : 0, go ? go->GetEntry() : 0);
return smart;
}
@@ -222,6 +222,7 @@ class SmartScript
SmartAIEventList mEvents;
SmartAIEventList mInstallEvents;
SmartAIEventList mTimedActionList;
+ bool isProcessingTimedActionList;
Creature* me;
uint64 meOrigGUID;
GameObject* go;
diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp
index d912de2a72c..993e6967ba5 100644
--- a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp
+++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp
@@ -583,6 +583,56 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
return false;
}
break;
+ case SMART_EVENT_DISTANCE_CREATURE:
+ if (e.event.distance.guid == 0 && e.event.distance.entry == 0)
+ {
+ TC_LOG_ERROR("sql.sql", "SmartAIMgr: Event SMART_EVENT_DISTANCE_CREATURE did not provide creature guid or entry, skipped.");
+ return false;
+ }
+
+ if (e.event.distance.guid != 0 && e.event.distance.entry != 0)
+ {
+ TC_LOG_ERROR("sql.sql", "SmartAIMgr: Event SMART_EVENT_DISTANCE_CREATURE provided both an entry and guid, skipped.");
+ return false;
+ }
+
+ if (e.event.distance.guid != 0 && !sObjectMgr->GetCreatureData(e.event.distance.guid))
+ {
+ TC_LOG_ERROR("sql.sql", "SmartAIMgr: Event SMART_EVENT_DISTANCE_CREATURE using invalid creature guid %u, skipped.", e.event.distance.guid);
+ return false;
+ }
+
+ if (e.event.distance.entry != 0 && !sObjectMgr->GetCreatureTemplate(e.event.distance.entry))
+ {
+ TC_LOG_ERROR("sql.sql", "SmartAIMgr: Event SMART_EVENT_DISTANCE_CREATURE using invalid creature entry %u, skipped.", e.event.distance.entry);
+ return false;
+ }
+ break;
+ case SMART_EVENT_DISTANCE_GAMEOBJECT:
+ if (e.event.distance.guid == 0 && e.event.distance.entry == 0)
+ {
+ TC_LOG_ERROR("sql.sql", "SmartAIMgr: Event SMART_EVENT_DISTANCE_GAMEOBJECT did not provide gameobject guid or entry, skipped.");
+ return false;
+ }
+
+ if (e.event.distance.guid != 0 && e.event.distance.entry != 0)
+ {
+ TC_LOG_ERROR("sql.sql", "SmartAIMgr: Event SMART_EVENT_DISTANCE_GAMEOBJECT provided both an entry and guid, skipped.");
+ return false;
+ }
+
+ if (e.event.distance.guid != 0 && !sObjectMgr->GetGOData(e.event.distance.guid))
+ {
+ TC_LOG_ERROR("sql.sql", "SmartAIMgr: Event SMART_EVENT_DISTANCE_GAMEOBJECT using invalid gameobject guid %u, skipped.", e.event.distance.guid);
+ return false;
+ }
+
+ if (e.event.distance.entry != 0 && !sObjectMgr->GetGameObjectTemplate(e.event.distance.entry))
+ {
+ TC_LOG_ERROR("sql.sql", "SmartAIMgr: Event SMART_EVENT_DISTANCE_GAMEOBJECT using invalid gameobject entry %u, skipped.", e.event.distance.entry);
+ return false;
+ }
+ break;
case SMART_EVENT_GO_STATE_CHANGED:
case SMART_EVENT_GO_EVENT_INFORM:
case SMART_EVENT_TIMED_EVENT_TRIGGERED:
@@ -887,6 +937,12 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
return false;
break;
}
+ case SMART_ACTION_CALL_RANDOM_RANGE_TIMED_ACTIONLIST:
+ {
+ if (!IsMinMaxValid(e, e.action.randTimedActionList.entry1, e.action.randTimedActionList.entry2))
+ return false;
+ break;
+ }
case SMART_ACTION_SET_POWER:
case SMART_ACTION_ADD_POWER:
case SMART_ACTION_REMOVE_POWER:
@@ -934,6 +990,24 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
}
break;
}
+ case SMART_ACTION_EQUIP:
+ {
+ if (e.GetScriptType() == SMART_SCRIPT_TYPE_CREATURE)
+ {
+ int8 equipId = (int8)e.action.equip.entry;
+
+ if (equipId)
+ {
+ EquipmentInfo const* einfo = sObjectMgr->GetEquipmentInfo(e.entryOrGuid, equipId);
+ if (!einfo)
+ {
+ TC_LOG_ERROR("sql.sql", "SmartScript: SMART_ACTION_EQUIP uses non-existent equipment info id %u for creature %u, skipped.", equipId, e.entryOrGuid);
+ return false;
+ }
+ }
+ }
+ break;
+ }
case SMART_ACTION_FOLLOW:
case SMART_ACTION_SET_ORIENTATION:
case SMART_ACTION_STORE_TARGET_LIST:
@@ -970,7 +1044,6 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
case SMART_ACTION_MOVE_TO_POS:
case SMART_ACTION_RESPAWN_TARGET:
case SMART_ACTION_CLOSE_GOSSIP:
- case SMART_ACTION_EQUIP:
case SMART_ACTION_TRIGGER_TIMED_EVENT:
case SMART_ACTION_REMOVE_TIMED_EVENT:
case SMART_ACTION_OVERRIDE_SCRIPT_BASE_OBJECT:
@@ -986,7 +1059,6 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
case SMART_ACTION_SIMPLE_TALK:
case SMART_ACTION_CROSS_CAST:
case SMART_ACTION_CALL_RANDOM_TIMED_ACTIONLIST:
- case SMART_ACTION_CALL_RANDOM_RANGE_TIMED_ACTIONLIST:
case SMART_ACTION_RANDOM_MOVE:
case SMART_ACTION_SET_UNIT_FIELD_BYTES_1:
case SMART_ACTION_REMOVE_UNIT_FIELD_BYTES_1:
diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.h b/src/server/game/AI/SmartScripts/SmartScriptMgr.h
index d47dbeae600..78a26e7e836 100644
--- a/src/server/game/AI/SmartScripts/SmartScriptMgr.h
+++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.h
@@ -166,8 +166,10 @@ enum SMART_EVENT
SMART_EVENT_ACTION_DONE = 72, // eventId (SharedDefines.EventId)
SMART_EVENT_ON_SPELLCLICK = 73, // clicker (unit)
SMART_EVENT_FRIENDLY_HEALTH_PCT = 74, // minHpPct, maxHpPct, repeatMin, repeatMax
+ SMART_EVENT_DISTANCE_CREATURE = 75, // guid, entry, distance, repeat
+ SMART_EVENT_DISTANCE_GAMEOBJECT = 76, // guid, entry, distance, repeat
- SMART_EVENT_END = 75
+ SMART_EVENT_END = 77
};
struct SmartEvent
@@ -389,6 +391,14 @@ struct SmartEvent
struct
{
+ uint32 guid;
+ uint32 entry;
+ uint32 dist;
+ uint32 repeat;
+ } distance;
+
+ struct
+ {
uint32 param1;
uint32 param2;
uint32 param3;
@@ -1221,7 +1231,7 @@ const uint32 SmartAIEventMask[SMART_EVENT_END][2] =
{SMART_EVENT_REACHED_HOME, SMART_SCRIPT_TYPE_MASK_CREATURE },
{SMART_EVENT_RECEIVE_EMOTE, SMART_SCRIPT_TYPE_MASK_CREATURE },
{SMART_EVENT_HAS_AURA, SMART_SCRIPT_TYPE_MASK_CREATURE },
- {SMART_EVENT_TARGET_BUFFED, SMART_SCRIPT_TYPE_MASK_CREATURE + SMART_SCRIPT_TYPE_MASK_GAMEOBJECT },
+ {SMART_EVENT_TARGET_BUFFED, SMART_SCRIPT_TYPE_MASK_CREATURE },
{SMART_EVENT_RESET, SMART_SCRIPT_TYPE_MASK_CREATURE },
{SMART_EVENT_IC_LOS, SMART_SCRIPT_TYPE_MASK_CREATURE },
{SMART_EVENT_PASSENGER_BOARDED, SMART_SCRIPT_TYPE_MASK_CREATURE },
@@ -1272,6 +1282,8 @@ const uint32 SmartAIEventMask[SMART_EVENT_END][2] =
{SMART_EVENT_ACTION_DONE, SMART_SCRIPT_TYPE_MASK_CREATURE },
{SMART_EVENT_ON_SPELLCLICK, SMART_SCRIPT_TYPE_MASK_CREATURE },
{SMART_EVENT_FRIENDLY_HEALTH_PCT, SMART_SCRIPT_TYPE_MASK_CREATURE },
+ {SMART_EVENT_DISTANCE_CREATURE, SMART_SCRIPT_TYPE_MASK_CREATURE },
+ {SMART_EVENT_DISTANCE_GAMEOBJECT, SMART_SCRIPT_TYPE_MASK_CREATURE },
};
enum SmartEventFlags
diff --git a/src/server/game/Achievements/AchievementMgr.cpp b/src/server/game/Achievements/AchievementMgr.cpp
index dd3ff925335..3cfa79ed8d0 100644
--- a/src/server/game/Achievements/AchievementMgr.cpp
+++ b/src/server/game/Achievements/AchievementMgr.cpp
@@ -98,6 +98,7 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria)
case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM: // only Children's Week achievements
case ACHIEVEMENT_CRITERIA_TYPE_GET_KILLING_BLOWS:
case ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL:
+ case ACHIEVEMENT_CRITERIA_TYPE_ON_LOGIN:
break;
default:
if (dataType != ACHIEVEMENT_CRITERIA_DATA_TYPE_SCRIPT)
@@ -111,7 +112,8 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria)
switch (dataType)
{
case ACHIEVEMENT_CRITERIA_DATA_TYPE_NONE:
- case ACHIEVEMENT_CRITERIA_DATA_INSTANCE_SCRIPT:
+ case ACHIEVEMENT_CRITERIA_DATA_TYPE_INSTANCE_SCRIPT:
+ case ACHIEVEMENT_CRITERIA_DATA_TYPE_NTH_BIRTHDAY:
return true;
case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_CREATURE:
if (!creature.id || !sObjectMgr->GetCreatureTemplate(creature.id))
@@ -242,7 +244,7 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria)
case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_EQUIPED_ITEM:
if (equipped_item.item_quality >= MAX_ITEM_QUALITY)
{
- TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_requirement` (Entry: %u Type: %u) for requirement ACHIEVEMENT_CRITERIA_REQUIRE_S_EQUIPED_ITEM (%u) has unknown quality state in value1 (%u), ignored.",
+ TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_S_EQUIPED_ITEM (%u) has unknown quality state in value1 (%u), ignored.",
criteria->ID, criteria->type, dataType, equipped_item.item_quality);
return false;
}
@@ -267,6 +269,14 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria)
return false;
}
return true;
+ case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_KNOWN_TITLE:
+ if (!sCharTitlesStore.LookupEntry(known_title.title_id))
+ {
+ TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_S_KNOWN_TITLE (%u) have unknown title_id in value1 (%u), ignore.",
+ criteria->ID, criteria->type, dataType, known_title.title_id);
+ return false;
+ }
+ return true;
default:
TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) has data for non-supported data type (%u), ignored.", criteria->ID, criteria->type, dataType);
return false;
@@ -292,7 +302,7 @@ bool AchievementCriteriaData::Meets(uint32 criteria_id, Player const* source, Un
return false;
return true;
case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_PLAYER_CLASS_RACE:
- if (!source || source->GetTypeId() != TYPEID_PLAYER)
+ if (source->GetTypeId() != TYPEID_PLAYER)
return false;
if (classRace.class_id && classRace.class_id != source->ToPlayer()->getClass())
return false;
@@ -338,22 +348,22 @@ bool AchievementCriteriaData::Meets(uint32 criteria_id, Player const* source, Un
uint32 score = bg->GetTeamScore(source->GetTeamId() == TEAM_ALLIANCE ? TEAM_HORDE : TEAM_ALLIANCE);
return score >= bg_loss_team_score.min_score && score <= bg_loss_team_score.max_score;
}
- case ACHIEVEMENT_CRITERIA_DATA_INSTANCE_SCRIPT:
+ case ACHIEVEMENT_CRITERIA_DATA_TYPE_INSTANCE_SCRIPT:
{
if (!source->IsInWorld())
return false;
Map* map = source->GetMap();
if (!map->IsDungeon())
{
- TC_LOG_ERROR("achievement", "Achievement system call ACHIEVEMENT_CRITERIA_DATA_INSTANCE_SCRIPT (%u) for achievement criteria %u for non-dungeon/non-raid map %u",
- ACHIEVEMENT_CRITERIA_DATA_INSTANCE_SCRIPT, criteria_id, map->GetId());
- return false;
+ TC_LOG_ERROR("achievement", "Achievement system call ACHIEVEMENT_CRITERIA_DATA_TYPE_INSTANCE_SCRIPT (%u) for achievement criteria %u for non-dungeon/non-raid map %u",
+ dataType, criteria_id, map->GetId());
+ return false;
}
- InstanceScript* instance = ((InstanceMap*)map)->GetInstanceScript();
+ InstanceScript* instance = map->ToInstanceMap()->GetInstanceScript();
if (!instance)
{
- TC_LOG_ERROR("achievement", "Achievement system call ACHIEVEMENT_CRITERIA_DATA_INSTANCE_SCRIPT (%u) for achievement criteria %u for map %u but map does not have a instance script",
- ACHIEVEMENT_CRITERIA_DATA_INSTANCE_SCRIPT, criteria_id, map->GetId());
+ TC_LOG_ERROR("achievement", "Achievement system call ACHIEVEMENT_CRITERIA_DATA_TYPE_INSTANCE_SCRIPT (%u) for achievement criteria %u for map %u but map does not have a instance script",
+ dataType, criteria_id, map->GetId());
return false;
}
return instance->CheckAchievementCriteriaMeet(criteria_id, source, target, miscValue1);
@@ -365,6 +375,27 @@ bool AchievementCriteriaData::Meets(uint32 criteria_id, Player const* source, Un
return false;
return pProto->ItemLevel >= equipped_item.item_level && pProto->Quality >= equipped_item.item_quality;
}
+ case ACHIEVEMENT_CRITERIA_DATA_TYPE_NTH_BIRTHDAY:
+ {
+ time_t birthday_start = time_t(sWorld->getIntConfig(CONFIG_BIRTHDAY_TIME));
+
+ tm birthday_tm;
+ ACE_OS::localtime_r(&birthday_start, &birthday_tm);
+
+ // exactly N birthday
+ birthday_tm.tm_year += birthday_login.nth_birthday;
+
+ time_t birthday = mktime(&birthday_tm);
+ time_t now = sWorld->GetGameTime();
+ return now <= birthday + DAY && now >= birthday;
+ }
+ case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_KNOWN_TITLE:
+ {
+ if (CharTitlesEntry const* titleInfo = sCharTitlesStore.LookupEntry(known_title.title_id))
+ return source && source->HasTitle(titleInfo->bit_index);
+
+ return false;
+ }
default:
break;
}
@@ -1359,7 +1390,6 @@ void AchievementMgr<T>::UpdateAchievementCriteria(AchievementCriteriaTypes type,
case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_RAID:
case ACHIEVEMENT_CRITERIA_TYPE_PLAY_ARENA:
case ACHIEVEMENT_CRITERIA_TYPE_OWN_RANK:
- case ACHIEVEMENT_CRITERIA_TYPE_EARNED_PVP_TITLE:
case ACHIEVEMENT_CRITERIA_TYPE_SPENT_GOLD_GUILD_REPAIRS:
case ACHIEVEMENT_CRITERIA_TYPE_CRAFT_ITEMS_GUILD:
case ACHIEVEMENT_CRITERIA_TYPE_CATCH_FROM_POOL:
@@ -1520,6 +1550,8 @@ bool AchievementMgr<T>::IsCompletedCriteria(AchievementCriteriaEntry const* achi
return progress->counter >= achievementCriteria->currencyGain.count;
case ACHIEVEMENT_CRITERIA_TYPE_WIN_ARENA:
return achievementCriteria->win_arena.count && progress->counter >= achievementCriteria->win_arena.count;
+ case ACHIEVEMENT_CRITERIA_TYPE_ON_LOGIN:
+ return true;
// handle all statistic-only criteria here
case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND:
case ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP:
@@ -1847,25 +1879,30 @@ void AchievementMgr<Player>::CompletedAchievement(AchievementEntry const* achiev
// mail
if (reward->sender)
{
- Item* item = reward->itemId ? Item::CreateItem(reward->itemId, 1, GetOwner()) : NULL;
-
- int loc_idx = GetOwner()->GetSession()->GetSessionDbLocaleIndex();
+ MailDraft draft(reward->mailTemplate);
- // subject and text
- std::string subject = reward->subject;
- std::string text = reward->text;
- if (loc_idx >= 0)
+ if (!reward->mailTemplate)
{
- if (AchievementRewardLocale const* loc = sAchievementMgr->GetAchievementRewardLocale(achievement))
+ // subject and text
+ std::string subject = reward->subject;
+ std::string text = reward->text;
+
+ int locIdx = GetOwner()->GetSession()->GetSessionDbLocaleIndex();
+ if (locIdx >= 0)
{
- ObjectMgr::GetLocaleString(loc->subject, loc_idx, subject);
- ObjectMgr::GetLocaleString(loc->text, loc_idx, text);
+ if (AchievementRewardLocale const* loc = sAchievementMgr->GetAchievementRewardLocale(achievement))
+ {
+ ObjectMgr::GetLocaleString(loc->subject, locIdx, subject);
+ ObjectMgr::GetLocaleString(loc->text, locIdx, text);
+ }
}
- }
- MailDraft draft(subject, text);
+ draft = MailDraft(subject, text);
+ }
SQLTransaction trans = CharacterDatabase.BeginTransaction();
+
+ Item* item = reward->itemId ? Item::CreateItem(reward->itemId, 1, GetOwner()) : NULL;
if (item)
{
// save new item before send
@@ -2329,7 +2366,6 @@ bool AchievementMgr<T>::RequirementsSatisfied(AchievementCriteriaEntry const* ac
case ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT:
case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST_DAILY:
case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT:
- case ACHIEVEMENT_CRITERIA_TYPE_EARNED_PVP_TITLE:
case ACHIEVEMENT_CRITERIA_TYPE_EARN_ACHIEVEMENT_POINTS:
case ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION:
case ACHIEVEMENT_CRITERIA_TYPE_GAIN_HONORED_REPUTATION:
@@ -2339,6 +2375,7 @@ bool AchievementMgr<T>::RequirementsSatisfied(AchievementCriteriaEntry const* ac
case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_TEAM_RATING:
case ACHIEVEMENT_CRITERIA_TYPE_KNOWN_FACTIONS:
case ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL:
+ case ACHIEVEMENT_CRITERIA_TYPE_ON_LOGIN:
break;
case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT:
if (m_completedAchievements.find(achievementCriteria->complete_achievement.linkedAchievement) == m_completedAchievements.end())
@@ -2919,8 +2956,8 @@ char const* AchievementGlobalMgr::GetCriteriaTypeString(AchievementCriteriaTypes
return "SPECIAL_PVP_KILL";
case ACHIEVEMENT_CRITERIA_TYPE_FISH_IN_GAMEOBJECT:
return "FISH_IN_GAMEOBJECT";
- case ACHIEVEMENT_CRITERIA_TYPE_EARNED_PVP_TITLE:
- return "EARNED_PVP_TITLE";
+ case ACHIEVEMENT_CRITERIA_TYPE_ON_LOGIN:
+ return "ON_LOGIN";
case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS:
return "LEARN_SKILLLINE_SPELLS";
case ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL:
@@ -3188,8 +3225,8 @@ void AchievementGlobalMgr::LoadRewards()
m_achievementRewards.clear(); // need for reload case
- // 0 1 2 3 4 5 6
- QueryResult result = WorldDatabase.Query("SELECT entry, title_A, title_H, item, sender, subject, text FROM achievement_reward");
+ // 0 1 2 3 4 5 6 7
+ QueryResult result = WorldDatabase.Query("SELECT entry, title_A, title_H, item, sender, subject, text, mailTemplate FROM achievement_reward");
if (!result)
{
@@ -3203,20 +3240,21 @@ void AchievementGlobalMgr::LoadRewards()
{
Field* fields = result->Fetch();
uint32 entry = fields[0].GetUInt32();
- const AchievementEntry* pAchievement = GetAchievement(entry);
- if (!pAchievement)
+ AchievementEntry const* achievement = GetAchievement(entry);
+ if (!achievement)
{
TC_LOG_ERROR("sql.sql", "Table `achievement_reward` has wrong achievement (Entry: %u), ignored.", entry);
continue;
}
AchievementReward reward;
- reward.titleId[0] = fields[1].GetUInt32();
- reward.titleId[1] = fields[2].GetUInt32();
- reward.itemId = fields[3].GetUInt32();
- reward.sender = fields[4].GetUInt32();
- reward.subject = fields[5].GetString();
- reward.text = fields[6].GetString();
+ reward.titleId[0] = fields[1].GetUInt32();
+ reward.titleId[1] = fields[2].GetUInt32();
+ reward.itemId = fields[3].GetUInt32();
+ reward.sender = fields[4].GetUInt32();
+ reward.subject = fields[5].GetString();
+ reward.text = fields[6].GetString();
+ reward.mailTemplate = fields[7].GetUInt32();
// must be title or mail at least
if (!reward.titleId[0] && !reward.titleId[1] && !reward.sender)
@@ -3225,7 +3263,7 @@ void AchievementGlobalMgr::LoadRewards()
continue;
}
- if (pAchievement->requiredFaction == ACHIEVEMENT_FACTION_ANY && ((reward.titleId[0] == 0) != (reward.titleId[1] == 0)))
+ if (achievement->requiredFaction == ACHIEVEMENT_FACTION_ANY && (!reward.titleId[0] ^ !reward.titleId[1]))
TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) has title (A: %u H: %u) for only one team.", entry, reward.titleId[0], reward.titleId[1]);
if (reward.titleId[0])
@@ -3267,6 +3305,20 @@ void AchievementGlobalMgr::LoadRewards()
if (!reward.text.empty())
TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) does not have sender data but has mail text.", entry);
+
+ if (reward.mailTemplate)
+ TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) does not have sender data but has mailTemplate.", entry);
+ }
+
+ if (reward.mailTemplate)
+ {
+ if (!sMailTemplateStore.LookupEntry(reward.mailTemplate))
+ {
+ TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) has invalid mailTemplate (%u).", entry, reward.mailTemplate);
+ reward.mailTemplate = 0;
+ }
+ else if (!reward.subject.empty() || !reward.text.empty())
+ TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) has mailTemplate (%u) and mail subject/text.", entry, reward.mailTemplate);
}
if (reward.itemId)
diff --git a/src/server/game/Achievements/AchievementMgr.h b/src/server/game/Achievements/AchievementMgr.h
index 25328dd3f98..65af3c88d74 100644
--- a/src/server/game/Achievements/AchievementMgr.h
+++ b/src/server/game/Achievements/AchievementMgr.h
@@ -64,12 +64,15 @@ enum AchievementCriteriaDataType
ACHIEVEMENT_CRITERIA_DATA_TYPE_S_DRUNK = 15, // drunken_state 0 (enum DrunkenState) of player
ACHIEVEMENT_CRITERIA_DATA_TYPE_HOLIDAY = 16, // holiday_id 0 event in holiday time
ACHIEVEMENT_CRITERIA_DATA_TYPE_BG_LOSS_TEAM_SCORE = 17, // min_score max_score player's team win bg and opposition team have team score in range
- ACHIEVEMENT_CRITERIA_DATA_INSTANCE_SCRIPT = 18, // 0 0 maker instance script call for check current criteria requirements fit
+ ACHIEVEMENT_CRITERIA_DATA_TYPE_INSTANCE_SCRIPT = 18, // 0 0 maker instance script call for check current criteria requirements fit
ACHIEVEMENT_CRITERIA_DATA_TYPE_S_EQUIPED_ITEM = 19, // item_level item_quality for equipped item in slot to check item level and quality
- ACHIEVEMENT_CRITERIA_DATA_TYPE_S_PLAYER_CLASS_RACE = 21 // class_id race_id
+ ACHIEVEMENT_CRITERIA_DATA_TYPE_MAP_ID = 20, // map_id 0 player must be on map with id in map_id
+ ACHIEVEMENT_CRITERIA_DATA_TYPE_S_PLAYER_CLASS_RACE = 21, // class_id race_id
+ ACHIEVEMENT_CRITERIA_DATA_TYPE_NTH_BIRTHDAY = 22, // N login on day of N-th Birthday
+ ACHIEVEMENT_CRITERIA_DATA_TYPE_S_KNOWN_TITLE = 23 // title_id known (pvp) title, values from dbc
};
-#define MAX_ACHIEVEMENT_CRITERIA_DATA_TYPE 22 // maximum value in AchievementCriteriaDataType enum
+#define MAX_ACHIEVEMENT_CRITERIA_DATA_TYPE 24 // maximum value in AchievementCriteriaDataType enum
struct AchievementCriteriaData
{
@@ -144,13 +147,28 @@ struct AchievementCriteriaData
uint32 min_score;
uint32 max_score;
} bg_loss_team_score;
- // ACHIEVEMENT_CRITERIA_DATA_INSTANCE_SCRIPT = 18 (no data)
+ // ACHIEVEMENT_CRITERIA_DATA_TYPE_INSTANCE_SCRIPT = 18 (no data)
// ACHIEVEMENT_CRITERIA_DATA_TYPE_S_EQUIPED_ITEM = 19
struct
{
uint32 item_level;
uint32 item_quality;
} equipped_item;
+ // ACHIEVEMENT_CRITERIA_DATA_TYPE_MAP_ID = 20
+ struct
+ {
+ uint32 mapId;
+ } map_id;
+ // ACHIEVEMENT_CRITERIA_DATA_TYPE_NTH_BIRTHDAY = 21
+ struct
+ {
+ uint32 nth_birthday;
+ } birthday_login;
+ // ACHIEVEMENT_CRITERIA_DATA_TYPE_KNOWN_TITLE = 22
+ struct
+ {
+ uint32 title_id;
+ } known_title;
// raw
struct
{
@@ -199,6 +217,7 @@ struct AchievementReward
uint32 sender;
std::string subject;
std::string text;
+ uint32 mailTemplate;
};
typedef UNORDERED_MAP<uint32, AchievementReward> AchievementRewards;
diff --git a/src/server/game/Battlegrounds/Battleground.cpp b/src/server/game/Battlegrounds/Battleground.cpp
index 4860f226a67..34892b737b6 100644
--- a/src/server/game/Battlegrounds/Battleground.cpp
+++ b/src/server/game/Battlegrounds/Battleground.cpp
@@ -1598,21 +1598,33 @@ void Battleground::DoorOpen(uint32 type)
type, GUID_LOPART(BgObjects[type]), m_MapId, m_InstanceID);
}
-GameObject* Battleground::GetBGObject(uint32 type)
+GameObject* Battleground::GetBGObject(uint32 type, bool logError)
{
GameObject* obj = GetBgMap()->GetGameObject(BgObjects[type]);
if (!obj)
- TC_LOG_ERROR("bg.battleground", "Battleground::GetBGObject: gameobject (type: %u, GUID: %u) not found for BG (map: %u, instance id: %u)!",
- type, GUID_LOPART(BgObjects[type]), m_MapId, m_InstanceID);
+ {
+ if (logError)
+ TC_LOG_ERROR("bg.battleground", "Battleground::GetBGObject: gameobject (type: %u, GUID: %u) not found for BG (map: %u, instance id: %u)!",
+ type, GUID_LOPART(BgObjects[type]), m_MapId, m_InstanceID);
+ else
+ TC_LOG_INFO("bg.battleground", "Battleground::GetBGObject: gameobject (type: %u, GUID: %u) not found for BG (map: %u, instance id: %u)!",
+ type, GUID_LOPART(BgObjects[type]), m_MapId, m_InstanceID);
+ }
return obj;
}
-Creature* Battleground::GetBGCreature(uint32 type)
+Creature* Battleground::GetBGCreature(uint32 type, bool logError)
{
Creature* creature = GetBgMap()->GetCreature(BgCreatures[type]);
if (!creature)
- TC_LOG_ERROR("bg.battleground", "Battleground::GetBGCreature: creature (type: %u, GUID: %u) not found for BG (map: %u, instance id: %u)!",
- type, GUID_LOPART(BgCreatures[type]), m_MapId, m_InstanceID);
+ {
+ if (logError)
+ TC_LOG_ERROR("bg.battleground", "Battleground::GetBGCreature: creature (type: %u, GUID: %u) not found for BG (map: %u, instance id: %u)!",
+ type, GUID_LOPART(BgCreatures[type]), m_MapId, m_InstanceID);
+ else
+ TC_LOG_INFO("bg.battleground", "Battleground::GetBGCreature: creature (type: %u, GUID: %u) not found for BG (map: %u, instance id: %u)!",
+ type, GUID_LOPART(BgCreatures[type]), m_MapId, m_InstanceID);
+ }
return creature;
}
diff --git a/src/server/game/Battlegrounds/Battleground.h b/src/server/game/Battlegrounds/Battleground.h
index 824a71bc89b..4f1f11480c5 100644
--- a/src/server/game/Battlegrounds/Battleground.h
+++ b/src/server/game/Battlegrounds/Battleground.h
@@ -387,8 +387,8 @@ class Battleground
void StartBattleground();
- GameObject* GetBGObject(uint32 type);
- Creature* GetBGCreature(uint32 type);
+ GameObject* GetBGObject(uint32 type, bool logError = true);
+ Creature* GetBGCreature(uint32 type, bool logError = true);
// Location
void SetMapId(uint32 MapID) { m_MapId = MapID; }
diff --git a/src/server/game/Battlegrounds/BattlegroundMgr.cpp b/src/server/game/Battlegrounds/BattlegroundMgr.cpp
index 823b869623d..3f39f8e3715 100644
--- a/src/server/game/Battlegrounds/BattlegroundMgr.cpp
+++ b/src/server/game/Battlegrounds/BattlegroundMgr.cpp
@@ -1410,6 +1410,17 @@ void BattlegroundMgr::LoadBattleMastersEntry()
Field* fields = result->Fetch();
uint32 entry = fields[0].GetUInt32();
+ if (CreatureTemplate const* cInfo = sObjectMgr->GetCreatureTemplate(entry))
+ {
+ if ((cInfo->npcflag & UNIT_NPC_FLAG_BATTLEMASTER) == 0)
+ TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) listed in `battlemaster_entry` is not a battlemaster.", entry);
+ }
+ else
+ {
+ TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) listed in `battlemaster_entry` does not exist.", entry);
+ continue;
+ }
+
uint32 bgTypeId = fields[1].GetUInt32();
if (!sBattlemasterListStore.LookupEntry(bgTypeId))
{
@@ -1421,9 +1432,24 @@ void BattlegroundMgr::LoadBattleMastersEntry()
}
while (result->NextRow());
+ CheckBattleMasters();
+
TC_LOG_INFO("server.loading", ">> Loaded %u battlemaster entries in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
}
+void BattlegroundMgr::CheckBattleMasters()
+{
+ CreatureTemplateContainer const* ctc = sObjectMgr->GetCreatureTemplates();
+ for (CreatureTemplateContainer::const_iterator itr = ctc->begin(); itr != ctc->end(); ++itr)
+ {
+ if ((itr->second.npcflag & UNIT_NPC_FLAG_BATTLEMASTER) && mBattleMastersMap.find(itr->second.Entry) == mBattleMastersMap.end())
+ {
+ TC_LOG_ERROR("sql.sql", "CreatureTemplate (Entry: %u) has UNIT_NPC_FLAG_BATTLEMASTER but no data in `battlemaster_entry` table. Removing flag!", itr->second.Entry);
+ const_cast<CreatureTemplate*>(&itr->second)->npcflag &= ~UNIT_NPC_FLAG_BATTLEMASTER;
+ }
+ }
+}
+
HolidayIds BattlegroundMgr::BGTypeToWeekendHolidayId(BattlegroundTypeId bgTypeId)
{
switch (bgTypeId)
diff --git a/src/server/game/Battlegrounds/BattlegroundMgr.h b/src/server/game/Battlegrounds/BattlegroundMgr.h
index 2c94636f3e8..6ee6a158803 100644
--- a/src/server/game/Battlegrounds/BattlegroundMgr.h
+++ b/src/server/game/Battlegrounds/BattlegroundMgr.h
@@ -124,12 +124,13 @@ class BattlegroundMgr
uint32 GetMaxRatingDifference() const;
uint32 GetRatingDiscardTimer() const;
void LoadBattleMastersEntry();
+ void CheckBattleMasters();
BattlegroundTypeId GetBattleMasterBG(uint32 entry) const
{
BattleMastersMap::const_iterator itr = mBattleMastersMap.find(entry);
if (itr != mBattleMastersMap.end())
return itr->second;
- return BATTLEGROUND_WS;
+ return BATTLEGROUND_TYPE_NONE;
}
private:
diff --git a/src/server/game/Battlegrounds/BattlegroundQueue.h b/src/server/game/Battlegrounds/BattlegroundQueue.h
index 37c7928b3de..af283cb825f 100644
--- a/src/server/game/Battlegrounds/BattlegroundQueue.h
+++ b/src/server/game/Battlegrounds/BattlegroundQueue.h
@@ -88,8 +88,8 @@ class BattlegroundQueue
typedef std::map<uint64, PlayerQueueInfo> QueuedPlayersMap;
QueuedPlayersMap m_QueuedPlayers;
- //we need constant add to begin and constant remove / add from the end, therefore deque suits our problem well
- typedef std::deque<GroupQueueInfo*> GroupsQueueType;
+ //do NOT use deque because deque.erase() invalidates ALL iterators
+ typedef std::list<GroupQueueInfo*> GroupsQueueType;
/*
This two dimensional array is used to store All queued groups
diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundAV.cpp b/src/server/game/Battlegrounds/Zones/BattlegroundAV.cpp
index 6256f53f07b..610e0bcecc7 100644
--- a/src/server/game/Battlegrounds/Zones/BattlegroundAV.cpp
+++ b/src/server/game/Battlegrounds/Zones/BattlegroundAV.cpp
@@ -790,7 +790,7 @@ void BattlegroundAV::PopulateNode(BG_AV_Nodes node)
if (node >= BG_AV_NODES_MAX)//fail safe
return;
- Creature* trigger = GetBGCreature(node + 302);//0-302 other creatures
+ Creature* trigger = GetBGCreature(node + 302, false);//0-302 other creatures
if (!trigger)
{
trigger = AddCreature(WORLD_TRIGGER,
diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundEY.cpp b/src/server/game/Battlegrounds/Zones/BattlegroundEY.cpp
index be2684631df..bc684aed176 100644
--- a/src/server/game/Battlegrounds/Zones/BattlegroundEY.cpp
+++ b/src/server/game/Battlegrounds/Zones/BattlegroundEY.cpp
@@ -780,7 +780,7 @@ void BattlegroundEY::EventTeamCapturedPoint(Player* player, uint32 Point)
if (Point >= EY_POINTS_MAX)
return;
- Creature* trigger = GetBGCreature(Point + 6);//0-5 spirit guides
+ Creature* trigger = GetBGCreature(Point + 6, false);//0-5 spirit guides
if (!trigger)
trigger = AddCreature(WORLD_TRIGGER, Point+6, Team, BG_EY_TriggerPositions[Point][0], BG_EY_TriggerPositions[Point][1], BG_EY_TriggerPositions[Point][2], BG_EY_TriggerPositions[Point][3]);
diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundIC.cpp b/src/server/game/Battlegrounds/Zones/BattlegroundIC.cpp
index 590a07541fe..fc3f839ae76 100644
--- a/src/server/game/Battlegrounds/Zones/BattlegroundIC.cpp
+++ b/src/server/game/Battlegrounds/Zones/BattlegroundIC.cpp
@@ -658,7 +658,7 @@ void BattlegroundIC::HandleCapturedNodes(ICNodePoint* nodePoint, bool recapture)
// we must del opposing faction vehicles when the node is captured (unused ones)
for (uint8 i = (nodePoint->faction == TEAM_ALLIANCE ? BG_IC_NPC_GLAIVE_THROWER_1_H : BG_IC_NPC_GLAIVE_THROWER_1_A); i < (nodePoint->faction == TEAM_ALLIANCE ? BG_IC_NPC_GLAIVE_THROWER_2_H : BG_IC_NPC_GLAIVE_THROWER_2_A); ++i)
{
- if (Creature* glaiveThrower = GetBGCreature(i))
+ if (Creature* glaiveThrower = GetBGCreature(i, false))
{
if (Vehicle* vehicleGlaive = glaiveThrower->GetVehicleKit())
{
@@ -670,7 +670,7 @@ void BattlegroundIC::HandleCapturedNodes(ICNodePoint* nodePoint, bool recapture)
for (uint8 i = (nodePoint->faction == TEAM_ALLIANCE ? BG_IC_NPC_CATAPULT_1_H : BG_IC_NPC_CATAPULT_1_A); i < (nodePoint->faction == TEAM_ALLIANCE ? BG_IC_NPC_CATAPULT_4_H : BG_IC_NPC_CATAPULT_4_A); ++i)
{
- if (Creature* catapult = GetBGCreature(i))
+ if (Creature* catapult = GetBGCreature(i, false))
{
if (Vehicle* vehicleGlaive = catapult->GetVehicleKit())
{
@@ -685,7 +685,7 @@ void BattlegroundIC::HandleCapturedNodes(ICNodePoint* nodePoint, bool recapture)
{
uint8 type = (nodePoint->faction == TEAM_ALLIANCE ? BG_IC_NPC_GLAIVE_THROWER_1_A : BG_IC_NPC_GLAIVE_THROWER_1_H)+i;
- if (GetBGCreature(type) && GetBGCreature(type)->IsAlive())
+ if (GetBGCreature(type, false) && GetBGCreature(type)->IsAlive())
continue;
if (AddCreature(nodePoint->faction == TEAM_ALLIANCE ? NPC_GLAIVE_THROWER_A : NPC_GLAIVE_THROWER_H, type, nodePoint->faction,
@@ -700,7 +700,7 @@ void BattlegroundIC::HandleCapturedNodes(ICNodePoint* nodePoint, bool recapture)
{
uint8 type = (nodePoint->faction == TEAM_ALLIANCE ? BG_IC_NPC_CATAPULT_1_A : BG_IC_NPC_CATAPULT_1_H)+i;
- if (GetBGCreature(type) && GetBGCreature(type)->IsAlive())
+ if (GetBGCreature(type, false) && GetBGCreature(type)->IsAlive())
continue;
if (AddCreature(NPC_CATAPULT, type, nodePoint->faction,
@@ -720,7 +720,7 @@ void BattlegroundIC::HandleCapturedNodes(ICNodePoint* nodePoint, bool recapture)
// we must del opposing faction vehicles when the node is captured (unused ones)
for (uint8 i = (nodePoint->faction == TEAM_ALLIANCE ? BG_IC_NPC_DEMOLISHER_1_H : BG_IC_NPC_DEMOLISHER_1_A); i < (nodePoint->faction == TEAM_ALLIANCE ? BG_IC_NPC_DEMOLISHER_4_H : BG_IC_NPC_DEMOLISHER_4_A); ++i)
{
- if (Creature* demolisher = GetBGCreature(i))
+ if (Creature* demolisher = GetBGCreature(i, false))
{
if (Vehicle* vehicleDemolisher = demolisher->GetVehicleKit())
{
@@ -735,7 +735,7 @@ void BattlegroundIC::HandleCapturedNodes(ICNodePoint* nodePoint, bool recapture)
{
uint8 type = (nodePoint->faction == TEAM_ALLIANCE ? BG_IC_NPC_DEMOLISHER_1_A : BG_IC_NPC_DEMOLISHER_1_H)+i;
- if (GetBGCreature(type) && GetBGCreature(type)->IsAlive())
+ if (GetBGCreature(type, false) && GetBGCreature(type)->IsAlive())
continue;
if (AddCreature(NPC_DEMOLISHER, type, nodePoint->faction,
@@ -748,7 +748,7 @@ void BattlegroundIC::HandleCapturedNodes(ICNodePoint* nodePoint, bool recapture)
// we check if the opossing siege engine is in use
int8 enemySiege = (nodePoint->faction == TEAM_ALLIANCE ? BG_IC_NPC_SIEGE_ENGINE_H : BG_IC_NPC_SIEGE_ENGINE_A);
- if (Creature* siegeEngine = GetBGCreature(enemySiege))
+ if (Creature* siegeEngine = GetBGCreature(enemySiege, false))
{
if (Vehicle* vehicleSiege = siegeEngine->GetVehicleKit())
{
@@ -759,7 +759,7 @@ void BattlegroundIC::HandleCapturedNodes(ICNodePoint* nodePoint, bool recapture)
}
uint8 siegeType = (nodePoint->faction == TEAM_ALLIANCE ? BG_IC_NPC_SIEGE_ENGINE_A : BG_IC_NPC_SIEGE_ENGINE_H);
- if (!GetBGCreature(siegeType) || !GetBGCreature(siegeType)->IsAlive())
+ if (!GetBGCreature(siegeType, false) || !GetBGCreature(siegeType)->IsAlive())
{
AddCreature((nodePoint->faction == TEAM_ALLIANCE ? NPC_SIEGE_ENGINE_A : NPC_SIEGE_ENGINE_H), siegeType, nodePoint->faction,
BG_IC_WorkshopVehicles[4].GetPositionX(), BG_IC_WorkshopVehicles[4].GetPositionY(),
diff --git a/src/server/game/Chat/Chat.cpp b/src/server/game/Chat/Chat.cpp
index c7fbb4c9df1..91f62c1a697 100644
--- a/src/server/game/Chat/Chat.cpp
+++ b/src/server/game/Chat/Chat.cpp
@@ -1198,10 +1198,34 @@ char* ChatHandler::extractQuotedArg(char* args)
return strtok(args+1, "\"");
else
{
- char* space = strtok(args, "\"");
- if (!space)
+ // skip spaces
+ while (*args == ' ')
+ {
+ args += 1;
+ continue;
+ }
+
+ // return NULL if we reached the end of the string
+ if (!*args)
+ return NULL;
+
+ // since we skipped all spaces, we expect another token now
+ if (*args == '"')
+ {
+ // return an empty string if there are 2 "" in a row.
+ // strtok doesn't handle this case
+ if (*(args + 1) == '"')
+ {
+ strtok(args, " ");
+ static char arg[1];
+ arg[0] = '\0';
+ return arg;
+ }
+ else
+ return strtok(args + 1, "\"");
+ }
+ else
return NULL;
- return strtok(NULL, "\"");
}
}
diff --git a/src/server/game/Chat/ChatLink.cpp b/src/server/game/Chat/ChatLink.cpp
index c1c59389397..3f32447a31f 100644
--- a/src/server/game/Chat/ChatLink.cpp
+++ b/src/server/game/Chat/ChatLink.cpp
@@ -312,7 +312,7 @@ bool SpellChatLink::ValidateName(char* buffer, const char* context)
// found the prefix, remove it to perform spellname validation below
// -2 = strlen(": ")
uint32 spellNameLength = strlen(buffer) - skillLineNameLength - 2;
- memcpy(buffer, buffer + skillLineNameLength + 2, spellNameLength + 1);
+ memmove(buffer, buffer + skillLineNameLength + 2, spellNameLength + 1);
}
}
diff --git a/src/server/game/Conditions/ConditionMgr.cpp b/src/server/game/Conditions/ConditionMgr.cpp
index 57b6b90cc2c..74beed534c6 100644
--- a/src/server/game/Conditions/ConditionMgr.cpp
+++ b/src/server/game/Conditions/ConditionMgr.cpp
@@ -1107,8 +1107,7 @@ bool ConditionMgr::addToGossipMenuItems(Condition* cond)
bool ConditionMgr::addToSpellImplicitTargetConditions(Condition* cond)
{
uint32 conditionEffMask = cond->SourceGroup;
- SpellInfo* spellInfo = const_cast<SpellInfo*>(sSpellMgr->GetSpellInfo(cond->SourceEntry));
- ASSERT(spellInfo);
+ SpellInfo* spellInfo = const_cast<SpellInfo*>(sSpellMgr->EnsureSpellInfo(cond->SourceEntry));
std::list<uint32> sharedMasks;
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
{
@@ -2025,7 +2024,7 @@ bool ConditionMgr::isConditionTypeValid(Condition* cond)
}
case CONDITION_UNIT_STATE:
{
- if (cond->ConditionValue1 > uint32(UNIT_STATE_ALL_STATE))
+ if (!(cond->ConditionValue1 & UNIT_STATE_ALL_STATE_SUPPORTED))
{
TC_LOG_ERROR("sql.sql", "UnitState condition has non existing UnitState in value1 (%u), skipped", cond->ConditionValue1);
return false;
diff --git a/src/server/game/DataStores/DBCEnums.h b/src/server/game/DataStores/DBCEnums.h
index c650da7c83b..c3cebcdaeb1 100644
--- a/src/server/game/DataStores/DBCEnums.h
+++ b/src/server/game/DataStores/DBCEnums.h
@@ -237,7 +237,7 @@ enum AchievementCriteriaTypes
ACHIEVEMENT_CRITERIA_TYPE_SPECIAL_PVP_KILL = 70,
ACHIEVEMENT_CRITERIA_TYPE_FISH_IN_GAMEOBJECT = 72,
/// @todo 73: Achievements 1515, 1241, 1103 (Name: Mal'Ganis)
- ACHIEVEMENT_CRITERIA_TYPE_EARNED_PVP_TITLE = 74, /// @todo title id is not mentioned in dbc
+ ACHIEVEMENT_CRITERIA_TYPE_ON_LOGIN = 74,
ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS = 75,
ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL = 76,
ACHIEVEMENT_CRITERIA_TYPE_LOSE_DUEL = 77,
@@ -386,6 +386,11 @@ enum MapTypes // Lua_IsInInstance
MAP_ARENA = 4 // arena
};
+enum MapFlags
+{
+ MAP_FLAG_DYNAMIC_DIFFICULTY = 0x100
+};
+
enum AbilytyLearnType
{
ABILITY_LEARNED_ON_GET_PROFESSION_SKILL = 1,
diff --git a/src/server/game/DataStores/DBCStores.cpp b/src/server/game/DataStores/DBCStores.cpp
index f05b2c714ce..5b96f8b8271 100644
--- a/src/server/game/DataStores/DBCStores.cpp
+++ b/src/server/game/DataStores/DBCStores.cpp
@@ -147,6 +147,7 @@ DBCStorage <ItemRandomSuffixEntry> sItemRandomSuffixStore(ItemRandomSuffixfmt);
DBCStorage <ItemSetEntry> sItemSetStore(ItemSetEntryfmt);
DBCStorage <LFGDungeonEntry> sLFGDungeonStore(LFGDungeonEntryfmt);
+DBCStorage <LightEntry> sLightStore(LightEntryfmt);
DBCStorage <LiquidTypeEntry> sLiquidTypeStore(LiquidTypefmt);
DBCStorage <LockEntry> sLockStore(LockEntryfmt);
@@ -461,6 +462,7 @@ void LoadDBCStores(const std::string& dataPath)
LoadDBC(availableDbcLocales, bad_dbc_files, sItemDisenchantLootStore, dbcPath, "ItemDisenchantLoot.dbc");
LoadDBC(availableDbcLocales, bad_dbc_files, sLFGDungeonStore, dbcPath, "LFGDungeons.dbc");//15595
+ LoadDBC(availableDbcLocales, bad_dbc_files, sLightStore, dbcPath, "Light.dbc"); //15595
LoadDBC(availableDbcLocales, bad_dbc_files, sLiquidTypeStore, dbcPath, "LiquidType.dbc");//15595
LoadDBC(availableDbcLocales, bad_dbc_files, sLockStore, dbcPath, "Lock.dbc");//15595
LoadDBC(availableDbcLocales, bad_dbc_files, sPhaseStores, dbcPath, "Phase.dbc");//15595
@@ -1276,3 +1278,18 @@ LFGDungeonEntry const* GetLFGDungeon(uint32 mapId, Difficulty difficulty)
return NULL;
}
+
+uint32 GetDefaultMapLight(uint32 mapId)
+{
+ for (int32 i = sLightStore.GetNumRows(); i >= 0; --i)
+ {
+ LightEntry const* light = sLightStore.LookupEntry(uint32(i));
+ if (!light)
+ continue;
+
+ if (light->MapId == mapId && light->X == 0.0f && light->Y == 0.0f && light->Z == 0.0f)
+ return light->Id;
+ }
+
+ return 0;
+}
diff --git a/src/server/game/DataStores/DBCStores.h b/src/server/game/DataStores/DBCStores.h
index e12d0ea45ac..ca91f1da1f2 100644
--- a/src/server/game/DataStores/DBCStores.h
+++ b/src/server/game/DataStores/DBCStores.h
@@ -81,6 +81,8 @@ CharStartOutfitEntry const* GetCharStartOutfitEntry(uint8 race, uint8 class_, ui
uint32 GetPowerIndexByClass(uint32 powerType, uint32 classId);
LFGDungeonEntry const* GetLFGDungeon(uint32 mapId, Difficulty difficulty);
+uint32 GetDefaultMapLight(uint32 mapId);
+
extern DBCStorage <AchievementEntry> sAchievementStore;
extern DBCStorage <AchievementCriteriaEntry> sAchievementCriteriaStore;
extern DBCStorage <AreaTableEntry> sAreaStore;// recommend access using functions
diff --git a/src/server/game/DataStores/DBCStructure.h b/src/server/game/DataStores/DBCStructure.h
index 6b08fed517a..6a548615c3b 100644
--- a/src/server/game/DataStores/DBCStructure.h
+++ b/src/server/game/DataStores/DBCStructure.h
@@ -690,9 +690,9 @@ struct CharTitlesEntry
{
uint32 ID; // 0, title ids, for example in Quest::GetCharTitleId()
//uint32 unk1; // 1 flags?
- char* name; // 2 m_name_lang
- //char* name2; // 3 m_name1_lang
- uint32 bit_index; // 4 m_mask_ID used in PLAYER_CHOSEN_TITLE and 1<<index in PLAYER__FIELD_KNOWN_TITLES
+ char* nameMale; // 2 m_name_lang
+ char* nameFemale; // 3 m_name1_lang
+ uint32 bit_index; // 4 m_mask_ID used in PLAYER_CHOSEN_TITLE and 1<<index in PLAYER__FIELD_KNOWN_TITLES
//uint32 // 5
};
@@ -1387,6 +1387,24 @@ struct LFGDungeonEntry
uint32 Entry() const { return ID + (type << 24); }
};
+struct LightEntry
+{
+ uint32 Id;
+ uint32 MapId;
+ float X;
+ float Y;
+ float Z;
+ //float FalloffStart;
+ //float FalloffEnd;
+ //uint32 SkyAndFog;
+ //uint32 WaterSettings;
+ //uint32 SunsetParams;
+ //uint32 OtherParams;
+ //uint32 DeathParams;
+ //uint32 Unknown;
+ //uint32 Unknown;
+ //uint32 Unknown;
+};
struct LiquidTypeEntry
{
@@ -1441,7 +1459,7 @@ struct MapEntry
uint32 MapID; // 0
//char* internalname; // 1 unused
uint32 map_type; // 2
- //uint32 unk_330; // 3
+ uint32 Flags; // 3
//uint32 unk4; // 4 4.0.1
//uint32 isPvP; // 5 m_PVP 0 or 1 for battlegrounds (not arenas)
char* name; // 6 m_MapName_lang
@@ -1456,7 +1474,7 @@ struct MapEntry
//uint32 timeOfDayOverride; // 15 m_timeOfDayOverride
uint32 addon; // 16 m_expansionID
uint32 expireTime; // 17 m_raidOffset
- //uint32 maxPlayers; // 18 m_maxPlayers
+ uint32 maxPlayers; // 18 m_maxPlayers
int32 rootPhaseMap; // 19 new 4.0.0, mapid, related to phasing
// Helpers
@@ -1485,6 +1503,8 @@ struct MapEntry
{
return MapID == 0 || MapID == 1 || MapID == 530 || MapID == 571;
}
+
+ bool IsDynamicDifficultyMap() const { return Flags & MAP_FLAG_DYNAMIC_DIFFICULTY; }
};
struct MapDifficultyEntry
diff --git a/src/server/game/DataStores/DBCfmt.h b/src/server/game/DataStores/DBCfmt.h
index 76dc9402fe5..4b7f59bf380 100644
--- a/src/server/game/DataStores/DBCfmt.h
+++ b/src/server/game/DataStores/DBCfmt.h
@@ -37,7 +37,7 @@ char const BannedAddOnsfmt[] = "nxxxxxxxxxx";
char const BarberShopStyleEntryfmt[] = "nixxxiii";
char const BattlemasterListEntryfmt[] = "niiiiiiiiixsiiiixxxx";
char const CharStartOutfitEntryfmt[] = "dbbbXiiiiiiiiiiiiiiiiiiiiiiiixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxii";
-char const CharTitlesEntryfmt[] = "nxsxix";
+char const CharTitlesEntryfmt[] = "nxssix";
char const ChatChannelsEntryfmt[] = "nixsx";
char const ChrClassesEntryfmt[] = "nixsxxxixiiiii";
char const ChrRacesEntryfmt[] = "nxixiixixxxxixsxxxxxixxx";
@@ -97,11 +97,12 @@ char const ItemRandomPropertiesfmt[] = "nxiiixxs";
char const ItemRandomSuffixfmt[] = "nsxiiiiiiiiii";
char const ItemSetEntryfmt[] = "dsiiiiiiiiiixxxxxxxiiiiiiiiiiiiiiiiii";
char const LFGDungeonEntryfmt[] = "nsiiiiiiiiixxixixixxx";
+char const LightEntryfmt[] = "nifffxxxxxxxxxx";
char const LiquidTypefmt[] = "nxxixixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
char const LockEntryfmt[] = "niiiiiiiiiiiiiiiiiiiiiiiixxxxxxxx";
char const PhaseEntryfmt[] = "nsi";
char const MailTemplateEntryfmt[] = "nxs";
-char const MapEntryfmt[] = "nxixxxsixxixiffxiixi";
+char const MapEntryfmt[] = "nxiixxsixxixiffxiiii";
char const MapDifficultyEntryfmt[] = "diisiix";
char const MovieEntryfmt[] = "nxxx";
char const MountCapabilityfmt[] = "niiiiiii";
diff --git a/src/server/game/DungeonFinding/LFGMgr.cpp b/src/server/game/DungeonFinding/LFGMgr.cpp
index 624a5657cfd..b205e890f48 100644
--- a/src/server/game/DungeonFinding/LFGMgr.cpp
+++ b/src/server/game/DungeonFinding/LFGMgr.cpp
@@ -60,8 +60,8 @@ void LFGMgr::_LoadFromDB(Field* fields, uint64 guid)
SetLeader(guid, MAKE_NEW_GUID(fields[0].GetUInt32(), 0, HIGHGUID_PLAYER));
- uint32 dungeon = fields[16].GetUInt32();
- uint8 state = fields[17].GetUInt8();
+ uint32 dungeon = fields[17].GetUInt32();
+ uint8 state = fields[18].GetUInt8();
if (!dungeon || !state)
return;
@@ -404,7 +404,7 @@ void LFGMgr::InitializeLockedDungeons(Player* player, uint8 level /* = 0 */)
lockData = LFG_LOCKSTATUS_NOT_IN_SEASON;
else if (AccessRequirement const* ar = sObjectMgr->GetAccessRequirement(dungeon->map, Difficulty(dungeon->difficulty)))
{
- if (player->GetAverageItemLevel() < ar->item_level)
+ if (ar->item_level && player->GetAverageItemLevel() < ar->item_level)
lockData = LFG_LOCKSTATUS_TOO_LOW_GEAR_SCORE;
else if (ar->achievement && !player->HasAchieved(ar->achievement))
lockData = LFG_LOCKSTATUS_MISSING_ACHIEVEMENT;
@@ -971,6 +971,7 @@ void LFGMgr::MakeNewGroup(LfgProposal const& proposal)
player->CastSpell(player, LFG_SPELL_DUNGEON_COOLDOWN, false);
}
+ ASSERT(grp);
grp->SetDungeonDifficulty(Difficulty(dungeon->difficulty));
uint64 gguid = grp->GetGUID();
SetDungeon(gguid, dungeon->Entry());
diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h
index d9236bbfb8e..19f9e110ab3 100644
--- a/src/server/game/Entities/Creature/Creature.h
+++ b/src/server/game/Entities/Creature/Creature.h
@@ -628,13 +628,13 @@ class Creature : public Unit, public GridObject<Creature>, public MapObject
void SetHomePosition(float x, float y, float z, float o) { m_homePosition.Relocate(x, y, z, o); }
void SetHomePosition(const Position &pos) { m_homePosition.Relocate(pos); }
- void GetHomePosition(float &x, float &y, float &z, float &ori) const { m_homePosition.GetPosition(x, y, z, ori); }
- Position GetHomePosition() const { return m_homePosition; }
+ void GetHomePosition(float& x, float& y, float& z, float& ori) const { m_homePosition.GetPosition(x, y, z, ori); }
+ Position const& GetHomePosition() const { return m_homePosition; }
void SetTransportHomePosition(float x, float y, float z, float o) { m_transportHomePosition.Relocate(x, y, z, o); }
void SetTransportHomePosition(const Position &pos) { m_transportHomePosition.Relocate(pos); }
- void GetTransportHomePosition(float &x, float &y, float &z, float &ori) { m_transportHomePosition.GetPosition(x, y, z, ori); }
- Position GetTransportHomePosition() { return m_transportHomePosition; }
+ void GetTransportHomePosition(float& x, float& y, float& z, float& ori) const { m_transportHomePosition.GetPosition(x, y, z, ori); }
+ Position const& GetTransportHomePosition() const { return m_transportHomePosition; }
uint32 GetWaypointPath() const { return m_path_id; }
void LoadPath(uint32 pathid) { m_path_id = pathid; }
diff --git a/src/server/game/Entities/Creature/GossipDef.cpp b/src/server/game/Entities/Creature/GossipDef.cpp
index 497b3a44b4f..e4eb99819c5 100644
--- a/src/server/game/Entities/Creature/GossipDef.cpp
+++ b/src/server/game/Entities/Creature/GossipDef.cpp
@@ -28,6 +28,7 @@ GossipMenu::GossipMenu()
{
_menuId = 0;
_locale = DEFAULT_LOCALE;
+ _senderGUID = 0;
}
GossipMenu::~GossipMenu()
@@ -166,8 +167,10 @@ void PlayerMenu::ClearMenus()
_questMenu.ClearMenu();
}
-void PlayerMenu::SendGossipMenu(uint32 titleTextId, uint64 objectGUID) const
+void PlayerMenu::SendGossipMenu(uint32 titleTextId, uint64 objectGUID)
{
+ _gossipMenu.SetSenderGUID(objectGUID);
+
WorldPacket data(SMSG_GOSSIP_MESSAGE, 100); // guess size
data << uint64(objectGUID);
data << uint32(_gossipMenu.GetMenuId()); // new 2.4.0
@@ -222,8 +225,10 @@ void PlayerMenu::SendGossipMenu(uint32 titleTextId, uint64 objectGUID) const
_session->SendPacket(&data);
}
-void PlayerMenu::SendCloseGossip() const
+void PlayerMenu::SendCloseGossip()
{
+ _gossipMenu.SetSenderGUID(0);
+
WorldPacket data(SMSG_GOSSIP_COMPLETE, 0);
_session->SendPacket(&data);
}
diff --git a/src/server/game/Entities/Creature/GossipDef.h b/src/server/game/Entities/Creature/GossipDef.h
index 47711993005..6d42d7c33f7 100644
--- a/src/server/game/Entities/Creature/GossipDef.h
+++ b/src/server/game/Entities/Creature/GossipDef.h
@@ -25,7 +25,7 @@
class WorldSession;
-#define GOSSIP_MAX_MENU_ITEMS 64 // client supported items unknown, but provided number must be enough
+#define GOSSIP_MAX_MENU_ITEMS 32
#define DEFAULT_GOSSIP_MESSAGE 0xffffff
enum Gossip_Option
@@ -167,6 +167,8 @@ class GossipMenu
void SetMenuId(uint32 menu_id) { _menuId = menu_id; }
uint32 GetMenuId() const { return _menuId; }
+ void SetSenderGUID(uint64 guid) { _senderGUID = guid; }
+ uint64 GetSenderGUID() const { return _senderGUID; }
void SetLocale(LocaleConstant locale) { _locale = locale; }
LocaleConstant GetLocale() const { return _locale; }
@@ -215,6 +217,7 @@ class GossipMenu
GossipMenuItemContainer _menuItems;
GossipMenuItemDataContainer _menuItemData;
uint32 _menuId;
+ uint64 _senderGUID;
LocaleConstant _locale;
};
@@ -264,8 +267,8 @@ class PlayerMenu
uint32 GetGossipOptionAction(uint32 selection) const { return _gossipMenu.GetMenuItemAction(selection); }
bool IsGossipOptionCoded(uint32 selection) const { return _gossipMenu.IsMenuItemCoded(selection); }
- void SendGossipMenu(uint32 titleTextId, uint64 objectGUID) const;
- void SendCloseGossip() const;
+ void SendGossipMenu(uint32 titleTextId, uint64 objectGUID);
+ void SendCloseGossip();
void SendPointOfInterest(uint32 poiId) const;
/*********************************************************/
diff --git a/src/server/game/Entities/DynamicObject/DynamicObject.cpp b/src/server/game/Entities/DynamicObject/DynamicObject.cpp
index 53e4e92d6cd..47ec8fceb0f 100644
--- a/src/server/game/Entities/DynamicObject/DynamicObject.cpp
+++ b/src/server/game/Entities/DynamicObject/DynamicObject.cpp
@@ -26,6 +26,7 @@
#include "CellImpl.h"
#include "GridNotifiersImpl.h"
#include "ScriptMgr.h"
+#include "Transport.h"
DynamicObject::DynamicObject(bool isWorldObject) : WorldObject(isWorldObject),
_aura(NULL), _removedAura(NULL), _caster(NULL), _duration(0), _isViewpoint(false)
@@ -47,6 +48,18 @@ DynamicObject::~DynamicObject()
delete _removedAura;
}
+void DynamicObject::CleanupsBeforeDelete(bool finalCleanup /* = true */)
+{
+ WorldObject::CleanupsBeforeDelete(finalCleanup);
+
+ if (Transport* transport = GetTransport())
+ {
+ transport->RemovePassenger(this);
+ SetTransport(NULL);
+ m_movementInfo.transport.Reset();
+ }
+}
+
void DynamicObject::AddToWorld()
{
///- Register the dynamicObject for guid lookup and for caster
@@ -102,8 +115,28 @@ bool DynamicObject::CreateDynamicObject(uint32 guidlow, Unit* caster, SpellInfo
if (IsWorldObject())
setActive(true); //must before add to map to be put in world container
+ Transport* transport = caster->GetTransport();
+ if (transport)
+ {
+ m_movementInfo.transport.guid = GetGUID();
+
+ float x, y, z, o;
+ pos.GetPosition(x, y, z, o);
+ transport->CalculatePassengerOffset(x, y, z, &o);
+ m_movementInfo.transport.pos.Relocate(x, y, z, o);
+
+ SetTransport(transport);
+ // This object must be added to transport before adding to map for the client to properly display it
+ transport->AddPassenger(this);
+ }
+
if (!GetMap()->AddToMap(this))
+ {
+ // Returning false will cause the object to be deleted - remove from transport
+ if (transport)
+ transport->RemovePassenger(this);
return false;
+ }
return true;
}
diff --git a/src/server/game/Entities/DynamicObject/DynamicObject.h b/src/server/game/Entities/DynamicObject/DynamicObject.h
index 66e3a2cff02..93b203cbc10 100644
--- a/src/server/game/Entities/DynamicObject/DynamicObject.h
+++ b/src/server/game/Entities/DynamicObject/DynamicObject.h
@@ -32,7 +32,7 @@ enum DynamicObjectType
DYNAMIC_OBJECT_FARSIGHT_FOCUS = 0x2
};
-class DynamicObject : public WorldObject, public GridObject<DynamicObject>
+class DynamicObject : public WorldObject, public GridObject<DynamicObject>, public MapObject
{
public:
DynamicObject(bool isWorldObject);
@@ -41,6 +41,8 @@ class DynamicObject : public WorldObject, public GridObject<DynamicObject>
void AddToWorld();
void RemoveFromWorld();
+ void CleanupsBeforeDelete(bool finalCleanup = true) OVERRIDE;
+
bool CreateDynamicObject(uint32 guidlow, Unit* caster, SpellInfo const* spell, Position const& pos, float radius, DynamicObjectType type);
void Update(uint32 p_time);
void Remove();
diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp
index 01bdf63d464..9cc9a1bab43 100644
--- a/src/server/game/Entities/GameObject/GameObject.cpp
+++ b/src/server/game/Entities/GameObject/GameObject.cpp
@@ -148,7 +148,7 @@ void GameObject::AddToWorld()
sObjectAccessor->AddObject(this);
// The state can be changed after GameObject::Create but before GameObject::AddToWorld
- bool toggledState = GetGoType() == GAMEOBJECT_TYPE_CHEST ? getLootState() == GO_READY : GetGoState() == GO_STATE_READY;
+ bool toggledState = GetGoType() == GAMEOBJECT_TYPE_CHEST ? getLootState() == GO_READY : (GetGoState() == GO_STATE_READY || IsTransport());
if (m_model)
GetMap()->InsertGameObjectModel(*m_model);
@@ -318,7 +318,6 @@ void GameObject::Update(uint32 diff)
m_lootState = GO_READY;
break;
}
- /* TODO: Fix movement in unloaded grid - currently GO will just disappear
case GAMEOBJECT_TYPE_TRANSPORT:
{
if (!m_goValue.Transport.AnimationInfo)
@@ -327,6 +326,7 @@ void GameObject::Update(uint32 diff)
if (GetGoState() == GO_STATE_READY)
{
m_goValue.Transport.PathProgress += diff;
+ /* TODO: Fix movement in unloaded grid - currently GO will just disappear
uint32 timer = m_goValue.Transport.PathProgress % m_goValue.Transport.AnimationInfo->TotalTime;
TransportAnimationEntry const* node = m_goValue.Transport.AnimationInfo->GetAnimNode(timer);
if (node && m_goValue.Transport.CurrentSeg != node->TimeSeg)
@@ -342,14 +342,14 @@ void GameObject::Update(uint32 diff)
G3D::Vector3 src(GetPositionX(), GetPositionY(), GetPositionZ());
- TC_LOG_INFO("misc", "Src: %s Dest: %s", src.toString().c_str(), pos.toString().c_str());
+ TC_LOG_DEBUG("misc", "Src: %s Dest: %s", src.toString().c_str(), pos.toString().c_str());
GetMap()->GameObjectRelocation(this, pos.x, pos.y, pos.z, GetOrientation());
}
+ */
}
break;
}
- */
case GAMEOBJECT_TYPE_FISHINGNODE:
{
// fishing code (bobber ready)
@@ -987,9 +987,17 @@ bool GameObject::ActivateToQuest(Player* target) const
switch (GetGoType())
{
- // scan GO chest with loot including quest items
+ case GAMEOBJECT_TYPE_QUESTGIVER:
+ {
+ GameObject* go = const_cast<GameObject*>(this);
+ QuestGiverStatus questStatus = target->GetQuestDialogStatus(go);
+ if (questStatus > DIALOG_STATUS_UNAVAILABLE)
+ return true;
+ break;
+ }
case GAMEOBJECT_TYPE_CHEST:
{
+ // scan GO chest with loot including quest items
if (LootTemplates_Gameobject.HaveQuestLootForPlayer(GetGOInfo()->GetLootId(), target))
{
if (Battleground const* bg = target->GetBattleground())
@@ -2012,7 +2020,7 @@ void GameObject::SetGoState(GOState state)
{
SetByteValue(GAMEOBJECT_BYTES_1, 0, state);
sScriptMgr->OnGameObjectStateChanged(this, state);
- if (m_model)
+ if (m_model && !IsTransport())
{
if (!IsInWorld())
return;
@@ -2149,6 +2157,10 @@ void GameObject::BuildValuesUpdate(uint8 updateType, ByteBuffer* data, Player* t
int16 pathProgress = -1;
switch (GetGoType())
{
+ case GAMEOBJECT_TYPE_QUESTGIVER:
+ if (ActivateToQuest(target))
+ dynFlags |= GO_DYNFLAG_LO_ACTIVATE;
+ break;
case GAMEOBJECT_TYPE_CHEST:
case GAMEOBJECT_TYPE_GOOBER:
if (ActivateToQuest(target))
@@ -2227,3 +2239,16 @@ float GameObject::GetInteractionDistance() const
return INTERACTION_DISTANCE;
}
}
+
+void GameObject::UpdateModelPosition()
+{
+ if (!m_model)
+ return;
+
+ if (GetMap()->ContainsGameObjectModel(*m_model))
+ {
+ GetMap()->RemoveGameObjectModel(*m_model);
+ m_model->Relocate(*this);
+ GetMap()->InsertGameObjectModel(*m_model);
+ }
+}
diff --git a/src/server/game/Entities/GameObject/GameObject.h b/src/server/game/Entities/GameObject/GameObject.h
index 8f32f5fe080..77d36e1290c 100644
--- a/src/server/game/Entities/GameObject/GameObject.h
+++ b/src/server/game/Entities/GameObject/GameObject.h
@@ -833,6 +833,8 @@ class GameObject : public WorldObject, public GridObject<GameObject>, public Map
float GetInteractionDistance() const;
+ void UpdateModelPosition();
+
protected:
bool AIM_Initialize();
void UpdateModel(); // updates model in case displayId were changed
diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp
index da22204a4e1..132149af93b 100644
--- a/src/server/game/Entities/Object/Object.cpp
+++ b/src/server/game/Entities/Object/Object.cpp
@@ -655,7 +655,12 @@ void Object::BuildMovementUpdate(ByteBuffer* data, uint16 flags) const
if (flags & UPDATEFLAG_TRANSPORT)
{
GameObject const* go = ToGameObject();
- if (go && go->IsTransport())
+ /** @TODO Use IsTransport() to also handle type 11 (TRANSPORT)
+ Currently grid objects are not updated if there are no nearby players,
+ this causes clients to receive different PathProgress
+ resulting in players seeing the object in a different position
+ */
+ if (go && go->ToTransport())
*data << uint32(go->GetGOValue()->Transport.PathProgress);
else
*data << uint32(getMSTime());
@@ -913,7 +918,7 @@ void Object::SetByteValue(uint16 index, uint8 offset, uint8 value)
{
ASSERT(index < m_valuesCount || PrintIndexError(index, true));
- if (offset > 4)
+ if (offset > 3)
{
TC_LOG_ERROR("misc", "Object::SetByteValue: wrong offset %u", offset);
return;
@@ -937,7 +942,7 @@ void Object::SetUInt16Value(uint16 index, uint8 offset, uint16 value)
{
ASSERT(index < m_valuesCount || PrintIndexError(index, true));
- if (offset > 2)
+ if (offset > 1)
{
TC_LOG_ERROR("misc", "Object::SetUInt16Value: wrong offset %u", offset);
return;
@@ -1077,7 +1082,7 @@ void Object::SetByteFlag(uint16 index, uint8 offset, uint8 newFlag)
{
ASSERT(index < m_valuesCount || PrintIndexError(index, true));
- if (offset > 4)
+ if (offset > 3)
{
TC_LOG_ERROR("misc", "Object::SetByteFlag: wrong offset %u", offset);
return;
@@ -1100,7 +1105,7 @@ void Object::RemoveByteFlag(uint16 index, uint8 offset, uint8 oldFlag)
{
ASSERT(index < m_valuesCount || PrintIndexError(index, true));
- if (offset > 4)
+ if (offset > 3)
{
TC_LOG_ERROR("misc", "Object::RemoveByteFlag: wrong offset %u", offset);
return;
@@ -1731,9 +1736,9 @@ void WorldObject::GetRandomPoint(const Position &srcPos, float distance, Positio
void WorldObject::UpdateGroundPositionZ(float x, float y, float &z) const
{
- float new_z = GetBaseMap()->GetHeight(GetPhaseMask(), x, y, z, true);
+ float new_z = GetMap()->GetHeight(GetPhaseMask(), x, y, z + 2.0f, true);
if (new_z > INVALID_HEIGHT)
- z = new_z+ 0.05f; // just to be sure that we are not a few pixel under the surface
+ z = new_z + 0.05f; // just to be sure that we are not a few pixel under the surface
}
void WorldObject::UpdateAllowedPositionZ(float x, float y, float &z) const
@@ -1753,8 +1758,8 @@ void WorldObject::UpdateAllowedPositionZ(float x, float y, float &z) const
bool canSwim = ToCreature()->CanSwim();
float ground_z = z;
float max_z = canSwim
- ? GetBaseMap()->GetWaterOrGroundLevel(x, y, z, &ground_z, !ToUnit()->HasAuraType(SPELL_AURA_WATER_WALK))
- : ((ground_z = GetBaseMap()->GetHeight(GetPhaseMask(), x, y, z, true)));
+ ? GetMap()->GetWaterOrGroundLevel(x, y, z, &ground_z, !ToUnit()->HasAuraType(SPELL_AURA_WATER_WALK))
+ : ((ground_z = GetMap()->GetHeight(GetPhaseMask(), x, y, z, true)));
if (max_z > INVALID_HEIGHT)
{
if (z > max_z)
@@ -1765,7 +1770,7 @@ void WorldObject::UpdateAllowedPositionZ(float x, float y, float &z) const
}
else
{
- float ground_z = GetBaseMap()->GetHeight(GetPhaseMask(), x, y, z, true);
+ float ground_z = GetMap()->GetHeight(GetPhaseMask(), x, y, z, true);
if (z < ground_z)
z = ground_z;
}
@@ -1777,7 +1782,7 @@ void WorldObject::UpdateAllowedPositionZ(float x, float y, float &z) const
if (!ToPlayer()->CanFly())
{
float ground_z = z;
- float max_z = GetBaseMap()->GetWaterOrGroundLevel(x, y, z, &ground_z, !ToUnit()->HasAuraType(SPELL_AURA_WATER_WALK));
+ float max_z = GetMap()->GetWaterOrGroundLevel(x, y, z, &ground_z, !ToUnit()->HasAuraType(SPELL_AURA_WATER_WALK));
if (max_z > INVALID_HEIGHT)
{
if (z > max_z)
@@ -1788,7 +1793,7 @@ void WorldObject::UpdateAllowedPositionZ(float x, float y, float &z) const
}
else
{
- float ground_z = GetBaseMap()->GetHeight(GetPhaseMask(), x, y, z, true);
+ float ground_z = GetMap()->GetHeight(GetPhaseMask(), x, y, z, true);
if (z < ground_z)
z = ground_z;
}
@@ -1796,7 +1801,7 @@ void WorldObject::UpdateAllowedPositionZ(float x, float y, float &z) const
}
default:
{
- float ground_z = GetBaseMap()->GetHeight(GetPhaseMask(), x, y, z, true);
+ float ground_z = GetMap()->GetHeight(GetPhaseMask(), x, y, z, true);
if (ground_z > INVALID_HEIGHT)
z = ground_z;
break;
diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h
index 0c99fac09be..cb9103d24d7 100644
--- a/src/server/game/Entities/Object/Object.h
+++ b/src/server/game/Entities/Object/Object.h
@@ -333,6 +333,11 @@ class Object
struct Position
{
+ Position(float x = 0, float y = 0, float z = 0, float o = 0)
+ : m_positionX(x), m_positionY(y), m_positionZ(z), m_orientation(NormalizeOrientation(o)) { }
+
+ Position(const Position &loc) { Relocate(loc); }
+
struct PositionXYZStreamer
{
explicit PositionXYZStreamer(Position& pos) : m_pos(&pos) { }
@@ -553,7 +558,7 @@ class WorldLocation : public Position
public:
explicit WorldLocation(uint32 _mapid = MAPID_INVALID, float _x = 0, float _y = 0, float _z = 0, float _o = 0)
: m_mapId(_mapid) { Relocate(_x, _y, _z, _o); }
- WorldLocation(const WorldLocation &loc) { WorldRelocate(loc); }
+ WorldLocation(const WorldLocation &loc) : Position(loc) { WorldRelocate(loc); }
void WorldRelocate(const WorldLocation &loc)
{ m_mapId = loc.GetMapId(); Relocate(loc); }
diff --git a/src/server/game/Entities/Pet/Pet.cpp b/src/server/game/Entities/Pet/Pet.cpp
index 13d738887b5..79f95e5db18 100644
--- a/src/server/game/Entities/Pet/Pet.cpp
+++ b/src/server/game/Entities/Pet/Pet.cpp
@@ -278,7 +278,7 @@ bool Pet::LoadPetFromDB(Player* owner, uint32 petEntry, uint32 petnumber, bool c
{
SQLTransaction trans = CharacterDatabase.BeginTransaction();
- stmt = CharacterDatabase.GetPreparedStatement(CHAR_UDP_CHAR_PET_SLOT_BY_SLOT_EXCLUDE_ID);
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_PET_SLOT_BY_SLOT_EXCLUDE_ID);
stmt->setUInt8(0, uint8(PET_SAVE_NOT_IN_SLOT));
stmt->setUInt32(1, ownerid);
stmt->setUInt8(2, uint8(PET_SAVE_AS_CURRENT));
@@ -429,7 +429,7 @@ void Pet::SavePetToDB(PetSaveMode mode)
// prevent duplicate using slot (except PET_SAVE_NOT_IN_SLOT)
if (mode <= PET_SAVE_LAST_STABLE_SLOT)
{
- stmt = CharacterDatabase.GetPreparedStatement(CHAR_UDP_CHAR_PET_SLOT_BY_SLOT);
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_PET_SLOT_BY_SLOT);
stmt->setUInt8(0, uint8(PET_SAVE_NOT_IN_SLOT));
stmt->setUInt32(1, ownerLowGUID);
stmt->setUInt8(2, uint8(mode));
@@ -1080,9 +1080,8 @@ void Pet::_LoadSpellCooldowns()
{
time_t curTime = time(NULL);
- WorldPacket data(SMSG_SPELL_COOLDOWN, size_t(8+1+result->GetRowCount()*8));
- data << GetGUID();
- data << uint8(0x0); // flags (0x1, 0x2)
+ PacketCooldowns cooldowns;
+ WorldPacket data;
do
{
@@ -1101,8 +1100,7 @@ void Pet::_LoadSpellCooldowns()
if (db_time <= curTime)
continue;
- data << uint32(spell_id);
- data << uint32(uint32(db_time-curTime)*IN_MILLISECONDS);
+ cooldowns[spell_id] = uint32(db_time - curTime)*IN_MILLISECONDS;
_AddCreatureSpellCooldown(spell_id, db_time);
@@ -1110,8 +1108,11 @@ void Pet::_LoadSpellCooldowns()
}
while (result->NextRow());
- if (!m_CreatureSpellCooldowns.empty() && GetOwner())
+ if (!cooldowns.empty() && GetOwner())
+ {
+ BuildCooldownPacket(data, SPELL_COOLDOWN_FLAG_NONE, cooldowns);
GetOwner()->GetSession()->SendPacket(&data);
+ }
}
}
@@ -2010,21 +2011,16 @@ void Pet::SynchronizeLevelWithOwner()
void Pet::ProhibitSpellSchool(SpellSchoolMask idSchoolMask, uint32 unTimeMs)
{
- WorldPacket data(SMSG_SPELL_COOLDOWN, 8+1+m_spells.size()*8);
- data << uint64(GetGUID());
- data << uint8(0x0); // flags (0x1, 0x2)
+ PacketCooldowns cooldowns;
+ WorldPacket data;
time_t curTime = time(NULL);
for (PetSpellMap::const_iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr)
{
if (itr->second.state == PETSPELL_REMOVED)
continue;
+
uint32 unSpellId = itr->first;
- SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(unSpellId);
- if (!spellInfo)
- {
- ASSERT(spellInfo);
- continue;
- }
+ SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(unSpellId);
// Not send cooldown for this spells
if (spellInfo->IsCooldownStartedOnEvent())
@@ -2035,14 +2031,18 @@ void Pet::ProhibitSpellSchool(SpellSchoolMask idSchoolMask, uint32 unTimeMs)
if ((idSchoolMask & spellInfo->GetSchoolMask()) && GetCreatureSpellCooldownDelay(unSpellId) < unTimeMs)
{
- data << uint32(unSpellId);
- data << uint32(unTimeMs); // in m.secs
+ cooldowns[unSpellId] = unTimeMs;
_AddCreatureSpellCooldown(unSpellId, curTime + unTimeMs/IN_MILLISECONDS);
}
}
- if (Player* owner = GetOwner())
- owner->GetSession()->SendPacket(&data);
+ if (!cooldowns.empty())
+ {
+ BuildCooldownPacket(data, SPELL_COOLDOWN_FLAG_NONE, cooldowns);
+
+ if (Player* owner = GetOwner())
+ owner->GetSession()->SendPacket(&data);
+ }
}
Player* Pet::GetOwner() const
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index 79f7c356376..173be677c14 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -1782,7 +1782,6 @@ void Player::Update(uint32 p_time)
if (p_time >= m_nextSave)
{
// m_nextSave reset in SaveToDB call
- sScriptMgr->OnPlayerSave(this);
SaveToDB();
TC_LOG_DEBUG("entities.player", "Player '%s' (GUID: %u) saved", GetName().c_str(), GetGUIDLow());
}
@@ -5017,7 +5016,19 @@ void Player::DeleteFromDB(uint64 playerguid, uint32 accountId, bool updateRealmC
stmt->setUInt32(0, guid);
trans->Append(stmt);
- stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_QUESTSTATUS_DAILY);
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHARACTER_QUESTSTATUS_DAILY);
+ stmt->setUInt32(0, guid);
+ trans->Append(stmt);
+
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHARACTER_QUESTSTATUS_WEEKLY);
+ stmt->setUInt32(0, guid);
+ trans->Append(stmt);
+
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHARACTER_QUESTSTATUS_MONTHLY);
+ stmt->setUInt32(0, guid);
+ trans->Append(stmt);
+
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHARACTER_QUESTSTATUS_SEASONAL);
stmt->setUInt32(0, guid);
trans->Append(stmt);
@@ -9017,7 +9028,7 @@ void Player::SendLoot(uint64 guid, LootType loot_type)
loot->FillLoot(lootid, LootTemplates_Gameobject, this, !groupRules, false, go->GetLootMode());
// get next RR player (for next loot)
- if (groupRules)
+ if (groupRules && !go->loot.empty())
group->UpdateLooterGuid(go);
}
@@ -9056,7 +9067,7 @@ void Player::SendLoot(uint64 guid, LootType loot_type)
switch (group->GetLootMethod())
{
case MASTER_LOOT:
- permission = MASTER_PERMISSION;
+ permission = group->GetMasterLooterGuid() == GetGUID() ? MASTER_PERMISSION : RESTRICTED_PERMISSION;
break;
case FREE_FOR_ALL:
permission = ALL_PERMISSION;
@@ -9234,7 +9245,7 @@ void Player::SendLoot(uint64 guid, LootType loot_type)
switch (group->GetLootMethod())
{
case MASTER_LOOT:
- permission = MASTER_PERMISSION;
+ permission = group->GetMasterLooterGuid() == GetGUID() ? MASTER_PERMISSION : RESTRICTED_PERMISSION;
break;
case FREE_FOR_ALL:
permission = ALL_PERMISSION;
@@ -12189,11 +12200,8 @@ Item* Player::EquipItem(uint16 pos, Item* pItem, bool update)
GetGlobalCooldownMgr().AddGlobalCooldown(spellProto, m_weaponChangeTimer);
- WorldPacket data(SMSG_SPELL_COOLDOWN, 8+1+4);
- data << uint64(GetGUID());
- data << uint8(1);
- data << uint32(cooldownSpell);
- data << uint32(0);
+ WorldPacket data;
+ BuildCooldownPacket(data, SPELL_COOLDOWN_FLAG_INCLUDE_GCD, cooldownSpell, 0);
GetSession()->SendPacket(&data);
}
}
@@ -12484,6 +12492,7 @@ void Player::DestroyItem(uint8 bag, uint8 slot, bool update)
RemoveAurasDueToSpell(proto->Spells[i].SpellId);
ItemRemovedQuestCheck(pItem->GetEntry(), pItem->GetCount());
+ sScriptMgr->OnItemRemove(this, pItem);
if (bag == INVENTORY_SLOT_BAG_0)
{
@@ -15112,7 +15121,7 @@ void Player::AddQuestAndCheckCompletion(Quest const* quest, Object* questGiver)
switch (questGiver->GetTypeId())
{
case TYPEID_UNIT:
- sScriptMgr->OnQuestAccept(this, (questGiver->ToCreature()), quest);
+ sScriptMgr->OnQuestAccept(this, questGiver->ToCreature(), quest);
questGiver->ToCreature()->AI()->sQuestAccept(this, quest);
break;
case TYPEID_ITEM:
@@ -15255,7 +15264,7 @@ void Player::AddQuest(Quest const* quest, Object* questGiver)
StartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_QUEST, quest_id);
- UpdateForQuestWorldObjects();
+ SendQuestUpdate(quest_id);
}
void Player::CompleteQuest(uint32 quest_id)
@@ -15432,7 +15441,7 @@ void Player::RewardQuest(Quest const* quest, uint32 reward, Object* questGiver,
else if (quest->IsSeasonal())
SetSeasonalQuestStatus(quest_id);
- RemoveActiveQuest(quest_id);
+ RemoveActiveQuest(quest_id, false);
m_RewardedQuests.insert(quest_id);
m_RewardedQuestsSave[quest_id] = true;
@@ -15484,6 +15493,8 @@ void Player::RewardQuest(Quest const* quest, uint32 reward, Object* questGiver,
UpdatePvPState();
}
+ SendQuestUpdate(quest_id);
+
//lets remove flag for delayed teleports
SetCanDelayTeleport(false);
}
@@ -15822,6 +15833,7 @@ bool Player::SatisfyQuestExclusiveGroup(Quest const* qInfo, bool msg)
// not allow have daily quest if daily quest from exclusive group already recently completed
Quest const* Nquest = sObjectMgr->GetQuestTemplate(exclude_Id);
+ ASSERT(Nquest);
if (!SatisfyQuestDay(Nquest, false) || !SatisfyQuestWeek(Nquest, false) || !SatisfyQuestSeasonal(Nquest, false))
{
if (msg)
@@ -16023,6 +16035,7 @@ bool Player::TakeQuestSourceItem(uint32 questId, bool msg)
return false;
}
+ ASSERT(item);
bool destroyItem = true;
for (uint8 n = 0; n < QUEST_ITEM_OBJECTIVES_COUNT; ++n)
if (item->StartQuest == questId && srcItemId == quest->RequiredItemId[n])
@@ -16077,22 +16090,64 @@ bool Player::CanShareQuest(uint32 quest_id) const
return false;
}
-void Player::SetQuestStatus(uint32 quest_id, QuestStatus status)
+void Player::SetQuestStatus(uint32 questId, QuestStatus status, bool update /*= true*/)
{
- if (sObjectMgr->GetQuestTemplate(quest_id))
+ if (sObjectMgr->GetQuestTemplate(questId))
{
- m_QuestStatus[quest_id].Status = status;
- m_QuestStatusSave[quest_id] = true;
+ m_QuestStatus[questId].Status = status;
+ m_QuestStatusSave[questId] = true;
}
PhaseUpdateData phaseUpdateData;
- phaseUpdateData.AddQuestUpdate(quest_id);
+ phaseUpdateData.AddQuestUpdate(questId);
phaseMgr.NotifyConditionChanged(phaseUpdateData);
+ if (update)
+ SendQuestUpdate(questId);
+}
+
+void Player::RemoveActiveQuest(uint32 questId, bool update /*= true*/)
+{
+ QuestStatusMap::iterator itr = m_QuestStatus.find(questId);
+ if (itr != m_QuestStatus.end())
+ {
+ m_QuestStatus.erase(itr);
+ m_QuestStatusSave[questId] = false;
+
+ PhaseUpdateData phaseUpdateData;
+ phaseUpdateData.AddQuestUpdate(questId);
+
+ phaseMgr.NotifyConditionChanged(phaseUpdateData);
+ }
+
+ if (update)
+ SendQuestUpdate(questId);
+}
+
+void Player::RemoveRewardedQuest(uint32 questId, bool update /*= true*/)
+{
+ RewardedQuestSet::iterator rewItr = m_RewardedQuests.find(questId);
+ if (rewItr != m_RewardedQuests.end())
+ {
+ m_RewardedQuests.erase(rewItr);
+ m_RewardedQuestsSave[questId] = false;
+
+ PhaseUpdateData phaseUpdateData;
+ phaseUpdateData.AddQuestUpdate(questId);
+
+ phaseMgr.NotifyConditionChanged(phaseUpdateData);
+ }
+
+ if (update)
+ SendQuestUpdate(questId);
+}
+
+void Player::SendQuestUpdate(uint32 questId)
+{
uint32 zone = 0, area = 0;
- SpellAreaForQuestMapBounds saBounds = sSpellMgr->GetSpellAreaForQuestMapBounds(quest_id);
+ SpellAreaForQuestMapBounds saBounds = sSpellMgr->GetSpellAreaForQuestMapBounds(questId);
if (saBounds.first != saBounds.second)
{
GetZoneAndAreaId(zone, area);
@@ -16103,7 +16158,7 @@ void Player::SetQuestStatus(uint32 quest_id, QuestStatus status)
CastSpell(this, itr->second->spellId, true);
}
- saBounds = sSpellMgr->GetSpellAreaForQuestEndMapBounds(quest_id);
+ saBounds = sSpellMgr->GetSpellAreaForQuestEndMapBounds(questId);
if (saBounds.first != saBounds.second)
{
if (!zone || !area)
@@ -16117,35 +16172,108 @@ void Player::SetQuestStatus(uint32 quest_id, QuestStatus status)
UpdateForQuestWorldObjects();
}
-void Player::RemoveActiveQuest(uint32 quest_id)
+QuestGiverStatus Player::GetQuestDialogStatus(Object* questgiver)
{
- QuestStatusMap::iterator itr = m_QuestStatus.find(quest_id);
- if (itr != m_QuestStatus.end())
+ QuestRelationBounds qr;
+ QuestRelationBounds qir;
+
+ switch (questgiver->GetTypeId())
{
- m_QuestStatus.erase(itr);
- m_QuestStatusSave[quest_id] = false;
+ case TYPEID_GAMEOBJECT:
+ {
+ QuestGiverStatus questStatus = QuestGiverStatus(sScriptMgr->GetDialogStatus(this, questgiver->ToGameObject()));
+ if (questStatus != DIALOG_STATUS_SCRIPTED_NO_STATUS)
+ return questStatus;
+ qr = sObjectMgr->GetGOQuestRelationBounds(questgiver->GetEntry());
+ qir = sObjectMgr->GetGOQuestInvolvedRelationBounds(questgiver->GetEntry());
+ break;
+ }
+ case TYPEID_UNIT:
+ {
+ QuestGiverStatus questStatus = QuestGiverStatus(sScriptMgr->GetDialogStatus(this, questgiver->ToCreature()));
+ if (questStatus != DIALOG_STATUS_SCRIPTED_NO_STATUS)
+ return questStatus;
+ qr = sObjectMgr->GetCreatureQuestRelationBounds(questgiver->GetEntry());
+ qir = sObjectMgr->GetCreatureQuestInvolvedRelationBounds(questgiver->GetEntry());
+ break;
+ }
+ default:
+ // it's impossible, but check
+ TC_LOG_ERROR("entities.player.quest", "GetQuestDialogStatus called for unexpected type %u", questgiver->GetTypeId());
+ return DIALOG_STATUS_NONE;
+ }
- PhaseUpdateData phaseUpdateData;
- phaseUpdateData.AddQuestUpdate(quest_id);
+ QuestGiverStatus result = DIALOG_STATUS_NONE;
- phaseMgr.NotifyConditionChanged(phaseUpdateData);
- return;
+ for (QuestRelations::const_iterator i = qir.first; i != qir.second; ++i)
+ {
+ QuestGiverStatus result2 = DIALOG_STATUS_NONE;
+ uint32 questId = i->second;
+ Quest const* quest = sObjectMgr->GetQuestTemplate(questId);
+ if (!quest)
+ continue;
+
+ ConditionList conditions = sConditionMgr->GetConditionsForNotGroupedEntry(CONDITION_SOURCE_TYPE_QUEST_SHOW_MARK, quest->GetQuestId());
+ if (!sConditionMgr->IsObjectMeetToConditions(this, conditions))
+ continue;
+
+ QuestStatus status = GetQuestStatus(questId);
+ if ((status == QUEST_STATUS_COMPLETE && !GetQuestRewardStatus(questId)) ||
+ (quest->IsAutoComplete() && CanTakeQuest(quest, false)))
+ {
+ if (quest->IsAutoComplete() && quest->IsRepeatable() && !quest->IsDailyOrWeekly())
+ result2 = DIALOG_STATUS_REWARD_REP;
+ else
+ result2 = DIALOG_STATUS_REWARD;
+ }
+ else if (status == QUEST_STATUS_INCOMPLETE)
+ result2 = DIALOG_STATUS_INCOMPLETE;
+
+ if (result2 > result)
+ result = result2;
}
-}
-void Player::RemoveRewardedQuest(uint32 quest_id)
-{
- RewardedQuestSet::iterator rewItr = m_RewardedQuests.find(quest_id);
- if (rewItr != m_RewardedQuests.end())
+ for (QuestRelations::const_iterator i = qr.first; i != qr.second; ++i)
{
- m_RewardedQuests.erase(rewItr);
- m_RewardedQuestsSave[quest_id] = false;
+ QuestGiverStatus result2 = DIALOG_STATUS_NONE;
+ uint32 questId = i->second;
+ Quest const* quest = sObjectMgr->GetQuestTemplate(questId);
+ if (!quest)
+ continue;
- PhaseUpdateData phaseUpdateData;
- phaseUpdateData.AddQuestUpdate(quest_id);
+ ConditionList conditions = sConditionMgr->GetConditionsForNotGroupedEntry(CONDITION_SOURCE_TYPE_QUEST_SHOW_MARK, quest->GetQuestId());
+ if (!sConditionMgr->IsObjectMeetToConditions(this, conditions))
+ continue;
- phaseMgr.NotifyConditionChanged(phaseUpdateData);
+ QuestStatus status = GetQuestStatus(questId);
+ if (status == QUEST_STATUS_NONE)
+ {
+ if (CanSeeStartQuest(quest))
+ {
+ if (SatisfyQuestLevel(quest, false))
+ {
+ if (quest->IsAutoComplete())
+ result2 = DIALOG_STATUS_REWARD_REP;
+ else if (getLevel() <= (GetQuestLevel(quest) + sWorld->getIntConfig(CONFIG_QUEST_LOW_LEVEL_HIDE_DIFF)))
+ {
+ if (quest->IsDaily())
+ result2 = DIALOG_STATUS_AVAILABLE_REP;
+ else
+ result2 = DIALOG_STATUS_AVAILABLE;
+ }
+ else
+ result2 = DIALOG_STATUS_LOW_LEVEL_AVAILABLE;
+ }
+ else
+ result2 = DIALOG_STATUS_UNAVAILABLE;
+ }
+ }
+
+ if (result2 > result)
+ result = result2;
}
+
+ return result;
}
// not used in Trinity, but used in scripting code
@@ -17848,13 +17976,13 @@ bool Player::isAllowedToLoot(const Creature* creature)
case FREE_FOR_ALL:
return true;
case ROUND_ROBIN:
- case MASTER_LOOT:
// may only loot if the player is the loot roundrobin player
// or if there are free/quest/conditional item for the player
if (loot->roundRobinPlayer == 0 || loot->roundRobinPlayer == GetGUID())
return true;
return loot->hasItemFor(this);
+ case MASTER_LOOT:
case GROUP_LOOT:
case NEED_BEFORE_GREED:
// may only loot if the player is the loot roundrobin player
@@ -19240,6 +19368,9 @@ void Player::SaveToDB(bool create /*=false*/)
TC_LOG_DEBUG("entities.unit", "The value of player %s at save: ", m_name.c_str());
outDebugValues();
+ if (!create)
+ sScriptMgr->OnPlayerSave(this);
+
PreparedStatement* stmt = NULL;
uint8 index = 0;
@@ -19547,7 +19678,7 @@ void Player::SaveInventoryAndGoldToDB(SQLTransaction& trans)
void Player::SaveGoldToDB(SQLTransaction& trans)
{
- PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UDP_CHAR_MONEY);
+ PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_MONEY);
stmt->setUInt64(0, GetMoney());
stmt->setUInt32(1, GetGUIDLow());
trans->Append(stmt);
@@ -20004,14 +20135,15 @@ void Player::_SaveDailyQuestStatus(SQLTransaction& trans)
// save last daily quest time for all quests: we need only mostly reset time for reset check anyway
// we don't need transactions here.
- PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_QUEST_STATUS_DAILY_CHAR);
+ PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHARACTER_QUESTSTATUS_DAILY);
stmt->setUInt32(0, GetGUIDLow());
trans->Append(stmt);
+
for (uint32 quest_daily_idx = 0; quest_daily_idx < PLAYER_MAX_DAILY_QUESTS; ++quest_daily_idx)
{
if (GetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx))
{
- stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHARACTER_DAILYQUESTSTATUS);
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHARACTER_QUESTSTATUS_DAILY);
stmt->setUInt32(0, GetGUIDLow());
stmt->setUInt32(1, GetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx));
stmt->setUInt64(2, uint64(m_lastDailyQuestTime));
@@ -20023,7 +20155,7 @@ void Player::_SaveDailyQuestStatus(SQLTransaction& trans)
{
for (DFQuestsDoneList::iterator itr = m_DFQuests.begin(); itr != m_DFQuests.end(); ++itr)
{
- stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHARACTER_DAILYQUESTSTATUS);
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHARACTER_QUESTSTATUS_DAILY);
stmt->setUInt32(0, GetGUIDLow());
stmt->setUInt32(1, (*itr));
stmt->setUInt64(2, uint64(m_lastDailyQuestTime));
@@ -20038,17 +20170,17 @@ void Player::_SaveWeeklyQuestStatus(SQLTransaction& trans)
return;
// we don't need transactions here.
- PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_QUEST_STATUS_WEEKLY_CHAR);
+ PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHARACTER_QUESTSTATUS_WEEKLY);
stmt->setUInt32(0, GetGUIDLow());
trans->Append(stmt);
for (QuestSet::const_iterator iter = m_weeklyquests.begin(); iter != m_weeklyquests.end(); ++iter)
{
- uint32 quest_id = *iter;
+ uint32 questId = *iter;
- stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHARACTER_WEEKLYQUESTSTATUS);
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHARACTER_QUESTSTATUS_WEEKLY);
stmt->setUInt32(0, GetGUIDLow());
- stmt->setUInt32(1, quest_id);
+ stmt->setUInt32(1, questId);
trans->Append(stmt);
}
@@ -20061,21 +20193,22 @@ void Player::_SaveSeasonalQuestStatus(SQLTransaction& trans)
return;
// we don't need transactions here.
- PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_QUEST_STATUS_SEASONAL_CHAR);
+ PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHARACTER_QUESTSTATUS_SEASONAL);
stmt->setUInt32(0, GetGUIDLow());
trans->Append(stmt);
for (SeasonalEventQuestMap::const_iterator iter = m_seasonalquests.begin(); iter != m_seasonalquests.end(); ++iter)
{
- uint16 event_id = iter->first;
+ uint16 eventId = iter->first;
+
for (SeasonalQuestSet::const_iterator itr = iter->second.begin(); itr != iter->second.end(); ++itr)
{
- uint32 quest_id = (*itr);
+ uint32 questId = *itr;
- stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHARACTER_SEASONALQUESTSTATUS);
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHARACTER_QUESTSTATUS_SEASONAL);
stmt->setUInt32(0, GetGUIDLow());
- stmt->setUInt32(1, quest_id);
- stmt->setUInt32(2, event_id);
+ stmt->setUInt32(1, questId);
+ stmt->setUInt32(2, eventId);
trans->Append(stmt);
}
}
@@ -20089,16 +20222,17 @@ void Player::_SaveMonthlyQuestStatus(SQLTransaction& trans)
return;
// we don't need transactions here.
- PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_QUEST_STATUS_MONTHLY_CHAR);
+ PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHARACTER_QUESTSTATUS_MONTHLY);
stmt->setUInt32(0, GetGUIDLow());
trans->Append(stmt);
for (QuestSet::const_iterator iter = m_monthlyquests.begin(); iter != m_monthlyquests.end(); ++iter)
{
- uint32 quest_id = *iter;
- stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHARACTER_MONTHLYQUESTSTATUS);
+ uint32 questId = *iter;
+
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHARACTER_QUESTSTATUS_MONTHLY);
stmt->setUInt32(0, GetGUIDLow());
- stmt->setUInt32(1, quest_id);
+ stmt->setUInt32(1, questId);
trans->Append(stmt);
}
@@ -20145,7 +20279,7 @@ void Player::_SaveSkills(SQLTransaction& trans)
trans->Append(stmt);
break;
case SKILL_CHANGED:
- stmt = CharacterDatabase.GetPreparedStatement(CHAR_UDP_CHAR_SKILLS);
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_SKILLS);
stmt->setUInt16(0, value);
stmt->setUInt16(1, max);
stmt->setUInt32(2, GetGUIDLow());
@@ -20690,7 +20824,7 @@ void Player::Say(const std::string& text, const uint32 language)
sScriptMgr->OnPlayerChat(this, CHAT_MSG_SAY, language, _text);
WorldPacket data;
- ChatHandler::BuildChatPacket(data, CHAT_MSG_SAY, Language(language), this, this, text);
+ ChatHandler::BuildChatPacket(data, CHAT_MSG_SAY, Language(language), this, this, _text);
SendMessageToSetInRange(&data, sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_SAY), true);
}
@@ -20700,7 +20834,7 @@ void Player::Yell(const std::string& text, const uint32 language)
sScriptMgr->OnPlayerChat(this, CHAT_MSG_YELL, language, _text);
WorldPacket data;
- ChatHandler::BuildChatPacket(data, CHAT_MSG_YELL, Language(language), this, this, text);
+ ChatHandler::BuildChatPacket(data, CHAT_MSG_YELL, Language(language), this, this, _text);
SendMessageToSetInRange(&data, sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_YELL), true);
}
@@ -20710,7 +20844,7 @@ void Player::TextEmote(const std::string& text)
sScriptMgr->OnPlayerChat(this, CHAT_MSG_EMOTE, LANG_UNIVERSAL, _text);
WorldPacket data;
- ChatHandler::BuildChatPacket(data, CHAT_MSG_EMOTE, LANG_UNIVERSAL, this, this, text);
+ ChatHandler::BuildChatPacket(data, CHAT_MSG_EMOTE, LANG_UNIVERSAL, this, this, _text);
SendMessageToSetInRange(&data, sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_TEXTEMOTE), true, !GetSession()->HasPermission(rbac::RBAC_PERM_TWO_SIDE_INTERACTION_CHAT));
}
@@ -20740,14 +20874,14 @@ void Player::Whisper(const std::string& text, uint32 language, uint64 receiver)
sScriptMgr->OnPlayerChat(this, CHAT_MSG_WHISPER, language, _text, rPlayer);
WorldPacket data;
- ChatHandler::BuildChatPacket(data, CHAT_MSG_WHISPER, Language(language), this, this, text);
+ ChatHandler::BuildChatPacket(data, CHAT_MSG_WHISPER, Language(language), this, this, _text);
rPlayer->GetSession()->SendPacket(&data);
// rest stuff shouldn't happen in case of addon message
if (isAddonMessage)
return;
- ChatHandler::BuildChatPacket(data, CHAT_MSG_WHISPER_INFORM, Language(language), rPlayer, rPlayer, text);
+ ChatHandler::BuildChatPacket(data, CHAT_MSG_WHISPER_INFORM, Language(language), rPlayer, rPlayer, _text);
GetSession()->SendPacket(&data);
if (!isAcceptWhispers() && !IsGameMaster() && !rPlayer->IsGameMaster())
@@ -21542,6 +21676,7 @@ bool Player::ActivateTaxiPathTo(std::vector<uint32> const& nodes, Creature* npc
if (sWorld->getBoolConfig(CONFIG_INSTANT_TAXI))
{
TaxiNodesEntry const* lastPathNode = sTaxiNodesStore.LookupEntry(nodes[nodes.size()-1]);
+ ASSERT(lastPathNode);
m_taxi.ClearTaxiDestinations();
TeleportTo(lastPathNode->map_id, lastPathNode->x, lastPathNode->y, lastPathNode->z, GetOrientation());
return false;
@@ -21635,10 +21770,8 @@ void Player::ContinueTaxiFlight()
void Player::ProhibitSpellSchool(SpellSchoolMask idSchoolMask, uint32 unTimeMs)
{
- // last check 2.0.10
- WorldPacket data(SMSG_SPELL_COOLDOWN, 8+1+m_spells.size()*8);
- data << uint64(GetGUID());
- data << uint8(0x0); // flags (0x1, 0x2)
+ PacketCooldowns cooldowns;
+ WorldPacket data;
time_t curTime = time(NULL);
for (PlayerSpellMap::const_iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr)
{
@@ -21661,12 +21794,16 @@ void Player::ProhibitSpellSchool(SpellSchoolMask idSchoolMask, uint32 unTimeMs)
if ((idSchoolMask & spellInfo->GetSchoolMask()) && GetSpellCooldownDelay(unSpellId) < unTimeMs)
{
- data << uint32(unSpellId);
- data << uint32(unTimeMs); // in m.secs
+ cooldowns[unSpellId] = unTimeMs;
AddSpellCooldown(unSpellId, 0, curTime + unTimeMs/IN_MILLISECONDS);
}
}
- GetSession()->SendPacket(&data);
+
+ if (!cooldowns.empty())
+ {
+ BuildCooldownPacket(data, SPELL_COOLDOWN_FLAG_NONE, cooldowns);
+ GetSession()->SendPacket(&data);
+ }
}
void Player::InitDataForForm(bool reapplyMods)
@@ -21760,6 +21897,7 @@ inline bool Player::_StoreOrEquipNewItem(uint32 vendorslot, uint32 item, uint8 c
if (crItem->ExtendedCost) // case for new honor system
{
ItemExtendedCostEntry const* iece = sItemExtendedCostStore.LookupEntry(crItem->ExtendedCost);
+ ASSERT(iece);
for (int i = 0; i < MAX_ITEM_EXT_COST_CURRENCIES; ++i)
{
if (iece->RequiredItem[i])
@@ -21986,6 +22124,15 @@ bool Player::BuyItemFromVendorSlot(uint64 vendorguid, uint32 vendorslot, uint32
return false;
}
+ if (!(pProto->AllowableClass & getClassMask()) && pProto->Bonding == BIND_WHEN_PICKED_UP && !IsGameMaster())
+ {
+ SendBuyError(BUY_ERR_CANT_FIND_ITEM, NULL, item, 0);
+ return false;
+ }
+
+ if (!IsGameMaster() && ((pProto->Flags2 & ITEM_FLAGS_EXTRA_HORDE_ONLY && GetTeam() == ALLIANCE) || (pProto->Flags2 == ITEM_FLAGS_EXTRA_ALLIANCE_ONLY && GetTeam() == HORDE)))
+ return false;
+
Creature* creature = GetNPCIfCanInteractWith(vendorguid, UNIT_NPC_FLAG_VENDOR);
if (!creature)
{
@@ -21994,6 +22141,14 @@ bool Player::BuyItemFromVendorSlot(uint64 vendorguid, uint32 vendorslot, uint32
return false;
}
+ ConditionList conditions = sConditionMgr->GetConditionsForNpcVendorEvent(creature->GetEntry(), item);
+ if (!sConditionMgr->IsObjectMeetToConditions(this, creature, conditions))
+ {
+ TC_LOG_DEBUG("condition", "BuyItemFromVendor: conditions not met for creature entry %u item %u", creature->GetEntry(), item);
+ SendBuyError(BUY_ERR_CANT_FIND_ITEM, creature, item, 0);
+ return false;
+ }
+
VendorItemData const* vItems = creature->GetVendorItems();
if (!vItems || vItems->Empty())
{
@@ -22340,6 +22495,8 @@ void Player::AddSpellAndCategoryCooldowns(SpellInfo const* spellInfo, uint32 ite
time_t catrecTime;
time_t recTime;
+ bool needsCooldownPacket = false;
+
// overwrite time for selected category
if (infinityCooldown)
{
@@ -22361,7 +22518,17 @@ void Player::AddSpellAndCategoryCooldowns(SpellInfo const* spellInfo, uint32 ite
if (catrec > 0 && !(spellInfo->AttributesEx6 & SPELL_ATTR6_IGNORE_CATEGORY_COOLDOWN_MODS))
ApplySpellMod(spellInfo->Id, SPELLMOD_COOLDOWN, catrec, spell);
-
+
+ if (int32 cooldownMod = GetTotalAuraModifier(SPELL_AURA_MOD_COOLDOWN))
+ {
+ // Apply SPELL_AURA_MOD_COOLDOWN only to own spells
+ if (HasSpell(spellInfo->Id))
+ {
+ needsCooldownPacket = true;
+ rec += cooldownMod * IN_MILLISECONDS; // SPELL_AURA_MOD_COOLDOWN does not affect category cooldows, verified with shaman shocks
+ }
+ }
+
// Apply SPELL_AURA_MOD_SPELL_CATEGORY_COOLDOWN modifiers
// Note: This aura applies its modifiers to all cooldowns of spells with set category, not to category cooldown only
if (cat)
@@ -22402,8 +22569,17 @@ void Player::AddSpellAndCategoryCooldowns(SpellInfo const* spellInfo, uint32 ite
// self spell cooldown
if (recTime > 0)
+ {
AddSpellCooldown(spellInfo->Id, itemId, recTime);
+ if (needsCooldownPacket)
+ {
+ WorldPacket data;
+ BuildCooldownPacket(data, SPELL_COOLDOWN_FLAG_NONE, spellInfo->Id, rec);
+ SendDirectMessage(&data);
+ }
+ }
+
// category spells
if (cat && catrec > 0)
{
@@ -23925,7 +24101,7 @@ void Player::UpdateForQuestWorldObjects()
UpdateData udata(GetMapId());
WorldPacket packet;
- for (ClientGUIDs::iterator itr=m_clientGUIDs.begin(); itr != m_clientGUIDs.end(); ++itr)
+ for (ClientGUIDs::iterator itr = m_clientGUIDs.begin(); itr != m_clientGUIDs.end(); ++itr)
{
if (IS_GAMEOBJECT_GUID(*itr))
{
@@ -24920,7 +25096,7 @@ bool Player::isTotalImmune()
return false;
}
-bool Player::HasTitle(uint32 bitIndex)
+bool Player::HasTitle(uint32 bitIndex) const
{
if (bitIndex > MAX_TITLE_INDEX)
return false;
@@ -25194,6 +25370,12 @@ void Player::StoreLootItem(uint8 lootSlot, Loot* loot)
return;
}
+ if (!item->AllowedForPlayer(this))
+ {
+ SendLootRelease(GetLootGUID());
+ return;
+ }
+
// questitems use the blocked field for other purposes
if (!qitem && item->is_blocked)
{
diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h
index 9a77a999a46..793a9b4d834 100644
--- a/src/server/game/Entities/Player/Player.h
+++ b/src/server/game/Entities/Player/Player.h
@@ -1562,9 +1562,11 @@ class Player : public Unit, public GridObject<Player>
bool TakeQuestSourceItem(uint32 questId, bool msg);
bool GetQuestRewardStatus(uint32 quest_id) const;
QuestStatus GetQuestStatus(uint32 quest_id) const;
- void SetQuestStatus(uint32 quest_id, QuestStatus status);
- void RemoveActiveQuest(uint32 quest_id);
- void RemoveRewardedQuest(uint32 quest_id);
+ void SetQuestStatus(uint32 questId, QuestStatus status, bool update = true);
+ void RemoveActiveQuest(uint32 questId, bool update = true);
+ void RemoveRewardedQuest(uint32 questId, bool update = true);
+ void SendQuestUpdate(uint32 questId);
+ QuestGiverStatus GetQuestDialogStatus(Object* questGiver);
void SetDailyQuestStatus(uint32 quest_id);
void SetWeeklyQuestStatus(uint32 quest_id);
@@ -2484,8 +2486,8 @@ class Player : public Unit, public GridObject<Player>
void RemoveTimedAchievement(AchievementCriteriaTimedTypes type, uint32 entry);
void CompletedAchievement(AchievementEntry const* entry);
- bool HasTitle(uint32 bitIndex);
- bool HasTitle(CharTitlesEntry const* title) { return HasTitle(title->bit_index); }
+ bool HasTitle(uint32 bitIndex) const;
+ bool HasTitle(CharTitlesEntry const* title) const { return HasTitle(title->bit_index); }
void SetTitle(CharTitlesEntry const* title, bool lost = false);
//bool isActiveObject() const { return true; }
diff --git a/src/server/game/Entities/Transport/Transport.cpp b/src/server/game/Entities/Transport/Transport.cpp
index 9906dc6d298..8099dc1bc8e 100644
--- a/src/server/game/Entities/Transport/Transport.cpp
+++ b/src/server/game/Entities/Transport/Transport.cpp
@@ -31,6 +31,7 @@
#include "Player.h"
#include "Cell.h"
#include "CellImpl.h"
+#include "Totem.h"
Transport::Transport() : GameObject(),
_transportInfo(NULL), _isMoving(true), _pendingStop(false),
@@ -95,9 +96,25 @@ bool Transport::Create(uint32 guidlow, uint32 entry, uint32 mapid, float x, floa
SetGoAnimProgress(animprogress);
SetName(goinfo->name);
UpdateRotationFields(0.0f, 1.0f);
+
+ m_model = GameObjectModel::Create(*this);
return true;
}
+void Transport::CleanupsBeforeDelete(bool finalCleanup /*= true*/)
+{
+ UnloadStaticPassengers();
+ while (!_passengers.empty())
+ {
+ WorldObject* obj = *_passengers.begin();
+ obj->m_movementInfo.transport.Reset();
+ obj->SetTransport(NULL);
+ RemovePassenger(obj);
+ }
+
+ GameObject::CleanupsBeforeDelete(finalCleanup);
+}
+
void Transport::Update(uint32 diff)
{
uint32 const positionUpdateDelay = 200;
@@ -202,6 +219,9 @@ void Transport::Update(uint32 diff)
void Transport::AddPassenger(WorldObject* passenger)
{
+ if (!IsInWorld())
+ return;
+
if (_passengers.insert(passenger).second)
{
TC_LOG_DEBUG("entities.transport", "Object %s boarded transport %s.", passenger->GetName().c_str(), GetName().c_str());
@@ -307,11 +327,134 @@ GameObject* Transport::CreateGOPassenger(uint32 guid, GameObjectData const* data
return go;
}
+TempSummon* Transport::SummonPassenger(uint32 entry, Position const& pos, TempSummonType summonType, SummonPropertiesEntry const* properties /*= NULL*/, uint32 duration /*= 0*/, Unit* summoner /*= NULL*/, uint32 spellId /*= 0*/, uint32 vehId /*= 0*/)
+{
+ Map* map = FindMap();
+ if (!map)
+ return NULL;
+
+ uint32 mask = UNIT_MASK_SUMMON;
+ if (properties)
+ {
+ switch (properties->Category)
+ {
+ case SUMMON_CATEGORY_PET:
+ mask = UNIT_MASK_GUARDIAN;
+ break;
+ case SUMMON_CATEGORY_PUPPET:
+ mask = UNIT_MASK_PUPPET;
+ break;
+ case SUMMON_CATEGORY_VEHICLE:
+ mask = UNIT_MASK_MINION;
+ break;
+ case SUMMON_CATEGORY_WILD:
+ case SUMMON_CATEGORY_ALLY:
+ case SUMMON_CATEGORY_UNK:
+ {
+ switch (properties->Type)
+ {
+ case SUMMON_TYPE_MINION:
+ case SUMMON_TYPE_GUARDIAN:
+ case SUMMON_TYPE_GUARDIAN2:
+ mask = UNIT_MASK_GUARDIAN;
+ break;
+ case SUMMON_TYPE_TOTEM:
+ case SUMMON_TYPE_LIGHTWELL:
+ mask = UNIT_MASK_TOTEM;
+ break;
+ case SUMMON_TYPE_VEHICLE:
+ case SUMMON_TYPE_VEHICLE2:
+ mask = UNIT_MASK_SUMMON;
+ break;
+ case SUMMON_TYPE_MINIPET:
+ mask = UNIT_MASK_MINION;
+ break;
+ default:
+ if (properties->Flags & 512) // Mirror Image, Summon Gargoyle
+ mask = UNIT_MASK_GUARDIAN;
+ break;
+ }
+ break;
+ }
+ default:
+ return NULL;
+ }
+ }
+
+ uint32 phase = PHASEMASK_NORMAL;
+ uint32 team = 0;
+ if (summoner)
+ {
+ phase = summoner->GetPhaseMask();
+ if (summoner->GetTypeId() == TYPEID_PLAYER)
+ team = summoner->ToPlayer()->GetTeam();
+ }
+
+ TempSummon* summon = NULL;
+ switch (mask)
+ {
+ case UNIT_MASK_SUMMON:
+ summon = new TempSummon(properties, summoner, false);
+ break;
+ case UNIT_MASK_GUARDIAN:
+ summon = new Guardian(properties, summoner, false);
+ break;
+ case UNIT_MASK_PUPPET:
+ summon = new Puppet(properties, summoner);
+ break;
+ case UNIT_MASK_TOTEM:
+ summon = new Totem(properties, summoner);
+ break;
+ case UNIT_MASK_MINION:
+ summon = new Minion(properties, summoner, false);
+ break;
+ }
+
+ float x, y, z, o;
+ pos.GetPosition(x, y, z, o);
+ CalculatePassengerPosition(x, y, z, &o);
+
+ if (!summon->Create(sObjectMgr->GenerateLowGuid(HIGHGUID_UNIT), map, phase, entry, vehId, team, x, y, z, o))
+ {
+ delete summon;
+ return NULL;
+ }
+
+ summon->SetUInt32Value(UNIT_CREATED_BY_SPELL, spellId);
+
+ summon->SetTransport(this);
+ summon->m_movementInfo.transport.guid = GetGUID();
+ summon->m_movementInfo.transport.pos.Relocate(pos);
+ summon->Relocate(x, y, z, o);
+ summon->SetHomePosition(x, y, z, o);
+ summon->SetTransportHomePosition(pos);
+
+ /// @HACK - transport models are not added to map's dynamic LoS calculations
+ /// because the current GameObjectModel cannot be moved without recreating
+ summon->AddUnitState(UNIT_STATE_IGNORE_PATHFINDING);
+
+ summon->InitStats(duration);
+
+ if (!map->AddToMap<Creature>(summon))
+ {
+ delete summon;
+ return NULL;
+ }
+
+ _staticPassengers.insert(summon);
+
+ summon->InitSummon();
+ summon->SetTempSummonType(summonType);
+
+ return summon;
+}
+
void Transport::UpdatePosition(float x, float y, float z, float o)
{
bool newActive = GetMap()->IsGridLoaded(x, y);
Relocate(x, y, z, o);
+ UpdateModelPosition();
UpdatePassengerPositions(_passengers);
@@ -449,12 +592,16 @@ bool Transport::TeleportTransport(uint32 newMapid, float x, float y, float z, fl
if (!obj->ToPlayer()->TeleportTo(newMapid, destX, destY, destZ, destO, TELE_TO_NOT_LEAVE_TRANSPORT))
_passengers.erase(obj);
break;
+ case TYPEID_DYNAMICOBJECT:
+ obj->AddObjectToRemoveList();
+ break;
default:
break;
}
}
Relocate(x, y, z, o);
+ UpdateModelPosition();
GetMap()->AddToMap<Transport>(this);
return true;
}
@@ -517,6 +664,9 @@ void Transport::UpdatePassengerPositions(std::set<WorldObject*>& passengers)
case TYPEID_GAMEOBJECT:
GetMap()->GameObjectRelocation(passenger->ToGameObject(), x, y, z, o, false);
break;
+ case TYPEID_DYNAMICOBJECT:
+ GetMap()->DynamicObjectRelocation(passenger->ToDynObject(), x, y, z, o);
+ break;
default:
break;
}
diff --git a/src/server/game/Entities/Transport/Transport.h b/src/server/game/Entities/Transport/Transport.h
index 55f4daddecc..398356c4980 100644
--- a/src/server/game/Entities/Transport/Transport.h
+++ b/src/server/game/Entities/Transport/Transport.h
@@ -34,9 +34,11 @@ class Transport : public GameObject, public TransportBase
~Transport();
bool Create(uint32 guidlow, uint32 entry, uint32 mapid, float x, float y, float z, float ang, uint32 animprogress);
- void Update(uint32 diff);
+ void CleanupsBeforeDelete(bool finalCleanup = true) OVERRIDE;
- void BuildUpdate(UpdateDataMapType& data_map);
+ void Update(uint32 diff) OVERRIDE;
+
+ void BuildUpdate(UpdateDataMapType& data_map) OVERRIDE;
void AddPassenger(WorldObject* passenger);
void RemovePassenger(WorldObject* passenger);
@@ -45,14 +47,32 @@ class Transport : public GameObject, public TransportBase
Creature* CreateNPCPassenger(uint32 guid, CreatureData const* data);
GameObject* CreateGOPassenger(uint32 guid, GameObjectData const* data);
+ /**
+ * @fn bool Transport::SummonPassenger(uint64, Position const&, TempSummonType, SummonPropertiesEntry const*, uint32, Unit*, uint32, uint32)
+ *
+ * @brief Temporarily summons a creature as passenger on this transport.
+ *
+ * @param entry Id of the creature from creature_template table
+ * @param pos Initial position of the creature (transport offsets)
+ * @param summonType
+ * @param properties
+ * @param duration Determines how long the creauture will exist in world depending on @summonType (in milliseconds)
+ * @param summoner Summoner of the creature (for AI purposes)
+ * @param spellId
+ * @param vehId If set, this value overrides vehicle id from creature_template that the creature will use
+ *
+ * @return Summoned creature.
+ */
+ TempSummon* SummonPassenger(uint32 entry, Position const& pos, TempSummonType summonType, SummonPropertiesEntry const* properties = NULL, uint32 duration = 0, Unit* summoner = NULL, uint32 spellId = 0, uint32 vehId = 0);
+
/// This method transforms supplied transport offsets into global coordinates
- void CalculatePassengerPosition(float& x, float& y, float& z, float* o /*= NULL*/) const
+ void CalculatePassengerPosition(float& x, float& y, float& z, float* o = NULL) const OVERRIDE
{
TransportBase::CalculatePassengerPosition(x, y, z, o, GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation());
}
/// This method transforms supplied global coordinates into local offsets
- void CalculatePassengerOffset(float& x, float& y, float& z, float* o /*= NULL*/) const
+ void CalculatePassengerOffset(float& x, float& y, float& z, float* o = NULL) const OVERRIDE
{
TransportBase::CalculatePassengerOffset(x, y, z, o, GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation());
}
diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp
index 7ff01c84ff9..fe69e3f08b1 100644
--- a/src/server/game/Entities/Unit/Unit.cpp
+++ b/src/server/game/Entities/Unit/Unit.cpp
@@ -4255,10 +4255,13 @@ uint32 Unit::GetDoTsByCaster(uint64 casterGUID) const
int32 Unit::GetTotalAuraModifier(AuraType auratype) const
{
+ AuraEffectList const& mTotalAuraList = GetAuraEffectsByType(auratype);
+ if (mTotalAuraList.empty())
+ return 0;
+
std::map<SpellGroup, int32> SameEffectSpellGroup;
int32 modifier = 0;
- AuraEffectList const& mTotalAuraList = GetAuraEffectsByType(auratype);
for (AuraEffectList::const_iterator i = mTotalAuraList.begin(); i != mTotalAuraList.end(); ++i)
if (!sSpellMgr->AddSameEffectStackRuleSpellGroups((*i)->GetSpellInfo(), (*i)->GetAmount(), SameEffectSpellGroup))
modifier += (*i)->GetAmount();
@@ -7280,7 +7283,7 @@ ReputationRank Unit::GetReactionTo(Unit const* target) const
Player const* targetPlayerOwner = target->GetAffectingPlayer();
// check forced reputation to support SPELL_AURA_FORCE_REACTION
- if (selfPlayerOwner)
+ if (selfPlayerOwner)
{
if (FactionTemplateEntry const* targetFactionTemplateEntry = target->GetFactionTemplateEntry())
if (ReputationRank const* repRank = selfPlayerOwner->GetReputationMgr().GetForcedRankIfAny(targetFactionTemplateEntry))
@@ -11245,6 +11248,8 @@ float Unit::GetSpellMaxRangeForTarget(Unit const* target, SpellInfo const* spell
return 0;
if (spellInfo->RangeEntry->maxRangeFriend == spellInfo->RangeEntry->maxRangeHostile)
return spellInfo->GetMaxRange();
+ if (target == NULL)
+ return spellInfo->GetMaxRange(true);
return spellInfo->GetMaxRange(!IsHostileTo(target));
}
@@ -13486,8 +13491,10 @@ void Unit::Kill(Unit* victim, bool durabilityLoss)
data << uint64(victim->GetGUID()); // victim
Player* looter = player;
+ Group* group = player->GetGroup();
+ bool hasLooterGuid = false;
- if (Group* group = player->GetGroup())
+ if (group)
{
group->BroadcastPacket(&data, group->GetMemberGroup(player->GetGUID()));
@@ -13499,16 +13506,10 @@ void Unit::Kill(Unit* victim, bool durabilityLoss)
looter = ObjectAccessor::FindPlayer(group->GetLooterGuid());
if (looter)
{
+ hasLooterGuid = true;
creature->SetLootRecipient(looter); // update creature loot recipient to the allowed looter.
- group->SendLooter(creature, looter);
}
- else
- group->SendLooter(creature, NULL);
}
- else
- group->SendLooter(creature, NULL);
-
- group->UpdateLooterGuid(creature);
}
}
else
@@ -13525,6 +13526,7 @@ void Unit::Kill(Unit* victim, bool durabilityLoss)
}
}
+ // Generate loot before updating looter
if (creature)
{
Loot* loot = &creature->loot;
@@ -13536,6 +13538,18 @@ void Unit::Kill(Unit* victim, bool durabilityLoss)
loot->FillLoot(lootid, LootTemplates_Creature, looter, false, false, creature->GetLootMode());
loot->generateMoneyLoot(creature->GetCreatureTemplate()->mingold, creature->GetCreatureTemplate()->maxgold);
+
+ if (group)
+ {
+ if (hasLooterGuid)
+ group->SendLooter(creature, looter);
+ else
+ group->SendLooter(creature, NULL);
+
+ // Update round robin looter only if the creature had loot
+ if (!creature->loot.empty())
+ group->UpdateLooterGuid(creature);
+ }
}
player->RewardPlayerAndGroupAtKill(victim, false);
@@ -15938,7 +15952,12 @@ bool CharmInfo::IsCommandFollow()
void CharmInfo::SaveStayPosition()
{
//! At this point a new spline destination is enabled because of Unit::StopMoving()
- G3D::Vector3 const stayPos = _unit->movespline->FinalDestination();
+ G3D::Vector3 stayPos = _unit->movespline->FinalDestination();
+
+ if (_unit->movespline->onTransport)
+ if (TransportBase* transport = _unit->GetDirectTransport())
+ transport->CalculatePassengerPosition(stayPos.x, stayPos.y, stayPos.z);
+
_stayX = stayPos.x;
_stayY = stayPos.y;
_stayZ = stayPos.z;
@@ -16413,3 +16432,24 @@ void Unit::BuildValuesUpdate(uint8 updateType, ByteBuffer* data, Player* target)
updateMask.AppendToPacket(data);
data->append(fieldBuffer);
}
+
+void Unit::BuildCooldownPacket(WorldPacket& data, uint8 flags, uint32 spellId, uint32 cooldown)
+{
+ data.Initialize(SMSG_SPELL_COOLDOWN, 8 + 1 + 4 + 4);
+ data << uint64(GetGUID());
+ data << uint8(flags);
+ data << uint32(spellId);
+ data << uint32(cooldown);
+}
+
+void Unit::BuildCooldownPacket(WorldPacket& data, uint8 flags, PacketCooldowns const& cooldowns)
+{
+ data.Initialize(SMSG_SPELL_COOLDOWN, 8 + 1 + (4 + 4) * cooldowns.size());
+ data << uint64(GetGUID());
+ data << uint8(flags);
+ for (UNORDERED_MAP<uint32, uint32>::const_iterator itr = cooldowns.begin(); itr != cooldowns.end(); ++itr)
+ {
+ data << uint32(itr->first);
+ data << uint32(itr->second);
+ }
+}
diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h
index ac892373a86..0225975223a 100644
--- a/src/server/game/Entities/Unit/Unit.h
+++ b/src/server/game/Entities/Unit/Unit.h
@@ -524,6 +524,12 @@ enum UnitState
UNIT_STATE_CHASE_MOVE = 0x04000000,
UNIT_STATE_FOLLOW_MOVE = 0x08000000,
UNIT_STATE_IGNORE_PATHFINDING = 0x10000000, // do not use pathfinding in any MovementGenerator
+ UNIT_STATE_ALL_STATE_SUPPORTED = UNIT_STATE_DIED | UNIT_STATE_MELEE_ATTACKING | UNIT_STATE_STUNNED | UNIT_STATE_ROAMING | UNIT_STATE_CHASE
+ | UNIT_STATE_FLEEING | UNIT_STATE_IN_FLIGHT | UNIT_STATE_FOLLOW | UNIT_STATE_ROOT | UNIT_STATE_CONFUSED
+ | UNIT_STATE_DISTRACTED | UNIT_STATE_ISOLATED | UNIT_STATE_ATTACK_PLAYER | UNIT_STATE_CASTING
+ | UNIT_STATE_POSSESSED | UNIT_STATE_CHARGING | UNIT_STATE_JUMPING | UNIT_STATE_MOVE | UNIT_STATE_ROTATING
+ | UNIT_STATE_EVADE | UNIT_STATE_ROAMING_MOVE | UNIT_STATE_CONFUSED_MOVE | UNIT_STATE_FLEEING_MOVE
+ | UNIT_STATE_CHASE_MOVE | UNIT_STATE_FOLLOW_MOVE | UNIT_STATE_IGNORE_PATHFINDING,
UNIT_STATE_UNATTACKABLE = UNIT_STATE_IN_FLIGHT,
// for real move using movegen check and stop (except unstoppable flight)
UNIT_STATE_MOVING = UNIT_STATE_ROAMING_MOVE | UNIT_STATE_CONFUSED_MOVE | UNIT_STATE_FLEEING_MOVE | UNIT_STATE_CHASE_MOVE | UNIT_STATE_FOLLOW_MOVE,
@@ -1250,6 +1256,16 @@ enum PlayerTotemType
SUMMON_TYPE_TOTEM_AIR = 83
};
+/// Spell cooldown flags sent in SMSG_SPELL_COOLDOWN
+enum SpellCooldownFlags
+{
+ SPELL_COOLDOWN_FLAG_NONE = 0x0,
+ SPELL_COOLDOWN_FLAG_INCLUDE_GCD = 0x1, ///< Starts GCD in addition to normal cooldown specified in the packet
+ SPELL_COOLDOWN_FLAG_INCLUDE_EVENT_COOLDOWNS = 0x2 ///< Starts GCD for spells that should start their cooldown on events, requires SPELL_COOLDOWN_FLAG_INCLUDE_GCD set
+};
+
+typedef UNORDERED_MAP<uint32, uint32> PacketCooldowns;
+
// delay time next attack to prevent client attack animation problems
#define ATTACK_DISPLAY_DELAY 200
#define MAX_PLAYER_STEALTH_DETECT_RANGE 30.0f // max distance for detection targets by player
@@ -1574,6 +1590,8 @@ class Unit : public WorldObject
Aura* AddAura(SpellInfo const* spellInfo, uint8 effMask, Unit* target);
void SetAuraStack(uint32 spellId, Unit* target, uint32 stack);
void SendPlaySpellVisualKit(uint32 id, uint32 unkParam);
+ void BuildCooldownPacket(WorldPacket& data, uint8 flags, uint32 spellId, uint32 cooldown);
+ void BuildCooldownPacket(WorldPacket& data, uint8 flags, PacketCooldowns const& cooldowns);
void DeMorph();
diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp
index 2936c31e1ae..47b9e840504 100644
--- a/src/server/game/Globals/ObjectMgr.cpp
+++ b/src/server/game/Globals/ObjectMgr.cpp
@@ -20,6 +20,7 @@
#include "AchievementMgr.h"
#include "ArenaTeam.h"
#include "ArenaTeamMgr.h"
+#include "BattlegroundMgr.h"
#include "Chat.h"
#include "Common.h"
#include "DatabaseEnv.h"
@@ -1112,6 +1113,11 @@ void ObjectMgr::LoadEquipmentTemplates()
}
uint8 id = fields[1].GetUInt8();
+ if (!id)
+ {
+ TC_LOG_ERROR("sql.sql", "Creature equipment template with id 0 found for creature %u, skipped.", entry);
+ continue;
+ }
EquipmentInfo& equipmentInfo = _equipmentInfoStore[entry][id];
@@ -1462,6 +1468,7 @@ bool ObjectMgr::SetCreatureLinkedRespawn(uint32 guidLow, uint32 linkedGuidLow)
return false;
const CreatureData* master = GetCreatureData(guidLow);
+ ASSERT(master);
uint64 guid = MAKE_NEW_GUID(guidLow, master->id, HIGHGUID_UNIT);
if (!linkedGuidLow) // we're removing the linking
@@ -7518,6 +7525,10 @@ void ObjectMgr::LoadGameObjectForQuests()
{
switch (itr->second.type)
{
+ case GAMEOBJECT_TYPE_QUESTGIVER:
+ _gameObjectForQuestStore.insert(itr->second.entry);
+ ++count;
+ break;
case GAMEOBJECT_TYPE_CHEST:
{
// scan GO chest with loot including quest items
diff --git a/src/server/game/Groups/Group.cpp b/src/server/game/Groups/Group.cpp
index be18852ce8e..f0ddd714657 100644
--- a/src/server/game/Groups/Group.cpp
+++ b/src/server/game/Groups/Group.cpp
@@ -56,7 +56,7 @@ Loot* Roll::getLoot()
Group::Group() : m_leaderGuid(0), m_leaderName(""), m_groupType(GROUPTYPE_NORMAL),
m_dungeonDifficulty(DUNGEON_DIFFICULTY_NORMAL), m_raidDifficulty(RAID_DIFFICULTY_10MAN_NORMAL),
m_bgGroup(NULL), m_bfGroup(NULL), m_lootMethod(FREE_FOR_ALL), m_lootThreshold(ITEM_QUALITY_UNCOMMON), m_looterGuid(0),
-m_subGroupsCounts(NULL), m_guid(0), m_counter(0), m_maxEnchantingLevel(0), m_dbStoreId(0)
+m_masterLooterGuid(0), m_subGroupsCounts(NULL), m_guid(0), m_counter(0), m_maxEnchantingLevel(0), m_dbStoreId(0)
{
for (uint8 i = 0; i < TARGETICONCOUNT; ++i)
m_targetIcons[i] = 0;
@@ -112,6 +112,7 @@ bool Group::Create(Player* leader)
m_lootThreshold = ITEM_QUALITY_UNCOMMON;
m_looterGuid = leaderGuid;
+ m_masterLooterGuid = 0;
m_dungeonDifficulty = DUNGEON_DIFFICULTY_NORMAL;
m_raidDifficulty = RAID_DIFFICULTY_10MAN_NORMAL;
@@ -146,6 +147,7 @@ bool Group::Create(Player* leader)
stmt->setUInt8(index++, uint8(m_groupType));
stmt->setUInt32(index++, uint8(m_dungeonDifficulty));
stmt->setUInt32(index++, uint8(m_raidDifficulty));
+ stmt->setUInt32(index++, GUID_LOPART(m_masterLooterGuid));
CharacterDatabase.Execute(stmt);
@@ -162,7 +164,7 @@ bool Group::Create(Player* leader)
void Group::LoadGroupFromDB(Field* fields)
{
- m_dbStoreId = fields[15].GetUInt32();
+ m_dbStoreId = fields[16].GetUInt32();
m_guid = MAKE_NEW_GUID(sGroupMgr->GenerateGroupId(), 0, HIGHGUID_GROUP);
m_leaderGuid = MAKE_NEW_GUID(fields[0].GetUInt32(), 0, HIGHGUID_PLAYER);
@@ -193,6 +195,8 @@ void Group::LoadGroupFromDB(Field* fields)
else
m_raidDifficulty = Difficulty(r_diff);
+ m_masterLooterGuid = MAKE_NEW_GUID(fields[15].GetUInt32(), 0, HIGHGUID_PLAYER);
+
if (m_groupType & GROUPTYPE_LFG)
sLFGMgr->_LoadFromDB(fields, GetGUID());
}
@@ -923,7 +927,11 @@ void Group::SendLooter(Creature* creature, Player* groupLooter)
WorldPacket data(SMSG_LOOT_LIST, (8+8));
data << uint64(creature->GetGUID());
- data << uint8(0); // unk1
+
+ if (GetLootMethod() == MASTER_LOOT && creature->loot.hasOverThresholdItem())
+ data.appendPackGUID(GetMasterLooterGuid());
+ else
+ data << uint8(0);
if (groupLooter)
data.append(groupLooter->GetPackGUID());
@@ -1229,10 +1237,26 @@ void Group::NeedBeforeGreed(Loot* loot, WorldObject* lootedObject)
}
}
-void Group::MasterLoot(Loot* /*loot*/, WorldObject* pLootedObject)
+void Group::MasterLoot(Loot* loot, WorldObject* pLootedObject)
{
TC_LOG_DEBUG("network", "Group::MasterLoot (SMSG_LOOT_MASTER_LIST)");
+ for (std::vector<LootItem>::iterator i = loot->items.begin(); i != loot->items.end(); ++i)
+ {
+ if (i->freeforall)
+ continue;
+
+ i->is_blocked = !i->is_underthreshold;
+ }
+
+ for (std::vector<LootItem>::iterator i = loot->quest_items.begin(); i != loot->quest_items.end(); ++i)
+ {
+ if (!i->follow_loot_rules)
+ continue;
+
+ i->is_blocked = !i->is_underthreshold;
+ }
+
uint32 real_count = 0;
WorldPacket data(SMSG_LOOT_MASTER_LIST, 1 + GetMembersCount() * 8);
@@ -1539,7 +1563,7 @@ void Group::SendUpdateToPlayer(uint64 playerGUID, MemberSlot* slot)
Player* member = ObjectAccessor::FindPlayer(citr->guid);
- uint8 onlineState = member ? MEMBER_STATUS_ONLINE : MEMBER_STATUS_OFFLINE;
+ uint8 onlineState = (member && !member->GetSession()->PlayerLogout()) ? MEMBER_STATUS_ONLINE : MEMBER_STATUS_OFFLINE;
onlineState = onlineState | ((isBGGroup() || isBFGroup()) ? MEMBER_STATUS_PVP : 0);
data << citr->name;
@@ -1555,7 +1579,12 @@ void Group::SendUpdateToPlayer(uint64 playerGUID, MemberSlot* slot)
if (GetMembersCount() - 1)
{
data << uint8(m_lootMethod); // loot method
- data << uint64(m_looterGuid); // looter guid
+
+ if (m_lootMethod == MASTER_LOOT)
+ data << uint64(m_masterLooterGuid); // master looter guid
+ else
+ data << uint64(0);
+
data << uint8(m_lootThreshold); // loot threshold
data << uint8(m_dungeonDifficulty); // Dungeon Difficulty
data << uint8(m_raidDifficulty); // Raid Difficulty
@@ -1725,7 +1754,7 @@ void Group::ChangeMembersGroup(uint64 guid, uint8 group)
// Retrieve the next Round-Roubin player for the group
//
-// No update done if loot method is Master or FFA.
+// No update done if loot method is FFA.
//
// If the RR player is not yet set for the group, the first group member becomes the round-robin player.
// If the RR player is set, the next player in group becomes the round-robin player.
@@ -1736,16 +1765,10 @@ void Group::ChangeMembersGroup(uint64 guid, uint8 group)
// if not, he loses his turn.
void Group::UpdateLooterGuid(WorldObject* pLootedObject, bool ifneed)
{
- switch (GetLootMethod())
- {
- case MASTER_LOOT:
- case FREE_FOR_ALL:
- return;
- default:
- // round robin style looting applies for all low
- // quality items in each loot method except free for all and master loot
- break;
- }
+ // round robin style looting applies for all low
+ // quality items in each loot method except free for all
+ if (GetLootMethod() == FREE_FOR_ALL)
+ return;
uint64 oldLooterGUID = GetLooterGuid();
member_citerator guid_itr = _getMemberCSlot(oldLooterGUID);
@@ -2169,6 +2192,11 @@ void Group::SetLooterGuid(uint64 guid)
m_looterGuid = guid;
}
+void Group::SetMasterLooterGuid(uint64 guid)
+{
+ m_masterLooterGuid = guid;
+}
+
void Group::SetLootThreshold(ItemQualities threshold)
{
m_lootThreshold = threshold;
@@ -2244,6 +2272,11 @@ uint64 Group::GetLooterGuid() const
return m_looterGuid;
}
+uint64 Group::GetMasterLooterGuid() const
+{
+ return m_masterLooterGuid;
+}
+
ItemQualities Group::GetLootThreshold() const
{
return m_lootThreshold;
diff --git a/src/server/game/Groups/Group.h b/src/server/game/Groups/Group.h
index d2981cf56bd..daaa849faef 100644
--- a/src/server/game/Groups/Group.h
+++ b/src/server/game/Groups/Group.h
@@ -199,6 +199,7 @@ class Group
void ChangeLeader(uint64 guid);
void SetLootMethod(LootMethod method);
void SetLooterGuid(uint64 guid);
+ void SetMasterLooterGuid(uint64 guid);
void UpdateLooterGuid(WorldObject* pLootedObject, bool ifneed = false);
void SetLootThreshold(ItemQualities threshold);
void Disband(bool hideDestroy=false);
@@ -217,6 +218,7 @@ class Group
const char * GetLeaderName() const;
LootMethod GetLootMethod() const;
uint64 GetLooterGuid() const;
+ uint64 GetMasterLooterGuid() const;
ItemQualities GetLootThreshold() const;
uint32 GetDbStoreId() const { return m_dbStoreId; };
@@ -338,6 +340,7 @@ class Group
LootMethod m_lootMethod;
ItemQualities m_lootThreshold;
uint64 m_looterGuid;
+ uint64 m_masterLooterGuid;
Rolls RollId;
BoundInstancesMap m_boundInstances[MAX_DIFFICULTY];
uint8* m_subGroupsCounts;
diff --git a/src/server/game/Groups/GroupMgr.cpp b/src/server/game/Groups/GroupMgr.cpp
index 20e6a0671a5..39735f5dce3 100644
--- a/src/server/game/Groups/GroupMgr.cpp
+++ b/src/server/game/Groups/GroupMgr.cpp
@@ -123,8 +123,8 @@ void GroupMgr::LoadGroups()
// 0 1 2 3 4 5 6 7 8 9
QueryResult result = CharacterDatabase.Query("SELECT g.leaderGuid, g.lootMethod, g.looterGuid, g.lootThreshold, g.icon1, g.icon2, g.icon3, g.icon4, g.icon5, g.icon6"
- // 10 11 12 13 14 15 16 17
- ", g.icon7, g.icon8, g.groupType, g.difficulty, g.raiddifficulty, g.guid, lfg.dungeon, lfg.state FROM groups g LEFT JOIN lfg_data lfg ON lfg.guid = g.guid ORDER BY g.guid ASC");
+ // 10 11 12 13 14 15 16 17 18
+ ", g.icon7, g.icon8, g.groupType, g.difficulty, g.raiddifficulty, g.masterLooterGuid, g.guid, lfg.dungeon, lfg.state FROM groups g LEFT JOIN lfg_data lfg ON lfg.guid = g.guid ORDER BY g.guid ASC");
if (!result)
{
TC_LOG_INFO("server.loading", ">> Loaded 0 group definitions. DB table `groups` is empty!");
diff --git a/src/server/game/Handlers/AddonHandler.cpp b/src/server/game/Handlers/AddonHandler.cpp
index 3c9a66bedb5..806cbd1c7fc 100644
--- a/src/server/game/Handlers/AddonHandler.cpp
+++ b/src/server/game/Handlers/AddonHandler.cpp
@@ -49,7 +49,7 @@ bool AddonHandler::BuildAddonPacket(WorldPacket* source, WorldPacket* target)
AddOnPacked.resize(AddonRealSize); // resize target for zlib action
- if (!uncompress(AddOnPacked.contents(), &AddonRealSize, source->contents() + CurrentPosition, source->size() - CurrentPosition)!= Z_OK)
+ if (uncompress(AddOnPacked.contents(), &AddonRealSize, source->contents() + CurrentPosition, source->size() - CurrentPosition) == Z_OK)
{
target->Initialize(SMSG_ADDON_INFO);
diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp
index e81506678ef..36e2d71555b 100644
--- a/src/server/game/Handlers/CharacterHandler.cpp
+++ b/src/server/game/Handlers/CharacterHandler.cpp
@@ -92,19 +92,19 @@ bool LoginQueryHolder::Initialize()
stmt->setUInt32(0, lowGuid);
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_QUEST_STATUS, stmt);
- stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_DAILYQUESTSTATUS);
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_QUESTSTATUS_DAILY);
stmt->setUInt32(0, lowGuid);
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_DAILY_QUEST_STATUS, stmt);
- stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_WEEKLYQUESTSTATUS);
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_QUESTSTATUS_WEEKLY);
stmt->setUInt32(0, lowGuid);
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_WEEKLY_QUEST_STATUS, stmt);
- stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_MONTHLYQUESTSTATUS);
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_QUESTSTATUS_MONTHLY);
stmt->setUInt32(0, lowGuid);
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_MONTHLY_QUEST_STATUS, stmt);
- stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_SEASONALQUESTSTATUS);
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_QUESTSTATUS_SEASONAL);
stmt->setUInt32(0, lowGuid);
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_SEASONAL_QUEST_STATUS, stmt);
@@ -1141,6 +1141,9 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder* holder)
m_playerLoading = false;
+ // Handle Login-Achievements (should be handled after loading)
+ _player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_ON_LOGIN, 1);
+
sScriptMgr->OnPlayerLogin(pCurrChar);
delete holder;
}
diff --git a/src/server/game/Handlers/GroupHandler.cpp b/src/server/game/Handlers/GroupHandler.cpp
index 8fb3935e74c..dcaa2727b5e 100644
--- a/src/server/game/Handlers/GroupHandler.cpp
+++ b/src/server/game/Handlers/GroupHandler.cpp
@@ -626,11 +626,20 @@ void WorldSession::HandleLootMethodOpcode(WorldPacket& recvData)
/** error handling **/
if (!group->IsLeader(GetPlayer()->GetGUID()))
return;
+
+ if (lootMethod > NEED_BEFORE_GREED)
+ return;
+
+ if (lootThreshold < ITEM_QUALITY_UNCOMMON || lootThreshold > ITEM_QUALITY_ARTIFACT)
+ return;
+
+ if (lootMethod == MASTER_LOOT && !group->IsMember(lootMaster))
+ return;
/********************/
// everything's fine, do it
group->SetLootMethod((LootMethod)lootMethod);
- group->SetLooterGuid(lootMaster);
+ group->SetMasterLooterGuid(lootMaster);
group->SetLootThreshold((ItemQualities)lootThreshold);
group->SendUpdate();
}
@@ -733,11 +742,20 @@ void WorldSession::HandleRaidTargetUpdateOpcode(WorldPacket& recvData)
group->SendTargetIconList(this);
else // target icon update
{
- if (!group->IsLeader(GetPlayer()->GetGUID()) && !group->IsAssistant(GetPlayer()->GetGUID()))
+ if (group->isRaidGroup() && !group->IsLeader(GetPlayer()->GetGUID()) && !group->IsAssistant(GetPlayer()->GetGUID()))
return;
uint64 guid;
recvData >> guid;
+
+ if (IS_PLAYER_GUID(guid))
+ {
+ Player* target = ObjectAccessor::FindPlayer(guid);
+
+ if (!target || target->IsHostileTo(GetPlayer()))
+ return;
+ }
+
group->SetTargetIcon(x, _player->GetGUID(), guid);
}
}
diff --git a/src/server/game/Handlers/ItemHandler.cpp b/src/server/game/Handlers/ItemHandler.cpp
index 323d5f5e489..b72d79539aa 100644
--- a/src/server/game/Handlers/ItemHandler.cpp
+++ b/src/server/game/Handlers/ItemHandler.cpp
@@ -1068,6 +1068,11 @@ void WorldSession::HandleSocketOpcode(WorldPacket& recvData)
for (int i = 0; i < MAX_GEM_SOCKETS; ++i) //get geminfo from dbc storage
GemProps[i] = (Gems[i]) ? sGemPropertiesStore.LookupEntry(Gems[i]->GetTemplate()->GemProperties) : NULL;
+ // Find first prismatic socket
+ int32 firstPrismatic = 0;
+ while (firstPrismatic < MAX_GEM_SOCKETS && itemProto->Socket[firstPrismatic].Color)
+ ++firstPrismatic;
+
for (int i = 0; i < MAX_GEM_SOCKETS; ++i) //check for hack maybe
{
if (!GemProps[i])
@@ -1080,11 +1085,8 @@ void WorldSession::HandleSocketOpcode(WorldPacket& recvData)
if (!itemTarget->GetEnchantmentId(PRISMATIC_ENCHANTMENT_SLOT))
return;
- // not first not-colored (not normaly used) socket
- if (i != 0 && !itemProto->Socket[i-1].Color && (i+1 >= MAX_GEM_SOCKETS || itemProto->Socket[i+1].Color))
+ if (i != firstPrismatic)
return;
-
- // ok, this is first not colored socket for item with prismatic socket
}
// tried to put normal gem in meta socket
diff --git a/src/server/game/Handlers/LootHandler.cpp b/src/server/game/Handlers/LootHandler.cpp
index 72d3fd5a746..b1132efd6a1 100644
--- a/src/server/game/Handlers/LootHandler.cpp
+++ b/src/server/game/Handlers/LootHandler.cpp
@@ -306,17 +306,7 @@ void WorldSession::DoLootRelease(uint64 lguid)
// if the round robin player release, reset it.
if (player->GetGUID() == loot->roundRobinPlayer)
- {
- if (Group* group = player->GetGroup())
- {
- if (group->GetLootMethod() != MASTER_LOOT)
- {
- loot->roundRobinPlayer = 0;
- }
- }
- else
- loot->roundRobinPlayer = 0;
- }
+ loot->roundRobinPlayer = 0;
}
}
else if (IS_CORPSE_GUID(lguid)) // ONLY remove insignia at BG
@@ -385,19 +375,15 @@ void WorldSession::DoLootRelease(uint64 lguid)
// if the round robin player release, reset it.
if (player->GetGUID() == loot->roundRobinPlayer)
{
+ loot->roundRobinPlayer = 0;
+
if (Group* group = player->GetGroup())
{
- if (group->GetLootMethod() != MASTER_LOOT)
- {
- loot->roundRobinPlayer = 0;
- group->SendLooter(creature, NULL);
-
- // force update of dynamic flags, otherwise other group's players still not able to loot.
- creature->ForceValuesUpdateAtIndex(UNIT_DYNAMIC_FLAGS);
- }
+ group->SendLooter(creature, NULL);
+
+ // force update of dynamic flags, otherwise other group's players still not able to loot.
+ creature->ForceValuesUpdateAtIndex(UNIT_DYNAMIC_FLAGS);
}
- else
- loot->roundRobinPlayer = 0;
}
}
}
@@ -413,7 +399,7 @@ void WorldSession::HandleLootMasterGiveOpcode(WorldPacket& recvData)
recvData >> lootguid >> slotid >> target_playerguid;
- if (!_player->GetGroup() || _player->GetGroup()->GetLooterGuid() != _player->GetGUID())
+ if (!_player->GetGroup() || _player->GetGroup()->GetMasterLooterGuid() != _player->GetGUID() || _player->GetGroup()->GetLootMethod() != MASTER_LOOT)
{
_player->SendLootRelease(GetPlayer()->GetLootGUID());
return;
@@ -434,7 +420,6 @@ void WorldSession::HandleLootMasterGiveOpcode(WorldPacket& recvData)
return;
}
-
Loot* loot = NULL;
if (IS_CRE_OR_VEH_GUID(GetPlayer()->GetLootGUID()))
diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp
index b0514467667..c46656f7a53 100644
--- a/src/server/game/Handlers/MiscHandler.cpp
+++ b/src/server/game/Handlers/MiscHandler.cpp
@@ -98,9 +98,19 @@ void WorldSession::HandleGossipSelectOptionOpcode(WorldPacket& recvData)
recvData >> guid >> menuId >> gossipListId;
+ if (!_player->PlayerTalkClass->GetGossipMenu().GetItem(gossipListId))
+ {
+ recvData.rfinish();
+ return;
+ }
+
if (_player->PlayerTalkClass->IsGossipOptionCoded(gossipListId))
recvData >> code;
+ // Prevent cheating on C++ scripted menus
+ if (_player->PlayerTalkClass->GetGossipMenu().GetSenderGUID() != guid)
+ return;
+
Creature* unit = NULL;
GameObject* go = NULL;
if (IS_CRE_OR_VEH_GUID(guid))
@@ -152,7 +162,8 @@ void WorldSession::HandleGossipSelectOptionOpcode(WorldPacket& recvData)
else
{
go->AI()->GossipSelectCode(_player, menuId, gossipListId, code.c_str());
- sScriptMgr->OnGossipSelectCode(_player, go, _player->PlayerTalkClass->GetGossipOptionSender(gossipListId), _player->PlayerTalkClass->GetGossipOptionAction(gossipListId), code.c_str());
+ if (!sScriptMgr->OnGossipSelectCode(_player, go, _player->PlayerTalkClass->GetGossipOptionSender(gossipListId), _player->PlayerTalkClass->GetGossipOptionAction(gossipListId), code.c_str()))
+ _player->OnGossipSelect(go, gossipListId, menuId);
}
}
else
diff --git a/src/server/game/Handlers/PetHandler.cpp b/src/server/game/Handlers/PetHandler.cpp
index 78fbd318a11..1d33747d1e9 100644
--- a/src/server/game/Handlers/PetHandler.cpp
+++ b/src/server/game/Handlers/PetHandler.cpp
@@ -664,7 +664,7 @@ void WorldSession::HandlePetRename(WorldPacket& recvData)
stmt->setUInt32(0, pet->GetCharmInfo()->GetPetNumber());
trans->Append(stmt);
- stmt = CharacterDatabase.GetPreparedStatement(CHAR_ADD_CHAR_PET_DECLINEDNAME);
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHAR_PET_DECLINEDNAME);
stmt->setUInt32(0, _player->GetGUIDLow());
for (uint8 i = 0; i < 5; i++)
diff --git a/src/server/game/Handlers/QuestHandler.cpp b/src/server/game/Handlers/QuestHandler.cpp
index 7e7c8326a65..206238cbea2 100644
--- a/src/server/game/Handlers/QuestHandler.cpp
+++ b/src/server/game/Handlers/QuestHandler.cpp
@@ -38,38 +38,30 @@ void WorldSession::HandleQuestgiverStatusQueryOpcode(WorldPacket& recvData)
recvData >> guid;
uint32 questStatus = DIALOG_STATUS_NONE;
- Object* questgiver = ObjectAccessor::GetObjectByTypeMask(*_player, guid, TYPEMASK_UNIT|TYPEMASK_GAMEOBJECT);
- if (!questgiver)
+ Object* questGiver = ObjectAccessor::GetObjectByTypeMask(*_player, guid, TYPEMASK_UNIT | TYPEMASK_GAMEOBJECT);
+ if (!questGiver)
{
TC_LOG_INFO("network", "Error in CMSG_QUESTGIVER_STATUS_QUERY, called for non-existing questgiver (Typeid: %u GUID: %u)", GuidHigh2TypeId(GUID_HIPART(guid)), GUID_LOPART(guid));
return;
}
- switch (questgiver->GetTypeId())
+ switch (questGiver->GetTypeId())
{
case TYPEID_UNIT:
{
- TC_LOG_DEBUG("network", "WORLD: Received CMSG_QUESTGIVER_STATUS_QUERY for npc, guid = %u", questgiver->GetGUIDLow());
- Creature* cr_questgiver = questgiver->ToCreature();
- if (!cr_questgiver->IsHostileTo(_player)) // do not show quest status to enemies
- {
- questStatus = sScriptMgr->GetDialogStatus(_player, cr_questgiver);
- if (questStatus == DIALOG_STATUS_SCRIPTED_NO_STATUS)
- questStatus = getDialogStatus(_player, cr_questgiver);
- }
+ TC_LOG_DEBUG("network", "WORLD: Received CMSG_QUESTGIVER_STATUS_QUERY for npc, guid = %u", questGiver->GetGUIDLow());
+ if (!questGiver->ToCreature()->IsHostileTo(_player)) // do not show quest status to enemies
+ questStatus = _player->GetQuestDialogStatus(questGiver);
break;
}
case TYPEID_GAMEOBJECT:
{
- TC_LOG_DEBUG("network", "WORLD: Received CMSG_QUESTGIVER_STATUS_QUERY for GameObject guid = %u", questgiver->GetGUIDLow());
- GameObject* go_questgiver = questgiver->ToGameObject();
- questStatus = sScriptMgr->GetDialogStatus(_player, go_questgiver);
- if (questStatus == DIALOG_STATUS_SCRIPTED_NO_STATUS)
- questStatus = getDialogStatus(_player, go_questgiver);
+ TC_LOG_DEBUG("network", "WORLD: Received CMSG_QUESTGIVER_STATUS_QUERY for GameObject guid = %u", questGiver->GetGUIDLow());
+ questStatus = _player->GetQuestDialogStatus(questGiver);
break;
}
default:
- TC_LOG_ERROR("network", "QuestGiver called for unexpected type %u", questgiver->GetTypeId());
+ TC_LOG_ERROR("network", "QuestGiver called for unexpected type %u", questGiver->GetTypeId());
break;
}
@@ -174,7 +166,7 @@ void WorldSession::HandleQuestgiverAcceptQuestOpcode(WorldPacket& recvData)
if (_player->CanAddQuest(quest, true))
{
- _player->AddQuest(quest, object);
+ _player->AddQuestAndCheckCompletion(quest, object);
if (quest->HasFlag(QUEST_FLAGS_PARTY_ACCEPT))
{
@@ -191,7 +183,7 @@ void WorldSession::HandleQuestgiverAcceptQuestOpcode(WorldPacket& recvData)
{
player->SetDivider(_player->GetGUID());
- //need confirmation that any gossip window will close
+ // need confirmation that any gossip window will close
player->PlayerTalkClass->SendCloseGossip();
_player->SendQuestConfirmAccept(quest, player);
@@ -200,45 +192,6 @@ void WorldSession::HandleQuestgiverAcceptQuestOpcode(WorldPacket& recvData)
}
}
- if (_player->CanCompleteQuest(questId))
- _player->CompleteQuest(questId);
-
- switch (object->GetTypeId())
- {
- case TYPEID_UNIT:
- sScriptMgr->OnQuestAccept(_player, (object->ToCreature()), quest);
- object->ToCreature()->AI()->sQuestAccept(_player, quest);
- break;
- case TYPEID_ITEM:
- case TYPEID_CONTAINER:
- {
- Item* item = (Item*)object;
- sScriptMgr->OnQuestAccept(_player, item, quest);
-
- // destroy not required for quest finish quest starting item
- bool destroyItem = true;
- for (int i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; ++i)
- {
- if (quest->RequiredItemId[i] == item->GetEntry() && item->GetTemplate()->MaxCount > 0)
- {
- destroyItem = false;
- break;
- }
- }
-
- if (destroyItem)
- _player->DestroyItem(item->GetBagSlot(), item->GetSlot(), true);
-
- break;
- }
- case TYPEID_GAMEOBJECT:
- sScriptMgr->OnQuestAccept(_player, object->ToGameObject(), quest);
- object->ToGameObject()->AI()->QuestAccept(_player, quest);
- break;
- default:
- break;
- }
-
_player->PlayerTalkClass->SendCloseGossip();
if (quest->GetSrcSpell() > 0)
@@ -372,7 +325,9 @@ void WorldSession::HandleQuestgiverChooseRewardOpcode(WorldPacket& recvData)
break;
}
case TYPEID_GAMEOBJECT:
- if (!sScriptMgr->OnQuestReward(_player, ((GameObject*)object), quest, reward))
+ {
+ GameObject* questGiver = object->ToGameObject();
+ if (!sScriptMgr->OnQuestReward(_player, questGiver, quest, reward))
{
// Send next quest
if (Quest const* nextQuest = _player->GetNextQuest(guid, quest))
@@ -388,9 +343,10 @@ void WorldSession::HandleQuestgiverChooseRewardOpcode(WorldPacket& recvData)
_player->PlayerTalkClass->SendQuestGiverQuestDetails(nextQuest, guid, true);
}
- object->ToGameObject()->AI()->QuestReward(_player, quest, reward);
+ questGiver->AI()->QuestReward(_player, quest, reward);
}
break;
+ }
default:
break;
}
@@ -407,7 +363,7 @@ void WorldSession::HandleQuestgiverRequestRewardOpcode(WorldPacket& recvData)
TC_LOG_DEBUG("network", "WORLD: Received CMSG_QUESTGIVER_REQUEST_REWARD npc = %u, quest = %u", uint32(GUID_LOPART(guid)), questId);
- Object* object = ObjectAccessor::GetObjectByTypeMask(*_player, guid, TYPEMASK_UNIT|TYPEMASK_GAMEOBJECT);
+ Object* object = ObjectAccessor::GetObjectByTypeMask(*_player, guid, TYPEMASK_UNIT | TYPEMASK_GAMEOBJECT);
if (!object || !object->hasInvolvedQuest(questId))
return;
@@ -491,17 +447,16 @@ void WorldSession::HandleQuestConfirmAccept(WorldPacket& recvData)
TC_LOG_DEBUG("network", "WORLD: Received CMSG_QUEST_CONFIRM_ACCEPT questId = %u", questId);
- if (const Quest* quest = sObjectMgr->GetQuestTemplate(questId))
+ if (Quest const* quest = sObjectMgr->GetQuestTemplate(questId))
{
if (!quest->HasFlag(QUEST_FLAGS_PARTY_ACCEPT))
return;
- Player* pOriginalPlayer = ObjectAccessor::FindPlayer(_player->GetDivider());
-
- if (!pOriginalPlayer)
+ Player* originalPlayer = ObjectAccessor::FindPlayer(_player->GetDivider());
+ if (!originalPlayer)
return;
- if (!_player->IsInSameRaidWith(pOriginalPlayer))
+ if (!_player->IsInSameRaidWith(originalPlayer))
return;
if (_player->CanAddQuest(quest, true))
@@ -664,104 +619,6 @@ void WorldSession::HandleQuestPushResult(WorldPacket& recvPacket)
}
}
-uint32 WorldSession::getDialogStatus(Player* player, Object* questgiver)
-{
- uint32 result = DIALOG_STATUS_NONE;
-
- QuestRelationBounds qr;
- QuestRelationBounds qir;
-
- switch (questgiver->GetTypeId())
- {
- case TYPEID_GAMEOBJECT:
- {
- qr = sObjectMgr->GetGOQuestRelationBounds(questgiver->GetEntry());
- qir = sObjectMgr->GetGOQuestInvolvedRelationBounds(questgiver->GetEntry());
- break;
- }
- case TYPEID_UNIT:
- {
- qr = sObjectMgr->GetCreatureQuestRelationBounds(questgiver->GetEntry());
- qir = sObjectMgr->GetCreatureQuestInvolvedRelationBounds(questgiver->GetEntry());
- break;
- }
- default:
- //its imposible, but check ^)
- TC_LOG_ERROR("network", "Warning: GetDialogStatus called for unexpected type %u", questgiver->GetTypeId());
- return DIALOG_STATUS_NONE;
- }
-
- for (QuestRelations::const_iterator i = qir.first; i != qir.second; ++i)
- {
- uint32 result2 = 0;
- uint32 quest_id = i->second;
- Quest const* quest = sObjectMgr->GetQuestTemplate(quest_id);
- if (!quest)
- continue;
-
- ConditionList conditions = sConditionMgr->GetConditionsForNotGroupedEntry(CONDITION_SOURCE_TYPE_QUEST_SHOW_MARK, quest->GetQuestId());
- if (!sConditionMgr->IsObjectMeetToConditions(player, conditions))
- continue;
-
- QuestStatus status = player->GetQuestStatus(quest_id);
- if ((status == QUEST_STATUS_COMPLETE && !player->GetQuestRewardStatus(quest_id)) ||
- (quest->IsAutoComplete() && player->CanTakeQuest(quest, false)))
- {
- if (quest->IsAutoComplete() && quest->IsRepeatable() && !quest->IsDailyOrWeekly())
- result2 = DIALOG_STATUS_REWARD_REP;
- else
- result2 = DIALOG_STATUS_REWARD;
- }
- else if (status == QUEST_STATUS_INCOMPLETE)
- result2 = DIALOG_STATUS_INCOMPLETE;
-
- if (result2 > result)
- result = result2;
- }
-
- for (QuestRelations::const_iterator i = qr.first; i != qr.second; ++i)
- {
- uint32 result2 = 0;
- uint32 quest_id = i->second;
- Quest const* quest = sObjectMgr->GetQuestTemplate(quest_id);
- if (!quest)
- continue;
-
- ConditionList conditions = sConditionMgr->GetConditionsForNotGroupedEntry(CONDITION_SOURCE_TYPE_QUEST_SHOW_MARK, quest->GetQuestId());
- if (!sConditionMgr->IsObjectMeetToConditions(player, conditions))
- continue;
-
- QuestStatus status = player->GetQuestStatus(quest_id);
- if (status == QUEST_STATUS_NONE)
- {
- if (player->CanSeeStartQuest(quest))
- {
- if (player->SatisfyQuestLevel(quest, false))
- {
- if (quest->IsAutoComplete())
- result2 = DIALOG_STATUS_REWARD_REP;
- else if (player->getLevel() <= ((player->GetQuestLevel(quest) == -1) ? player->getLevel() : player->GetQuestLevel(quest) + sWorld->getIntConfig(CONFIG_QUEST_LOW_LEVEL_HIDE_DIFF)))
- {
- if (quest->IsDaily())
- result2 = DIALOG_STATUS_AVAILABLE_REP;
- else
- result2 = DIALOG_STATUS_AVAILABLE;
- }
- else
- result2 = DIALOG_STATUS_LOW_LEVEL_AVAILABLE;
- }
- else
- result2 = DIALOG_STATUS_UNAVAILABLE;
- }
- }
-
- if (result2 > result)
- result = result2;
- }
-
- return result;
-}
-
void WorldSession::HandleQuestgiverStatusMultipleQuery(WorldPacket& /*recvPacket*/)
{
TC_LOG_DEBUG("network", "WORLD: Received CMSG_QUESTGIVER_STATUS_MULTIPLE_QUERY");
@@ -784,9 +641,7 @@ void WorldSession::HandleQuestgiverStatusMultipleQuery(WorldPacket& /*recvPacket
if (!questgiver->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER))
continue;
- questStatus = sScriptMgr->GetDialogStatus(_player, questgiver);
- if (questStatus == DIALOG_STATUS_SCRIPTED_NO_STATUS)
- questStatus = getDialogStatus(_player, questgiver);
+ questStatus = _player->GetQuestDialogStatus(questgiver);
data << uint64(questgiver->GetGUID());
data << uint32(questStatus);
@@ -798,9 +653,7 @@ void WorldSession::HandleQuestgiverStatusMultipleQuery(WorldPacket& /*recvPacket
if (!questgiver || questgiver->GetGoType() != GAMEOBJECT_TYPE_QUESTGIVER)
continue;
- questStatus = sScriptMgr->GetDialogStatus(_player, questgiver);
- if (questStatus == DIALOG_STATUS_SCRIPTED_NO_STATUS)
- questStatus = getDialogStatus(_player, questgiver);
+ questStatus = _player->GetQuestDialogStatus(questgiver);
data << uint64(questgiver->GetGUID());
data << uint32(questStatus);
diff --git a/src/server/game/Loot/LootMgr.cpp b/src/server/game/Loot/LootMgr.cpp
index 899637a7615..60475382d5e 100644
--- a/src/server/game/Loot/LootMgr.cpp
+++ b/src/server/game/Loot/LootMgr.cpp
@@ -555,7 +555,7 @@ QuestItemList* Loot::FillQuestLoot(Player* player)
{
LootItem &item = quest_items[i];
- if (!item.is_looted && (item.AllowedForPlayer(player) || (item.follow_loot_rules && player->GetGroup() && ((player->GetGroup()->GetLootMethod() == MASTER_LOOT && player->GetGroup()->GetLooterGuid() == player->GetGUID()) || player->GetGroup()->GetLootMethod() != MASTER_LOOT ))))
+ if (!item.is_looted && (item.AllowedForPlayer(player) || (item.follow_loot_rules && player->GetGroup() && ((player->GetGroup()->GetLootMethod() == MASTER_LOOT && player->GetGroup()->GetMasterLooterGuid() == player->GetGUID()) || player->GetGroup()->GetLootMethod() != MASTER_LOOT))))
{
ql->push_back(QuestItem(i));
@@ -589,7 +589,7 @@ QuestItemList* Loot::FillNonQuestNonFFAConditionalLoot(Player* player, bool pres
for (uint8 i = 0; i < items.size(); ++i)
{
LootItem &item = items[i];
- if (!item.is_looted && !item.freeforall && (item.AllowedForPlayer(player) || (item.follow_loot_rules && player->GetGroup() && ((player->GetGroup()->GetLootMethod() == MASTER_LOOT && player->GetGroup()->GetLooterGuid() == player->GetGUID()) || player->GetGroup()->GetLootMethod() != MASTER_LOOT ))))
+ if (!item.is_looted && !item.freeforall && (item.AllowedForPlayer(player) || (item.follow_loot_rules && player->GetGroup() && ((player->GetGroup()->GetLootMethod() == MASTER_LOOT && player->GetGroup()->GetMasterLooterGuid() == player->GetGUID()) || player->GetGroup()->GetLootMethod() != MASTER_LOOT))))
{
if (presentAtLooting)
item.AddAllowedLooter(player);
@@ -883,6 +883,8 @@ ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv)
switch (lv.permission)
{
case GROUP_PERMISSION:
+ case MASTER_PERMISSION:
+ case RESTRICTED_PERMISSION:
{
// if you are not the round-robin group looter, you can only see
// blocked rolled items and quest items, and !ffa items
@@ -892,9 +894,24 @@ ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv)
{
uint8 slot_type;
- if (l.items[i].is_blocked)
- slot_type = LOOT_SLOT_TYPE_ROLL_ONGOING;
- else if (l.roundRobinPlayer == 0 || !l.items[i].is_underthreshold || lv.viewer->GetGUID() == l.roundRobinPlayer)
+ if (l.items[i].is_blocked) // for ML & restricted is_blocked = !is_underthreshold
+ {
+ switch (lv.permission)
+ {
+ case GROUP_PERMISSION:
+ slot_type = LOOT_SLOT_TYPE_ROLL_ONGOING;
+ break;
+ case MASTER_PERMISSION:
+ slot_type = LOOT_SLOT_TYPE_MASTER;
+ break;
+ case RESTRICTED_PERMISSION:
+ slot_type = LOOT_SLOT_TYPE_LOCKED;
+ break;
+ default:
+ continue;
+ }
+ }
+ else if (l.roundRobinPlayer == 0 || lv.viewer->GetGUID() == l.roundRobinPlayer || !l.items[i].is_underthreshold)
{
// no round robin owner or he has released the loot
// or it IS the round robin group owner
@@ -930,22 +947,9 @@ ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv)
break;
}
case ALL_PERMISSION:
- case MASTER_PERMISSION:
case OWNER_PERMISSION:
{
- uint8 slot_type = LOOT_SLOT_TYPE_ALLOW_LOOT;
- switch (lv.permission)
- {
- case MASTER_PERMISSION:
- slot_type = LOOT_SLOT_TYPE_MASTER;
- break;
- case OWNER_PERMISSION:
- slot_type = LOOT_SLOT_TYPE_OWNER;
- break;
- default:
- break;
- }
-
+ uint8 slot_type = lv.permission == OWNER_PERMISSION ? LOOT_SLOT_TYPE_OWNER : LOOT_SLOT_TYPE_ALLOW_LOOT;
for (uint8 i = 0; i < l.items.size(); ++i)
{
if (!l.items[i].is_looted && !l.items[i].freeforall && l.items[i].conditions.empty() && l.items[i].AllowedForPlayer(lv.viewer))
@@ -981,6 +985,9 @@ ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv)
case MASTER_PERMISSION:
b << uint8(LOOT_SLOT_TYPE_MASTER);
break;
+ case RESTRICTED_PERMISSION:
+ b << (item.is_blocked ? uint8(LOOT_SLOT_TYPE_LOCKED) : uint8(slotType));
+ break;
case GROUP_PERMISSION:
case ROUND_ROBIN_PERMISSION:
if (!item.is_blocked)
@@ -1037,6 +1044,9 @@ ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv)
case MASTER_PERMISSION:
b << uint8(LOOT_SLOT_TYPE_MASTER);
break;
+ case RESTRICTED_PERMISSION:
+ b << (item.is_blocked ? uint8(LOOT_SLOT_TYPE_LOCKED) : uint8(slotType));
+ break;
case GROUP_PERMISSION:
case ROUND_ROBIN_PERMISSION:
if (!item.is_blocked)
diff --git a/src/server/game/Loot/LootMgr.h b/src/server/game/Loot/LootMgr.h
index 96af1919e90..fe21e4726ef 100644
--- a/src/server/game/Loot/LootMgr.h
+++ b/src/server/game/Loot/LootMgr.h
@@ -68,9 +68,10 @@ enum PermissionTypes
ALL_PERMISSION = 0,
GROUP_PERMISSION = 1,
MASTER_PERMISSION = 2,
- ROUND_ROBIN_PERMISSION = 3,
- OWNER_PERMISSION = 4,
- NONE_PERMISSION = 5
+ RESTRICTED_PERMISSION = 3,
+ ROUND_ROBIN_PERMISSION = 4,
+ OWNER_PERMISSION = 5,
+ NONE_PERMISSION = 6
};
enum LootType
diff --git a/src/server/game/Mails/Mail.cpp b/src/server/game/Mails/Mail.cpp
index 8c82ef8f45b..2c65e363dd5 100644
--- a/src/server/game/Mails/Mail.cpp
+++ b/src/server/game/Mails/Mail.cpp
@@ -193,10 +193,12 @@ void MailDraft::SendMailTo(SQLTransaction& trans, MailReceiver const& receiver,
expire_delay = DAY;
// default case: expire time if COD 3 days, if no COD 30 days (or 90 days if sender is a game master)
else
+ {
if (m_COD)
expire_delay = 3 * DAY;
else
expire_delay = pSender && pSender->IsGameMaster() ? 90 * DAY : 30 * DAY;
+ }
time_t expire_time = deliver_time + expire_delay;
diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp
index 79e63cf2035..5fb4a54fac1 100644
--- a/src/server/game/Maps/Map.cpp
+++ b/src/server/game/Maps/Map.cpp
@@ -217,13 +217,13 @@ void Map::DeleteStateMachine()
}
Map::Map(uint32 id, time_t expiry, uint32 InstanceId, uint8 SpawnMode, Map* _parent):
-_creatureToMoveLock(false), _gameObjectsToMoveLock(false),
+_creatureToMoveLock(false), _gameObjectsToMoveLock(false), _dynamicObjectsToMoveLock(false),
i_mapEntry(sMapStore.LookupEntry(id)), i_spawnMode(SpawnMode), i_InstanceId(InstanceId),
m_unloadTimer(0), m_VisibleDistance(DEFAULT_VISIBILITY_DISTANCE),
m_VisibilityNotifyPeriod(DEFAULT_VISIBILITY_NOTIFY_PERIOD),
m_activeNonPlayersIter(m_activeNonPlayers.end()), _transportsUpdateIter(_transports.end()),
i_gridExpiry(expiry),
-i_scriptLock(false)
+i_scriptLock(false), _defaultLight(GetDefaultMapLight(id))
{
m_parentMap = (_parent ? _parent : this);
for (unsigned int idx=0; idx < MAX_NUMBER_OF_GRIDS; ++idx)
@@ -281,6 +281,15 @@ void Map::AddToGrid(GameObject* obj, Cell const& cell)
obj->SetCurrentCell(cell);
}
+template<>
+void Map::AddToGrid(DynamicObject* obj, Cell const& cell)
+{
+ NGridType* grid = getNGrid(cell.GridX(), cell.GridY());
+ grid->GetGridType(cell.CellX(), cell.CellY()).AddGridObject(obj);
+
+ obj->SetCurrentCell(cell);
+}
+
template<class T>
void Map::SwitchGridContainers(T* /*obj*/, bool /*on*/) { }
@@ -468,6 +477,7 @@ bool Map::AddPlayerToMap(Player* player)
SendInitSelf(player);
SendInitTransports(player);
+ SendZoneDynamicInfo(player);
player->m_clientGUIDs.clear();
player->UpdateObjectVisibility(false);
@@ -944,6 +954,7 @@ void Map::GameObjectRelocation(GameObject* go, float x, float y, float z, float
else
{
go->Relocate(x, y, z, orientation);
+ go->UpdateModelPosition();
go->UpdateObjectVisibility(false);
RemoveGameObjectFromMoveList(go);
}
@@ -953,6 +964,38 @@ void Map::GameObjectRelocation(GameObject* go, float x, float y, float z, float
ASSERT(integrity_check == old_cell);
}
+void Map::DynamicObjectRelocation(DynamicObject* dynObj, float x, float y, float z, float orientation)
+{
+ Cell integrity_check(dynObj->GetPositionX(), dynObj->GetPositionY());
+ Cell old_cell = dynObj->GetCurrentCell();
+
+ ASSERT(integrity_check == old_cell);
+ Cell new_cell(x, y);
+
+ if (!getNGrid(new_cell.GridX(), new_cell.GridY()))
+ return;
+
+ // delay creature move for grid/cell to grid/cell moves
+ if (old_cell.DiffCell(new_cell) || old_cell.DiffGrid(new_cell))
+ {
+#ifdef TRINITY_DEBUG
+ TC_LOG_DEBUG("maps", "GameObject (GUID: %u) added to moving list from grid[%u, %u]cell[%u, %u] to grid[%u, %u]cell[%u, %u].", dynObj->GetGUIDLow(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.GridX(), new_cell.GridY(), new_cell.CellX(), new_cell.CellY());
+#endif
+ AddDynamicObjectToMoveList(dynObj, x, y, z, orientation);
+ // in diffcell/diffgrid case notifiers called at finishing move dynObj in Map::MoveAllGameObjectsInMoveList
+ }
+ else
+ {
+ dynObj->Relocate(x, y, z, orientation);
+ dynObj->UpdateObjectVisibility(false);
+ RemoveDynamicObjectFromMoveList(dynObj);
+ }
+
+ old_cell = dynObj->GetCurrentCell();
+ integrity_check = Cell(dynObj->GetPositionX(), dynObj->GetPositionY());
+ ASSERT(integrity_check == old_cell);
+}
+
void Map::AddCreatureToMoveList(Creature* c, float x, float y, float z, float ang)
{
if (_creatureToMoveLock) //can this happen?
@@ -991,6 +1034,25 @@ void Map::RemoveGameObjectFromMoveList(GameObject* go)
go->_moveState = MAP_OBJECT_CELL_MOVE_INACTIVE;
}
+void Map::AddDynamicObjectToMoveList(DynamicObject* dynObj, float x, float y, float z, float ang)
+{
+ if (_dynamicObjectsToMoveLock) //can this happen?
+ return;
+
+ if (dynObj->_moveState == MAP_OBJECT_CELL_MOVE_NONE)
+ _dynamicObjectsToMove.push_back(dynObj);
+ dynObj->SetNewCellPosition(x, y, z, ang);
+}
+
+void Map::RemoveDynamicObjectFromMoveList(DynamicObject* dynObj)
+{
+ if (_dynamicObjectsToMoveLock) //can this happen?
+ return;
+
+ if (dynObj->_moveState == MAP_OBJECT_CELL_MOVE_ACTIVE)
+ dynObj->_moveState = MAP_OBJECT_CELL_MOVE_INACTIVE;
+}
+
void Map::MoveAllCreaturesInMoveList()
{
_creatureToMoveLock = true;
@@ -1071,6 +1133,7 @@ void Map::MoveAllGameObjectsInMoveList()
{
// update pos
go->Relocate(go->_newPosition);
+ go->UpdateModelPosition();
go->UpdateObjectVisibility(false);
}
else
@@ -1091,6 +1154,44 @@ void Map::MoveAllGameObjectsInMoveList()
_gameObjectsToMoveLock = false;
}
+void Map::MoveAllDynamicObjectsInMoveList()
+{
+ _dynamicObjectsToMoveLock = true;
+ for (std::vector<DynamicObject*>::iterator itr = _dynamicObjectsToMove.begin(); itr != _dynamicObjectsToMove.end(); ++itr)
+ {
+ DynamicObject* dynObj = *itr;
+ if (dynObj->FindMap() != this) //transport is teleported to another map
+ continue;
+
+ if (dynObj->_moveState != MAP_OBJECT_CELL_MOVE_ACTIVE)
+ {
+ dynObj->_moveState = MAP_OBJECT_CELL_MOVE_NONE;
+ continue;
+ }
+
+ dynObj->_moveState = MAP_OBJECT_CELL_MOVE_NONE;
+ if (!dynObj->IsInWorld())
+ continue;
+
+ // do move or do move to respawn or remove creature if previous all fail
+ if (DynamicObjectCellRelocation(dynObj, Cell(dynObj->_newPosition.m_positionX, dynObj->_newPosition.m_positionY)))
+ {
+ // update pos
+ dynObj->Relocate(dynObj->_newPosition);
+ dynObj->UpdateObjectVisibility(false);
+ }
+ else
+ {
+#ifdef TRINITY_DEBUG
+ TC_LOG_DEBUG("maps", "DynamicObject (GUID: %u) cannot be moved to unloaded grid.", dynObj->GetGUIDLow());
+#endif
+ }
+ }
+
+ _dynamicObjectsToMove.clear();
+ _dynamicObjectsToMoveLock = false;
+}
+
bool Map::CreatureCellRelocation(Creature* c, Cell new_cell)
{
Cell const& old_cell = c->GetCurrentCell();
@@ -1213,6 +1314,67 @@ bool Map::GameObjectCellRelocation(GameObject* go, Cell new_cell)
return false;
}
+bool Map::DynamicObjectCellRelocation(DynamicObject* go, Cell new_cell)
+{
+ Cell const& old_cell = go->GetCurrentCell();
+ if (!old_cell.DiffGrid(new_cell)) // in same grid
+ {
+ // if in same cell then none do
+ if (old_cell.DiffCell(new_cell))
+ {
+ #ifdef TRINITY_DEBUG
+ TC_LOG_DEBUG("maps", "DynamicObject (GUID: %u) moved in grid[%u, %u] from cell[%u, %u] to cell[%u, %u].", go->GetGUIDLow(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.CellX(), new_cell.CellY());
+ #endif
+
+ go->RemoveFromGrid();
+ AddToGrid(go, new_cell);
+ }
+ else
+ {
+ #ifdef TRINITY_DEBUG
+ TC_LOG_DEBUG("maps", "DynamicObject (GUID: %u) moved in same grid[%u, %u]cell[%u, %u].", go->GetGUIDLow(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY());
+ #endif
+ }
+
+ return true;
+ }
+
+ // in diff. grids but active GameObject
+ if (go->isActiveObject())
+ {
+ EnsureGridLoadedForActiveObject(new_cell, go);
+
+ #ifdef TRINITY_DEBUG
+ TC_LOG_DEBUG("maps", "Active DynamicObject (GUID: %u) moved from grid[%u, %u]cell[%u, %u] to grid[%u, %u]cell[%u, %u].", go->GetGUIDLow(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.GridX(), new_cell.GridY(), new_cell.CellX(), new_cell.CellY());
+ #endif
+
+ go->RemoveFromGrid();
+ AddToGrid(go, new_cell);
+
+ return true;
+ }
+
+ // in diff. loaded grid normal GameObject
+ if (IsGridLoaded(GridCoord(new_cell.GridX(), new_cell.GridY())))
+ {
+ #ifdef TRINITY_DEBUG
+ TC_LOG_DEBUG("maps", "DynamicObject (GUID: %u) moved from grid[%u, %u]cell[%u, %u] to grid[%u, %u]cell[%u, %u].", go->GetGUIDLow(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.GridX(), new_cell.GridY(), new_cell.CellX(), new_cell.CellY());
+ #endif
+
+ go->RemoveFromGrid();
+ EnsureGridCreated(GridCoord(new_cell.GridX(), new_cell.GridY()));
+ AddToGrid(go, new_cell);
+
+ return true;
+ }
+
+ // fail to move: normal GameObject attempt move to unloaded grid
+ #ifdef TRINITY_DEBUG
+ TC_LOG_DEBUG("maps", "DynamicObject (GUID: %u) attempted to move from grid[%u, %u]cell[%u, %u] to unloaded grid[%u, %u]cell[%u, %u].", go->GetGUIDLow(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.GridX(), new_cell.GridY(), new_cell.CellX(), new_cell.CellY());
+ #endif
+ return false;
+}
+
bool Map::CreatureRespawnRelocation(Creature* c, bool diffGridOnly)
{
float resp_x, resp_y, resp_z, resp_o;
@@ -2493,7 +2655,10 @@ void Map::RemoveAllObjectsInRemoveList()
RemoveFromMap((AreaTrigger*)obj, true);
break;
case TYPEID_GAMEOBJECT:
- RemoveFromMap((GameObject*)obj, true);
+ if (Transport* transport = obj->ToGameObject()->ToTransport())
+ RemoveFromMap(transport, true);
+ else
+ RemoveFromMap(obj->ToGameObject(), true);
break;
case TYPEID_UNIT:
// in case triggered sequence some spell can continue casting after prev CleanupsBeforeDelete call
@@ -3024,18 +3189,11 @@ MapDifficulty const* Map::GetMapDifficulty() const
uint32 InstanceMap::GetMaxPlayers() const
{
- if (MapDifficulty const* mapDiff = GetMapDifficulty())
- {
- if (mapDiff->maxPlayers || IsRegularDifficulty()) // Normal case (expect that regular difficulty always have correct maxplayers)
- return mapDiff->maxPlayers;
- else // DBC have 0 maxplayers for heroic instances with expansion < 2
- { // The heroic entry exists, so we don't have to check anything, simply return normal max players
- MapDifficulty const* normalDiff = GetMapDifficultyData(GetId(), REGULAR_DIFFICULTY);
- return normalDiff ? normalDiff->maxPlayers : 0;
- }
- }
- else // I'd rather ASSERT(false);
- return 0;
+ MapDifficulty const* mapDiff = GetMapDifficulty();
+ if (mapDiff && mapDiff->maxPlayers)
+ return mapDiff->maxPlayers;
+
+ return GetEntry()->maxPlayers;
}
uint32 InstanceMap::GetMaxResetDelay() const
@@ -3280,3 +3438,103 @@ time_t Map::GetLinkedRespawnTime(uint64 guid) const
return time_t(0);
}
+void Map::SendZoneDynamicInfo(Player* player)
+{
+ uint32 zoneId = GetZoneId(player->GetPositionX(), player->GetPositionY(), player->GetPositionZ());
+ ZoneDynamicInfoMap::const_iterator itr = _zoneDynamicInfo.find(zoneId);
+ if (itr == _zoneDynamicInfo.end())
+ return;
+
+ if (uint32 music = itr->second.MusicId)
+ {
+ WorldPacket data(SMSG_PLAY_MUSIC, 4);
+ data << uint32(music);
+ player->SendDirectMessage(&data);
+ }
+
+ if (uint32 weather = itr->second.WeatherId)
+ {
+ WorldPacket data(SMSG_WEATHER, 4 + 4 + 1);
+ data << uint32(weather);
+ data << float(itr->second.WeatherGrade);
+ data << uint8(0);
+ player->SendDirectMessage(&data);
+ }
+
+ if (uint32 overrideLight = itr->second.OverrideLightId)
+ {
+ WorldPacket data(SMSG_OVERRIDE_LIGHT, 4 + 4 + 1);
+ data << uint32(_defaultLight);
+ data << uint32(overrideLight);
+ data << uint32(itr->second.LightFadeInTime);
+ player->SendDirectMessage(&data);
+ }
+}
+
+void Map::SetZoneMusic(uint32 zoneId, uint32 musicId)
+{
+ if (_zoneDynamicInfo.find(zoneId) == _zoneDynamicInfo.end())
+ _zoneDynamicInfo.insert(ZoneDynamicInfoMap::value_type(zoneId, ZoneDynamicInfo()));
+
+ _zoneDynamicInfo[zoneId].MusicId = musicId;
+
+ Map::PlayerList const& players = GetPlayers();
+ if (!players.isEmpty())
+ {
+ WorldPacket data(SMSG_PLAY_MUSIC, 4);
+ data << uint32(musicId);
+
+ for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
+ if (Player* player = itr->GetSource())
+ if (player->GetZoneId() == zoneId)
+ player->SendDirectMessage(&data);
+ }
+}
+
+void Map::SetZoneWeather(uint32 zoneId, uint32 weatherId, float weatherGrade)
+{
+ if (_zoneDynamicInfo.find(zoneId) == _zoneDynamicInfo.end())
+ _zoneDynamicInfo.insert(ZoneDynamicInfoMap::value_type(zoneId, ZoneDynamicInfo()));
+
+ ZoneDynamicInfo& info = _zoneDynamicInfo[zoneId];
+ info.WeatherId = weatherId;
+ info.WeatherGrade = weatherGrade;
+ Map::PlayerList const& players = GetPlayers();
+
+ if (!players.isEmpty())
+ {
+ WorldPacket data(SMSG_WEATHER, 4 + 4 + 1);
+ data << uint32(weatherId);
+ data << float(weatherGrade);
+ data << uint8(0);
+
+ for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
+ if (Player* player = itr->GetSource())
+ if (player->GetZoneId() == zoneId)
+ player->SendDirectMessage(&data);
+ }
+}
+
+void Map::SetZoneOverrideLight(uint32 zoneId, uint32 lightId, uint32 fadeInTime)
+{
+ if (_zoneDynamicInfo.find(zoneId) == _zoneDynamicInfo.end())
+ _zoneDynamicInfo.insert(ZoneDynamicInfoMap::value_type(zoneId, ZoneDynamicInfo()));
+
+ ZoneDynamicInfo& info = _zoneDynamicInfo[zoneId];
+ info.OverrideLightId = lightId;
+ info.LightFadeInTime = fadeInTime;
+ Map::PlayerList const& players = GetPlayers();
+
+ if (!players.isEmpty())
+ {
+ WorldPacket data(SMSG_OVERRIDE_LIGHT, 4 + 4 + 1);
+ data << uint32(_defaultLight);
+ data << uint32(lightId);
+ data << uint32(fadeInTime);
+
+ for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
+ if (Player* player = itr->GetSource())
+ if (player->GetZoneId() == zoneId)
+ player->SendDirectMessage(&data);
+ }
+}
diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h
index 01e8760f85f..cb077731c1d 100644
--- a/src/server/game/Maps/Map.h
+++ b/src/server/game/Maps/Map.h
@@ -229,6 +229,18 @@ enum LevelRequirementVsMode
LEVELREQUIREMENT_HEROIC = 70
};
+struct ZoneDynamicInfo
+{
+ ZoneDynamicInfo() : MusicId(0), WeatherId(0), WeatherGrade(0.0f),
+ OverrideLightId(0), LightFadeInTime(0) { }
+
+ uint32 MusicId;
+ uint32 WeatherId;
+ float WeatherGrade;
+ uint32 OverrideLightId;
+ uint32 LightFadeInTime;
+};
+
#if defined(__GNUC__)
#pragma pack()
#else
@@ -243,6 +255,8 @@ enum LevelRequirementVsMode
typedef std::map<uint32/*leaderDBGUID*/, CreatureGroup*> CreatureGroupHolderType;
+typedef UNORDERED_MAP<uint32 /*zoneId*/, ZoneDynamicInfo> ZoneDynamicInfoMap;
+
class Map : public GridRefManager<NGridType>
{
friend class MapReference;
@@ -280,6 +294,7 @@ class Map : public GridRefManager<NGridType>
void PlayerRelocation(Player*, float x, float y, float z, float orientation);
void CreatureRelocation(Creature* creature, float x, float y, float z, float ang, bool respawnRelocationOnFail = true);
void GameObjectRelocation(GameObject* go, float x, float y, float z, float orientation, bool respawnRelocationOnFail = true);
+ void DynamicObjectRelocation(DynamicObject* go, float x, float y, float z, float orientation);
template<class T, class CONTAINER> void Visit(const Cell& cell, TypeContainerVisitor<T, CONTAINER> &visitor);
@@ -353,6 +368,7 @@ class Map : public GridRefManager<NGridType>
void MoveAllCreaturesInMoveList();
void MoveAllGameObjectsInMoveList();
+ void MoveAllDynamicObjectsInMoveList();
void RemoveAllObjectsInRemoveList();
virtual void RemoveAllPlayers();
@@ -489,6 +505,11 @@ class Map : public GridRefManager<NGridType>
void SendInitTransports(Player* player);
void SendRemoveTransports(Player* player);
+ void SendZoneDynamicInfo(Player* player);
+
+ void SetZoneMusic(uint32 zoneId, uint32 musicId);
+ void SetZoneWeather(uint32 zoneId, uint32 weatherId, float weatherGrade);
+ void SetZoneOverrideLight(uint32 zoneId, uint32 lightId, uint32 fadeInTime);
private:
void LoadMapAndVMap(int gx, int gy);
@@ -503,12 +524,15 @@ class Map : public GridRefManager<NGridType>
bool CreatureCellRelocation(Creature* creature, Cell new_cell);
bool GameObjectCellRelocation(GameObject* go, Cell new_cell);
+ bool DynamicObjectCellRelocation(DynamicObject* go, Cell new_cell);
template<class T> void InitializeObject(T* obj);
void AddCreatureToMoveList(Creature* c, float x, float y, float z, float ang);
void RemoveCreatureFromMoveList(Creature* c);
void AddGameObjectToMoveList(GameObject* go, float x, float y, float z, float ang);
void RemoveGameObjectFromMoveList(GameObject* go);
+ void AddDynamicObjectToMoveList(DynamicObject* go, float x, float y, float z, float ang);
+ void RemoveDynamicObjectFromMoveList(DynamicObject* go);
bool _creatureToMoveLock;
std::vector<Creature*> _creaturesToMove;
@@ -516,6 +540,9 @@ class Map : public GridRefManager<NGridType>
bool _gameObjectsToMoveLock;
std::vector<GameObject*> _gameObjectsToMove;
+ bool _dynamicObjectsToMoveLock;
+ std::vector<DynamicObject*> _dynamicObjectsToMove;
+
bool IsGridLoaded(const GridCoord &) const;
void EnsureGridCreated(const GridCoord &);
void EnsureGridCreated_i(const GridCoord &);
@@ -627,6 +654,9 @@ class Map : public GridRefManager<NGridType>
UNORDERED_MAP<uint32 /*dbGUID*/, time_t> _creatureRespawnTimes;
UNORDERED_MAP<uint32 /*dbGUID*/, time_t> _goRespawnTimes;
+
+ ZoneDynamicInfoMap _zoneDynamicInfo;
+ uint32 _defaultLight;
};
enum InstanceResetMethod
diff --git a/src/server/game/Maps/MapManager.cpp b/src/server/game/Maps/MapManager.cpp
index 8ecb9979706..d03358c821c 100644
--- a/src/server/game/Maps/MapManager.cpp
+++ b/src/server/game/Maps/MapManager.cpp
@@ -122,7 +122,7 @@ bool MapManager::CanPlayerEnter(uint32 mapid, Player* player, bool loginCheck)
{
MapEntry const* entry = sMapStore.LookupEntry(mapid);
if (!entry)
- return false;
+ return false;
if (!entry->IsDungeon())
return true;
@@ -220,12 +220,12 @@ bool MapManager::CanPlayerEnter(uint32 mapid, Player* player, bool loginCheck)
// players are only allowed to enter 5 instances per hour
if (entry->IsDungeon() && (!player->GetGroup() || (player->GetGroup() && !player->GetGroup()->isLFGGroup())))
{
- uint32 instaceIdToCheck = 0;
+ uint32 instanceIdToCheck = 0;
if (InstanceSave* save = player->GetInstanceSave(mapid, entry->IsRaid()))
- instaceIdToCheck = save->GetInstanceId();
+ instanceIdToCheck = save->GetInstanceId();
// instanceId can never be 0 - will not be found
- if (!player->CheckInstanceCount(instaceIdToCheck) && !player->isDead())
+ if (!player->CheckInstanceCount(instanceIdToCheck) && !player->isDead())
{
player->SendTransferAborted(mapid, TRANSFER_ABORT_TOO_MANY_INSTANCES);
return false;
diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h
index 75e190ed20d..cd57789ed60 100644
--- a/src/server/game/Miscellaneous/SharedDefines.h
+++ b/src/server/game/Miscellaneous/SharedDefines.h
@@ -3772,20 +3772,20 @@ enum BanReturn
// indexes of BattlemasterList.dbc
enum BattlegroundTypeId
{
- BATTLEGROUND_TYPE_NONE = 0, // None
- BATTLEGROUND_AV = 1, // Alterac Valley
- BATTLEGROUND_WS = 2, // Warsong Gulch
- BATTLEGROUND_AB = 3, // Arathi Basin
- BATTLEGROUND_NA = 4, // Nagrand Arena
- BATTLEGROUND_BE = 5, // Blade's Edge Arena
- BATTLEGROUND_AA = 6, // All Arenas
- BATTLEGROUND_EY = 7, // Eye of the Storm
- BATTLEGROUND_RL = 8, // Ruins of Lordaernon
- BATTLEGROUND_SA = 9, // Strand of the Ancients
- BATTLEGROUND_DS = 10, // Dalaran Sewers
- BATTLEGROUND_RV = 11, // Ring of Valor
- BATTLEGROUND_IC = 30, // Isle of Conquest
- BATTLEGROUND_RB = 32, // Random Battleground
+ BATTLEGROUND_TYPE_NONE = 0, // None
+ BATTLEGROUND_AV = 1, // Alterac Valley
+ BATTLEGROUND_WS = 2, // Warsong Gulch
+ BATTLEGROUND_AB = 3, // Arathi Basin
+ BATTLEGROUND_NA = 4, // Nagrand Arena
+ BATTLEGROUND_BE = 5, // Blade's Edge Arena
+ BATTLEGROUND_AA = 6, // All Arenas
+ BATTLEGROUND_EY = 7, // Eye of the Storm
+ BATTLEGROUND_RL = 8, // Ruins of Lordaernon
+ BATTLEGROUND_SA = 9, // Strand of the Ancients
+ BATTLEGROUND_DS = 10, // Dalaran Sewers
+ BATTLEGROUND_RV = 11, // Ring of Valor
+ BATTLEGROUND_IC = 30, // Isle of Conquest
+ BATTLEGROUND_RB = 32, // Random Battleground
BATTLEGROUND_RATED_10_VS_10 = 100, // Rated BG 10 vs 10
BATTLEGROUND_RATED_15_VS_15 = 101, // Rated BG 15 vs 15
BATTLEGROUND_RATED_25_VS_25 = 102, // Rated BG 25 vs 25
@@ -4068,7 +4068,7 @@ enum PartyResult
};
const uint32 MMAP_MAGIC = 0x4d4d4150; // 'MMAP'
-#define MMAP_VERSION 4
+#define MMAP_VERSION 5
struct MmapTileHeader
{
diff --git a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp
index f1656592000..28e58c87323 100755
--- a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp
+++ b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp
@@ -25,6 +25,7 @@
#include "MoveSplineInit.h"
#include "MoveSpline.h"
#include "Player.h"
+#include "VehicleDefines.h"
template<class T, typename D>
void TargetedMovementGeneratorMedium<T, D>::_setTargetLocation(T* owner, bool updateDestination)
@@ -154,6 +155,9 @@ bool TargetedMovementGeneratorMedium<T, D>::DoUpdate(T* owner, uint32 time_diff)
//More distance let have better performance, less distance let have more sensitive reaction at target move.
float allowed_dist = owner->GetCombatReach() + sWorld->getRate(RATE_TARGET_POS_RECALCULATION_RANGE);
G3D::Vector3 dest = owner->movespline->FinalDestination();
+ if (owner->movespline->onTransport)
+ if (TransportBase* transport = owner->GetDirectTransport())
+ transport->CalculatePassengerPosition(dest.x, dest.y, dest.z);
if (owner->GetTypeId() == TYPEID_UNIT && owner->ToCreature()->CanFly())
targetMoved = !i_target->IsWithinDist3d(dest.x, dest.y, dest.z, allowed_dist);
diff --git a/src/server/game/Movement/PathGenerator.cpp b/src/server/game/Movement/PathGenerator.cpp
index d8a300a2bab..4de47036b59 100644
--- a/src/server/game/Movement/PathGenerator.cpp
+++ b/src/server/game/Movement/PathGenerator.cpp
@@ -30,7 +30,7 @@
////////////////// PathGenerator //////////////////
PathGenerator::PathGenerator(const Unit* owner) :
_polyLength(0), _type(PATHFIND_BLANK), _useStraightPath(false),
- _forceDestination(false), _pointPathLimit(MAX_POINT_PATH_LENGTH),
+ _forceDestination(false), _pointPathLimit(MAX_POINT_PATH_LENGTH), _straightLine(false),
_endPosition(G3D::Vector3::zero()), _sourceUnit(owner), _navMesh(NULL),
_navMeshQuery(NULL)
{
@@ -54,7 +54,7 @@ PathGenerator::~PathGenerator()
TC_LOG_DEBUG("maps", "++ PathGenerator::~PathGenerator() for %u \n", _sourceUnit->GetGUIDLow());
}
-bool PathGenerator::CalculatePath(float destX, float destY, float destZ, bool forceDest)
+bool PathGenerator::CalculatePath(float destX, float destY, float destZ, bool forceDest, bool straightLine)
{
float x, y, z;
_sourceUnit->GetPosition(x, y, z);
@@ -69,6 +69,7 @@ bool PathGenerator::CalculatePath(float destX, float destY, float destZ, bool fo
SetStartPosition(start);
_forceDestination = forceDest;
+ _straightLine = straightLine;
TC_LOG_DEBUG("maps", "++ PathGenerator::CalculatePath() for %u \n", _sourceUnit->GetGUIDLow());
@@ -100,7 +101,7 @@ dtPolyRef PathGenerator::GetPathPolyByPosition(dtPolyRef const* polyPath, uint32
for (uint32 i = 0; i < polyPathSize; ++i)
{
float closestPoint[VERTEX_SIZE];
- if (dtStatusFailed(_navMeshQuery->closestPointOnPoly(polyPath[i], point, closestPoint)))
+ if (dtStatusFailed(_navMeshQuery->closestPointOnPoly(polyPath[i], point, closestPoint, NULL)))
continue;
float d = dtVdist2DSqr(point, closestPoint);
@@ -231,7 +232,7 @@ void PathGenerator::BuildPolyPath(G3D::Vector3 const& startPos, G3D::Vector3 con
{
float closestPoint[VERTEX_SIZE];
// we may want to use closestPointOnPolyBoundary instead
- if (dtStatusSucceed(_navMeshQuery->closestPointOnPoly(endPoly, endPoint, closestPoint)))
+ if (dtStatusSucceed(_navMeshQuery->closestPointOnPoly(endPoly, endPoint, closestPoint, NULL)))
{
dtVcopy(endPoint, closestPoint);
SetActualEndPosition(G3D::Vector3(endPoint[2], endPoint[0], endPoint[1]));
@@ -270,8 +271,16 @@ void PathGenerator::BuildPolyPath(G3D::Vector3 const& startPos, G3D::Vector3 con
{
for (; pathStartIndex < _polyLength; ++pathStartIndex)
{
- // here to carch few bugs
- ASSERT(_pathPolyRefs[pathStartIndex] != INVALID_POLYREF);
+ // here to catch few bugs
+ if (_pathPolyRefs[pathStartIndex] == INVALID_POLYREF)
+ {
+ TC_LOG_ERROR("maps", "Invalid poly ref in BuildPolyPath. _polyLength: %u, pathStartIndex: %u,"
+ " startPos: %s, endPos: %s, mapid: %u",
+ _polyLength, pathStartIndex, startPos.toString().c_str(), endPos.toString().c_str(),
+ _sourceUnit->GetMapId());
+
+ break;
+ }
if (_pathPolyRefs[pathStartIndex] == startPoly)
{
@@ -322,13 +331,13 @@ void PathGenerator::BuildPolyPath(G3D::Vector3 const& startPos, G3D::Vector3 con
// we need any point on our suffix start poly to generate poly-path, so we need last poly in prefix data
float suffixEndPoint[VERTEX_SIZE];
- if (dtStatusFailed(_navMeshQuery->closestPointOnPoly(suffixStartPoly, endPoint, suffixEndPoint)))
+ if (dtStatusFailed(_navMeshQuery->closestPointOnPoly(suffixStartPoly, endPoint, suffixEndPoint, NULL)))
{
// we can hit offmesh connection as last poly - closestPointOnPoly() don't like that
// try to recover by using prev polyref
--prefixPolyLength;
suffixStartPoly = _pathPolyRefs[prefixPolyLength-1];
- if (dtStatusFailed(_navMeshQuery->closestPointOnPoly(suffixStartPoly, endPoint, suffixEndPoint)))
+ if (dtStatusFailed(_navMeshQuery->closestPointOnPoly(suffixStartPoly, endPoint, suffixEndPoint, NULL)))
{
// suffixStartPoly is still invalid, error state
BuildShortcut();
@@ -339,15 +348,45 @@ void PathGenerator::BuildPolyPath(G3D::Vector3 const& startPos, G3D::Vector3 con
// generate suffix
uint32 suffixPolyLength = 0;
- dtStatus dtResult = _navMeshQuery->findPath(
- suffixStartPoly, // start polygon
- endPoly, // end polygon
- suffixEndPoint, // start position
- endPoint, // end position
- &_filter, // polygon search filter
- _pathPolyRefs + prefixPolyLength - 1, // [out] path
- (int*)&suffixPolyLength,
- MAX_PATH_LENGTH-prefixPolyLength); // max number of polygons in output path
+
+ dtStatus dtResult;
+ if (_straightLine)
+ {
+ float hit = 0;
+ float hitNormal[3];
+ memset(hitNormal, 0, sizeof(hitNormal));
+
+ dtResult = _navMeshQuery->raycast(
+ suffixStartPoly,
+ suffixEndPoint,
+ endPoint,
+ &_filter,
+ &hit,
+ hitNormal,
+ _pathPolyRefs + prefixPolyLength - 1,
+ (int*)&suffixPolyLength,
+ MAX_PATH_LENGTH - prefixPolyLength);
+
+ // raycast() sets hit to FLT_MAX if there is a ray between start and end
+ if (hit != FLT_MAX)
+ {
+ // the ray hit something, return no path instead of the incomplete one
+ _type = PATHFIND_NOPATH;
+ return;
+ }
+ }
+ else
+ {
+ dtResult = _navMeshQuery->findPath(
+ suffixStartPoly, // start polygon
+ endPoly, // end polygon
+ suffixEndPoint, // start position
+ endPoint, // end position
+ &_filter, // polygon search filter
+ _pathPolyRefs + prefixPolyLength - 1, // [out] path
+ (int*)&suffixPolyLength,
+ MAX_PATH_LENGTH - prefixPolyLength); // max number of polygons in output path
+ }
if (!suffixPolyLength || dtStatusFailed(dtResult))
{
@@ -373,15 +412,44 @@ void PathGenerator::BuildPolyPath(G3D::Vector3 const& startPos, G3D::Vector3 con
// free and invalidate old path data
Clear();
- dtStatus dtResult = _navMeshQuery->findPath(
- startPoly, // start polygon
- endPoly, // end polygon
- startPoint, // start position
- endPoint, // end position
- &_filter, // polygon search filter
- _pathPolyRefs, // [out] path
- (int*)&_polyLength,
- MAX_PATH_LENGTH); // max number of polygons in output path
+ dtStatus dtResult;
+ if (_straightLine)
+ {
+ float hit = 0;
+ float hitNormal[3];
+ memset(hitNormal, 0, sizeof(hitNormal));
+
+ dtResult = _navMeshQuery->raycast(
+ startPoly,
+ startPoint,
+ endPoint,
+ &_filter,
+ &hit,
+ hitNormal,
+ _pathPolyRefs,
+ (int*)&_polyLength,
+ MAX_PATH_LENGTH);
+
+ // raycast() sets hit to FLT_MAX if there is a ray between start and end
+ if (hit != FLT_MAX)
+ {
+ // the ray hit something, return no path instead of the incomplete one
+ _type = PATHFIND_NOPATH;
+ return;
+ }
+ }
+ else
+ {
+ dtResult = _navMeshQuery->findPath(
+ startPoly, // start polygon
+ endPoly, // end polygon
+ startPoint, // start position
+ endPoint, // end position
+ &_filter, // polygon search filter
+ _pathPolyRefs, // [out] path
+ (int*)&_polyLength,
+ MAX_PATH_LENGTH); // max number of polygons in output path
+ }
if (!_polyLength || dtStatusFailed(dtResult))
{
@@ -408,7 +476,15 @@ void PathGenerator::BuildPointPath(const float *startPoint, const float *endPoin
float pathPoints[MAX_POINT_PATH_LENGTH*VERTEX_SIZE];
uint32 pointCount = 0;
dtStatus dtResult = DT_FAILURE;
- if (_useStraightPath)
+ if (_straightLine)
+ {
+ // if the path is a straight line then start and end position are enough
+ dtResult = DT_SUCCESS;
+ pointCount = 2;
+ memcpy(&pathPoints[0], startPoint, sizeof(float)* 3);
+ memcpy(&pathPoints[3], endPoint, sizeof(float)* 3);
+ }
+ else if (_useStraightPath)
{
dtResult = _navMeshQuery->findStraightPath(
startPoint, // start position
diff --git a/src/server/game/Movement/PathGenerator.h b/src/server/game/Movement/PathGenerator.h
index ac66b7cec57..6e0d72ec8da 100644
--- a/src/server/game/Movement/PathGenerator.h
+++ b/src/server/game/Movement/PathGenerator.h
@@ -57,7 +57,7 @@ class PathGenerator
// Calculate the path from owner to given destination
// return: true if new path was calculated, false otherwise (no change needed)
- bool CalculatePath(float destX, float destY, float destZ, bool forceDest = false);
+ bool CalculatePath(float destX, float destY, float destZ, bool forceDest = false, bool straightLine = false);
// option setters - use optional
void SetUseStraightPath(bool useStraightPath) { _useStraightPath = useStraightPath; }
@@ -83,6 +83,7 @@ class PathGenerator
bool _useStraightPath; // type of path will be generated
bool _forceDestination; // when set, we will always arrive at given point
uint32 _pointPathLimit; // limit point path size; min(this, MAX_POINT_PATH_LENGTH)
+ bool _straightLine; // use raycast if true for a straight line path
G3D::Vector3 _startPosition; // {x, y, z} of current location
G3D::Vector3 _endPosition; // {x, y, z} of the destination
diff --git a/src/server/game/Movement/Spline/MovementTypedefs.h b/src/server/game/Movement/Spline/MovementTypedefs.h
index 1a0f3d54d27..57958f604d2 100644
--- a/src/server/game/Movement/Spline/MovementTypedefs.h
+++ b/src/server/game/Movement/Spline/MovementTypedefs.h
@@ -47,12 +47,6 @@ namespace Movement
float computeFallTime(float path_length, bool isSafeFall);
float computeFallElevation(float t_passed, bool isSafeFall, float start_velocity = 0.0f);
-#ifndef static_assert
- #define CONCAT(x, y) CONCAT1 (x, y)
- #define CONCAT1(x, y) x##y
- #define static_assert(expr, msg) typedef char CONCAT(static_assert_failed_at_line_, __LINE__) [(expr) ? 1 : -1]
-#endif
-
template<class T, T limit>
class counter
{
diff --git a/src/server/game/Movement/Spline/Spline.h b/src/server/game/Movement/Spline/Spline.h
index 1444b2872d1..6e8a5a0281d 100644
--- a/src/server/game/Movement/Spline/Spline.h
+++ b/src/server/game/Movement/Spline/Spline.h
@@ -56,7 +56,7 @@ protected:
// client's value is 20, blizzs use 2-3 steps to compute length
STEPS_PER_SEGMENT = 3
};
- static_assert(STEPS_PER_SEGMENT > 0, "shouldn't be lesser than 1");
+ static_assert(STEPS_PER_SEGMENT > 0, "STEPS_PER_SEGMENT shouldn't be lesser than 1");
protected:
void EvaluateLinear(index_type, float, Vector3&) const;
diff --git a/src/server/game/Scripting/ScriptLoader.cpp b/src/server/game/Scripting/ScriptLoader.cpp
index e4d2922cb6c..a744915a25a 100644
--- a/src/server/game/Scripting/ScriptLoader.cpp
+++ b/src/server/game/Scripting/ScriptLoader.cpp
@@ -525,6 +525,7 @@ void AddSC_boss_falric();
void AddSC_boss_marwyn();
void AddSC_boss_lord_marrowgar(); // Icecrown Citadel
void AddSC_boss_lady_deathwhisper();
+void AddSC_boss_icecrown_gunship_battle();
void AddSC_boss_deathbringer_saurfang();
void AddSC_boss_festergut();
void AddSC_boss_rotface();
@@ -1373,6 +1374,7 @@ void AddNorthrendScripts()
AddSC_boss_marwyn();
AddSC_boss_lord_marrowgar(); // Icecrown Citadel
AddSC_boss_lady_deathwhisper();
+ AddSC_boss_icecrown_gunship_battle();
AddSC_boss_deathbringer_saurfang();
AddSC_boss_festergut();
AddSC_boss_rotface();
diff --git a/src/server/game/Scripting/ScriptMgr.cpp b/src/server/game/Scripting/ScriptMgr.cpp
index f035810eca3..d99975e72d8 100644
--- a/src/server/game/Scripting/ScriptMgr.cpp
+++ b/src/server/game/Scripting/ScriptMgr.cpp
@@ -691,6 +691,15 @@ bool ScriptMgr::OnItemExpire(Player* player, ItemTemplate const* proto)
return tmpscript->OnExpire(player, proto);
}
+bool ScriptMgr::OnItemRemove(Player* player, Item* item)
+{
+ ASSERT(player);
+ ASSERT(item);
+
+ GET_SCRIPT_RET(ItemScript, item->GetScriptId(), tmpscript, false);
+ return tmpscript->OnRemove(player, item);
+}
+
bool ScriptMgr::OnDummyEffect(Unit* caster, uint32 spellId, SpellEffIndex effIndex, Creature* target)
{
ASSERT(caster);
diff --git a/src/server/game/Scripting/ScriptMgr.h b/src/server/game/Scripting/ScriptMgr.h
index ada1953c305..6c79d66b9ca 100644
--- a/src/server/game/Scripting/ScriptMgr.h
+++ b/src/server/game/Scripting/ScriptMgr.h
@@ -387,6 +387,9 @@ class ItemScript : public ScriptObject
// Called when the item expires (is destroyed).
virtual bool OnExpire(Player* /*player*/, ItemTemplate const* /*proto*/) { return false; }
+
+ // Called when the item is destroyed.
+ virtual bool OnRemove(Player* /*player*/, Item* /*item*/) { return false; }
};
class UnitScript : public ScriptObject
@@ -917,6 +920,7 @@ class ScriptMgr
bool OnQuestAccept(Player* player, Item* item, Quest const* quest);
bool OnItemUse(Player* player, Item* item, SpellCastTargets const& targets);
bool OnItemExpire(Player* player, ItemTemplate const* proto);
+ bool OnItemRemove(Player* player, Item* item);
public: /* CreatureScript */
diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h
index 336e28ea0fe..b50b2a9d0ec 100644
--- a/src/server/game/Server/WorldSession.h
+++ b/src/server/game/Server/WorldSession.h
@@ -365,7 +365,6 @@ class WorldSession
uint32 GetLatency() const { return m_latency; }
void SetLatency(uint32 latency) { m_latency = latency; }
void ResetClientTimeDelay() { m_clientTimeDelay = 0; }
- uint32 getDialogStatus(Player* player, Object* questgiver);
ACE_Atomic_Op<ACE_Thread_Mutex, time_t> m_timeOutTime;
void UpdateTimeOutTime(uint32 diff)
diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp
index abd4a5c1d1e..4d4c23e2170 100644
--- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp
+++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp
@@ -1868,6 +1868,7 @@ void AuraEffect::HandleAuraModShapeshift(AuraApplication const* aurApp, uint8 mo
if (target->GetTypeId() == TYPEID_PLAYER)
{
SpellShapeshiftFormEntry const* shapeInfo = sSpellShapeshiftFormStore.LookupEntry(form);
+ ASSERT(shapeInfo);
// Learn spells for shapeshift form - no need to send action bars or add spells to spellbook
for (uint8 i = 0; i < MAX_SHAPESHIFT_SPELLS; ++i)
{
@@ -4748,7 +4749,7 @@ void AuraEffect::HandleAuraDummy(AuraApplication const* aurApp, uint8 mode, bool
uint32 spellId = 24659;
if (apply && caster)
{
- SpellInfo const* spell = sSpellMgr->GetSpellInfo(spellId);
+ SpellInfo const* spell = sSpellMgr->EnsureSpellInfo(spellId);
for (uint32 i = 0; i < spell->StackAmount; ++i)
caster->CastSpell(target, spell->Id, true, NULL, NULL, GetCasterGUID());
@@ -4763,7 +4764,7 @@ void AuraEffect::HandleAuraDummy(AuraApplication const* aurApp, uint8 mode, bool
uint32 spellId = 24662;
if (apply && caster)
{
- SpellInfo const* spell = sSpellMgr->GetSpellInfo(spellId);
+ SpellInfo const* spell = sSpellMgr->EnsureSpellInfo(spellId);
for (uint32 i = 0; i < spell->StackAmount; ++i)
caster->CastSpell(target, spell->Id, true, NULL, NULL, GetCasterGUID());
break;
@@ -6034,8 +6035,8 @@ void AuraEffect::HandlePeriodicHealAurasTick(Unit* target, Unit* caster) const
caster->DealDamage(caster, funnelDamage, &cleanDamage, NODAMAGE, GetSpellInfo()->GetSchoolMask(), GetSpellInfo(), true);
}
- uint32 procAttacker = PROC_FLAG_DONE_PERIODIC;
- uint32 procVictim = PROC_FLAG_TAKEN_PERIODIC;
+ uint32 procAttacker = PROC_FLAG_DONE_PERIODIC | PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_POS;
+ uint32 procVictim = PROC_FLAG_TAKEN_PERIODIC | PROC_FLAG_TAKEN_SPELL_MAGIC_DMG_CLASS_POS;
uint32 procEx = (crit ? PROC_EX_CRITICAL_HIT : PROC_EX_NORMAL_HIT) | PROC_EX_INTERNAL_HOT;
// ignore item heals
if (!haveCastItem)
diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp
index edf9271df1a..b68a9831d64 100644
--- a/src/server/game/Spells/Spell.cpp
+++ b/src/server/game/Spells/Spell.cpp
@@ -958,7 +958,7 @@ void Spell::SelectImplicitChannelTargets(SpellEffIndex effIndex, SpellImplicitTa
case TARGET_UNIT_CHANNEL_TARGET:
{
WorldObject* target = ObjectAccessor::GetUnit(*m_caster, m_originalCaster->GetUInt64Value(UNIT_FIELD_CHANNEL_OBJECT));
- CallScriptObjectTargetSelectHandlers(target, effIndex);
+ CallScriptObjectTargetSelectHandlers(target, effIndex, targetType);
// unit target may be no longer avalible - teleported out of map for example
if (target && target->ToUnit())
AddUnitTarget(target->ToUnit(), 1 << effIndex);
@@ -971,7 +971,7 @@ void Spell::SelectImplicitChannelTargets(SpellEffIndex effIndex, SpellImplicitTa
m_targets.SetDst(channeledSpell->m_targets);
else if (WorldObject* target = ObjectAccessor::GetWorldObject(*m_caster, m_originalCaster->GetUInt64Value(UNIT_FIELD_CHANNEL_OBJECT)))
{
- CallScriptObjectTargetSelectHandlers(target, effIndex);
+ CallScriptObjectTargetSelectHandlers(target, effIndex, targetType);
if (target)
m_targets.SetDst(*target);
}
@@ -1052,17 +1052,34 @@ void Spell::SelectImplicitNearbyTargets(SpellEffIndex effIndex, SpellImplicitTar
return;
}
- CallScriptObjectTargetSelectHandlers(target, effIndex);
+ CallScriptObjectTargetSelectHandlers(target, effIndex, targetType);
+ if (!target)
+ {
+ TC_LOG_DEBUG("spells", "Spell::SelectImplicitNearbyTargets: OnObjectTargetSelect script hook for spell Id %u set NULL target, effect %u", m_spellInfo->Id, effIndex);
+ return;
+ }
switch (targetType.GetObjectType())
{
case TARGET_OBJECT_TYPE_UNIT:
+ {
if (Unit* unitTarget = target->ToUnit())
AddUnitTarget(unitTarget, effMask, true, false);
+ else
+ {
+ TC_LOG_DEBUG("spells", "Spell::SelectImplicitNearbyTargets: OnObjectTargetSelect script hook for spell Id %u set object of wrong type, expected unit, got %s, effect %u", m_spellInfo->Id, GetLogNameForGuid(target->GetGUID()), effMask);
+ return;
+ }
break;
+ }
case TARGET_OBJECT_TYPE_GOBJ:
if (GameObject* gobjTarget = target->ToGameObject())
AddGOTarget(gobjTarget, effMask);
+ else
+ {
+ TC_LOG_DEBUG("spells", "Spell::SelectImplicitNearbyTargets: OnObjectTargetSelect script hook for spell Id %u set object of wrong type, expected gameobject, got %s, effect %u", m_spellInfo->Id, GetLogNameForGuid(target->GetGUID()), effMask);
+ return;
+ }
break;
case TARGET_OBJECT_TYPE_DEST:
m_targets.SetDst(*target);
@@ -1095,7 +1112,7 @@ void Spell::SelectImplicitConeTargets(SpellEffIndex effIndex, SpellImplicitTarge
Trinity::WorldObjectListSearcher<Trinity::WorldObjectSpellConeTargetCheck> searcher(m_caster, targets, check, containerTypeMask);
SearchTargets<Trinity::WorldObjectListSearcher<Trinity::WorldObjectSpellConeTargetCheck> >(searcher, containerTypeMask, m_caster, m_caster, radius);
- CallScriptObjectAreaTargetSelectHandlers(targets, effIndex);
+ CallScriptObjectAreaTargetSelectHandlers(targets, effIndex, targetType);
if (!targets.empty())
{
@@ -1171,7 +1188,7 @@ void Spell::SelectImplicitAreaTargets(SpellEffIndex effIndex, SpellImplicitTarge
float radius = m_spellInfo->Effects[effIndex].CalcRadius(m_caster) * m_spellValue->RadiusMod;
SearchAreaTargets(targets, radius, center, referer, targetType.GetObjectType(), targetType.GetCheckType(), m_spellInfo->Effects[effIndex].ImplicitTargetConditions);
- CallScriptObjectAreaTargetSelectHandlers(targets, effIndex);
+ CallScriptObjectAreaTargetSelectHandlers(targets, effIndex, targetType);
if (!targets.empty())
{
@@ -1273,7 +1290,7 @@ void Spell::SelectImplicitCasterDestTargets(SpellEffIndex effIndex, SpellImplici
}
}
- CallScriptDestinationTargetSelectHandlers(dest, effIndex);
+ CallScriptDestinationTargetSelectHandlers(dest, effIndex, targetType);
m_targets.SetDst(dest);
}
@@ -1306,7 +1323,7 @@ void Spell::SelectImplicitTargetDestTargets(SpellEffIndex effIndex, SpellImplici
}
}
- CallScriptDestinationTargetSelectHandlers(dest, effIndex);
+ CallScriptDestinationTargetSelectHandlers(dest, effIndex, targetType);
m_targets.SetDst(dest);
}
@@ -1345,7 +1362,7 @@ void Spell::SelectImplicitDestDestTargets(SpellEffIndex effIndex, SpellImplicitT
}
}
- CallScriptDestinationTargetSelectHandlers(dest, effIndex);
+ CallScriptDestinationTargetSelectHandlers(dest, effIndex, targetType);
m_targets.ModDst(dest);
}
@@ -1388,7 +1405,7 @@ void Spell::SelectImplicitCasterObjectTargets(SpellEffIndex effIndex, SpellImpli
break;
}
- CallScriptObjectTargetSelectHandlers(target, effIndex);
+ CallScriptObjectTargetSelectHandlers(target, effIndex, targetType);
if (target && target->ToUnit())
AddUnitTarget(target->ToUnit(), 1 << effIndex, checkIfValid);
@@ -1400,7 +1417,7 @@ void Spell::SelectImplicitTargetObjectTargets(SpellEffIndex effIndex, SpellImpli
WorldObject* target = m_targets.GetObjectTarget();
- CallScriptObjectTargetSelectHandlers(target, effIndex);
+ CallScriptObjectTargetSelectHandlers(target, effIndex, targetType);
if (target)
{
@@ -1435,7 +1452,7 @@ void Spell::SelectImplicitChainTargets(SpellEffIndex effIndex, SpellImplicitTarg
, m_spellInfo->Effects[effIndex].ImplicitTargetConditions, targetType.GetTarget() == TARGET_UNIT_TARGET_CHAINHEAL_ALLY);
// Chain primary target is added earlier
- CallScriptObjectAreaTargetSelectHandlers(targets, effIndex);
+ CallScriptObjectAreaTargetSelectHandlers(targets, effIndex, targetType);
for (std::list<WorldObject*>::iterator itr = targets.begin(); itr != targets.end(); ++itr)
if (Unit* unitTarget = (*itr)->ToUnit())
@@ -1589,7 +1606,7 @@ void Spell::SelectImplicitTrajTargets(SpellEffIndex effIndex)
SpellDestination dest(*m_targets.GetDst());
dest.Relocate(trajDst);
- CallScriptDestinationTargetSelectHandlers(dest, effIndex);
+ CallScriptDestinationTargetSelectHandlers(dest, effIndex, SpellImplicitTargetInfo(TARGET_DEST_TRAJ));
m_targets.ModDst(dest);
}
@@ -1609,7 +1626,7 @@ void Spell::SelectEffectTypeImplicitTargets(uint8 effIndex)
{
WorldObject* target = ObjectAccessor::FindPlayer(m_caster->GetTarget());
- CallScriptObjectTargetSelectHandlers(target, SpellEffIndex(effIndex));
+ CallScriptObjectTargetSelectHandlers(target, SpellEffIndex(effIndex), SpellImplicitTargetInfo());
if (target && target->ToPlayer())
AddUnitTarget(target->ToUnit(), 1 << effIndex, false);
@@ -1669,7 +1686,7 @@ void Spell::SelectEffectTypeImplicitTargets(uint8 effIndex)
break;
}
- CallScriptObjectTargetSelectHandlers(target, SpellEffIndex(effIndex));
+ CallScriptObjectTargetSelectHandlers(target, SpellEffIndex(effIndex), SpellImplicitTargetInfo());
if (target)
{
@@ -3028,7 +3045,7 @@ void Spell::cast(bool skipCheck)
if (this->GetSpellInfo()->DmgClass != SPELL_DAMAGE_CLASS_NONE)
if (Pet* playerPet = playerCaster->GetPet())
if (playerPet->IsAlive() && playerPet->isControlled() && (m_targets.GetTargetMask() & TARGET_FLAG_UNIT))
- playerPet->AI()->OwnerAttacked(m_targets.GetObjectTarget()->ToUnit());
+ playerPet->AI()->OwnerAttacked(m_targets.GetUnitTarget());
}
SetExecutedCurrently(true);
@@ -5062,7 +5079,7 @@ SpellCastResult Spell::CheckCast(bool strict)
target->GetFirstCollisionPosition(pos, CONTACT_DISTANCE, target->GetRelativeAngle(m_caster));
m_preGeneratedPath.SetPathLengthLimit(m_spellInfo->GetMaxRange(true) * 1.5f);
- bool result = m_preGeneratedPath.CalculatePath(pos.m_positionX, pos.m_positionY, pos.m_positionZ + target->GetObjectSize());
+ bool result = m_preGeneratedPath.CalculatePath(pos.m_positionX, pos.m_positionY, pos.m_positionZ + target->GetObjectSize(), false, true);
if (m_preGeneratedPath.GetPathType() & PATHFIND_SHORT)
return SPELL_FAILED_OUT_OF_RANGE;
else if (!result || m_preGeneratedPath.GetPathType() & PATHFIND_NOPATH)
@@ -5182,6 +5199,7 @@ SpellCastResult Spell::CheckCast(bool strict)
case SUMMON_CATEGORY_PET:
if (m_caster->GetPetGUID())
return SPELL_FAILED_ALREADY_HAVE_SUMMON;
+ // intentional missing break, check both GetPetGUID() and GetCharmGUID for SUMMON_CATEGORY_PET
case SUMMON_CATEGORY_PUPPET:
if (m_caster->GetCharmGUID())
return SPELL_FAILED_ALREADY_HAVE_CHARM;
@@ -6996,42 +7014,42 @@ void Spell::CallScriptAfterHitHandlers()
}
}
-void Spell::CallScriptObjectAreaTargetSelectHandlers(std::list<WorldObject*>& targets, SpellEffIndex effIndex)
+void Spell::CallScriptObjectAreaTargetSelectHandlers(std::list<WorldObject*>& targets, SpellEffIndex effIndex, SpellImplicitTargetInfo const& targetType)
{
for (std::list<SpellScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr)
{
(*scritr)->_PrepareScriptCall(SPELL_SCRIPT_HOOK_OBJECT_AREA_TARGET_SELECT);
std::list<SpellScript::ObjectAreaTargetSelectHandler>::iterator hookItrEnd = (*scritr)->OnObjectAreaTargetSelect.end(), hookItr = (*scritr)->OnObjectAreaTargetSelect.begin();
for (; hookItr != hookItrEnd; ++hookItr)
- if ((*hookItr).IsEffectAffected(m_spellInfo, effIndex))
- (*hookItr).Call(*scritr, targets);
+ if (hookItr->IsEffectAffected(m_spellInfo, effIndex) && targetType.GetTarget() == hookItr->GetTarget())
+ hookItr->Call(*scritr, targets);
(*scritr)->_FinishScriptCall();
}
}
-void Spell::CallScriptObjectTargetSelectHandlers(WorldObject*& target, SpellEffIndex effIndex)
+void Spell::CallScriptObjectTargetSelectHandlers(WorldObject*& target, SpellEffIndex effIndex, SpellImplicitTargetInfo const& targetType)
{
for (std::list<SpellScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr)
{
(*scritr)->_PrepareScriptCall(SPELL_SCRIPT_HOOK_OBJECT_TARGET_SELECT);
std::list<SpellScript::ObjectTargetSelectHandler>::iterator hookItrEnd = (*scritr)->OnObjectTargetSelect.end(), hookItr = (*scritr)->OnObjectTargetSelect.begin();
for (; hookItr != hookItrEnd; ++hookItr)
- if ((*hookItr).IsEffectAffected(m_spellInfo, effIndex))
- (*hookItr).Call(*scritr, target);
+ if (hookItr->IsEffectAffected(m_spellInfo, effIndex) && targetType.GetTarget() == hookItr->GetTarget())
+ hookItr->Call(*scritr, target);
(*scritr)->_FinishScriptCall();
}
}
-void Spell::CallScriptDestinationTargetSelectHandlers(SpellDestination& target, SpellEffIndex effIndex)
+void Spell::CallScriptDestinationTargetSelectHandlers(SpellDestination& target, SpellEffIndex effIndex, SpellImplicitTargetInfo const& targetType)
{
for (std::list<SpellScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr)
{
(*scritr)->_PrepareScriptCall(SPELL_SCRIPT_HOOK_DESTINATION_TARGET_SELECT);
std::list<SpellScript::DestinationTargetSelectHandler>::iterator hookItrEnd = (*scritr)->OnDestinationTargetSelect.end(), hookItr = (*scritr)->OnDestinationTargetSelect.begin();
for (; hookItr != hookItrEnd; ++hookItr)
- if (hookItr->IsEffectAffected(m_spellInfo, effIndex))
+ if (hookItr->IsEffectAffected(m_spellInfo, effIndex) && targetType.GetTarget() == hookItr->GetTarget())
hookItr->Call(*scritr, target);
(*scritr)->_FinishScriptCall();
diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h
index 0fa162f06c7..9cb0baea049 100644
--- a/src/server/game/Spells/Spell.h
+++ b/src/server/game/Spells/Spell.h
@@ -644,9 +644,9 @@ class Spell
void CallScriptBeforeHitHandlers();
void CallScriptOnHitHandlers();
void CallScriptAfterHitHandlers();
- void CallScriptObjectAreaTargetSelectHandlers(std::list<WorldObject*>& targets, SpellEffIndex effIndex);
- void CallScriptObjectTargetSelectHandlers(WorldObject*& target, SpellEffIndex effIndex);
- void CallScriptDestinationTargetSelectHandlers(SpellDestination& target, SpellEffIndex effIndex);
+ void CallScriptObjectAreaTargetSelectHandlers(std::list<WorldObject*>& targets, SpellEffIndex effIndex, SpellImplicitTargetInfo const& targetType);
+ void CallScriptObjectTargetSelectHandlers(WorldObject*& target, SpellEffIndex effIndex, SpellImplicitTargetInfo const& targetType);
+ void CallScriptDestinationTargetSelectHandlers(SpellDestination& target, SpellEffIndex effIndex, SpellImplicitTargetInfo const& targetType);
bool CheckScriptEffectImplicitTargets(uint32 effIndex, uint32 effIndexToCheck);
std::list<SpellScript*> m_loadedScripts;
diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp
index ba8fd63f853..0b7c3d3d5f8 100644
--- a/src/server/game/Spells/SpellEffects.cpp
+++ b/src/server/game/Spells/SpellEffects.cpp
@@ -1666,7 +1666,7 @@ void Spell::EffectEnergize(SpellEffIndex effIndex)
sSpellMgr->GetSetOfSpellsInSpellGroup(SPELL_GROUP_ELIXIR_BATTLE, avalibleElixirs);
for (std::set<uint32>::iterator itr = avalibleElixirs.begin(); itr != avalibleElixirs.end();)
{
- SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(*itr);
+ SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(*itr);
if (spellInfo->SpellLevel < m_spellInfo->SpellLevel || spellInfo->SpellLevel > unitTarget->getLevel())
avalibleElixirs.erase(itr++);
else if (sSpellMgr->IsSpellMemberOfSpellGroup(*itr, SPELL_GROUP_ELIXIR_SHATTRATH))
@@ -3294,9 +3294,6 @@ void Spell::EffectScriptEffect(SpellEffIndex effIndex)
}
return;
}
- case 45204: // Clone Me!
- m_caster->CastSpell(unitTarget, damage, true);
- break;
case 55693: // Remove Collapsing Cave Aura
if (!unitTarget)
return;
@@ -4577,7 +4574,7 @@ void Spell::EffectQuestClear(SpellEffIndex effIndex)
}
}
- player->RemoveActiveQuest(quest_id);
+ player->RemoveActiveQuest(quest_id, false);
player->RemoveRewardedQuest(quest_id);
}
diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp
index 49bab7946a1..cbc245ba967 100644
--- a/src/server/game/Spells/SpellMgr.cpp
+++ b/src/server/game/Spells/SpellMgr.cpp
@@ -817,7 +817,7 @@ bool SpellMgr::IsSpellProcEventCanTriggeredBy(SpellProcEventEntry const* spellPr
{
if (EventProcFlag & PROC_FLAG_TAKEN_SPELL_MAGIC_DMG_CLASS_POS)
{
- if (!(procExtra & PROC_EX_INTERNAL_HOT))
+ if (!(procExtra & PROC_EX_INTERNAL_DOT))
return false;
}
else if (procExtra & PROC_EX_INTERNAL_HOT)
@@ -3390,6 +3390,15 @@ void SpellMgr::LoadSpellInfoCorrections()
case 71169: // Shadow's Fate
spellInfo->AttributesEx3 |= SPELL_ATTR3_STACK_FOR_DIFF_CASTERS;
break;
+ case 72347: // Lock Players and Tap Chest
+ spellInfo->AttributesEx3 &= ~SPELL_ATTR3_NO_INITIAL_AGGRO;
+ break;
+ case 73843: // Award Reputation - Boss Kill
+ case 73844: // Award Reputation - Boss Kill
+ case 73845: // Award Reputation - Boss Kill
+ case 73846: // Award Reputation - Boss Kill
+ spellInfo->Effects[EFFECT_0].RadiusEntry = sSpellRadiusStore.LookupEntry(EFFECT_RADIUS_50000_YARDS); // 50000yd
+ break;
case 72378: // Blood Nova (Deathbringer Saurfang)
case 73058: // Blood Nova (Deathbringer Saurfang)
spellInfo->Effects[EFFECT_0].RadiusEntry = sSpellRadiusStore.LookupEntry(EFFECT_RADIUS_200_YARDS);
diff --git a/src/server/game/Spells/SpellMgr.h b/src/server/game/Spells/SpellMgr.h
index 184b3ad0cb2..1359b9f2002 100644
--- a/src/server/game/Spells/SpellMgr.h
+++ b/src/server/game/Spells/SpellMgr.h
@@ -690,6 +690,14 @@ class SpellMgr
// SpellInfo object management
SpellInfo const* GetSpellInfo(uint32 spellId) const { return spellId < GetSpellInfoStoreSize() ? mSpellInfoMap[spellId] : NULL; }
+ // Use this only with 100% valid spellIds
+ SpellInfo const* EnsureSpellInfo(uint32 spellId) const
+ {
+ ASSERT(spellId < GetSpellInfoStoreSize());
+ SpellInfo const* spellInfo = mSpellInfoMap[spellId];
+ ASSERT(spellInfo);
+ return spellInfo;
+ }
uint32 GetSpellInfoStoreSize() const { return mSpellInfoMap.size(); }
private:
diff --git a/src/server/game/Spells/SpellScript.h b/src/server/game/Spells/SpellScript.h
index 6378a8bed9b..75a191a9801 100644
--- a/src/server/game/Spells/SpellScript.h
+++ b/src/server/game/Spells/SpellScript.h
@@ -208,6 +208,7 @@ class SpellScript : public _SpellScript
TargetHook(uint8 _effectIndex, uint16 _targetType, bool _area, bool _dest);
bool CheckEffect(SpellInfo const* spellInfo, uint8 effIndex);
std::string ToString();
+ uint16 GetTarget() const { return targetType; }
protected:
uint16 targetType;
bool area;
diff --git a/src/server/game/Texts/CreatureTextMgr.cpp b/src/server/game/Texts/CreatureTextMgr.cpp
index da2fbaf0ece..7d7e9eb7c21 100644
--- a/src/server/game/Texts/CreatureTextMgr.cpp
+++ b/src/server/game/Texts/CreatureTextMgr.cpp
@@ -185,7 +185,7 @@ uint32 CreatureTextMgr::SendChat(Creature* source, uint8 textGroup, WorldObject
CreatureTextMap::const_iterator sList = mTextMap.find(source->GetEntry());
if (sList == mTextMap.end())
{
- TC_LOG_ERROR("sql.sql", "CreatureTextMgr: Could not find Text for Creature(%s) Entry %u in 'creature_text' table. Ignoring.", source->GetName().c_str(), source->GetEntry());
+ TC_LOG_ERROR("sql.sql", "CreatureTextMgr: Could not find Text for Creature %s (Entry %u, GUID %u) in 'creature_text' table. Ignoring.", source->GetName().c_str(), source->GetEntry(), source->GetGUIDLow());
return 0;
}
@@ -193,7 +193,7 @@ uint32 CreatureTextMgr::SendChat(Creature* source, uint8 textGroup, WorldObject
CreatureTextHolder::const_iterator itr = textHolder.find(textGroup);
if (itr == textHolder.end())
{
- TC_LOG_ERROR("sql.sql", "CreatureTextMgr: Could not find TextGroup %u for Creature(%s) GuidLow %u Entry %u. Ignoring.", uint32(textGroup), source->GetName().c_str(), source->GetGUIDLow(), source->GetEntry());
+ TC_LOG_ERROR("sql.sql", "CreatureTextMgr: Could not find TextGroup %u for Creature %s (Entry %u, GUID %u) in 'creature_text' table. Ignoring.", uint32(textGroup), source->GetName().c_str(), source->GetEntry(), source->GetGUIDLow());
return 0;
}
diff --git a/src/server/game/Tickets/TicketMgr.cpp b/src/server/game/Tickets/TicketMgr.cpp
index e9f8748e6d9..77fbe92400d 100644
--- a/src/server/game/Tickets/TicketMgr.cpp
+++ b/src/server/game/Tickets/TicketMgr.cpp
@@ -36,7 +36,9 @@ GmTicket::GmTicket() : _id(0), _playerGuid(0), _posX(0), _posY(0), _posZ(0), _ma
_closedBy(0), _assignedTo(0), _completed(false), _escalatedStatus(TICKET_UNASSIGNED), _viewed(false),
_needResponse(false), _needMoreHelp(false) { }
-GmTicket::GmTicket(Player* player) : _createTime(time(NULL)), _lastModifiedTime(time(NULL)), _closedBy(0), _assignedTo(0), _completed(false), _escalatedStatus(TICKET_UNASSIGNED), _viewed(false), _needResponse(false), _needMoreHelp(false)
+GmTicket::GmTicket(Player* player) : _posX(0), _posY(0), _posZ(0), _mapId(0), _createTime(time(NULL)), _lastModifiedTime(time(NULL)),
+ _closedBy(0), _assignedTo(0), _completed(false), _escalatedStatus(TICKET_UNASSIGNED), _viewed(false),
+ _needResponse(false), _needMoreHelp(false)
{
_id = sTicketMgr->GenerateTicketId();
_playerName = player->GetName();
diff --git a/src/server/game/Tools/PlayerDump.cpp b/src/server/game/Tools/PlayerDump.cpp
index 821d2c568a5..3cde5a6d70f 100644
--- a/src/server/game/Tools/PlayerDump.cpp
+++ b/src/server/game/Tools/PlayerDump.cpp
@@ -43,7 +43,6 @@ static DumpTable dumpTables[DUMP_TABLE_COUNT] =
{ "character_cuf_profiles", DTT_CHAR_TABLE },
{ "character_declinedname", DTT_CHAR_TABLE },
{ "character_equipmentsets", DTT_EQSET_TABLE},
- { "character_gifts", DTT_ITEM_GIFT },
{ "character_glyphs", DTT_CHAR_TABLE },
{ "character_homebind", DTT_CHAR_TABLE },
{ "character_inventory", DTT_INVENTORY },
@@ -56,12 +55,13 @@ static DumpTable dumpTables[DUMP_TABLE_COUNT] =
{ "character_spell", DTT_CHAR_TABLE },
{ "character_spell_cooldown", DTT_CHAR_TABLE },
{ "character_talent", DTT_CHAR_TABLE },
- { "item_instance", DTT_ITEM },
{ "mail", DTT_MAIL },
- { "mail_items", DTT_MAIL_ITEM },
- { "pet_aura", DTT_PET_TABLE },
- { "pet_spell", DTT_PET_TABLE },
- { "pet_spell_cooldown", DTT_PET_TABLE },
+ { "mail_items", DTT_MAIL_ITEM }, // must be after mail
+ { "pet_aura", DTT_PET_TABLE }, // must be after character_pet
+ { "pet_spell", DTT_PET_TABLE }, // must be after character_pet
+ { "pet_spell_cooldown", DTT_PET_TABLE }, // must be after character_pet
+ { "item_instance", DTT_ITEM }, // must be after character_inventory and mail_items
+ { "character_gifts", DTT_ITEM_GIFT }, // must be after item_instance
};
// Low level functions
diff --git a/src/server/game/Warden/Warden.cpp b/src/server/game/Warden/Warden.cpp
index 4611a4da884..42872bba22e 100644
--- a/src/server/game/Warden/Warden.cpp
+++ b/src/server/game/Warden/Warden.cpp
@@ -223,6 +223,9 @@ std::string Warden::Penalty(WardenCheck* check /*= NULL*/)
void WorldSession::HandleWardenDataOpcode(WorldPacket& recvData)
{
+ if (!_warden)
+ return;
+
_warden->DecryptData(recvData.contents(), recvData.size());
uint8 opcode;
recvData >> opcode;
diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp
index 5f6fb683e8f..34c38343ebf 100644
--- a/src/server/game/World/World.cpp
+++ b/src/server/game/World/World.cpp
@@ -1308,6 +1308,8 @@ void World::LoadConfigSettings(bool reload)
m_int_configs[CONFIG_PACKET_SPOOF_BANDURATION] = sConfigMgr->GetIntDefault("PacketSpoof.BanDuration", 86400);
+ m_int_configs[CONFIG_BIRTHDAY_TIME] = sConfigMgr->GetIntDefault("BirthdayTime", 1222964635);
+
// call ScriptMgr if we're reloading the configuration
if (reload)
sScriptMgr->OnConfigLoad(reload);
@@ -1693,7 +1695,7 @@ void World::SetInitialWorldSettings()
sObjectMgr->LoadGameObjectForQuests();
TC_LOG_INFO("server.loading", "Loading BattleMasters...");
- sBattlegroundMgr->LoadBattleMastersEntry();
+ sBattlegroundMgr->LoadBattleMastersEntry(); // must be after load CreatureTemplate
TC_LOG_INFO("server.loading", "Loading GameTeleports...");
sObjectMgr->LoadGameTele();
@@ -2942,7 +2944,7 @@ void World::ResetDailyQuests()
{
TC_LOG_INFO("misc", "Daily quests reset for all characters.");
- PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_QUEST_STATUS_DAILY);
+ PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_RESET_CHARACTER_QUESTSTATUS_DAILY);
CharacterDatabase.Execute(stmt);
for (SessionMap::const_iterator itr = m_sessions.begin(); itr != m_sessions.end(); ++itr)
@@ -2988,7 +2990,7 @@ void World::ResetWeeklyQuests()
{
TC_LOG_INFO("misc", "Weekly quests reset for all characters.");
- PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_QUEST_STATUS_WEEKLY);
+ PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_RESET_CHARACTER_QUESTSTATUS_WEEKLY);
CharacterDatabase.Execute(stmt);
for (SessionMap::const_iterator itr = m_sessions.begin(); itr != m_sessions.end(); ++itr)
@@ -3006,7 +3008,7 @@ void World::ResetMonthlyQuests()
{
TC_LOG_INFO("misc", "Monthly quests reset for all characters.");
- PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_QUEST_STATUS_MONTHLY);
+ PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_RESET_CHARACTER_QUESTSTATUS_MONTHLY);
CharacterDatabase.Execute(stmt);
for (SessionMap::const_iterator itr = m_sessions.begin(); itr != m_sessions.end(); ++itr)
@@ -3048,7 +3050,9 @@ void World::ResetMonthlyQuests()
void World::ResetEventSeasonalQuests(uint16 event_id)
{
- PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_QUEST_STATUS_SEASONAL);
+ TC_LOG_INFO("misc", "Seasonal quests reset for all characters.");
+
+ PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_RESET_CHARACTER_QUESTSTATUS_SEASONAL_BY_EVENT);
stmt->setUInt16(0, event_id);
CharacterDatabase.Execute(stmt);
diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h
index c8937a062b6..38c8ebffb30 100644
--- a/src/server/game/World/World.h
+++ b/src/server/game/World/World.h
@@ -356,6 +356,7 @@ enum WorldIntConfigs
CONFIG_BG_REWARD_LOSER_HONOR_LAST,
CONFIG_BG_REWARD_WINNER_CONQUEST_FIRST,
CONFIG_BG_REWARD_WINNER_CONQUEST_LAST,
+ CONFIG_BIRTHDAY_TIME,
INT_CONFIG_VALUE_COUNT
};