@@ -29,6 +29,7 @@
|
||||
#include "Config.h"
|
||||
#include "DatabaseEnv.h"
|
||||
#include "DatabaseLoader.h"
|
||||
#include "GitRevision.h"
|
||||
#include "IPLocation.h"
|
||||
#include "IoContext.h"
|
||||
#include "Log.h"
|
||||
@@ -75,7 +76,7 @@ int main(int argc, char** argv)
|
||||
auto vm = GetConsoleArguments(argc, argv, configFile);
|
||||
|
||||
// exit if help or version is enabled
|
||||
if (vm.count("help"))
|
||||
if (vm.count("help") || vm.count("version"))
|
||||
return 0;
|
||||
|
||||
// Add file and args in config
|
||||
@@ -292,13 +293,11 @@ variables_map GetConsoleArguments(int argc, char** argv, fs::path& configFile)
|
||||
}
|
||||
|
||||
if (variablesMap.count("help"))
|
||||
{
|
||||
std::cout << all << "\n";
|
||||
}
|
||||
else if (variablesMap.count("version"))
|
||||
std::cout << GitRevision::GetFullVersion() << "\n";
|
||||
else if (variablesMap.count("dry-run"))
|
||||
{
|
||||
sConfigMgr->setDryRun(true);
|
||||
}
|
||||
|
||||
return variablesMap;
|
||||
}
|
||||
|
||||
@@ -127,7 +127,7 @@ int main(int argc, char** argv)
|
||||
auto vm = GetConsoleArguments(argc, argv, configFile, configService);
|
||||
|
||||
// exit if help or version is enabled
|
||||
if (vm.count("help"))
|
||||
if (vm.count("help") || vm.count("version"))
|
||||
return 0;
|
||||
|
||||
#if AC_PLATFORM == AC_PLATFORM_WINDOWS
|
||||
@@ -744,13 +744,11 @@ variables_map GetConsoleArguments(int argc, char** argv, fs::path& configFile, [
|
||||
}
|
||||
|
||||
if (vm.count("help"))
|
||||
{
|
||||
std::cout << all << "\n";
|
||||
}
|
||||
else if (vm.count("version"))
|
||||
std::cout << GitRevision::GetFullVersion() << "\n";
|
||||
else if (vm.count("dry-run"))
|
||||
{
|
||||
sConfigMgr->setDryRun(true);
|
||||
}
|
||||
|
||||
return vm;
|
||||
}
|
||||
|
||||
@@ -657,7 +657,7 @@ Appender.Console=1,4,0,"1 9 3 6 5 8"
|
||||
Appender.Server=2,5,0,Server.log,w
|
||||
Appender.Playerbots=2,5,0,Playerbots.log,w
|
||||
# Appender.GM=2,5,15,gm_%s.log
|
||||
Appender.Errors=2,5,0,Errors.log,w
|
||||
Appender.Errors=2,2,0,Errors.log,w
|
||||
# Appender.DB=3,5,0
|
||||
|
||||
# Logger config values: Given a logger "name"
|
||||
@@ -686,6 +686,7 @@ Logger.mmaps=4,Server
|
||||
Logger.scripts.hotswap=4,Console Server
|
||||
Logger.server=4,Console Server
|
||||
Logger.sql.sql=2,Console Errors
|
||||
Logger.sql.updates=4,Console Server Errors
|
||||
Logger.sql=4,Console Server
|
||||
Logger.time.update=4,Console Server
|
||||
Logger.module=4,Console Server
|
||||
@@ -785,7 +786,6 @@ Logger.playerbots=5,Console Playerbots
|
||||
#Logger.spells=4,Console Server
|
||||
#Logger.sql.dev=4,Console Server
|
||||
#Logger.sql.driver=4,Console Server
|
||||
#Logger.sql.updates=4,Console Server
|
||||
#Logger.vehicles=4,Console Server
|
||||
#Logger.warden=4,Console Server
|
||||
#Logger.weather=4,Console Server
|
||||
@@ -3143,6 +3143,16 @@ LeaveGroupOnLogout.Enabled = 0
|
||||
|
||||
Group.Raid.LevelRestriction = 10
|
||||
|
||||
#
|
||||
# Group.RandomRollMaximum
|
||||
#
|
||||
# The maximum value for use with the client '/roll' command.
|
||||
# Blizzlike and maximum value is 1000000. (Based on Classic and 3.3.5a client testing respectively)
|
||||
# Default: 1000000
|
||||
#
|
||||
|
||||
Group.RandomRollMaximum = 1000000
|
||||
|
||||
#
|
||||
###################################################################################################
|
||||
|
||||
|
||||
@@ -607,7 +607,7 @@ void CharacterDatabaseConnection::DoPrepareStatements()
|
||||
PrepareStatement(CHAR_INS_PROFANITY_PLAYER_NAME, "INSERT IGNORE INTO profanity_name (name) VALUES (?)", CONNECTION_ASYNC);
|
||||
|
||||
// Character settings
|
||||
PrepareStatement(CHAR_SEL_CHAR_SETTINGS, "SELECT source, data FROM character_settings WHERE guid = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_SEL_CHAR_SETTINGS, "SELECT source, data FROM character_settings WHERE guid = ?", CONNECTION_BOTH);
|
||||
PrepareStatement(CHAR_REP_CHAR_SETTINGS, "REPLACE INTO character_settings (guid, source, data) VALUES (?, ?, ?)", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_DEL_CHAR_SETTINGS, "DELETE FROM character_settings WHERE guid = ?", CONNECTION_ASYNC);
|
||||
|
||||
|
||||
@@ -72,6 +72,8 @@ SmartAI::SmartAI(Creature* c) : CreatureAI(c)
|
||||
|
||||
mcanSpawn = true;
|
||||
|
||||
_chaseOnInterrupt = false;
|
||||
|
||||
// Xinef: Vehicle conditions
|
||||
m_ConditionsTimer = 0;
|
||||
if (me->GetVehicleKit())
|
||||
|
||||
@@ -212,6 +212,9 @@ public:
|
||||
// Xinef
|
||||
void SetWPPauseTimer(uint32 time) { mWPPauseTimer = time; }
|
||||
|
||||
void SetChaseOnInterrupt(bool apply) { _chaseOnInterrupt = apply; }
|
||||
[[nodiscard]] bool CanChaseOnInterrupt() const { return _chaseOnInterrupt; }
|
||||
|
||||
private:
|
||||
bool mIsCharmed;
|
||||
uint32 mFollowCreditType;
|
||||
@@ -257,6 +260,8 @@ private:
|
||||
void CheckConditions(const uint32 diff);
|
||||
ConditionList conditions;
|
||||
uint32 m_ConditionsTimer;
|
||||
|
||||
bool _chaseOnInterrupt;
|
||||
};
|
||||
|
||||
class SmartGameObjectAI : public GameObjectAI
|
||||
|
||||
@@ -742,6 +742,8 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
|
||||
|
||||
if (e.action.cast.castFlags & SMARTCAST_COMBAT_MOVE)
|
||||
{
|
||||
CAST_AI(SmartAI, me->AI())->SetChaseOnInterrupt(true);
|
||||
|
||||
if (!me->isMoving()) // Don't try to reposition while we are moving
|
||||
{
|
||||
// If cast flag SMARTCAST_COMBAT_MOVE is set combat movement will not be allowed unless target is outside spell range, out of mana, or LOS.
|
||||
@@ -893,7 +895,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
|
||||
go->SetLootState(GO_READY);
|
||||
}
|
||||
|
||||
go->UseDoorOrButton(0, !!e.action.activateObject.alternative, unit);
|
||||
go->UseDoorOrButton(0, e.action.activateObject.alternative, unit);
|
||||
LOG_DEBUG("sql.sql", "SmartScript::ProcessAction:: SMART_ACTION_ACTIVATE_GOBJECT. Gameobject {} activated", go->GetGUID().ToString());
|
||||
}
|
||||
}
|
||||
@@ -1483,14 +1485,14 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
|
||||
{
|
||||
for (WorldObject* target : targets)
|
||||
if (IsUnit(target))
|
||||
target->ToUnit()->SetVisible(!!e.action.visibility.state);
|
||||
target->ToUnit()->SetVisible(e.action.visibility.state);
|
||||
|
||||
break;
|
||||
}
|
||||
case SMART_ACTION_SET_ACTIVE:
|
||||
{
|
||||
for (WorldObject* target : targets)
|
||||
target->setActive(!!e.action.setActive.state);
|
||||
target->setActive(e.action.setActive.state);
|
||||
break;
|
||||
}
|
||||
case SMART_ACTION_ATTACK_START:
|
||||
|
||||
@@ -202,10 +202,29 @@ void SmartAIMgr::LoadSmartAIFromDB()
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!sObjectMgr->GetCreatureData(uint32(std::abs(temp.entryOrGuid))))
|
||||
switch (source_type)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "SmartAIMgr::LoadSmartAIFromDB: Creature guid ({}) does not exist, skipped loading.", uint32(std::abs(temp.entryOrGuid)));
|
||||
continue;
|
||||
case SMART_SCRIPT_TYPE_CREATURE:
|
||||
{
|
||||
if (!sObjectMgr->GetCreatureData(uint32(std::abs(temp.entryOrGuid))))
|
||||
{
|
||||
LOG_ERROR("sql.sql", "SmartAIMgr::LoadSmartAIFromDB: Creature guid ({}) does not exist, skipped loading.", uint32(std::abs(temp.entryOrGuid)));
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SMART_SCRIPT_TYPE_GAMEOBJECT:
|
||||
{
|
||||
if (!sObjectMgr->GetGameObjectData(uint32(std::abs(temp.entryOrGuid))))
|
||||
{
|
||||
LOG_ERROR("sql.sql", "SmartAIMgr::LoadSmartAIFromDB: GameObject guid ({}) does not exist, skipped loading.", uint32(temp.entryOrGuid));
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOG_ERROR("sql.sql", "SmartAIMgr::LoadSmartAIFromDB: not yet implemented source_type {}", (uint32)source_type);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -488,7 +488,7 @@ void AuctionHouseObject::AddAuction(AuctionEntry* auction)
|
||||
|
||||
bool AuctionHouseObject::RemoveAuction(AuctionEntry* auction)
|
||||
{
|
||||
bool wasInMap = !!_auctionsMap.erase(auction->Id);
|
||||
bool wasInMap = _auctionsMap.erase(auction->Id);
|
||||
sAuctionMgr->GetAuctionHouseSearcher()->RemoveAuction(auction);
|
||||
|
||||
sScriptMgr->OnAuctionRemove(this, auction);
|
||||
|
||||
@@ -206,7 +206,7 @@ bool Acore::Hyperlinks::LinkTags::spell::StoreTo(SpellInfo const*& val, std::str
|
||||
if (!(t.TryConsumeTo(spellId) && t.IsEmpty()))
|
||||
return false;
|
||||
|
||||
return !!(val = sSpellMgr->GetSpellInfo(spellId));
|
||||
return (val = sSpellMgr->GetSpellInfo(spellId));
|
||||
}
|
||||
|
||||
bool Acore::Hyperlinks::LinkTags::talent::StoreTo(TalentLinkData& val, std::string_view text)
|
||||
|
||||
@@ -68,7 +68,7 @@ bool Condition::Meets(ConditionSourceInfo& sourceInfo)
|
||||
{
|
||||
// don't allow 0 items (it's checked during table load)
|
||||
ASSERT(ConditionValue2);
|
||||
bool checkBank = !!ConditionValue3;
|
||||
bool checkBank = ConditionValue3;
|
||||
condMeets = player->HasItemCount(ConditionValue1, ConditionValue2, checkBank);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1987,8 +1987,6 @@ void Creature::setDeathState(DeathState state, bool despawn)
|
||||
|
||||
Dismount(); // if creature is mounted on a virtual mount, remove it at death
|
||||
|
||||
setActive(false);
|
||||
|
||||
if (HasSearchedAssistance())
|
||||
{
|
||||
SetNoSearchAssistance(false);
|
||||
@@ -3904,7 +3902,7 @@ bool Creature::IsUpdateNeeded()
|
||||
if (IsInCombat())
|
||||
return true;
|
||||
|
||||
if (IsVisibilityOverridden())
|
||||
if (!GetObjectVisibilityContainer().GetVisiblePlayersMap().empty())
|
||||
return true;
|
||||
|
||||
if (ToTempSummon())
|
||||
|
||||
@@ -67,7 +67,7 @@ struct FormationInfo
|
||||
uint32 point_1;
|
||||
uint32 point_2;
|
||||
|
||||
bool HasGroupFlag(uint16 flag) const { return !!(groupAI & flag); }
|
||||
bool HasGroupFlag(uint16 flag) const { return (groupAI & flag); }
|
||||
};
|
||||
|
||||
typedef std::unordered_map<ObjectGuid::LowType/*memberDBGUID*/, FormationInfo /*formationInfo*/> CreatureGroupInfoType;
|
||||
|
||||
@@ -3086,7 +3086,7 @@ bool GameObject::IsUpdateNeeded()
|
||||
if (GetMap()->isCellMarked(GetCurrentCell().GetCellCoord().GetId()))
|
||||
return true;
|
||||
|
||||
if (IsVisibilityOverridden())
|
||||
if (!GetObjectVisibilityContainer().GetVisiblePlayersMap().empty())
|
||||
return true;
|
||||
|
||||
if (IsTransport())
|
||||
|
||||
@@ -156,8 +156,8 @@ public:
|
||||
|
||||
void SaveToDB(bool saveAddon = false);
|
||||
void SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask, bool saveAddon = false);
|
||||
bool LoadFromDB(ObjectGuid::LowType guid, Map* map) { return LoadGameObjectFromDB(guid, map, false); }
|
||||
bool LoadGameObjectFromDB(ObjectGuid::LowType guid, Map* map, bool addToMap = true);
|
||||
virtual bool LoadFromDB(ObjectGuid::LowType guid, Map* map) { return LoadGameObjectFromDB(guid, map, false); }
|
||||
virtual bool LoadGameObjectFromDB(ObjectGuid::LowType guid, Map* map, bool addToMap = true);
|
||||
void DeleteFromDB();
|
||||
|
||||
void SetOwnerGUID(ObjectGuid owner)
|
||||
|
||||
@@ -1696,7 +1696,7 @@ bool WorldObject::CanSeeOrDetect(WorldObject const* obj, bool ignoreStealth, boo
|
||||
if (this == obj)
|
||||
return true;
|
||||
|
||||
if (obj->IsNeverVisible() || CanNeverSee(obj))
|
||||
if (CanNeverSee(obj))
|
||||
return false;
|
||||
|
||||
if (obj->IsAlwaysVisibleFor(this) || CanAlwaysSee(obj))
|
||||
@@ -1840,6 +1840,12 @@ bool WorldObject::CanSeeOrDetect(WorldObject const* obj, bool ignoreStealth, boo
|
||||
|
||||
bool WorldObject::CanNeverSee(WorldObject const* obj) const
|
||||
{
|
||||
if (!IsInWorld())
|
||||
return true;
|
||||
|
||||
if (obj->IsNeverVisible())
|
||||
return true;
|
||||
|
||||
if (IsCreature() && obj->IsCreature())
|
||||
return GetMap() != obj->GetMap() || (!InSamePhase(obj) && ToUnit()->GetVehicleBase() != obj && this != obj->ToUnit()->GetVehicleBase());
|
||||
return GetMap() != obj->GetMap() || !InSamePhase(obj);
|
||||
|
||||
@@ -66,6 +66,10 @@ void ObjectVisibilityContainer::LinkWorldObjectVisibility(WorldObject* worldObje
|
||||
if (worldObject == _selfObject)
|
||||
return;
|
||||
|
||||
// Transports are special and should not be added to our visibility map
|
||||
if (worldObject->IsGameObject() && worldObject->ToGameObject()->IsTransport())
|
||||
return;
|
||||
|
||||
// Only players can link visibility
|
||||
if (!_visibleWorldObjectsMap)
|
||||
return;
|
||||
|
||||
@@ -4552,6 +4552,9 @@ void Player::ResurrectPlayer(float restore_percent, bool applySickness)
|
||||
// update visibility
|
||||
UpdateObjectVisibility();
|
||||
|
||||
// recast lost by death auras of any items held in the inventory
|
||||
CastAllObtainSpells();
|
||||
|
||||
sScriptMgr->OnPlayerResurrect(this, restore_percent, applySickness);
|
||||
|
||||
if (!applySickness)
|
||||
@@ -6019,6 +6022,7 @@ void Player::RewardReputation(Unit* victim)
|
||||
if (Rep->RepFaction1 && (!Rep->TeamDependent || teamId == TEAM_ALLIANCE))
|
||||
{
|
||||
float donerep1 = CalculateReputationGain(REPUTATION_SOURCE_KILL, victim->GetLevel(), static_cast<float>(Rep->RepValue1), ChampioningFaction ? ChampioningFaction : Rep->RepFaction1);
|
||||
sScriptMgr->OnPlayerGiveReputation(this, Rep->RepFaction1, donerep1, REPUTATION_SOURCE_KILL);
|
||||
|
||||
FactionEntry const* factionEntry1 = sFactionStore.LookupEntry(ChampioningFaction ? ChampioningFaction : Rep->RepFaction1);
|
||||
if (factionEntry1)
|
||||
@@ -6030,6 +6034,7 @@ void Player::RewardReputation(Unit* victim)
|
||||
if (Rep->RepFaction2 && (!Rep->TeamDependent || teamId == TEAM_HORDE))
|
||||
{
|
||||
float donerep2 = CalculateReputationGain(REPUTATION_SOURCE_KILL, victim->GetLevel(), static_cast<float>(Rep->RepValue2), ChampioningFaction ? ChampioningFaction : Rep->RepFaction2);
|
||||
sScriptMgr->OnPlayerGiveReputation(this, Rep->RepFaction2, donerep2, REPUTATION_SOURCE_KILL);
|
||||
|
||||
FactionEntry const* factionEntry2 = sFactionStore.LookupEntry(ChampioningFaction ? ChampioningFaction : Rep->RepFaction2);
|
||||
if (factionEntry2)
|
||||
@@ -6069,22 +6074,27 @@ void Player::RewardReputation(Quest const* quest)
|
||||
if (quest->IsDaily())
|
||||
{
|
||||
rep = CalculateReputationGain(REPUTATION_SOURCE_DAILY_QUEST, GetQuestLevel(quest), rep, quest->RewardFactionId[i], false);
|
||||
sScriptMgr->OnPlayerGiveReputation(this, quest->RewardFactionId[i], rep, REPUTATION_SOURCE_DAILY_QUEST);
|
||||
}
|
||||
else if (quest->IsWeekly())
|
||||
{
|
||||
rep = CalculateReputationGain(REPUTATION_SOURCE_WEEKLY_QUEST, GetQuestLevel(quest), rep, quest->RewardFactionId[i], false);
|
||||
sScriptMgr->OnPlayerGiveReputation(this, quest->RewardFactionId[i], rep, REPUTATION_SOURCE_WEEKLY_QUEST);
|
||||
}
|
||||
else if (quest->IsMonthly())
|
||||
{
|
||||
rep = CalculateReputationGain(REPUTATION_SOURCE_MONTHLY_QUEST, GetQuestLevel(quest), rep, quest->RewardFactionId[i], false);
|
||||
sScriptMgr->OnPlayerGiveReputation(this, quest->RewardFactionId[i], rep, REPUTATION_SOURCE_MONTHLY_QUEST);
|
||||
}
|
||||
else if (quest->IsRepeatable())
|
||||
{
|
||||
rep = CalculateReputationGain(REPUTATION_SOURCE_REPEATABLE_QUEST, GetQuestLevel(quest), rep, quest->RewardFactionId[i], false);
|
||||
sScriptMgr->OnPlayerGiveReputation(this, quest->RewardFactionId[i], rep, REPUTATION_SOURCE_REPEATABLE_QUEST);
|
||||
}
|
||||
else
|
||||
{
|
||||
rep = CalculateReputationGain(REPUTATION_SOURCE_QUEST, GetQuestLevel(quest), rep, quest->RewardFactionId[i], false);
|
||||
sScriptMgr->OnPlayerGiveReputation(this, quest->RewardFactionId[i], rep, REPUTATION_SOURCE_QUEST);
|
||||
}
|
||||
|
||||
if (FactionEntry const* factionEntry = sFactionStore.LookupEntry(quest->RewardFactionId[i]))
|
||||
@@ -7028,6 +7038,46 @@ void Player::_ApplyWeaponDamage(uint8 slot, ItemTemplate const* proto, ScalingSt
|
||||
UpdateDamagePhysical(WeaponAttackType(attType));
|
||||
}
|
||||
|
||||
void Player::CastAllObtainSpells()
|
||||
{
|
||||
for (uint8 slot = INVENTORY_SLOT_ITEM_START; slot < INVENTORY_SLOT_ITEM_END; ++slot)
|
||||
if (Item* item = GetItemByPos(INVENTORY_SLOT_BAG_0, slot))
|
||||
ApplyItemObtainSpells(item, true);
|
||||
|
||||
for (uint8 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
|
||||
{
|
||||
Bag* bag = GetBagByPos(i);
|
||||
if (!bag)
|
||||
continue;
|
||||
|
||||
for (uint32 slot = 0; slot < bag->GetBagSize(); ++slot)
|
||||
if (Item* item = bag->GetItemByPos(slot))
|
||||
ApplyItemObtainSpells(item, true);
|
||||
}
|
||||
}
|
||||
|
||||
void Player::ApplyItemObtainSpells(Item* item, bool apply)
|
||||
{
|
||||
ItemTemplate const* itemTemplate = item->GetTemplate();
|
||||
for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
|
||||
{
|
||||
if (itemTemplate->Spells[i].SpellTrigger != ITEM_SPELLTRIGGER_ON_NO_DELAY_USE) // On obtain trigger
|
||||
continue;
|
||||
|
||||
int32 const spellId = itemTemplate->Spells[i].SpellId;
|
||||
if (spellId <= 0)
|
||||
continue;
|
||||
|
||||
if (apply)
|
||||
{
|
||||
if (!HasAura(spellId))
|
||||
CastSpell(this, spellId, true, item);
|
||||
}
|
||||
else
|
||||
RemoveAurasDueToSpell(spellId);
|
||||
}
|
||||
}
|
||||
|
||||
SpellSchoolMask Player::GetMeleeDamageSchoolMask(WeaponAttackType attackType /*= BASE_ATTACK*/, uint8 damageIndex /*= 0*/) const
|
||||
{
|
||||
if (Item const* weapon = GetWeaponForAttack(attackType, true))
|
||||
|
||||
@@ -235,17 +235,6 @@ enum ActionButtonType
|
||||
ACTION_BUTTON_ITEM = 0x80
|
||||
};
|
||||
|
||||
enum ReputationSource
|
||||
{
|
||||
REPUTATION_SOURCE_KILL,
|
||||
REPUTATION_SOURCE_QUEST,
|
||||
REPUTATION_SOURCE_DAILY_QUEST,
|
||||
REPUTATION_SOURCE_WEEKLY_QUEST,
|
||||
REPUTATION_SOURCE_MONTHLY_QUEST,
|
||||
REPUTATION_SOURCE_REPEATABLE_QUEST,
|
||||
REPUTATION_SOURCE_SPELL
|
||||
};
|
||||
|
||||
enum QuestSound
|
||||
{
|
||||
QUEST_SOUND_FAILURE = 847
|
||||
@@ -1682,7 +1671,7 @@ public:
|
||||
|
||||
bool RemoveMItem(ObjectGuid::LowType itemLowGuid)
|
||||
{
|
||||
return !!mMitems.erase(itemLowGuid);
|
||||
return mMitems.erase(itemLowGuid);
|
||||
}
|
||||
|
||||
void PetSpellInitialize();
|
||||
@@ -2208,6 +2197,9 @@ public:
|
||||
|
||||
void ResetAllPowers();
|
||||
|
||||
void CastAllObtainSpells();
|
||||
void ApplyItemObtainSpells(Item* item, bool apply);
|
||||
|
||||
SpellSchoolMask GetMeleeDamageSchoolMask(WeaponAttackType attackType = BASE_ATTACK, uint8 damageIndex = 0) const override;
|
||||
|
||||
void _ApplyWeaponDependentAuraMods(Item* item, WeaponAttackType attackType, bool apply);
|
||||
@@ -2632,8 +2624,8 @@ public:
|
||||
std::string GetPlayerName();
|
||||
|
||||
// Settings
|
||||
[[nodiscard]] PlayerSetting GetPlayerSetting(std::string const& source, uint8 index);
|
||||
void UpdatePlayerSetting(std::string const& source, uint8 index, uint32 value);
|
||||
[[nodiscard]] PlayerSetting GetPlayerSetting(std::string const& source, uint32 index);
|
||||
void UpdatePlayerSetting(std::string const& source, uint32 index, uint32 value);
|
||||
|
||||
void SendSystemMessage(std::string_view msg, bool escapeCharacters = false);
|
||||
|
||||
|
||||
@@ -18,11 +18,95 @@
|
||||
#include "Player.h"
|
||||
#include "StringConvert.h"
|
||||
#include "Tokenize.h"
|
||||
#include "CharacterDatabase.h"
|
||||
|
||||
/*********************************************************/
|
||||
/*** PLAYER SETTINGS SYSTEM ***/
|
||||
/*********************************************************/
|
||||
|
||||
namespace PlayerSettingsStore
|
||||
{
|
||||
// Common helper: parse space-separated data string into PlayerSettingVector
|
||||
PlayerSettingVector ParseSettingsData(std::string const& data)
|
||||
{
|
||||
PlayerSettingVector result;
|
||||
std::vector<std::string_view> tokens = Acore::Tokenize(data, ' ', false);
|
||||
result.reserve(tokens.size());
|
||||
for (auto const& token : tokens)
|
||||
{
|
||||
if (token.empty())
|
||||
continue;
|
||||
if (auto parsed = Acore::StringTo<uint32>(token))
|
||||
result.emplace_back(*parsed);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Common helper: serialize PlayerSettingVector to space-separated string
|
||||
std::string SerializeSettingsData(PlayerSettingVector const& settings)
|
||||
{
|
||||
if (settings.empty())
|
||||
return "";
|
||||
|
||||
std::ostringstream data;
|
||||
data << settings[0].value;
|
||||
for (size_t i = 1; i < settings.size(); ++i)
|
||||
data << ' ' << settings[i].value;
|
||||
return data.str();
|
||||
}
|
||||
|
||||
// helper: load a single source row for a player and parse to vector
|
||||
static PlayerSettingVector LoadPlayerSettings(uint32 playerLowGuid, std::string const& source)
|
||||
{
|
||||
PlayerSettingVector result;
|
||||
|
||||
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_SETTINGS);
|
||||
stmt->SetData(0, playerLowGuid);
|
||||
PreparedQueryResult dbRes = CharacterDatabase.Query(stmt);
|
||||
if (!dbRes)
|
||||
return result;
|
||||
|
||||
do
|
||||
{
|
||||
Field* fields = dbRes->Fetch();
|
||||
std::string rowSource = fields[0].Get<std::string>();
|
||||
if (rowSource != source)
|
||||
continue;
|
||||
|
||||
std::string data = fields[1].Get<std::string>();
|
||||
return ParseSettingsData(data);
|
||||
} while (dbRes->NextRow());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void UpdateSetting(uint32 playerLowGuid, std::string const& source, uint32 index, uint32 value)
|
||||
{
|
||||
if (!sWorld->getBoolConfig(CONFIG_PLAYER_SETTINGS_ENABLED))
|
||||
return;
|
||||
|
||||
PlayerSettingVector settings = LoadPlayerSettings(playerLowGuid, source);
|
||||
size_t const requiredSize = static_cast<size_t>(index) + 1;
|
||||
if (settings.size() < requiredSize)
|
||||
settings.resize(requiredSize); // zero-initialized PlayerSetting::value
|
||||
|
||||
settings[index].value = value;
|
||||
|
||||
CharacterDatabasePreparedStatement* stmt = PlayerSettingsStore::PrepareReplaceStatement(playerLowGuid, source, settings);
|
||||
CharacterDatabase.Execute(stmt);
|
||||
}
|
||||
}
|
||||
|
||||
// Implementation of PrepareReplaceStatement
|
||||
CharacterDatabasePreparedStatement* PlayerSettingsStore::PrepareReplaceStatement(uint32 playerLowGuid, std::string const& source, PlayerSettingVector const& settings)
|
||||
{
|
||||
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_CHAR_SETTINGS);
|
||||
stmt->SetData(0, playerLowGuid);
|
||||
stmt->SetData(1, source);
|
||||
stmt->SetData(2, SerializeSettingsData(settings));
|
||||
return stmt;
|
||||
}
|
||||
|
||||
void Player::_LoadCharacterSettings(PreparedQueryResult result)
|
||||
{
|
||||
m_charSettingsMap.clear();
|
||||
@@ -39,27 +123,13 @@ void Player::_LoadCharacterSettings(PreparedQueryResult result)
|
||||
std::string source = fields[0].Get<std::string>();
|
||||
std::string data = fields[1].Get<std::string>();
|
||||
|
||||
std::vector<std::string_view> tokens = Acore::Tokenize(data, ' ', false);
|
||||
|
||||
PlayerSettingVector settings;
|
||||
settings.reserve(tokens.size()); // reserve capacity but don't resize
|
||||
|
||||
for (auto const& token : tokens)
|
||||
{
|
||||
if (token.empty())
|
||||
continue;
|
||||
|
||||
// Try to parse the value safely
|
||||
if (auto parsed = Acore::StringTo<uint32>(token))
|
||||
settings.emplace_back(*parsed);
|
||||
}
|
||||
|
||||
PlayerSettingVector settings = PlayerSettingsStore::ParseSettingsData(data);
|
||||
m_charSettingsMap.emplace(std::move(source), std::move(settings));
|
||||
|
||||
} while (result->NextRow());
|
||||
}
|
||||
|
||||
PlayerSetting Player::GetPlayerSetting(std::string const& source, uint8 index)
|
||||
PlayerSetting Player::GetPlayerSetting(std::string const& source, uint32 index)
|
||||
{
|
||||
auto it = m_charSettingsMap.find(source);
|
||||
if (it == m_charSettingsMap.end() || static_cast<size_t>(index) >= it->second.size())
|
||||
@@ -81,21 +151,12 @@ void Player::_SavePlayerSettings(CharacterDatabaseTransaction trans)
|
||||
if (settings.empty())
|
||||
continue;
|
||||
|
||||
std::ostringstream data;
|
||||
data << settings[0].value;
|
||||
|
||||
for (size_t i = 1; i < settings.size(); ++i)
|
||||
data << ' ' << settings[i].value;
|
||||
|
||||
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_CHAR_SETTINGS);
|
||||
stmt->SetData(0, GetGUID().GetCounter());
|
||||
stmt->SetData(1, source);
|
||||
stmt->SetData(2, data.str());
|
||||
CharacterDatabasePreparedStatement* stmt = PlayerSettingsStore::PrepareReplaceStatement(GetGUID().GetCounter(), source, settings);
|
||||
trans->Append(stmt);
|
||||
}
|
||||
}
|
||||
|
||||
void Player::UpdatePlayerSetting(std::string const& source, uint8 index, uint32 value)
|
||||
void Player::UpdatePlayerSetting(std::string const& source, uint32 index, uint32 value)
|
||||
{
|
||||
auto it = m_charSettingsMap.find(source);
|
||||
size_t const requiredSize = static_cast<size_t>(index) + 1;
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
#ifndef _PLAYER_SETTINGS_H
|
||||
#define _PLAYER_SETTINGS_H
|
||||
#include "DatabaseEnvFwd.h"
|
||||
|
||||
class Player;
|
||||
|
||||
@@ -51,4 +52,18 @@ struct PlayerSetting
|
||||
typedef std::vector<PlayerSetting> PlayerSettingVector;
|
||||
typedef std::map<std::string, PlayerSettingVector> PlayerSettingMap;
|
||||
|
||||
// Standalone API: update a player's setting directly on DB by GUID (low part) without requiring a Player instance
|
||||
namespace PlayerSettingsStore
|
||||
{
|
||||
// Update a single setting value for any player by GUID (works for online or offline players).
|
||||
// This reads the existing "source" row from character_settings, adjusts the index, and REPLACE's it back.
|
||||
void UpdateSetting(uint32 playerLowGuid, std::string const& source, uint32 index, uint32 value);
|
||||
|
||||
// Common helpers for parsing and serializing settings data
|
||||
PlayerSettingVector ParseSettingsData(std::string const& data);
|
||||
std::string SerializeSettingsData(PlayerSettingVector const& settings);
|
||||
// Prepare a REPLACE statement populated with given settings data. Caller may execute or append to a transaction.
|
||||
CharacterDatabasePreparedStatement* PrepareReplaceStatement(uint32 playerLowGuid, std::string const& source, PlayerSettingVector const& settings);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -2577,8 +2577,6 @@ Item* Player::StoreItem(ItemPosCountVec const& dest, Item* pItem, bool update)
|
||||
return nullptr;
|
||||
|
||||
Item* lastItem = pItem;
|
||||
ItemTemplate const* proto = pItem->GetTemplate();
|
||||
|
||||
for (ItemPosCountVec::const_iterator itr = dest.begin(); itr != dest.end();)
|
||||
{
|
||||
uint16 pos = itr->pos;
|
||||
@@ -2595,13 +2593,6 @@ Item* Player::StoreItem(ItemPosCountVec const& dest, Item* pItem, bool update)
|
||||
lastItem = _StoreItem(pos, pItem, count, true, update);
|
||||
}
|
||||
|
||||
// cast after item storing - some checks in checkcast requires item to be present!!
|
||||
if (lastItem)
|
||||
for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
|
||||
if (proto->Spells[i].SpellTrigger == ITEM_SPELLTRIGGER_ON_NO_DELAY_USE && proto->Spells[i].SpellId > 0) // On obtain trigger
|
||||
if (!HasAura(proto->Spells[i].SpellId))
|
||||
CastSpell(this, proto->Spells[i].SpellId, true, lastItem);
|
||||
|
||||
return lastItem;
|
||||
}
|
||||
|
||||
@@ -2664,6 +2655,9 @@ Item* Player::_StoreItem(uint16 pos, Item* pItem, uint32 count, bool clone, bool
|
||||
AddEnchantmentDurations(pItem);
|
||||
AddItemDurations(pItem);
|
||||
|
||||
if (bag == INVENTORY_SLOT_BAG_0 || (bag >= INVENTORY_SLOT_BAG_START && bag < INVENTORY_SLOT_BAG_END))
|
||||
ApplyItemObtainSpells(pItem, true);
|
||||
|
||||
return pItem;
|
||||
}
|
||||
else
|
||||
@@ -2700,6 +2694,9 @@ Item* Player::_StoreItem(uint16 pos, Item* pItem, uint32 count, bool clone, bool
|
||||
|
||||
pItem2->SetState(ITEM_CHANGED, this);
|
||||
|
||||
if (bag == INVENTORY_SLOT_BAG_0 || (bag >= INVENTORY_SLOT_BAG_START && bag < INVENTORY_SLOT_BAG_END))
|
||||
ApplyItemObtainSpells(pItem2, true);
|
||||
|
||||
return pItem2;
|
||||
}
|
||||
}
|
||||
@@ -3046,10 +3043,7 @@ void Player::DestroyItem(uint8 bag, uint8 slot, bool update)
|
||||
pItem->ClearSoulboundTradeable(this);
|
||||
RemoveTradeableItem(pItem);
|
||||
|
||||
ItemTemplate const* proto = pItem->GetTemplate();
|
||||
for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
|
||||
if (proto->Spells[i].SpellTrigger == ITEM_SPELLTRIGGER_ON_NO_DELAY_USE && proto->Spells[i].SpellId > 0) // On obtain trigger
|
||||
RemoveAurasDueToSpell(proto->Spells[i].SpellId);
|
||||
ApplyItemObtainSpells(pItem, false);
|
||||
|
||||
ItemRemovedQuestCheck(pItem->GetEntry(), pItem->GetCount());
|
||||
|
||||
@@ -3102,8 +3096,9 @@ void Player::DestroyItem(uint8 bag, uint8 slot, bool update)
|
||||
pBag->RemoveItem(slot, update);
|
||||
|
||||
// Xinef: item is removed, remove loot from storage if any
|
||||
if (proto->HasFlag(ITEM_FLAG_HAS_LOOT))
|
||||
sLootItemStorage->RemoveStoredLoot(pItem->GetGUID());
|
||||
if (ItemTemplate const* proto = pItem->GetTemplate())
|
||||
if (proto->HasFlag(ITEM_FLAG_HAS_LOOT))
|
||||
sLootItemStorage->RemoveStoredLoot(pItem->GetGUID());
|
||||
|
||||
if (IsInWorld() && update)
|
||||
{
|
||||
|
||||
@@ -1646,10 +1646,7 @@ template <>
|
||||
inline void UpdateVisibilityOf_helper(Player* player, GameObject* target,
|
||||
std::vector<Unit*>& /*v*/)
|
||||
{
|
||||
// @HACK: This is to prevent objects like deeprun tram from disappearing
|
||||
// when player moves far from its spawn point while riding it
|
||||
if ((target->GetGOInfo()->type != GAMEOBJECT_TYPE_TRANSPORT))
|
||||
player->GetObjectVisibilityContainer().LinkWorldObjectVisibility(target);
|
||||
player->GetObjectVisibilityContainer().LinkWorldObjectVisibility(target);
|
||||
}
|
||||
|
||||
template <>
|
||||
|
||||
@@ -607,12 +607,12 @@ void MotionTransport::DelayedTeleportTransport()
|
||||
}
|
||||
|
||||
Map* newMap = sMapMgr->CreateBaseMap(newMapId);
|
||||
GetMap()->RemoveFromMap<MotionTransport>(this, false);
|
||||
GetMap()->RemoveFromMap<Transport>(this, false);
|
||||
newMap->LoadGrid(x, y); // xinef: load before adding passengers to new map
|
||||
SetMap(newMap);
|
||||
|
||||
Relocate(x, y, z, o);
|
||||
GetMap()->AddToMap<MotionTransport>(this);
|
||||
GetMap()->AddToMap<Transport>(this);
|
||||
|
||||
LoadStaticPassengers();
|
||||
}
|
||||
@@ -690,6 +690,40 @@ StaticTransport::~StaticTransport()
|
||||
ASSERT(_passengers.empty());
|
||||
}
|
||||
|
||||
bool StaticTransport::LoadGameObjectFromDB(ObjectGuid::LowType spawnId, Map* map, bool addToMap)
|
||||
{
|
||||
GameObjectData const* data = sObjectMgr->GetGameObjectData(spawnId);
|
||||
|
||||
if (!data)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Gameobject (GUID: {}) not found in table `gameobject`, can't load. ", spawnId);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32 entry = data->id;
|
||||
//uint32 map_id = data->mapid; // already used before call
|
||||
uint32 phaseMask = data->phaseMask;
|
||||
float x = data->posX;
|
||||
float y = data->posY;
|
||||
float z = data->posZ;
|
||||
float ang = data->orientation;
|
||||
|
||||
uint32 animprogress = data->animprogress;
|
||||
GOState go_state = data->go_state;
|
||||
uint32 artKit = data->artKit;
|
||||
|
||||
m_goData = data;
|
||||
m_spawnId = spawnId;
|
||||
|
||||
if (!Create(map->GenerateLowGuid<HighGuid::Transport>(), entry, map, phaseMask, x, y, z, ang, data->rotation, animprogress, go_state, artKit))
|
||||
return false;
|
||||
|
||||
if (addToMap && !GetMap()->AddToMap<Transport>(this))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StaticTransport::Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, uint32 phaseMask, float x, float y, float z, float ang, G3D::Quat const& rotation, uint32 animprogress, GOState go_state, uint32 artKit)
|
||||
{
|
||||
ASSERT(map);
|
||||
@@ -794,7 +828,6 @@ bool StaticTransport::Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* m
|
||||
LastUsedScriptID = GetGOInfo()->ScriptId;
|
||||
AIM_Initialize();
|
||||
|
||||
this->setActive(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -929,7 +962,9 @@ void StaticTransport::UpdatePosition(float x, float y, float z, float o)
|
||||
if (!GetMap()->IsGridLoaded(x, y)) // pussywizard: should not happen, but just in case
|
||||
GetMap()->LoadGrid(x, y);
|
||||
|
||||
GetMap()->GameObjectRelocation(this, x, y, z, o); // this also relocates the model
|
||||
Relocate(x, y, z, o);
|
||||
UpdateModelPosition();
|
||||
|
||||
UpdatePassengerPositions();
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,8 @@ public:
|
||||
virtual void RemovePassenger(WorldObject* passenger, bool withAll = false) = 0;
|
||||
PassengerSet const& GetPassengers() const { return _passengers; }
|
||||
|
||||
virtual void DelayedUpdate(uint32 /*diff*/) {}
|
||||
|
||||
uint32 GetPathProgress() const { return GetGOValue()->Transport.PathProgress; }
|
||||
void SetPathProgress(uint32 val) { m_goValue.Transport.PathProgress = val; }
|
||||
|
||||
@@ -57,7 +59,7 @@ public:
|
||||
void BuildUpdate(UpdateDataMapType& data_map) override;
|
||||
|
||||
void Update(uint32 diff) override;
|
||||
void DelayedUpdate(uint32 diff);
|
||||
void DelayedUpdate(uint32 diff) override;
|
||||
void UpdatePosition(float x, float y, float z, float o);
|
||||
|
||||
void AddPassenger(WorldObject* passenger, bool withAll = false) override;
|
||||
@@ -115,6 +117,8 @@ public:
|
||||
StaticTransport();
|
||||
~StaticTransport() override;
|
||||
|
||||
bool LoadFromDB(ObjectGuid::LowType guid, Map* map) override { return LoadGameObjectFromDB(guid, map, false); }
|
||||
bool LoadGameObjectFromDB(ObjectGuid::LowType guid, Map* map, bool addToMap = true) override;
|
||||
bool Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, uint32 phaseMask, float x, float y, float z, float ang, G3D::Quat const& rotation, uint32 animprogress, GOState go_state, uint32 artKit = 0) override;
|
||||
void CleanupsBeforeDelete(bool finalCleanup = true) override;
|
||||
void BuildUpdate(UpdateDataMapType& data_map) override;
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
#include "Player.h"
|
||||
#include "ReputationMgr.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "SmartAI.h"
|
||||
#include "Spell.h"
|
||||
#include "SpellAuraEffects.h"
|
||||
#include "SpellAuras.h"
|
||||
@@ -4109,8 +4110,8 @@ void Unit::InterruptSpell(CurrentSpellTypes spellType, bool withDelayed, bool wi
|
||||
//LOG_DEBUG("entities.unit", "Interrupt spell for unit {}.", GetEntry());
|
||||
Spell* spell = m_currentSpells[spellType];
|
||||
if (spell
|
||||
&& (withDelayed || spell->getState() != SPELL_STATE_DELAYED)
|
||||
&& (withInstant || spell->GetCastTime() > 0 || spell->getState() == SPELL_STATE_CASTING)) // xinef: or spell is in casting state (channeled spells only)
|
||||
&& (withDelayed || spell->getState() != SPELL_STATE_DELAYED)
|
||||
&& (withInstant || spell->GetCastTime() > 0 || spell->getState() == SPELL_STATE_CASTING)) // xinef: or spell is in casting state (channeled spells only)
|
||||
{
|
||||
// for example, do not let self-stun aura interrupt itself
|
||||
if (!spell->IsInterruptable())
|
||||
@@ -4128,6 +4129,15 @@ void Unit::InterruptSpell(CurrentSpellTypes spellType, bool withDelayed, bool wi
|
||||
m_currentSpells[spellType] = nullptr;
|
||||
spell->SetReferencedFromCurrent(false);
|
||||
}
|
||||
|
||||
// SAI creatures only
|
||||
// Start chasing victim if they are spell casters (at least one SMC spell) if interrupted/silenced.
|
||||
if (IsCreature())
|
||||
{
|
||||
if (SmartAI* ai = dynamic_cast<SmartAI*>(ToCreature()->AI()))
|
||||
if (ai->CanChaseOnInterrupt())
|
||||
ai->SetCombatMove(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -64,15 +64,28 @@ void GridObjectLoader::LoadGameObjects(CellGuidSet const& guid_set, Map* map)
|
||||
for (ObjectGuid::LowType const& guid : guid_set)
|
||||
{
|
||||
GameObjectData const* data = sObjectMgr->GetGameObjectData(guid);
|
||||
GameObject* obj = data && sObjectMgr->IsGameObjectStaticTransport(data->id) ? new StaticTransport() : new GameObject();
|
||||
|
||||
if (!obj->LoadFromDB(guid, map))
|
||||
if (data && sObjectMgr->IsGameObjectStaticTransport(data->id))
|
||||
{
|
||||
delete obj;
|
||||
continue;
|
||||
}
|
||||
StaticTransport* transport = new StaticTransport();
|
||||
|
||||
AddObjectHelper<GameObject>(map, obj);
|
||||
// Special case for static transports - we are loaded via grids
|
||||
// but we do not want to actually be stored in the grid
|
||||
if (!transport->LoadGameObjectFromDB(guid, map, true))
|
||||
delete transport;
|
||||
}
|
||||
else
|
||||
{
|
||||
GameObject* obj = new GameObject();
|
||||
|
||||
if (!obj->LoadFromDB(guid, map))
|
||||
{
|
||||
delete obj;
|
||||
continue;
|
||||
}
|
||||
|
||||
AddObjectHelper<GameObject>(map, obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "LFGMgr.h"
|
||||
#include "Log.h"
|
||||
#include "MapMgr.h"
|
||||
#include "MiscPackets.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "Opcodes.h"
|
||||
#include "Player.h"
|
||||
@@ -2059,6 +2060,16 @@ GroupJoinBattlegroundResult Group::CanJoinBattlegroundQueue(Battleground const*
|
||||
return GroupJoinBattlegroundResult(bgTemplate->GetBgTypeID());
|
||||
}
|
||||
|
||||
void Group::DoMinimapPing(ObjectGuid sourceGuid, float mapX, float mapY)
|
||||
{
|
||||
WorldPackets::Misc::MinimapPing minimapPing;
|
||||
minimapPing.SourceGuid = sourceGuid;
|
||||
minimapPing.MapX = mapX;
|
||||
minimapPing.MapY = mapY;
|
||||
|
||||
BroadcastPacket(minimapPing.Write(), true, -1, sourceGuid);
|
||||
}
|
||||
|
||||
//===================================================
|
||||
//============== Roll ===============================
|
||||
//===================================================
|
||||
|
||||
@@ -255,6 +255,8 @@ public:
|
||||
void SetBattlefieldGroup(Battlefield* bf);
|
||||
GroupJoinBattlegroundResult CanJoinBattlegroundQueue(Battleground const* bgTemplate, BattlegroundQueueTypeId bgQueueTypeId, uint32 MinPlayerCount, uint32 MaxPlayerCount, bool isRated, uint32 arenaSlot);
|
||||
|
||||
void DoMinimapPing(ObjectGuid sourceGuid, float mapX, float mapY);
|
||||
|
||||
void ChangeMembersGroup(ObjectGuid guid, uint8 group);
|
||||
void SetTargetIcon(uint8 id, ObjectGuid whoGuid, ObjectGuid targetGuid);
|
||||
void SetGroupMemberFlag(ObjectGuid guid, bool apply, GroupMemberFlags flag);
|
||||
|
||||
@@ -1175,6 +1175,10 @@ void WorldSession::HandlePlayerLoginToCharInWorld(Player* pCurrChar)
|
||||
pCurrChar->GetMap()->SendInitTransports(pCurrChar);
|
||||
pCurrChar->GetMap()->SendInitSelf(pCurrChar);
|
||||
pCurrChar->GetMap()->SendZoneDynamicInfo(pCurrChar);
|
||||
|
||||
// If we are logging into an existing player, simply clear visibility references
|
||||
// so player will receive a fresh list of new objects on the next vis update.
|
||||
pCurrChar->GetObjectVisibilityContainer().CleanVisibilityReferences();
|
||||
pCurrChar->UpdateObjectVisibility(false);
|
||||
|
||||
pCurrChar->CleanupChannels();
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "LFGMgr.h"
|
||||
#include "Language.h"
|
||||
#include "Log.h"
|
||||
#include "MapMgr.h"
|
||||
#include "MiscPackets.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "Opcodes.h"
|
||||
@@ -522,24 +523,17 @@ void WorldSession::HandleLootRoll(WorldPacket& recvData)
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleMinimapPingOpcode(WorldPacket& recvData)
|
||||
void WorldSession::HandleMinimapPingOpcode(WorldPackets::Misc::MinimapPingClient& packet)
|
||||
{
|
||||
if (!GetPlayer()->GetGroup())
|
||||
if (!sMapMgr->IsValidMapCoord(GetPlayer()->GetMap()->GetId(), packet.MapX, packet.MapY))
|
||||
return;
|
||||
|
||||
float x, y;
|
||||
recvData >> x;
|
||||
recvData >> y;
|
||||
Group* group = GetPlayer()->GetGroup();
|
||||
|
||||
/** error handling **/
|
||||
/********************/
|
||||
if (!group)
|
||||
return;
|
||||
|
||||
// everything's fine, do it
|
||||
WorldPacket data(MSG_MINIMAP_PING, (8 + 4 + 4));
|
||||
data << GetPlayer()->GetGUID();
|
||||
data << float(x);
|
||||
data << float(y);
|
||||
GetPlayer()->GetGroup()->BroadcastPacket(&data, true, -1, GetPlayer()->GetGUID());
|
||||
group->DoMinimapPing(GetPlayer()->GetGUID(), packet.MapX, packet.MapY);
|
||||
}
|
||||
|
||||
void WorldSession::HandleRandomRollOpcode(WorldPackets::Misc::RandomRollClient& packet)
|
||||
@@ -549,10 +543,8 @@ void WorldSession::HandleRandomRollOpcode(WorldPackets::Misc::RandomRollClient&
|
||||
maximum = packet.Max;
|
||||
|
||||
/** error handling **/
|
||||
if (minimum > maximum || maximum > 10000) // < 32768 for urand call
|
||||
{
|
||||
if (minimum > maximum || maximum > sWorld->getIntConfig(CONFIG_RANDOM_ROLL_MAXIMUM))
|
||||
return;
|
||||
}
|
||||
|
||||
GetPlayer()->DoRandomRoll(minimum, maximum);
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "Opcodes.h"
|
||||
#include "Pet.h"
|
||||
#include "Player.h"
|
||||
#include "QueryPackets.h"
|
||||
#include "World.h"
|
||||
#include "WorldPacket.h"
|
||||
#include "WorldSession.h"
|
||||
@@ -32,62 +33,55 @@ void WorldSession::SendNameQueryOpcode(ObjectGuid guid)
|
||||
{
|
||||
CharacterCacheEntry const* playerData = sCharacterCache->GetCharacterCacheByGuid(guid);
|
||||
|
||||
WorldPacket data(SMSG_NAME_QUERY_RESPONSE, (8 + 1 + 1 + 1 + 1 + 1 + 10));
|
||||
data << guid.WriteAsPacked();
|
||||
WorldPackets::Query::NameQueryResponse nameQueryResponse;
|
||||
nameQueryResponse.Guid = guid.WriteAsPacked();
|
||||
if (!playerData)
|
||||
{
|
||||
data << uint8(1); // name unknown
|
||||
SendPacket(&data);
|
||||
nameQueryResponse.NameUnknown = true;
|
||||
SendPacket(nameQueryResponse.Write());
|
||||
return;
|
||||
}
|
||||
|
||||
Player* player = ObjectAccessor::FindConnectedPlayer(guid);
|
||||
|
||||
data << uint8(0); // name known
|
||||
data << playerData->Name; // played name
|
||||
data << uint8(0); // realm name - only set for cross realm interaction (such as Battlegrounds)
|
||||
data << uint8(player ? player->getRace() : playerData->Race);
|
||||
data << uint8(playerData->Sex);
|
||||
data << uint8(playerData->Class);
|
||||
nameQueryResponse.NameUnknown = false;
|
||||
nameQueryResponse.Name = playerData->Name;
|
||||
nameQueryResponse.Race = player ? player->getRace() : playerData->Race;
|
||||
nameQueryResponse.Sex = player ? player->getGender() : playerData->Sex;
|
||||
nameQueryResponse.Class = player ? player->getClass() : playerData->Class;
|
||||
|
||||
// pussywizard: optimization
|
||||
/*Player* player = ObjectAccessor::FindConnectedPlayer(guid);
|
||||
if (DeclinedName const* names = (player ? player->GetDeclinedNames() : nullptr))
|
||||
{
|
||||
data << uint8(1); // Name is declined
|
||||
for (uint8 i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
|
||||
data << names->name[i];
|
||||
nameQueryResponse.Declined = true;
|
||||
nameQueryResponse.DeclinedNames = *names;
|
||||
}
|
||||
else*/
|
||||
data << uint8(0); // Name is not declined
|
||||
else
|
||||
nameQueryResponse.Declined = false;
|
||||
|
||||
SendPacket(&data);
|
||||
SendPacket(nameQueryResponse.Write());
|
||||
}
|
||||
|
||||
void WorldSession::HandleNameQueryOpcode(WorldPacket& recvData)
|
||||
void WorldSession::HandleNameQueryOpcode(WorldPackets::Query::NameQuery& packet)
|
||||
{
|
||||
ObjectGuid guid;
|
||||
recvData >> guid;
|
||||
|
||||
// This is disable by default to prevent lots of console spam
|
||||
// LOG_INFO("network.opcode", "HandleNameQueryOpcode {}", guid);
|
||||
|
||||
SendNameQueryOpcode(guid);
|
||||
SendNameQueryOpcode(packet.Guid);
|
||||
}
|
||||
|
||||
void WorldSession::HandleQueryTimeOpcode(WorldPacket& /*recvData*/)
|
||||
void WorldSession::HandleTimeQueryOpcode(WorldPackets::Query::TimeQuery& /*packet*/)
|
||||
{
|
||||
SendQueryTimeResponse();
|
||||
SendTimeQueryResponse();
|
||||
}
|
||||
|
||||
void WorldSession::SendQueryTimeResponse()
|
||||
void WorldSession::SendTimeQueryResponse()
|
||||
{
|
||||
auto timeResponse = sWorld->GetNextDailyQuestsResetTime() - GameTime::GetGameTime();
|
||||
|
||||
WorldPacket data(SMSG_QUERY_TIME_RESPONSE, 4 + 4);
|
||||
data << uint32(GameTime::GetGameTime().count());
|
||||
data << uint32(timeResponse.count());
|
||||
SendPacket(&data);
|
||||
WorldPackets::Query::TimeQueryResponse timeQueryResponse;
|
||||
timeQueryResponse.ServerTime = GameTime::GetGameTime().count();
|
||||
timeQueryResponse.TimeResponse = timeResponse.count();
|
||||
SendPacket(timeQueryResponse.Write());
|
||||
}
|
||||
|
||||
/// Only _static_ data is sent in this packet !!!
|
||||
@@ -402,13 +396,10 @@ void WorldSession::HandlePageTextQueryOpcode(WorldPacket& recvData)
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleCorpseMapPositionQuery(WorldPacket& recvData)
|
||||
void WorldSession::HandleCorpseMapPositionQuery(WorldPackets::Query::CorpseMapPositionQuery& /*packet*/)
|
||||
{
|
||||
LOG_DEBUG("network", "WORLD: Recv CMSG_CORPSE_MAP_POSITION_QUERY");
|
||||
|
||||
uint32 unk;
|
||||
recvData >> unk;
|
||||
|
||||
WorldPacket data(SMSG_CORPSE_MAP_POSITION_QUERY_RESPONSE, 4 + 4 + 4 + 4);
|
||||
data << float(0);
|
||||
data << float(0);
|
||||
|
||||
@@ -172,7 +172,7 @@ void WorldSession::HandleGMTicketDeleteOpcode(WorldPacket& /*recv_data*/)
|
||||
|
||||
void WorldSession::HandleGMTicketGetTicketOpcode(WorldPacket& /*recv_data*/)
|
||||
{
|
||||
SendQueryTimeResponse();
|
||||
SendTimeQueryResponse();
|
||||
|
||||
if (GmTicket* ticket = sTicketMgr->GetTicketByPlayer(GetPlayer()->GetGUID()))
|
||||
{
|
||||
|
||||
@@ -345,7 +345,7 @@ bool Map::AddToMap(T* obj, bool checkTransport)
|
||||
}
|
||||
|
||||
template<>
|
||||
bool Map::AddToMap(MotionTransport* obj, bool /*checkTransport*/)
|
||||
bool Map::AddToMap(Transport* obj, bool /*checkTransport*/)
|
||||
{
|
||||
//TODO: Needs clean up. An object should not be added to map twice.
|
||||
if (obj->IsInWorld())
|
||||
@@ -360,26 +360,22 @@ bool Map::AddToMap(MotionTransport* obj, bool /*checkTransport*/)
|
||||
}
|
||||
|
||||
Cell cell(cellCoord);
|
||||
if (obj->isActiveObject())
|
||||
EnsureGridLoaded(cell);
|
||||
EnsureGridLoaded(cell);
|
||||
|
||||
obj->AddToWorld();
|
||||
|
||||
_transports.insert(obj);
|
||||
|
||||
// Broadcast creation to players
|
||||
if (!GetPlayers().IsEmpty())
|
||||
for (Map::PlayerList::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr)
|
||||
{
|
||||
for (Map::PlayerList::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr)
|
||||
if (itr->GetSource()->GetTransport() != obj)
|
||||
{
|
||||
if (itr->GetSource()->GetTransport() != obj)
|
||||
{
|
||||
UpdateData data;
|
||||
obj->BuildCreateUpdateBlockForPlayer(&data, itr->GetSource());
|
||||
WorldPacket packet;
|
||||
data.BuildPacket(packet);
|
||||
itr->GetSource()->SendDirectMessage(&packet);
|
||||
}
|
||||
UpdateData data;
|
||||
obj->BuildCreateUpdateBlockForPlayer(&data, itr->GetSource());
|
||||
WorldPacket packet;
|
||||
data.BuildPacket(packet);
|
||||
itr->GetSource()->SendDirectMessage(&packet);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -667,7 +663,7 @@ void Map::RemoveFromMap(T* obj, bool remove)
|
||||
}
|
||||
|
||||
template<>
|
||||
void Map::RemoveFromMap(MotionTransport* obj, bool remove)
|
||||
void Map::RemoveFromMap(Transport* obj, bool remove)
|
||||
{
|
||||
obj->RemoveFromWorld();
|
||||
|
||||
@@ -697,8 +693,6 @@ void Map::RemoveFromMap(MotionTransport* obj, bool remove)
|
||||
|
||||
obj->ResetMap();
|
||||
|
||||
// Transports are never actually deleted, but it *should* be safe to clear
|
||||
// from update list when removing from world
|
||||
RemoveObjectFromMapUpdateList(obj);
|
||||
|
||||
if (remove)
|
||||
@@ -966,11 +960,10 @@ void Map::UnloadAll()
|
||||
|
||||
for (TransportsContainer::iterator itr = _transports.begin(); itr != _transports.end();)
|
||||
{
|
||||
MotionTransport* transport = *itr;
|
||||
Transport* transport = *itr;
|
||||
++itr;
|
||||
|
||||
transport->RemoveFromWorld();
|
||||
delete transport;
|
||||
RemoveFromMap<Transport>(transport, true);
|
||||
}
|
||||
|
||||
_transports.clear();
|
||||
@@ -1589,6 +1582,9 @@ void Map::SendInitSelf(Player* player)
|
||||
|
||||
void Map::SendInitTransports(Player* player)
|
||||
{
|
||||
if (_transports.empty())
|
||||
return;
|
||||
|
||||
// Hack to send out transports
|
||||
UpdateData transData;
|
||||
for (TransportsContainer::const_iterator itr = _transports.begin(); itr != _transports.end(); ++itr)
|
||||
@@ -1605,24 +1601,15 @@ void Map::SendInitTransports(Player* player)
|
||||
|
||||
void Map::SendRemoveTransports(Player* player)
|
||||
{
|
||||
if (_transports.empty())
|
||||
return;
|
||||
|
||||
// Hack to send out transports
|
||||
UpdateData transData;
|
||||
for (TransportsContainer::const_iterator itr = _transports.begin(); itr != _transports.end(); ++itr)
|
||||
if (*itr != player->GetTransport())
|
||||
(*itr)->BuildOutOfRangeUpdateBlock(&transData);
|
||||
|
||||
// pussywizard: remove static transports from client
|
||||
/*for (GuidUnorderedSet::const_iterator it = player->m_clientGUIDs.begin(); it != player->m_clientGUIDs.end(); )
|
||||
{
|
||||
if ((*it).IsTransport())
|
||||
{
|
||||
transData.AddOutOfRangeGUID(*it);
|
||||
it = player->m_clientGUIDs.erase(it);
|
||||
}
|
||||
else
|
||||
++it;
|
||||
}*/
|
||||
|
||||
if (!transData.HasData())
|
||||
return;
|
||||
|
||||
@@ -1699,7 +1686,7 @@ void Map::DelayedUpdate(const uint32 t_diff)
|
||||
{
|
||||
for (_transportsUpdateIter = _transports.begin(); _transportsUpdateIter != _transports.end();)
|
||||
{
|
||||
MotionTransport* transport = *_transportsUpdateIter;
|
||||
Transport* transport = *_transportsUpdateIter;
|
||||
++_transportsUpdateIter;
|
||||
|
||||
if (!transport->IsInWorld())
|
||||
@@ -1744,7 +1731,7 @@ void Map::RemoveAllObjectsInRemoveList()
|
||||
RemoveFromMap((DynamicObject*)obj, true);
|
||||
break;
|
||||
case TYPEID_GAMEOBJECT:
|
||||
if (MotionTransport* transport = obj->ToGameObject()->ToMotionTransport())
|
||||
if (Transport* transport = obj->ToGameObject()->ToTransport())
|
||||
RemoveFromMap(transport, true);
|
||||
else
|
||||
RemoveFromMap(obj->ToGameObject(), true);
|
||||
|
||||
@@ -146,7 +146,7 @@ struct ZoneDynamicInfo
|
||||
|
||||
typedef std::map<uint32/*leaderDBGUID*/, CreatureGroup*> CreatureGroupHolderType;
|
||||
typedef std::unordered_map<uint32 /*zoneId*/, ZoneDynamicInfo> ZoneDynamicInfoMap;
|
||||
typedef std::set<MotionTransport*> TransportsContainer;
|
||||
typedef std::unordered_set<Transport*> TransportsContainer;
|
||||
|
||||
enum EncounterCreditType : uint8
|
||||
{
|
||||
@@ -479,7 +479,7 @@ public:
|
||||
_updateObjects.erase(obj);
|
||||
}
|
||||
|
||||
size_t GetUpdateObjectsCount() const { return _updateObjects.size(); }
|
||||
size_t GetUpdatableObjectsCount() const { return _updatableObjectList.size(); }
|
||||
|
||||
virtual std::string GetDebugInfo() const;
|
||||
|
||||
@@ -535,7 +535,6 @@ protected:
|
||||
MapRefMgr m_mapRefMgr;
|
||||
MapRefMgr::iterator m_mapRefIter;
|
||||
|
||||
// Objects that must update even in inactive grids without activating them
|
||||
TransportsContainer _transports;
|
||||
TransportsContainer::iterator _transportsUpdateIter;
|
||||
|
||||
|
||||
@@ -412,10 +412,8 @@ MotionTransport* TransportMgr::CreateTransport(uint32 entry, ObjectGuid::LowType
|
||||
if (map && map->IsDungeon())
|
||||
trans->m_zoneScript = map->ToInstanceMap()->GetInstanceScript();
|
||||
|
||||
// xinef: transports are active so passengers can be relocated (grids must be loaded)
|
||||
trans->setActive(true);
|
||||
HashMapHolder<MotionTransport>::Insert(trans);
|
||||
trans->GetMap()->AddToMap<MotionTransport>(trans);
|
||||
trans->GetMap()->AddToMap<Transport>(trans);
|
||||
return trans;
|
||||
}
|
||||
|
||||
|
||||
@@ -139,6 +139,11 @@ void ScriptMgr::OnPlayerReputationRankChange(Player* player, uint32 factionID, R
|
||||
CALL_ENABLED_HOOKS(PlayerScript, PLAYERHOOK_ON_REPUTATION_RANK_CHANGE, script->OnPlayerReputationRankChange(player, factionID, newRank, oldRank, increased));
|
||||
}
|
||||
|
||||
void ScriptMgr::OnPlayerGiveReputation(Player* player, int32 factionID, float& amount, ReputationSource repSource)
|
||||
{
|
||||
CALL_ENABLED_HOOKS(PlayerScript, PLAYERHOOK_ON_GIVE_REPUTATION, script->OnPlayerGiveReputation(player, factionID, amount, repSource));
|
||||
}
|
||||
|
||||
void ScriptMgr::OnPlayerLearnSpell(Player* player, uint32 spellID)
|
||||
{
|
||||
CALL_ENABLED_HOOKS(PlayerScript, PLAYERHOOK_ON_LEARN_SPELL, script->OnPlayerLearnSpell(player, spellID));
|
||||
|
||||
@@ -211,6 +211,7 @@ enum PlayerHook
|
||||
PLAYERHOOK_CAN_RESURRECT,
|
||||
PLAYERHOOK_ON_CAN_GIVE_LEVEL,
|
||||
PLAYERHOOK_ON_SEND_LIST_INVENTORY,
|
||||
PLAYERHOOK_ON_GIVE_REPUTATION,
|
||||
PLAYERHOOK_END
|
||||
};
|
||||
|
||||
@@ -285,6 +286,9 @@ public:
|
||||
// Called when a player's reputation rank changes (before it is actually changed)
|
||||
virtual void OnPlayerReputationRankChange(Player* /*player*/, uint32 /*factionID*/, ReputationRank /*newRank*/, ReputationRank /*olRank*/, bool /*increased*/) { }
|
||||
|
||||
// Called when a player gains Reputation (before anything is given)
|
||||
virtual void OnPlayerGiveReputation(Player* /*player*/, int32 /*factionID*/, float& /*amount*/, ReputationSource /*repSource*/) { }
|
||||
|
||||
// Called when a player learned new spell
|
||||
virtual void OnPlayerLearnSpell(Player* /*player*/, uint32 /*spellID*/) {}
|
||||
|
||||
|
||||
@@ -334,6 +334,7 @@ public: /* PlayerScript */
|
||||
void OnPlayerGiveXP(Player* player, uint32& amount, Unit* victim, uint8 xpSource);
|
||||
bool OnPlayerReputationChange(Player* player, uint32 factionID, int32& standing, bool incremental);
|
||||
void OnPlayerReputationRankChange(Player* player, uint32 factionID, ReputationRank newRank, ReputationRank oldRank, bool increased);
|
||||
void OnPlayerGiveReputation(Player* player, int32 factionID, float& amount, ReputationSource repSource);
|
||||
void OnPlayerLearnSpell(Player* player, uint32 spellID);
|
||||
void OnPlayerForgotSpell(Player* player, uint32 spellID);
|
||||
void OnPlayerDuelRequest(Player* target, Player* challenger);
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "LFGPackets.h"
|
||||
#include "MiscPackets.h"
|
||||
#include "PetPackets.h"
|
||||
#include "QueryPackets.h"
|
||||
#include "TotemPackets.h"
|
||||
#include "WorldStatePackets.h"
|
||||
|
||||
|
||||
@@ -67,6 +67,21 @@ WorldPacket const* WorldPackets::Misc::Playsound::Write()
|
||||
return &_worldPacket;
|
||||
}
|
||||
|
||||
void WorldPackets::Misc::MinimapPingClient::Read()
|
||||
{
|
||||
_worldPacket >> MapX;
|
||||
_worldPacket >> MapY;
|
||||
}
|
||||
|
||||
WorldPacket const* WorldPackets::Misc::MinimapPing::Write()
|
||||
{
|
||||
_worldPacket << SourceGuid;
|
||||
_worldPacket << float(MapX);
|
||||
_worldPacket << float(MapY);
|
||||
|
||||
return &_worldPacket;
|
||||
}
|
||||
|
||||
void WorldPackets::Misc::RandomRollClient::Read()
|
||||
{
|
||||
_worldPacket >> Min;
|
||||
|
||||
@@ -93,6 +93,29 @@ namespace WorldPackets
|
||||
uint32 SoundKitID = 0;
|
||||
};
|
||||
|
||||
class MinimapPingClient final : public ClientPacket
|
||||
{
|
||||
public:
|
||||
MinimapPingClient(WorldPacket&& packet) : ClientPacket(MSG_MINIMAP_PING, std::move(packet)) {}
|
||||
|
||||
void Read() override;
|
||||
|
||||
float MapX = 0.0f; // Raw position coordinates
|
||||
float MapY = 0.0f;
|
||||
};
|
||||
|
||||
class MinimapPing final : public ServerPacket
|
||||
{
|
||||
public:
|
||||
MinimapPing() : ServerPacket(MSG_MINIMAP_PING, 8 + 4 + 4) { }
|
||||
|
||||
WorldPacket const* Write() override;
|
||||
|
||||
ObjectGuid SourceGuid;
|
||||
float MapX = 0.0f;
|
||||
float MapY = 0.0f;
|
||||
};
|
||||
|
||||
class RandomRollClient final : public ClientPacket
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by the
|
||||
* Free Software Foundation; either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "QueryPackets.h"
|
||||
|
||||
void WorldPackets::Query::NameQuery::Read()
|
||||
{
|
||||
_worldPacket >> Guid;
|
||||
}
|
||||
|
||||
WorldPacket const* WorldPackets::Query::NameQueryResponse::Write()
|
||||
{
|
||||
_worldPacket << Guid;
|
||||
_worldPacket << NameUnknown;
|
||||
if (NameUnknown)
|
||||
return &_worldPacket;
|
||||
|
||||
_worldPacket << Name;
|
||||
_worldPacket << RealmName;
|
||||
_worldPacket << Race;
|
||||
_worldPacket << Sex;
|
||||
_worldPacket << Class;
|
||||
_worldPacket << Declined;
|
||||
if (Declined)
|
||||
{
|
||||
for (uint8 i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
|
||||
_worldPacket << DeclinedNames.name[i];
|
||||
}
|
||||
|
||||
return &_worldPacket;
|
||||
}
|
||||
|
||||
WorldPacket const* WorldPackets::Query::TimeQueryResponse::Write()
|
||||
{
|
||||
_worldPacket << ServerTime;
|
||||
_worldPacket << TimeResponse;
|
||||
|
||||
return &_worldPacket;
|
||||
}
|
||||
|
||||
void WorldPackets::Query::CorpseMapPositionQuery::Read()
|
||||
{
|
||||
_worldPacket >> unk;
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by the
|
||||
* Free Software Foundation; either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef QueryPackets_h__
|
||||
#define QueryPackets_h__
|
||||
|
||||
#include "Packet.h"
|
||||
#include "Unit.h"
|
||||
|
||||
namespace WorldPackets
|
||||
{
|
||||
namespace Query
|
||||
{
|
||||
class NameQuery final : public ClientPacket
|
||||
{
|
||||
public:
|
||||
NameQuery(WorldPacket&& packet) : ClientPacket(CMSG_NAME_QUERY, std::move(packet)) {}
|
||||
|
||||
void Read() override;
|
||||
|
||||
ObjectGuid Guid;
|
||||
};
|
||||
|
||||
class NameQueryResponse final : public ServerPacket
|
||||
{
|
||||
public:
|
||||
NameQueryResponse() : ServerPacket(SMSG_NAME_QUERY_RESPONSE, 8 + 1 + 1 + 1 + 1 + 1 + 10) {}
|
||||
|
||||
WorldPacket const* Write() override;
|
||||
|
||||
PackedGuid Guid;
|
||||
uint8 NameUnknown = false;
|
||||
std::string_view Name;
|
||||
std::string_view RealmName = ""; // Only set for cross realm interaction (such as Battlegrounds)
|
||||
uint8 Race = RACE_NONE;
|
||||
uint8 Sex = GENDER_MALE;
|
||||
uint8 Class = CLASS_NONE;
|
||||
uint8 Declined = false;
|
||||
DeclinedName DeclinedNames;
|
||||
};
|
||||
|
||||
class TimeQuery final : public ClientPacket
|
||||
{
|
||||
public:
|
||||
TimeQuery(WorldPacket&& packet) : ClientPacket(CMSG_QUERY_TIME, std::move(packet)) {}
|
||||
|
||||
void Read() override {};
|
||||
};
|
||||
|
||||
class TimeQueryResponse final : public ServerPacket
|
||||
{
|
||||
public:
|
||||
TimeQueryResponse() : ServerPacket(SMSG_QUERY_TIME_RESPONSE, 4 + 4) {}
|
||||
|
||||
WorldPacket const* Write() override;
|
||||
|
||||
uint32 ServerTime;
|
||||
uint32 TimeResponse;
|
||||
};
|
||||
|
||||
class CorpseMapPositionQuery final : public ClientPacket
|
||||
{
|
||||
public:
|
||||
CorpseMapPositionQuery(WorldPacket&& packet) : ClientPacket(CMSG_CORPSE_MAP_POSITION_QUERY, std::move(packet)) {}
|
||||
|
||||
void Read() override;
|
||||
|
||||
uint32 unk;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif // QueryPackets_h__
|
||||
@@ -590,7 +590,7 @@ void OpcodeTable::Initialize()
|
||||
/*0x1CB*/ DEFINE_SERVER_OPCODE_HANDLER(SMSG_NOTIFICATION, STATUS_NEVER);
|
||||
/*0x1CC*/ DEFINE_HANDLER(CMSG_PLAYED_TIME, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandlePlayedTime );
|
||||
/*0x1CD*/ DEFINE_SERVER_OPCODE_HANDLER(SMSG_PLAYED_TIME, STATUS_NEVER);
|
||||
/*0x1CE*/ DEFINE_HANDLER(CMSG_QUERY_TIME, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleQueryTimeOpcode );
|
||||
/*0x1CE*/ DEFINE_HANDLER(CMSG_QUERY_TIME, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleTimeQueryOpcode );
|
||||
/*0x1CF*/ DEFINE_SERVER_OPCODE_HANDLER(SMSG_QUERY_TIME_RESPONSE, STATUS_NEVER);
|
||||
/*0x1D0*/ DEFINE_SERVER_OPCODE_HANDLER(SMSG_LOG_XPGAIN, STATUS_NEVER);
|
||||
/*0x1D1*/ DEFINE_SERVER_OPCODE_HANDLER(SMSG_AURACASTLOG, STATUS_NEVER);
|
||||
|
||||
@@ -148,6 +148,7 @@ namespace WorldPackets
|
||||
|
||||
namespace Misc
|
||||
{
|
||||
class MinimapPingClient;
|
||||
class RandomRollClient;
|
||||
}
|
||||
|
||||
@@ -159,6 +160,13 @@ namespace WorldPackets
|
||||
class PetSpellAutocast;
|
||||
class RequestPetInfo;
|
||||
}
|
||||
|
||||
namespace Query
|
||||
{
|
||||
class NameQuery;
|
||||
class TimeQuery;
|
||||
class CorpseMapPositionQuery;
|
||||
}
|
||||
}
|
||||
|
||||
enum AccountDataType
|
||||
@@ -389,7 +397,7 @@ public:
|
||||
}
|
||||
|
||||
void SendSetPhaseShift(uint32 phaseShift);
|
||||
void SendQueryTimeResponse();
|
||||
void SendTimeQueryResponse();
|
||||
|
||||
void SendAuthResponse(uint8 code, bool shortForm, uint32 queuePos = 0);
|
||||
void SendClientCacheVersion(uint32 version);
|
||||
@@ -680,9 +688,9 @@ public: // opcodes handlers
|
||||
void HandleGameObjectUseOpcode(WorldPacket& recPacket);
|
||||
void HandleGameobjectReportUse(WorldPacket& recvPacket);
|
||||
|
||||
void HandleNameQueryOpcode(WorldPacket& recvPacket);
|
||||
void HandleNameQueryOpcode(WorldPackets::Query::NameQuery& packet);
|
||||
|
||||
void HandleQueryTimeOpcode(WorldPacket& recvPacket);
|
||||
void HandleTimeQueryOpcode(WorldPackets::Query::TimeQuery& packet);
|
||||
|
||||
void HandleCreatureQueryOpcode(WorldPacket& recvPacket);
|
||||
|
||||
@@ -882,7 +890,7 @@ public: // opcodes handlers
|
||||
|
||||
void HandleReclaimCorpseOpcode(WorldPacket& recvPacket);
|
||||
void HandleCorpseQueryOpcode(WorldPacket& recvPacket);
|
||||
void HandleCorpseMapPositionQuery(WorldPacket& recvPacket);
|
||||
void HandleCorpseMapPositionQuery(WorldPackets::Query::CorpseMapPositionQuery& packet);
|
||||
void HandleResurrectResponseOpcode(WorldPacket& recvPacket);
|
||||
void HandleSummonResponseOpcode(WorldPacket& recvData);
|
||||
|
||||
@@ -953,7 +961,7 @@ public: // opcodes handlers
|
||||
|
||||
void HandleWardenDataOpcode(WorldPacket& recvData);
|
||||
void HandleWorldTeleportOpcode(WorldPacket& recvData);
|
||||
void HandleMinimapPingOpcode(WorldPacket& recvData);
|
||||
void HandleMinimapPingOpcode(WorldPackets::Misc::MinimapPingClient& packet);
|
||||
void HandleRandomRollOpcode(WorldPackets::Misc::RandomRollClient& packet);
|
||||
void HandleFarSightOpcode(WorldPacket& recvData);
|
||||
void HandleSetDungeonDifficultyOpcode(WorldPacket& recvData);
|
||||
|
||||
@@ -492,6 +492,8 @@ void WorldConfig::BuildConfigCache()
|
||||
|
||||
SetConfigValue<bool>(CONFIG_LEAVE_GROUP_ON_LOGOUT, "LeaveGroupOnLogout.Enabled", false);
|
||||
|
||||
SetConfigValue<uint32>(CONFIG_RANDOM_ROLL_MAXIMUM, "Group.RandomRollMaximum", 1000000);
|
||||
|
||||
SetConfigValue<bool>(CONFIG_QUEST_POI_ENABLED, "QuestPOI.Enabled", true);
|
||||
|
||||
SetConfigValue<uint32>(CONFIG_CHANGE_FACTION_MAX_MONEY, "ChangeFaction.MaxMoney", 0);
|
||||
|
||||
@@ -129,6 +129,7 @@ enum ServerConfigs
|
||||
CONFIG_ALLOW_JOIN_BG_AND_LFG,
|
||||
CONFIG_MISS_CHANCE_MULTIPLIER_ONLY_FOR_PLAYERS,
|
||||
CONFIG_LEAVE_GROUP_ON_LOGOUT,
|
||||
CONFIG_RANDOM_ROLL_MAXIMUM,
|
||||
CONFIG_QUEST_POI_ENABLED,
|
||||
CONFIG_VMAP_BLIZZLIKE_PVP_LOS,
|
||||
CONFIG_VMAP_BLIZZLIKE_LOS_OPEN_WORLD,
|
||||
|
||||
@@ -73,7 +73,7 @@ public:
|
||||
{ "hostile", HandleDebugHostileRefListCommand, SEC_ADMINISTRATOR, Console::No },
|
||||
{ "anim", HandleDebugAnimCommand, SEC_ADMINISTRATOR, Console::No },
|
||||
{ "arena", HandleDebugArenaCommand, SEC_ADMINISTRATOR, Console::No },
|
||||
{ "bg", HandleDebugBattlegroundCommand, SEC_ADMINISTRATOR, Console::No },
|
||||
{ "bg", HandleDebugBattlegroundCommand, SEC_ADMINISTRATOR, Console::Yes},
|
||||
{ "cooldown", HandleDebugCooldownCommand, SEC_ADMINISTRATOR, Console::No },
|
||||
{ "getitemstate", HandleDebugGetItemStateCommand, SEC_ADMINISTRATOR, Console::No },
|
||||
{ "lootrecipient", HandleDebugGetLootRecipientCommand, SEC_ADMINISTRATOR, Console::No },
|
||||
@@ -92,7 +92,7 @@ public:
|
||||
{ "update", HandleDebugUpdateCommand, SEC_ADMINISTRATOR, Console::No },
|
||||
{ "itemexpire", HandleDebugItemExpireCommand, SEC_ADMINISTRATOR, Console::No },
|
||||
{ "areatriggers", HandleDebugAreaTriggersCommand, SEC_ADMINISTRATOR, Console::No },
|
||||
{ "lfg", HandleDebugDungeonFinderCommand, SEC_ADMINISTRATOR, Console::No },
|
||||
{ "lfg", HandleDebugDungeonFinderCommand, SEC_ADMINISTRATOR, Console::Yes},
|
||||
{ "los", HandleDebugLoSCommand, SEC_ADMINISTRATOR, Console::No },
|
||||
{ "moveflags", HandleDebugMoveflagsCommand, SEC_ADMINISTRATOR, Console::No },
|
||||
{ "unitstate", HandleDebugUnitStateCommand, SEC_ADMINISTRATOR, Console::No },
|
||||
@@ -1351,7 +1351,7 @@ public:
|
||||
map->GetId(), map->GetMapName(), map->GetInstanceId(),
|
||||
uint64(map->GetObjectsStore().Size<Creature>()),
|
||||
uint64(map->GetObjectsStore().Size<GameObject>()),
|
||||
uint64(map->GetUpdateObjectsCount()));
|
||||
uint64(map->GetUpdatableObjectsCount()));
|
||||
|
||||
CreatureCountWorker worker;
|
||||
TypeContainerVisitor<CreatureCountWorker, MapStoredObjectTypesContainer> visitor(worker);
|
||||
|
||||
@@ -0,0 +1,375 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by the
|
||||
* Free Software Foundation; either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "karazhan.h"
|
||||
#include "AreaTriggerScript.h"
|
||||
#include "CreatureScript.h"
|
||||
#include "GameObjectAI.h"
|
||||
#include "GameObjectScript.h"
|
||||
#include "Player.h"
|
||||
#include "ScriptedCreature.h"
|
||||
#include "SpellInfo.h"
|
||||
#include "SpellScript.h"
|
||||
#include "SpellScriptLoader.h"
|
||||
#include "UnitAI.h"
|
||||
|
||||
enum Text
|
||||
{
|
||||
SAY_APPROACH = 0,
|
||||
SAY_AGGRO = 1,
|
||||
SAY_SUMMON = 2
|
||||
};
|
||||
|
||||
enum Spells
|
||||
{
|
||||
SPELL_BLOOD_MIRROR0 = 50844,
|
||||
SPELL_BLOOD_MIRROR1 = 50845,
|
||||
SPELL_BLOOD_MIRROR_TARGET_PICKER = 50883,
|
||||
SPELL_BLOOD_MIRROR_TRANSITION_VISUAL = 50910,
|
||||
SPELL_BLOOD_MIRROR_DAMAGE = 50846,
|
||||
|
||||
SPELL_BLOOD_TAP = 51135,
|
||||
|
||||
SPELL_BLOOD_SWOOP = 50922,
|
||||
SPELL_DASH_GASH_PRE_SPELL = 50923,
|
||||
SPELL_DASH_GASH_RETURN_TO_TANK = 50924,
|
||||
// SPELL_DASH_GASH_RETURN_TO_TANK_PRE_SPELL = 50925,
|
||||
// SPELL_DASH_GASH_RETURN_TO_TANK_PRE_SPELL_ROOT = 50932,
|
||||
|
||||
SPELL_DESPAWN_SANGUINE_SPIRIT_VISUAL = 51214,
|
||||
SPELL_DESPAWN_SANGUINE_SPIRITS = 51212,
|
||||
SPELL_SANGUINE_SPIRIT_AURA = 50993,
|
||||
SPELL_SANGUINE_SPIRIT_PRE_AURA = 51282,
|
||||
SPELL_SANGUINE_SPIRIT_PRE_AURA2 = 51283,
|
||||
SPELL_SUMMON_SANGUINE_SPIRIT0 = 50996,
|
||||
SPELL_SUMMON_SANGUINE_SPIRIT1 = 50998,
|
||||
// SPELL_SUMMON_SANGUINE_SPIRIT2 = 51204,
|
||||
SPELL_SUMMON_SANGUINE_SPIRIT_MISSILE_BURST = 51208,
|
||||
SPELL_SUMMON_SANGUINE_SPIRIT_SHORT_MISSILE_BURST = 51280,
|
||||
SPELL_SUMMON_SANGUINE_SPIRIT_ON_KILL = 51205,
|
||||
SPELL_EXSANGUINATE = 51013,
|
||||
SPELL_DUMMY_NUKE_RANGE_SELF = 51106,
|
||||
};
|
||||
|
||||
enum Events
|
||||
{
|
||||
EVENT_SAY = 1,
|
||||
EVENT_FLAG = 2
|
||||
};
|
||||
|
||||
struct boss_tenris_mirkblood : public BossAI
|
||||
{
|
||||
boss_tenris_mirkblood(Creature* creature) : BossAI(creature, DATA_MIRKBLOOD)
|
||||
{
|
||||
scheduler.SetValidator([this]
|
||||
{
|
||||
return !me->HasUnitState(UNIT_STATE_CASTING);
|
||||
});
|
||||
}
|
||||
|
||||
void Reset() override
|
||||
{
|
||||
_Reset();
|
||||
|
||||
me->SetImmuneToPC(true);
|
||||
|
||||
ScheduleHealthCheckEvent(50, [&] {
|
||||
Talk(SAY_SUMMON);
|
||||
DoCast(SPELL_SUMMON_SANGUINE_SPIRIT_MISSILE_BURST);
|
||||
});
|
||||
|
||||
ScheduleHealthCheckEvent(45, [&] {
|
||||
ScheduleTimedEvent(10s, 15s, [&] {
|
||||
DoCast(SPELL_BLOOD_TAP);
|
||||
}, 15s, 40s);
|
||||
});
|
||||
}
|
||||
|
||||
void JustEngagedWith(Unit* /*who*/) override
|
||||
{
|
||||
DoZoneInCombat();
|
||||
|
||||
ScheduleTimedEvent(1s, 5s, [&] {
|
||||
// Blood Mirror
|
||||
DoCast(SPELL_BLOOD_MIRROR_TARGET_PICKER);
|
||||
}, 20s, 50s);
|
||||
ScheduleTimedEvent(30s, [&] {
|
||||
// Blood Swoop
|
||||
DoCast(SPELL_DASH_GASH_PRE_SPELL);
|
||||
}, 15s, 40s);
|
||||
ScheduleTimedEvent(6s, 15s, [&] {
|
||||
// Sanguine Spirit
|
||||
DoCast(SPELL_SUMMON_SANGUINE_SPIRIT_SHORT_MISSILE_BURST);
|
||||
}, 6s, 15s);
|
||||
}
|
||||
|
||||
void KilledUnit(Unit* victim) override
|
||||
{
|
||||
if (!victim)
|
||||
return;
|
||||
|
||||
DoCast(victim, SPELL_SUMMON_SANGUINE_SPIRIT_ON_KILL);
|
||||
}
|
||||
|
||||
void DamageTaken(Unit* attacker, uint32& damage, DamageEffectType damageType, SpellSchoolMask damageSchoolMask) override
|
||||
{
|
||||
BossAI::DamageTaken(attacker, damage, damageType, damageSchoolMask);
|
||||
|
||||
if (!me->HasAura(SPELL_BLOOD_MIRROR0))
|
||||
return;
|
||||
|
||||
if (!_mirrorTarget)
|
||||
return;
|
||||
|
||||
int32 damageTaken = damage;
|
||||
|
||||
me->CastCustomSpell(_mirrorTarget, SPELL_BLOOD_MIRROR_DAMAGE, &damageTaken, &damageTaken, &damageTaken, true, nullptr, nullptr, me->GetGUID());
|
||||
}
|
||||
|
||||
void SpellHit(Unit* caster, SpellInfo const* spell) override
|
||||
{
|
||||
if (spell->Id == SPELL_BLOOD_MIRROR0 && caster != me)
|
||||
_mirrorTarget = caster;
|
||||
}
|
||||
|
||||
void EnterEvadeMode(EvadeReason why) override
|
||||
{
|
||||
_EnterEvadeMode(why);
|
||||
me->SetImmuneToPC(false);
|
||||
}
|
||||
|
||||
private:
|
||||
Unit* _mirrorTarget = nullptr;
|
||||
};
|
||||
|
||||
struct npc_sanguine_spirit : public ScriptedAI
|
||||
{
|
||||
npc_sanguine_spirit(Creature* creature) : ScriptedAI(creature) {}
|
||||
|
||||
void Reset() override
|
||||
{
|
||||
scheduler.CancelAll();
|
||||
me->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_ALL, true);
|
||||
|
||||
me->SetReactState(REACT_PASSIVE);
|
||||
|
||||
DoCastSelf(SPELL_SANGUINE_SPIRIT_PRE_AURA);
|
||||
|
||||
scheduler.Schedule(5s, [this](TaskContext /*context*/)
|
||||
{
|
||||
DoCastSelf(SPELL_SANGUINE_SPIRIT_PRE_AURA2);
|
||||
}).Schedule(3s, [this](TaskContext /*context*/)
|
||||
{
|
||||
me->SetReactState(REACT_AGGRESSIVE);
|
||||
me->SetInCombatWithZone();
|
||||
DoCastSelf(SPELL_SANGUINE_SPIRIT_AURA);
|
||||
});
|
||||
}
|
||||
|
||||
void UpdateAI(uint32 diff) override
|
||||
{
|
||||
scheduler.Update(diff);
|
||||
UpdateVictim();
|
||||
}
|
||||
};
|
||||
|
||||
class spell_mirkblood_blood_mirror_target_picker : public SpellScript
|
||||
{
|
||||
PrepareSpellScript(spell_mirkblood_blood_mirror_target_picker)
|
||||
|
||||
bool Validate(SpellInfo const* /*spell*/) override
|
||||
{
|
||||
return ValidateSpellInfo({ SPELL_BLOOD_MIRROR0, SPELL_BLOOD_MIRROR1, SPELL_BLOOD_MIRROR_TRANSITION_VISUAL });
|
||||
}
|
||||
|
||||
void HandleHit()
|
||||
{
|
||||
Unit* caster = GetCaster();
|
||||
|
||||
if (!caster->ToCreature())
|
||||
return;
|
||||
|
||||
Unit* target = caster->GetAI()->SelectTarget(SelectTargetMethod::Random, 0, 0.0f, true, false);
|
||||
|
||||
if (!target) // Only Blood Mirror the tank if they're the only one around
|
||||
target = caster->GetVictim();
|
||||
|
||||
if (!target)
|
||||
return;
|
||||
|
||||
caster->CastSpell(caster, SPELL_BLOOD_MIRROR_TRANSITION_VISUAL, TRIGGERED_FULL_MASK);
|
||||
caster->CastSpell(target, SPELL_BLOOD_MIRROR_TRANSITION_VISUAL, TRIGGERED_FULL_MASK);
|
||||
|
||||
caster->AddAura(SPELL_BLOOD_MIRROR1, caster); // Should be a cast, but channeled spell results in either Mirkblood or player being unactionable
|
||||
caster->AddAura(SPELL_BLOOD_MIRROR1, target); // Adding aura manually causes visual to not appear properly, but better than breaking gameplay
|
||||
|
||||
target->CastSpell(caster, SPELL_BLOOD_MIRROR0); // Clone player
|
||||
}
|
||||
|
||||
void Register() override
|
||||
{
|
||||
OnCast += SpellCastFn(spell_mirkblood_blood_mirror_target_picker::HandleHit);
|
||||
}
|
||||
};
|
||||
|
||||
class spell_mirkblood_dash_gash_return_to_tank_pre_spell : public SpellScript
|
||||
{
|
||||
PrepareSpellScript(spell_mirkblood_dash_gash_return_to_tank_pre_spell)
|
||||
|
||||
bool Validate(SpellInfo const* /*spell*/) override
|
||||
{
|
||||
return ValidateSpellInfo({ SPELL_DASH_GASH_RETURN_TO_TANK });
|
||||
}
|
||||
|
||||
void HandleCast()
|
||||
{
|
||||
if (!GetCaster() || !GetCaster()->GetThreatMgr().GetCurrentVictim())
|
||||
return;
|
||||
// Probably wrong, maybe don't charge if would charge the same target?
|
||||
if (GetCaster()->GetDistance2d(GetCaster()->GetThreatMgr().GetCurrentVictim()) < 5.0f)
|
||||
return;
|
||||
|
||||
GetCaster()->CastSpell(GetCaster()->GetVictim(), SPELL_DASH_GASH_RETURN_TO_TANK);
|
||||
}
|
||||
|
||||
void Register() override
|
||||
{
|
||||
OnCast += SpellCastFn(spell_mirkblood_dash_gash_return_to_tank_pre_spell::HandleCast);
|
||||
}
|
||||
};
|
||||
|
||||
class spell_mirkblood_exsanguinate : public SpellScript
|
||||
{
|
||||
PrepareSpellScript(spell_mirkblood_exsanguinate)
|
||||
|
||||
void CalculateDamage()
|
||||
{
|
||||
if (!GetHitUnit())
|
||||
return;
|
||||
|
||||
SetHitDamage(std::max((GetHitUnit()->GetHealth() * 0.66f), 2000.0f));
|
||||
}
|
||||
|
||||
void Register() override
|
||||
{
|
||||
OnHit += SpellHitFn(spell_mirkblood_exsanguinate::CalculateDamage);
|
||||
}
|
||||
};
|
||||
|
||||
class at_karazhan_mirkblood_approach : public AreaTriggerScript
|
||||
{
|
||||
public:
|
||||
at_karazhan_mirkblood_approach() : AreaTriggerScript("at_karazhan_mirkblood_approach") {}
|
||||
|
||||
bool OnTrigger(Player* player, AreaTrigger const* /*trigger*/) override
|
||||
{
|
||||
if (InstanceScript* instance = player->GetInstanceScript())
|
||||
if (instance->GetBossState(DATA_MIRKBLOOD) != DONE)
|
||||
if (Creature* mirkblood = instance->GetCreature(DATA_MIRKBLOOD))
|
||||
mirkblood->AI()->Talk(SAY_APPROACH, player);
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
class at_karazhan_mirkblood_entrance : public AreaTriggerScript
|
||||
{
|
||||
public:
|
||||
at_karazhan_mirkblood_entrance() : AreaTriggerScript("at_karazhan_mirkblood_entrance") {}
|
||||
|
||||
bool OnTrigger(Player* player, AreaTrigger const* /*trigger*/) override
|
||||
{
|
||||
if (InstanceScript* instance = player->GetInstanceScript())
|
||||
if (instance->GetBossState(DATA_MIRKBLOOD) != DONE)
|
||||
if (Creature* mirkblood = instance->GetCreature(DATA_MIRKBLOOD))
|
||||
mirkblood->SetImmuneToPC(false);
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
class go_blood_drenched_door : public GameObjectScript
|
||||
{
|
||||
public:
|
||||
go_blood_drenched_door() : GameObjectScript("go_blood_drenched_door") {}
|
||||
|
||||
struct go_blood_drenched_doorAI : public GameObjectAI
|
||||
{
|
||||
go_blood_drenched_doorAI(GameObject* go) : GameObjectAI(go) {}
|
||||
|
||||
EventMap events;
|
||||
Creature* mirkblood;
|
||||
Player* opener;
|
||||
|
||||
bool GossipHello(Player* player, bool /*reportUse*/) override
|
||||
{
|
||||
events.Reset();
|
||||
|
||||
if (InstanceScript* instance = player->GetInstanceScript())
|
||||
if (instance->GetBossState(DATA_MIRKBLOOD) != DONE)
|
||||
{
|
||||
opener = player;
|
||||
mirkblood = instance->GetCreature(DATA_MIRKBLOOD);
|
||||
|
||||
events.ScheduleEvent(EVENT_SAY, 1s);
|
||||
events.ScheduleEvent(EVENT_FLAG, 5s);
|
||||
me->SetGameObjectFlag(GO_FLAG_NOT_SELECTABLE);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void UpdateAI(uint32 diff) override
|
||||
{
|
||||
if (events.Empty())
|
||||
return;
|
||||
|
||||
events.Update(diff);
|
||||
switch (events.ExecuteEvent())
|
||||
{
|
||||
case EVENT_SAY:
|
||||
if (!mirkblood)
|
||||
return;
|
||||
mirkblood->AI()->Talk(SAY_AGGRO, opener);
|
||||
break;
|
||||
case EVENT_FLAG:
|
||||
if (!mirkblood)
|
||||
return;
|
||||
mirkblood->SetImmuneToPC(false);
|
||||
me->Delete();
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
GameObjectAI* GetAI(GameObject* go) const override
|
||||
{
|
||||
return new go_blood_drenched_doorAI(go);
|
||||
}
|
||||
};
|
||||
|
||||
void AddSC_boss_tenris_mirkblood()
|
||||
{
|
||||
RegisterKarazhanCreatureAI(boss_tenris_mirkblood);
|
||||
RegisterKarazhanCreatureAI(npc_sanguine_spirit);
|
||||
RegisterSpellScript(spell_mirkblood_blood_mirror_target_picker);
|
||||
RegisterSpellScript(spell_mirkblood_dash_gash_return_to_tank_pre_spell);
|
||||
RegisterSpellScript(spell_mirkblood_exsanguinate);
|
||||
new at_karazhan_mirkblood_approach();
|
||||
new at_karazhan_mirkblood_entrance();
|
||||
new go_blood_drenched_door();
|
||||
}
|
||||
@@ -47,6 +47,7 @@ ObjectData const creatureData[] =
|
||||
{ NPC_JULIANNE, DATA_JULIANNE },
|
||||
{ NPC_NIGHTBANE, DATA_NIGHTBANE },
|
||||
{ NPC_TERESTIAN_ILLHOOF, DATA_TERESTIAN },
|
||||
{ NPC_TENRIS_MIRKBLOOD, DATA_MIRKBLOOD },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
|
||||
@@ -73,7 +73,9 @@ enum KZDataTypes
|
||||
DATA_ROAR = 41,
|
||||
DATA_STRAWMAN = 42,
|
||||
DATA_TINHEAD = 43,
|
||||
DATA_TITO = 44
|
||||
DATA_TITO = 44,
|
||||
|
||||
DATA_MIRKBLOOD = 45
|
||||
};
|
||||
|
||||
enum KZOperaEvents
|
||||
@@ -135,7 +137,9 @@ enum KZCreatures
|
||||
|
||||
// Malchezaar Helpers
|
||||
NPC_INFERNAL_TARGET = 17644,
|
||||
NPC_INFERNAL_RELAY = 17645
|
||||
NPC_INFERNAL_RELAY = 17645,
|
||||
|
||||
NPC_TENRIS_MIRKBLOOD = 28194
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -1181,6 +1181,26 @@ class spell_kiljaeden_dragon_breath : public SpellScript
|
||||
}
|
||||
};
|
||||
|
||||
// 45848 - Shield of the Blue
|
||||
class spell_kiljaeden_shield_of_the_blue : public AuraScript
|
||||
{
|
||||
PrepareAuraScript(spell_kiljaeden_shield_of_the_blue);
|
||||
|
||||
void HandleEffectApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
|
||||
{
|
||||
if (Unit* target = GetTarget())
|
||||
{
|
||||
target->RemoveAurasDueToSpell(SPELL_FIRE_BLOOM);
|
||||
target->RemoveMovementImpairingAuras(false);
|
||||
}
|
||||
}
|
||||
|
||||
void Register() override
|
||||
{
|
||||
OnEffectApply += AuraEffectApplyFn(spell_kiljaeden_shield_of_the_blue::HandleEffectApply, EFFECT_0, SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN, AURA_EFFECT_HANDLE_REAL);
|
||||
}
|
||||
};
|
||||
|
||||
void AddSC_boss_kiljaeden()
|
||||
{
|
||||
RegisterSunwellPlateauCreatureAI(npc_kiljaeden_controller);
|
||||
@@ -1196,4 +1216,5 @@ void AddSC_boss_kiljaeden()
|
||||
RegisterSpellScript(spell_kiljaeden_armageddon_periodic_aura);
|
||||
RegisterSpellScript(spell_kiljaeden_armageddon_missile);
|
||||
RegisterSpellScript(spell_kiljaeden_dragon_breath);
|
||||
RegisterSpellScript(spell_kiljaeden_shield_of_the_blue);
|
||||
}
|
||||
|
||||
@@ -78,6 +78,7 @@ void AddSC_bosses_opera();
|
||||
void AddSC_boss_netherspite();
|
||||
void AddSC_karazhan();
|
||||
void AddSC_boss_nightbane();
|
||||
void AddSC_boss_tenris_mirkblood();
|
||||
void AddSC_boss_felblood_kaelthas(); // Magister's Terrace
|
||||
void AddSC_boss_selin_fireheart();
|
||||
void AddSC_boss_vexallus();
|
||||
@@ -229,6 +230,7 @@ void AddEasternKingdomsScripts()
|
||||
AddSC_boss_netherspite();
|
||||
AddSC_karazhan();
|
||||
AddSC_boss_nightbane();
|
||||
AddSC_boss_tenris_mirkblood();
|
||||
AddSC_boss_felblood_kaelthas(); // Magister's Terrace
|
||||
AddSC_boss_selin_fireheart();
|
||||
AddSC_boss_vexallus();
|
||||
|
||||
@@ -245,6 +245,20 @@ struct npc_pet_dk_ghoul : public CombatAI
|
||||
{
|
||||
npc_pet_dk_ghoul(Creature* c) : CombatAI(c) { }
|
||||
|
||||
void IsSummonedBy(WorldObject* summoner) override
|
||||
{
|
||||
if (!summoner || !summoner->IsPlayer())
|
||||
return;
|
||||
|
||||
Player* player = summoner->ToPlayer();
|
||||
|
||||
if (Unit* victim = player->GetVictim())
|
||||
{
|
||||
me->Attack(victim, true);
|
||||
me->GetMotionMaster()->MoveChase(victim);
|
||||
}
|
||||
}
|
||||
|
||||
void JustDied(Unit* /*who*/) override
|
||||
{
|
||||
if (me->IsGuardian() || me->IsSummon())
|
||||
@@ -278,6 +292,28 @@ struct npc_pet_dk_army_of_the_dead : public CombatAI
|
||||
CombatAI::InitializeAI();
|
||||
((Minion*)me)->SetFollowAngle(rand_norm() * 2 * M_PI);
|
||||
}
|
||||
|
||||
void IsSummonedBy(WorldObject* summoner) override
|
||||
{
|
||||
if (Unit* owner = summoner->ToUnit())
|
||||
{
|
||||
Unit* victim = owner->GetVictim();
|
||||
|
||||
if (victim && me->IsValidAttackTarget(victim))
|
||||
{
|
||||
AttackStart(victim);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If there is no valid target, attack the nearest enemy within 30m
|
||||
if (Unit* nearest = me->SelectNearbyTarget(nullptr, 30.0f))
|
||||
{
|
||||
if (me->IsValidAttackTarget(nearest))
|
||||
AttackStart(nearest);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct npc_pet_dk_dancing_rune_weapon : public NullCreatureAI
|
||||
|
||||
@@ -345,7 +345,7 @@ class spell_dk_death_and_decay_aura : public AuraScript
|
||||
if (GetCaster() && GetTarget())
|
||||
{
|
||||
int32 basePoints0 = aurEff->GetAmount();
|
||||
GetCaster()->CastCustomSpell(GetTarget(), SPELL_DK_DEATH_AND_DECAY_TRIGGER, &basePoints0, nullptr, nullptr, false, 0, aurEff);
|
||||
GetCaster()->CastCustomSpell(GetTarget(), SPELL_DK_DEATH_AND_DECAY_TRIGGER, &basePoints0, nullptr, nullptr, true, 0, aurEff);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -187,6 +187,17 @@ enum ReputationRank : uint8
|
||||
REP_EXALTED = 7
|
||||
};
|
||||
|
||||
enum ReputationSource
|
||||
{
|
||||
REPUTATION_SOURCE_KILL,
|
||||
REPUTATION_SOURCE_QUEST,
|
||||
REPUTATION_SOURCE_DAILY_QUEST,
|
||||
REPUTATION_SOURCE_WEEKLY_QUEST,
|
||||
REPUTATION_SOURCE_MONTHLY_QUEST,
|
||||
REPUTATION_SOURCE_REPEATABLE_QUEST,
|
||||
REPUTATION_SOURCE_SPELL
|
||||
};
|
||||
|
||||
enum FactionTemplates
|
||||
{
|
||||
FACTION_NONE = 0,
|
||||
|
||||
Reference in New Issue
Block a user