diff options
| author | Vincent-Michael <Vincent_Michael@gmx.de> | 2014-04-05 20:43:05 +0200 |
|---|---|---|
| committer | Vincent-Michael <Vincent_Michael@gmx.de> | 2014-04-05 20:43:05 +0200 |
| commit | e4b14b943d3a2aa5d7b41107adc8c5236b86d1af (patch) | |
| tree | 2ef54a0bc7d4f5385b9266afe528265ed9f35f8d /src/server/game | |
| parent | a903c335024d0ef4a3d9899587fcd0b10a92b31e (diff) | |
| parent | 29610b250dd5017f068264d9b1a37748c9f30feb (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')
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 }; |
