Merge branch 'master' into Playerbot

This commit is contained in:
Yunfan Li
2024-07-07 13:32:00 +08:00
87 changed files with 1928 additions and 1062 deletions
+11
View File
@@ -23,6 +23,17 @@
#include "Unit.h"
#include <list>
#define CAST_AI(a, b) (dynamic_cast<a*>(b))
#define ENSURE_AI(a,b) (EnsureAI<a>(b))
template<class T, class U>
T* EnsureAI(U* ai)
{
T* cast_ai = dynamic_cast<T*>(ai);
ASSERT(cast_ai);
return cast_ai;
}
class Player;
class Quest;
class Unit;
@@ -25,8 +25,6 @@
#include "InstanceScript.h"
#include "TaskScheduler.h"
#define CAST_AI(a, b) (dynamic_cast<a*>(b))
typedef std::list<WorldObject*> ObjectList;
class InstanceScript;
+4 -4
View File
@@ -962,9 +962,9 @@ uint32 SmartAI::GetData(uint32 /*id*/) const
return 0;
}
void SmartAI::SetData(uint32 id, uint32 value)
void SmartAI::SetData(uint32 id, uint32 value, Unit* invoker)
{
GetScript()->ProcessEventsFor(SMART_EVENT_DATA_SET, nullptr, id, value);
GetScript()->ProcessEventsFor(SMART_EVENT_DATA_SET, invoker, id, value);
}
void SmartAI::SetGUID(ObjectGuid /*guid*/, int32 /*id*/)
@@ -1232,9 +1232,9 @@ void SmartGameObjectAI::Destroyed(Player* player, uint32 eventId)
GetScript()->ProcessEventsFor(SMART_EVENT_DEATH, player, eventId, 0, false, nullptr, me);
}
void SmartGameObjectAI::SetData(uint32 id, uint32 value)
void SmartGameObjectAI::SetData(uint32 id, uint32 value, Unit* invoker)
{
GetScript()->ProcessEventsFor(SMART_EVENT_DATA_SET, nullptr, id, value);
GetScript()->ProcessEventsFor(SMART_EVENT_DATA_SET, invoker, id, value);
}
void SmartGameObjectAI::SetScript9(SmartScriptHolder& e, uint32 entry, Unit* invoker)
+4 -2
View File
@@ -159,7 +159,8 @@ public:
uint32 GetData(uint32 id = 0) const override;
// Used in scripts to share variables
void SetData(uint32 id, uint32 value) override;
void SetData(uint32 id, uint32 value) override { SetData(id, value, nullptr); }
void SetData(uint32 id, uint32 value, Unit* invoker);
// Used in scripts to share variables
void SetGUID(ObjectGuid guid, int32 id = 0) override;
@@ -272,7 +273,8 @@ public:
bool QuestAccept(Player* player, Quest const* quest) override;
bool QuestReward(Player* player, Quest const* quest, uint32 opt) override;
void Destroyed(Player* player, uint32 eventId) override;
void SetData(uint32 id, uint32 value) override;
void SetData(uint32 id, uint32 value) override { SetData(id, value, nullptr); }
void SetData(uint32 id, uint32 value, Unit* invoker);
void SetScript9(SmartScriptHolder& e, uint32 entry, Unit* invoker);
void OnGameEvent(bool start, uint16 eventId) override;
void OnStateChanged(uint32 state, Unit* unit) override;
+121 -62
View File
@@ -63,7 +63,46 @@ SmartScript::SmartScript()
SmartScript::~SmartScript()
{
}
bool SmartScript::IsSmart(Creature* c, bool silent) const
{
if (!c)
return false;
bool smart = true;
if (!dynamic_cast<SmartAI*>(c->AI()))
smart = false;
if (!smart && !silent)
LOG_ERROR("sql.sql", "SmartScript: Action target Creature(entry: {}) is not using SmartAI, action skipped to prevent crash.", c ? c->GetEntry() : (me ? me->GetEntry() : 0));
return smart;
}
bool SmartScript::IsSmart(GameObject* g, bool silent) const
{
if (!g)
return false;
bool smart = true;
if (!dynamic_cast<SmartGameObjectAI*>(g->AI()))
smart = false;
if (!smart && !silent)
LOG_ERROR("sql.sql", "SmartScript: Action target GameObject(entry: {}) is not using SmartGameObjectAI, action skipped to prevent crash.", g ? g->GetEntry() : (go ? go->GetEntry() : 0));
return smart;
}
bool SmartScript::IsSmart(bool silent) const
{
if (me)
return IsSmart(me, silent);
if (go)
return IsSmart(go, silent);
return false;
}
void SmartScript::OnReset()
@@ -1272,22 +1311,29 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
{
for (WorldObject* target : targets)
{
Milliseconds despawnDelay(e.action.forceDespawn.delay);
if (e.action.forceDespawn.removeObjectFromWorld)
{
if (e.action.forceDespawn.delay || e.action.forceDespawn.forceRespawnTimer)
LOG_ERROR("sql.sql", "SmartScript: SMART_ACTION_FORCE_DESPAWN has removeObjectFromWorld set. delay and forceRespawnTimer ignored.");
// Wait at least one world update tick before despawn, so it doesn't break linked actions.
if (despawnDelay <= 0ms)
{
despawnDelay = 1ms;
if (Creature* creature = target->ToCreature())
creature->AddObjectToRemoveList();
else if (GameObject* go = target->ToGameObject())
go->AddObjectToRemoveList();
}
else
{
Milliseconds despawnDelay(e.action.forceDespawn.delay);
Seconds forceRespawnTimer(e.action.forceDespawn.forceRespawnTimer);
if (Creature* creature = target->ToCreature())
{
creature->DespawnOrUnsummon(despawnDelay, forceRespawnTimer);
}
else if (GameObject* go = target->ToGameObject())
{
go->DespawnOrUnsummon(despawnDelay, forceRespawnTimer);
// Wait at least one world update tick before despawn, so it doesn't break linked actions.
if (despawnDelay <= 0ms)
despawnDelay = 1ms;
Seconds forceRespawnTimer(e.action.forceDespawn.forceRespawnTimer);
if (Creature* creature = target->ToCreature())
creature->DespawnOrUnsummon(despawnDelay, forceRespawnTimer);
else if (GameObject* go = target->ToGameObject())
go->DespawnOrUnsummon(despawnDelay, forceRespawnTimer);
}
}
@@ -1348,10 +1394,22 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
{
for (WorldObject* target : targets)
{
if (IsCreature(target))
target->ToCreature()->AI()->SetData(e.action.setData.field, e.action.setData.data);
else if (IsGameObject(target))
target->ToGameObject()->AI()->SetData(e.action.setData.field, e.action.setData.data);
if (Creature* cTarget = target->ToCreature())
{
CreatureAI* ai = cTarget->AI();
if (IsSmart(cTarget))
ENSURE_AI(SmartAI, ai)->SetData(e.action.setData.field, e.action.setData.data, me);
else
ai->SetData(e.action.setData.field, e.action.setData.data);
}
else if (GameObject* oTarget = target->ToGameObject())
{
GameObjectAI* ai = oTarget->AI();
if (IsSmart(oTarget))
ENSURE_AI(SmartGameObjectAI, ai)->SetData(e.action.setData.field, e.action.setData.data, me);
else
ai->SetData(e.action.setData.field, e.action.setData.data);
}
}
break;
}
@@ -1737,60 +1795,61 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
WorldObject* target = nullptr;
if (e.GetTargetType() == SMART_TARGET_RANDOM_POINT)
switch (e.GetTargetType())
{
case SMART_TARGET_POSITION:
{
G3D::Vector3 dest(e.target.x, e.target.y, e.target.z);
if (e.action.moveToPos.transport)
if (TransportBase* trans = me->GetDirectTransport())
trans->CalculatePassengerPosition(dest.x, dest.y, dest.z);
me->GetMotionMaster()->MovePoint(e.action.moveToPos.pointId, dest.x, dest.y, dest.z, true, true,
isControlled ? MOTION_SLOT_CONTROLLED : MOTION_SLOT_ACTIVE, e.target.o);
break;
}
case SMART_TARGET_RANDOM_POINT:
if (me)
{
float range = (float)e.target.randomPoint.range;
Position srcPos = { e.target.x, e.target.y, e.target.z, e.target.o };
Position randomPoint = me->GetRandomPoint(srcPos, range);
me->GetMotionMaster()->MovePoint(
e.action.moveToPos.pointId,
randomPoint.m_positionX,
randomPoint.m_positionY,
randomPoint.m_positionZ,
true,
true,
isControlled ? MOTION_SLOT_CONTROLLED : MOTION_SLOT_ACTIVE
e.action.moveToPos.pointId,
randomPoint.m_positionX,
randomPoint.m_positionY,
randomPoint.m_positionZ,
true,
true,
isControlled ? MOTION_SLOT_CONTROLLED : MOTION_SLOT_ACTIVE
);
break;
}
break;
}
/*if (e.GetTargetType() == SMART_TARGET_CREATURE_RANGE || e.GetTargetType() == SMART_TARGET_CREATURE_GUID ||
e.GetTargetType() == SMART_TARGET_CREATURE_DISTANCE || e.GetTargetType() == SMART_TARGET_GAMEOBJECT_RANGE ||
e.GetTargetType() == SMART_TARGET_GAMEOBJECT_GUID || e.GetTargetType() == SMART_TARGET_GAMEOBJECT_DISTANCE ||
e.GetTargetType() == SMART_TARGET_CLOSEST_CREATURE || e.GetTargetType() == SMART_TARGET_CLOSEST_GAMEOBJECT ||
e.GetTargetType() == SMART_TARGET_OWNER_OR_SUMMONER || e.GetTargetType() == SMART_TARGET_ACTION_INVOKER ||
e.GetTargetType() == SMART_TARGET_CLOSEST_ENEMY || e.GetTargetType() == SMART_TARGET_CLOSEST_FRIENDLY ||
e.GetTargetType() == SMART_TARGET_SELF || e.GetTargetType() == SMART_TARGET_STORED)) */
{
// we want to move to random element
if (!targets.empty())
target = Acore::Containers::SelectRandomContainerElement(targets);
}
if (!target)
{
G3D::Vector3 dest(e.target.x, e.target.y, e.target.z);
if (e.action.moveToPos.transport)
if (TransportBase* trans = me->GetDirectTransport())
trans->CalculatePassengerPosition(dest.x, dest.y, dest.z);
me->GetMotionMaster()->MovePoint(e.action.moveToPos.pointId, dest.x, dest.y, dest.z, true, true,
isControlled ? MOTION_SLOT_CONTROLLED : MOTION_SLOT_ACTIVE, e.target.o);
}
else // Xinef: we can use dest.x, dest.y, dest.z to make offset
{
float x, y, z;
target->GetPosition(x, y, z);
if (e.action.moveToPos.ContactDistance > 0)
// Can use target floats as offset
default:
{
target->GetNearPoint(me, x, y, z, e.action.moveToPos.ContactDistance, 0, target->GetAngle(me));
// we want to move to random element
if (targets.empty())
return;
else
target = Acore::Containers::SelectRandomContainerElement(targets);
float x, y, z;
target->GetPosition(x, y, z);
if (e.action.moveToPos.combatReach)
target->GetNearPoint(me, x, y, z, target->GetCombatReach() + e.action.moveToPos.ContactDistance, 0, target->GetAngle(me));
else if (e.action.moveToPos.ContactDistance)
target->GetNearPoint(me, x, y, z, e.action.moveToPos.ContactDistance, 0, target->GetAngle(me));
me->GetMotionMaster()->MovePoint(e.action.moveToPos.pointId, x + e.target.x, y + e.target.y, z + e.target.z, true, true, isControlled ? MOTION_SLOT_CONTROLLED : MOTION_SLOT_ACTIVE);
break;
}
me->GetMotionMaster()->MovePoint(e.action.moveToPos.pointId, x + e.target.x, y + e.target.y, z + e.target.z, true, true, isControlled ? MOTION_SLOT_CONTROLLED : MOTION_SLOT_ACTIVE);
}
break;
}
case SMART_ACTION_MOVE_TO_POS_TARGET:
@@ -1966,7 +2025,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
}
else if (GameObject* go = target->ToGameObject())
{
if (IsSmartGO(go))
if (IsSmart(go))
CAST_AI(SmartGameObjectAI, go->AI())->SetScript9(e, e.action.timedActionList.id, GetLastInvoker());
}
}
@@ -2053,7 +2112,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
}
else if (GameObject* go = target->ToGameObject())
{
if (IsSmartGO(go))
if (IsSmart(go))
CAST_AI(SmartGameObjectAI, go->AI())->SetScript9(e, id, GetLastInvoker());
}
}
@@ -2077,7 +2136,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
}
else if (GameObject* go = target->ToGameObject())
{
if (IsSmartGO(go))
if (IsSmart(go))
CAST_AI(SmartGameObjectAI, go->AI())->SetScript9(e, id, GetLastInvoker());
}
}
+4 -29
View File
@@ -76,6 +76,10 @@ public:
void DoFindFriendlyMissingBuff(std::vector<Creature*>& creatures, float range, uint32 spellid) const;
Unit* DoFindClosestFriendlyInRange(float range, bool playerOnly) const;
bool IsSmart(Creature* c, bool silent = false) const;
bool IsSmart(GameObject* g, bool silent = false) const;
bool IsSmart(bool silent = false) const;
void StoreTargetList(ObjectVector const& targets, uint32 id)
{
// insert or replace
@@ -83,35 +87,6 @@ public:
_storedTargets.emplace(id, ObjectGuidVector(targets));
}
bool IsSmart(Creature* c = nullptr)
{
bool smart = true;
if (c && c->GetAIName() != "SmartAI")
smart = false;
if (!me || me->GetAIName() != "SmartAI")
smart = false;
if (!smart)
LOG_ERROR("sql.sql", "SmartScript: Action target Creature(entry: {}) is not using SmartAI, action skipped to prevent crash.", c ? c->GetEntry() : (me ? me->GetEntry() : 0));
return smart;
}
bool IsSmartGO(GameObject* g = nullptr)
{
bool smart = true;
if (g && g->GetAIName() != "SmartGameObjectAI")
smart = false;
if (!go || go->GetAIName() != "SmartGameObjectAI")
smart = false;
if (!smart)
LOG_ERROR("sql.sql", "SmartScript: Action target GameObject(entry: {}) is not using SmartGameObjectAI, action skipped to prevent crash.", g ? g->GetEntry() : (go ? go->GetEntry() : 0));
return smart;
}
ObjectVector const* GetStoredTargetVector(uint32 id, WorldObject const& ref) const
{
auto itr = _storedTargets.find(id);
@@ -346,6 +346,7 @@ void SmartAIMgr::LoadSmartAIFromDB()
case SMART_EVENT_TRANSPORT_ADDCREATURE:
case SMART_EVENT_NEAR_PLAYERS:
case SMART_EVENT_SUMMONED_UNIT_EVADE:
case SMART_EVENT_DATA_SET:
return true;
default:
return false;
@@ -978,6 +978,7 @@ struct SmartAction
{
uint32 delay;
uint32 forceRespawnTimer;
SAIBool removeObjectFromWorld;
} forceDespawn;
struct
@@ -1201,6 +1202,7 @@ struct SmartAction
SAIBool transport;
uint32 controlled;
uint32 ContactDistance;
uint32 combatReach;
} moveToPos;
struct
+103
View File
@@ -33,6 +33,7 @@
#include "World.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include <boost/algorithm/string/replace.hpp>
Player* ChatHandler::GetPlayer() const
{
@@ -888,3 +889,105 @@ int CliHandler::GetSessionDbLocaleIndex() const
{
return sObjectMgr->GetDBCLocaleIndex();
}
bool AddonChannelCommandHandler::ParseCommands(std::string_view str)
{
if (memcmp(str.data(), "AzerothCore\t", 12))
return false;
char opcode = str[12];
if (!opcode) // str[12] is opcode
return false;
if (!str[13] || !str[14] || !str[15] || !str[16]) // str[13] through str[16] is 4-character command counter
return false;
echo = str.substr(13);
switch (opcode)
{
case 'p': // p Ping
SendAck();
return true;
case 'h': // h Issue human-readable command
case 'i': // i Issue command
if (!str[17])
return false;
humanReadable = (opcode == 'h');
if (_ParseCommands(str.substr(17))) // actual command starts at str[17]
{
if (!hadAck)
SendAck();
if (HasSentErrorMessage())
SendFailed();
else
SendOK();
}
else
{
SendSysMessage(LANG_CMD_INVALID);
SendFailed();
}
return true;
default:
return false;
}
}
void AddonChannelCommandHandler::Send(std::string const& msg)
{
WorldPacket data;
ChatHandler::BuildChatPacket(data, CHAT_MSG_WHISPER, LANG_ADDON, GetSession()->GetPlayer(), GetSession()->GetPlayer(), msg);
GetSession()->SendPacket(&data);
}
void AddonChannelCommandHandler::SendAck() // a Command acknowledged, no body
{
ASSERT(echo.size());
std::string ack = "AzerothCore\ta";
ack.resize(18);
memcpy(&ack[13], echo.data(), 4);
ack[17] = '\0';
Send(ack);
hadAck = true;
}
void AddonChannelCommandHandler::SendOK() // o Command OK, no body
{
ASSERT(echo.size());
std::string ok = "AzerothCore\to";
ok.resize(18);
memcpy(&ok[13], echo.data(), 4);
ok[17] = '\0';
Send(ok);
}
void AddonChannelCommandHandler::SendFailed() // f Command failed, no body
{
ASSERT(echo.size());
std::string fail = "AzerothCore\tf";
fail.resize(18);
memcpy(&fail[13], echo.data(), 4);
fail[17] = '\0';
Send(fail);
}
// m Command message, message in body
void AddonChannelCommandHandler::SendSysMessage(std::string_view str, bool escapeCharacters)
{
ASSERT(echo.size());
if (!hadAck)
SendAck();
std::string msg = "AzerothCore\tm";
msg.append(echo.data(), 4);
std::string body(str);
if (escapeCharacters)
boost::replace_all(body, "|", "||");
size_t pos, lastpos;
for (lastpos = 0, pos = body.find('\n', lastpos); pos != std::string::npos; lastpos = pos + 1, pos = body.find('\n', lastpos))
{
std::string line(msg);
line.append(body, lastpos, pos - lastpos);
Send(line);
}
msg.append(body, lastpos, pos - lastpos);
Send(msg);
}
+20
View File
@@ -168,4 +168,24 @@ private:
Print* m_print;
};
class AC_GAME_API AddonChannelCommandHandler : public ChatHandler
{
public:
using ChatHandler::ChatHandler;
bool ParseCommands(std::string_view str) override;
void SendSysMessage(std::string_view str, bool escapeCharacters) override;
using ChatHandler::SendSysMessage;
bool IsHumanReadable() const override { return humanReadable; }
private:
void Send(std::string const& msg);
void SendAck();
void SendOK();
void SendFailed();
std::string echo;
bool hadAck = false;
bool humanReadable = false;
};
#endif
+2 -2
View File
@@ -1054,10 +1054,10 @@ void ConditionMgr::LoadConditions(bool isReload)
LootTemplates_Spell.ResetConditions();
LootTemplates_Player.ResetConditions();
LOG_INFO("server.loading", "Re-Loading `gossip_menu` Table for Conditions!");
LOG_INFO("server.loading", "Reloading `gossip_menu` Table for Conditions!");
sObjectMgr->LoadGossipMenu();
LOG_INFO("server.loading", "Re-Loading `gossip_menu_option` Table for Conditions!");
LOG_INFO("server.loading", "Reloading `gossip_menu_option` Table for Conditions!");
sObjectMgr->LoadGossipMenuItems();
sSpellMgr->UnloadSpellInfoImplicitTargetConditionLists();
}
+2 -2
View File
@@ -170,7 +170,7 @@ DBCStorage <SpellFocusObjectEntry> sSpellFocusObjectStore(SpellFocusObjectfmt);
DBCStorage <SpellRadiusEntry> sSpellRadiusStore(SpellRadiusfmt);
DBCStorage <SpellRangeEntry> sSpellRangeStore(SpellRangefmt);
DBCStorage <SpellRuneCostEntry> sSpellRuneCostStore(SpellRuneCostfmt);
DBCStorage <SpellShapeshiftEntry> sSpellShapeshiftStore(SpellShapeshiftfmt);
DBCStorage <SpellShapeshiftFormEntry> sSpellShapeshiftFormStore(SpellShapeshiftFormEntryfmt);
DBCStorage <SpellVisualEntry> sSpellVisualStore(SpellVisualfmt);
DBCStorage <StableSlotPricesEntry> sStableSlotPricesStore(StableSlotPricesfmt);
DBCStorage <SummonPropertiesEntry> sSummonPropertiesStore(SummonPropertiesfmt);
@@ -375,7 +375,7 @@ void LoadDBCStores(const std::string& dataPath)
LOAD_DBC(sSpellRadiusStore, "SpellRadius.dbc", "spellradius_dbc");
LOAD_DBC(sSpellRangeStore, "SpellRange.dbc", "spellrange_dbc");
LOAD_DBC(sSpellRuneCostStore, "SpellRuneCost.dbc", "spellrunecost_dbc");
LOAD_DBC(sSpellShapeshiftStore, "SpellShapeshiftForm.dbc", "spellshapeshiftform_dbc");
LOAD_DBC(sSpellShapeshiftFormStore, "SpellShapeshiftForm.dbc", "spellshapeshiftform_dbc");
LOAD_DBC(sSpellVisualStore, "SpellVisual.dbc", "spellvisual_dbc");
LOAD_DBC(sStableSlotPricesStore, "StableSlotPrices.dbc", "stableslotprices_dbc");
LOAD_DBC(sSummonPropertiesStore, "SummonProperties.dbc", "summonproperties_dbc");
+2 -2
View File
@@ -139,7 +139,7 @@ extern DBCStorage <ItemLimitCategoryEntry> sItemLimitCategoryStore;
extern DBCStorage <ItemRandomPropertiesEntry> sItemRandomPropertiesStore;
extern DBCStorage <ItemRandomSuffixEntry> sItemRandomSuffixStore;
extern DBCStorage <ItemSetEntry> sItemSetStore;
extern DBCStorage <LFGDungeonEntry> sLFGDungeonStore;
extern DBCStorage <LFGDungeonEntry> sLFGDungeonStore;
extern DBCStorage <LiquidTypeEntry> sLiquidTypeStore;
extern DBCStorage <LockEntry> sLockStore;
extern DBCStorage <MailTemplateEntry> sMailTemplateStore;
@@ -175,7 +175,7 @@ extern std::unordered_set<uint32> sPetTalentSpells;
extern DBCStorage <SpellRadiusEntry> sSpellRadiusStore;
extern DBCStorage <SpellRangeEntry> sSpellRangeStore;
extern DBCStorage <SpellRuneCostEntry> sSpellRuneCostStore;
extern DBCStorage <SpellShapeshiftEntry> sSpellShapeshiftStore;
extern DBCStorage <SpellShapeshiftFormEntry> sSpellShapeshiftFormStore;
extern DBCStorage <SpellEntry> sSpellStore;
extern DBCStorage <SpellVisualEntry> sSpellVisualStore;
extern DBCStorage <StableSlotPricesEntry> sStableSlotPricesStore;
@@ -2173,15 +2173,6 @@ bool GameObject::IsInRange(float x, float y, float z, float radius) const
&& dz < (info->maxZ * scale) + radius && dz > (info->minZ * scale) - radius;
}
void GameObject::SendMessageToSetInRange(WorldPacket const* data, float dist, bool /*self*/, bool includeMargin, Player const* skipped_rcvr) const
{
dist += GetObjectSize();
if (includeMargin)
dist += VISIBILITY_COMPENSATION * 2.0f; // pussywizard: to ensure everyone receives all important packets
Acore::MessageDistDeliverer notifier(this, data, dist, false, skipped_rcvr);
Cell::VisitWorldObjects(this, notifier, dist);
}
void GameObject::EventInform(uint32 eventId)
{
if (!eventId)
@@ -289,8 +289,6 @@ public:
void SendCustomAnim(uint32 anim);
[[nodiscard]] bool IsInRange(float x, float y, float z, float radius) const;
void SendMessageToSetInRange(WorldPacket const* data, float dist, bool /*self*/, bool includeMargin = false, Player const* skipped_rcvr = nullptr) const override; // pussywizard!
void ModifyHealth(int32 change, Unit* attackerOrHealer = nullptr, uint32 spellId = 0);
void SetDestructibleBuildingModifyState(bool allow) { m_allowModifyDestructibleBuilding = allow; }
// sets GameObject type 33 destruction flags and optionally default health for that state
@@ -15,8 +15,8 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Define.h"
#include "Item.h"
#include "Define.h"
#include "SmartEnum.h"
#include <stdexcept>
+14 -5
View File
@@ -2077,15 +2077,24 @@ void Unit::BuildHeartBeatMsg(WorldPacket* data) const
BuildMovementPacket(data);
}
void WorldObject::SendMessageToSetInRange(WorldPacket const* data, float dist, bool /*self*/, bool includeMargin, Player const* skipped_rcvr) const
void WorldObject::SendMessageToSet(WorldPacket const* data, bool self) const
{
dist += GetObjectSize();
if (includeMargin)
dist += VISIBILITY_COMPENSATION; // pussywizard: to ensure everyone receives all important packets
Acore::MessageDistDeliverer notifier(this, data, dist, false, skipped_rcvr);
if (IsInWorld())
SendMessageToSetInRange(data, GetVisibilityRange(), self);
}
void WorldObject::SendMessageToSetInRange(WorldPacket const* data, float dist, bool /*self*/) const
{
Acore::MessageDistDeliverer notifier(this, data, dist);
Cell::VisitWorldObjects(this, notifier, dist);
}
void WorldObject::SendMessageToSet(WorldPacket const* data, Player const* skipped_rcvr) const
{
Acore::MessageDistDeliverer notifier(this, data, GetVisibilityRange(), false, skipped_rcvr);
Cell::VisitWorldObjects(this, notifier, GetVisibilityRange());
}
void WorldObject::SendObjectDeSpawnAnim(ObjectGuid guid)
{
WorldPacket data(SMSG_GAMEOBJECT_DESPAWN_ANIM, 8);
+3 -3
View File
@@ -479,9 +479,9 @@ public:
virtual void CleanupsBeforeDelete(bool finalCleanup = true); // used in destructor or explicitly before mass creature delete to remove cross-references to already deleted units
virtual void SendMessageToSet(WorldPacket const* data, bool self) const { if (IsInWorld()) SendMessageToSetInRange(data, GetVisibilityRange(), self, true); } // pussywizard!
virtual void SendMessageToSetInRange(WorldPacket const* data, float dist, bool /*self*/, bool includeMargin = false, Player const* skipped_rcvr = nullptr) const; // pussywizard!
virtual void SendMessageToSet(WorldPacket const* data, Player const* skipped_rcvr) const { if (IsInWorld()) SendMessageToSetInRange(data, GetVisibilityRange(), false, true, skipped_rcvr); } // pussywizard!
virtual void SendMessageToSet(WorldPacket const* data, bool self) const;
virtual void SendMessageToSetInRange(WorldPacket const* data, float dist, bool self) const;
virtual void SendMessageToSet(WorldPacket const* data, Player const* skipped_rcvr) const;
virtual uint8 getLevelForTarget(WorldObject const* /*target*/) const { return 1; }
+8 -6
View File
@@ -394,7 +394,7 @@ bool Pet::LoadPetFromDB(Player* owner, uint32 petEntry, uint32 petnumber, bool c
// Send fake summon spell cast - this is needed for correct cooldown application for spells
// Example: 46584 - without this cooldown (which should be set always when pet is loaded) isn't set clientside
/// @todo pets should be summoned from real cast instead of just faking it?
if (petInfo->CreatedBySpellId)
if (petInfo->CreatedBySpellId && spellInfo && (spellInfo->CategoryRecoveryTime > 0 || spellInfo->RecoveryTime > 0))
{
WorldPacket data(SMSG_SPELL_GO, (8 + 8 + 4 + 4 + 2));
data << owner->GetPackGUID();
@@ -2499,11 +2499,6 @@ float Pet::GetNativeObjectScale() const
{
uint8 ctFamily = GetCreatureTemplate()->family;
// hackfix: Edge case where DBC scale values for DEVILSAUR pets make them too small.
// Therefore we take data from spirit beast instead.
if (ctFamily && ctFamily == CREATURE_FAMILY_DEVILSAUR)
ctFamily = CREATURE_FAMILY_SPIRIT_BEAST;
CreatureFamilyEntry const* creatureFamily = sCreatureFamilyStore.LookupEntry(ctFamily);
if (creatureFamily && creatureFamily->minScale > 0.0f && getPetType() & HUNTER_PET)
{
@@ -2520,6 +2515,13 @@ float Pet::GetNativeObjectScale() const
float scale = (creatureFamily->maxScale - creatureFamily->minScale) * scaleMod + creatureFamily->minScale;
scale = std::min(scale, creatureFamily->maxScale);
if (CreatureDisplayInfoEntry const* displayInfo = sCreatureDisplayInfoStore.LookupEntry(GetNativeDisplayId()))
{
if (scale < 1.f && displayInfo->scale > 1.f)
scale *= displayInfo->scale;
}
return scale;
}
+27 -19
View File
@@ -5612,25 +5612,40 @@ void Player::SaveRecallPosition()
m_recallO = GetOrientation();
}
void Player::SendMessageToSetInRange(WorldPacket const* data, float dist, bool self, bool includeMargin, Player const* skipped_rcvr) const
void Player::SendMessageToSet(WorldPacket const* data, bool self) const
{
SendMessageToSetInRange(data, GetVisibilityRange(), self);
}
void Player::SendMessageToSetInRange(WorldPacket const* data, float dist, bool self) const
{
if (self)
GetSession()->SendPacket(data);
SendDirectMessage(data);
Acore::MessageDistDeliverer notifier(this, data, dist);
Cell::VisitWorldObjects(this, notifier, dist);
}
void Player::SendMessageToSetInRange(WorldPacket const* data, float dist, bool self, bool includeMargin, bool ownTeamOnly, bool required3dDist) const
{
if (self)
SendDirectMessage(data);
dist += GetObjectSize();
if (includeMargin)
dist += VISIBILITY_COMPENSATION; // pussywizard: to ensure everyone receives all important packets
Acore::MessageDistDeliverer notifier(this, data, dist, false, skipped_rcvr);
Acore::MessageDistDeliverer notifier(this, data, dist, ownTeamOnly, nullptr, required3dDist);
Cell::VisitWorldObjects(this, notifier, dist);
}
void Player::SendMessageToSetInRange_OwnTeam(WorldPacket const* data, float dist, bool self) const
void Player::SendMessageToSet(WorldPacket const* data, Player const* skipped_rcvr) const
{
if (self)
GetSession()->SendPacket(data);
if (skipped_rcvr != this)
SendDirectMessage(data);
Acore::MessageDistDeliverer notifier(this, data, dist, true);
Cell::VisitWorldObjects(this, notifier, dist);
Acore::MessageDistDeliverer notifier(this, data, GetVisibilityRange(), false, skipped_rcvr);
Cell::VisitWorldObjects(this, notifier, GetVisibilityRange());
}
void Player::SendDirectMessage(WorldPacket const* data) const
@@ -9291,7 +9306,7 @@ void Player::Say(std::string_view text, Language language, WorldObject const* /*
WorldPacket data;
ChatHandler::BuildChatPacket(data, CHAT_MSG_SAY, language, this, this, _text);
SendMessageToSetInRange(&data, sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_SAY), true);
SendMessageToSetInRange(&data, sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_SAY), true, false, false, true);
}
void Player::Say(uint32 textId, WorldObject const* target /*= nullptr*/)
@@ -9312,7 +9327,7 @@ void Player::Yell(std::string_view text, Language language, WorldObject const* /
WorldPacket data;
ChatHandler::BuildChatPacket(data, CHAT_MSG_YELL, language, this, this, _text);
SendMessageToSetInRange(&data, sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_YELL), true);
SendMessageToSetInRange(&data, sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_YELL), true, false, false, true);
}
void Player::Yell(uint32 textId, WorldObject const* target /*= nullptr*/)
@@ -9334,14 +9349,7 @@ void Player::TextEmote(std::string_view text, WorldObject const* /*= nullptr*/,
WorldPacket data;
ChatHandler::BuildChatPacket(data, CHAT_MSG_EMOTE, LANG_UNIVERSAL, this, this, _text);
if (sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_EMOTE))
{
SendMessageToSetInRange(&data, sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_TEXTEMOTE), true);
}
else
{
SendMessageToSetInRange_OwnTeam(&data, sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_TEXTEMOTE), true);
}
SendMessageToSetInRange(&data, sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_TEXTEMOTE), true, false, !sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_EMOTE), true);
}
void Player::TextEmote(uint32 textId, WorldObject const* target /*= nullptr*/, bool /*isBossEmote = false*/)
@@ -10521,7 +10529,7 @@ void Player::InitDataForForm(bool reapplyMods)
{
ShapeshiftForm form = GetShapeshiftForm();
SpellShapeshiftEntry const* ssEntry = sSpellShapeshiftStore.LookupEntry(form);
SpellShapeshiftFormEntry const* ssEntry = sSpellShapeshiftFormStore.LookupEntry(form);
if (ssEntry && ssEntry->attackSpeed)
{
SetAttackTime(BASE_ATTACK, ssEntry->attackSpeed);
+4 -5
View File
@@ -2000,11 +2000,10 @@ public:
void ProcessTerrainStatusUpdate() override;
void SendMessageToSet(WorldPacket const* data, bool self) const override { SendMessageToSetInRange(data, GetVisibilityRange(), self, true); } // pussywizard!
void SendMessageToSetInRange(WorldPacket const* data, float dist, bool self, bool includeMargin = false, Player const* skipped_rcvr = nullptr) const override; // pussywizard!
void SendMessageToSetInRange_OwnTeam(WorldPacket const* data, float dist, bool self) const; // pussywizard! param includeMargin not needed here
void SendMessageToSet(WorldPacket const* data, Player const* skipped_rcvr) const override { SendMessageToSetInRange(data, GetVisibilityRange(), skipped_rcvr != this, true, skipped_rcvr); } // pussywizard!
void SendMessageToSet(WorldPacket const* data, bool self) const override;
void SendMessageToSetInRange(WorldPacket const* data, float dist, bool self) const override;
void SendMessageToSetInRange(WorldPacket const* data, float dist, bool self, bool includeMargin, bool ownTeamOnly, bool required3dDist = false) const;
void SendMessageToSet(WorldPacket const* data, Player const* skipped_rcvr) const override;
void SendTeleportAckPacket();
[[nodiscard]] Corpse* GetCorpse() const;
@@ -753,7 +753,7 @@ void Player::RewardQuest(Quest const* quest, uint32 reward, Object* questGiver,
else
{
sScriptMgr->OnGivePlayerXP(this, XP, nullptr, isLFGReward ? PlayerXPSource::XPSOURCE_QUEST_DF : PlayerXPSource::XPSOURCE_QUEST);
GiveXP(XP, nullptr, isLFGReward);
GiveXP(XP, nullptr, 1.0f, isLFGReward);
}
// Give player extra money if GetRewOrReqMoney > 0 and get ReqMoney if negative
+24 -7
View File
@@ -3453,7 +3453,8 @@ SpellMissInfo Unit::SpellHitResult(Unit* victim, SpellInfo const* spell, bool Ca
return SPELL_MISS_NONE;
// Return evade for units in evade mode
if (victim->GetTypeId() == TYPEID_UNIT && victim->ToCreature()->IsEvadingAttacks() && !spell->HasAura(SPELL_AURA_CONTROL_VEHICLE) && !spell->HasAttribute(SPELL_ATTR0_CU_IGNORE_EVADE))
if (victim->GetTypeId() == TYPEID_UNIT && victim->ToCreature()->IsEvadingAttacks() && !spell->HasAura(SPELL_AURA_CONTROL_VEHICLE)
&& !spell->HasAttribute(SPELL_ATTR0_CU_IGNORE_EVADE) && !spell->HasAttribute(SPELL_ATTR1_AURA_STAYS_AFTER_COMBAT))
return SPELL_MISS_EVADE;
// Try victim reflect spell
@@ -3528,7 +3529,7 @@ SpellMissInfo Unit::SpellHitResult(Unit* victim, Spell const* spell, bool CanRef
// Return evade for units in evade mode
if (victim->GetTypeId() == TYPEID_UNIT && victim->ToCreature()->IsEvadingAttacks() && !spellInfo->HasAura(SPELL_AURA_CONTROL_VEHICLE) &&
!spellInfo->HasAttribute(SPELL_ATTR0_CU_IGNORE_EVADE))
!spellInfo->HasAttribute(SPELL_ATTR0_CU_IGNORE_EVADE) && !spellInfo->HasAttribute(SPELL_ATTR1_AURA_STAYS_AFTER_COMBAT))
{
return SPELL_MISS_EVADE;
}
@@ -5384,7 +5385,8 @@ void Unit::RemoveEvadeAuras()
{
Aura const* aura = iter->second->GetBase();
SpellInfo const* spellInfo = aura->GetSpellInfo();
if (spellInfo->HasAttribute(SPELL_ATTR0_CU_IGNORE_EVADE) || spellInfo->HasAura(SPELL_AURA_CONTROL_VEHICLE) || spellInfo->HasAura(SPELL_AURA_CLONE_CASTER) || (aura->IsPassive() && GetOwnerGUID().IsPlayer()))
if (spellInfo->HasAttribute(SPELL_ATTR0_CU_IGNORE_EVADE) || spellInfo->HasAttribute(SPELL_ATTR1_AURA_STAYS_AFTER_COMBAT) || spellInfo->HasAura(SPELL_AURA_CONTROL_VEHICLE)
|| spellInfo->HasAura(SPELL_AURA_CLONE_CASTER) || (aura->IsPassive() && GetOwnerGUID().IsPlayer()))
++iter;
else
_UnapplyAura(iter, AURA_REMOVE_BY_DEFAULT);
@@ -5394,7 +5396,8 @@ void Unit::RemoveEvadeAuras()
{
Aura* aura = iter->second;
SpellInfo const* spellInfo = aura->GetSpellInfo();
if (spellInfo->HasAttribute(SPELL_ATTR0_CU_IGNORE_EVADE) || spellInfo->HasAura(SPELL_AURA_CONTROL_VEHICLE) || spellInfo->HasAura(SPELL_AURA_CLONE_CASTER) || (aura->IsPassive() && GetOwnerGUID().IsPlayer()))
if (spellInfo->HasAttribute(SPELL_ATTR0_CU_IGNORE_EVADE) || spellInfo->HasAttribute(SPELL_ATTR1_AURA_STAYS_AFTER_COMBAT) || spellInfo->HasAura(SPELL_AURA_CONTROL_VEHICLE)
|| spellInfo->HasAura(SPELL_AURA_CLONE_CASTER) || (aura->IsPassive() && GetOwnerGUID().IsPlayer()))
++iter;
else
RemoveOwnedAura(iter, AURA_REMOVE_BY_DEFAULT);
@@ -15190,7 +15193,7 @@ uint32 Unit::GetCreatureType() const
if (GetTypeId() == TYPEID_PLAYER)
{
ShapeshiftForm form = GetShapeshiftForm();
SpellShapeshiftEntry const* ssEntry = sSpellShapeshiftStore.LookupEntry(form);
SpellShapeshiftFormEntry const* ssEntry = sSpellShapeshiftFormStore.LookupEntry(form);
if (ssEntry && ssEntry->creatureType > 0)
return ssEntry->creatureType;
else
@@ -19677,7 +19680,7 @@ uint32 Unit::GetModelForForm(ShapeshiftForm form, uint32 spellId) const
}
uint32 modelid = 0;
SpellShapeshiftEntry const* formEntry = sSpellShapeshiftStore.LookupEntry(form);
SpellShapeshiftFormEntry const* formEntry = sSpellShapeshiftFormStore.LookupEntry(form);
if (formEntry && formEntry->modelID_A)
{
// Take the alliance modelid as default
@@ -21613,7 +21616,7 @@ bool Unit::IsInDisallowedMountForm() const
if (ShapeshiftForm form = GetShapeshiftForm())
{
SpellShapeshiftEntry const* shapeshift = sSpellShapeshiftStore.LookupEntry(form);
SpellShapeshiftFormEntry const* shapeshift = sSpellShapeshiftFormStore.LookupEntry(form);
if (!shapeshift)
{
return true;
@@ -21656,6 +21659,20 @@ bool Unit::IsInDisallowedMountForm() const
return false;
}
void Unit::SetUInt32Value(uint16 index, uint32 value)
{
Object::SetUInt32Value(index, value);
switch (index)
{
// Invalidating the cache on health change should fix an issue where the client sees dead NPCs when they are not.
// We might also need to invalidate the cache for some other fields as well.
case UNIT_FIELD_HEALTH:
InvalidateValuesUpdateCache();
break;
}
}
std::string Unit::GetDebugInfo() const
{
std::stringstream sstr;
+2
View File
@@ -1571,6 +1571,8 @@ public:
void ApplyAttackTimePercentMod(WeaponAttackType att, float val, bool apply);
void ApplyCastTimePercentMod(float val, bool apply);
void SetUInt32Value(uint16 index, uint32 value);
UnitFlags GetUnitFlags() const { return UnitFlags(GetUInt32Value(UNIT_FIELD_FLAGS)); }
bool HasUnitFlag(UnitFlags flags) const { return HasFlag(UNIT_FIELD_FLAGS, flags); }
void SetUnitFlag(UnitFlags flags) { SetFlag(UNIT_FIELD_FLAGS, flags); }
@@ -15,9 +15,9 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Unit.h"
#include "Define.h"
#include "SmartEnum.h"
#include "Unit.h"
#include <stdexcept>
namespace Acore::Impl::EnumUtilsImpl
@@ -217,8 +217,14 @@ void MessageDistDeliverer::Visit(PlayerMapType& m)
if (!target->InSamePhase(i_phaseMask))
continue;
if (target->GetExactDist2dSq(i_source) > i_distSq)
continue;
if (required3dDist)
{
if (target->GetExactDistSq(i_source) > i_distSq)
continue;
}
else
if (target->GetExactDist2dSq(i_source) > i_distSq)
continue;
// Send packet to all who are sharing the player's vision
if (target->HasSharedVision())
@@ -242,8 +248,14 @@ void MessageDistDeliverer::Visit(CreatureMapType& m)
if (!target->HasSharedVision() || !target->InSamePhase(i_phaseMask))
continue;
if (target->GetExactDist2dSq(i_source) > i_distSq)
continue;
if (required3dDist)
{
if (target->GetExactDistSq(i_source) > i_distSq)
continue;
}
else
if (target->GetExactDist2dSq(i_source) > i_distSq)
continue;
// Send packet to all who are sharing the creature's vision
SharedVisionList::const_iterator i = target->GetSharedVisionList().begin();
@@ -265,8 +277,14 @@ void MessageDistDeliverer::Visit(DynamicObjectMapType& m)
if (!target->IsViewpoint())
continue;
if (target->GetExactDist2dSq(i_source) > i_distSq)
continue;
if (required3dDist)
{
if (target->GetExactDistSq(i_source) > i_distSq)
continue;
}
else
if (target->GetExactDist2dSq(i_source) > i_distSq)
continue;
// Send packet back to the caster if the caster has vision of dynamic object
Player* caster = (Player*)target->GetCaster();
@@ -102,10 +102,11 @@ namespace Acore
float i_distSq;
TeamId teamId;
Player const* skipped_receiver;
MessageDistDeliverer(WorldObject const* src, WorldPacket const* msg, float dist, bool own_team_only = false, Player const* skipped = nullptr)
bool required3dDist;
MessageDistDeliverer(WorldObject const* src, WorldPacket const* msg, float dist, bool own_team_only = false, Player const* skipped = nullptr, bool req3dDist = false)
: i_source(src), i_message(msg), i_phaseMask(src->GetPhaseMask()), i_distSq(dist * dist)
, teamId((own_team_only && src->GetTypeId() == TYPEID_PLAYER) ? src->ToPlayer()->GetTeamId() : TEAM_NEUTRAL)
, skipped_receiver(skipped)
, skipped_receiver(skipped), required3dDist(req3dDist)
{
}
void Visit(PlayerMapType& m);
+103 -42
View File
@@ -41,6 +41,8 @@
#include "World.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include "ArenaTeam.h"
#include "ArenaTeamMgr.h"
Roll::Roll(ObjectGuid _guid, LootItem const& li) : itemGUID(_guid), itemid(li.itemid),
itemRandomPropId(li.randomPropertyId), itemRandomSuffix(li.randomSuffix), itemCount(li.count),
@@ -896,7 +898,7 @@ void Group::SendLootStartRollToPlayer(uint32 countDown, uint32 mapId, Player* p,
p->GetSession()->SendPacket(&data);
}
void Group::SendLootRoll(ObjectGuid sourceGuid, ObjectGuid targetGuid, uint8 rollNumber, uint8 rollType, Roll const& roll)
void Group::SendLootRoll(ObjectGuid sourceGuid, ObjectGuid targetGuid, uint8 rollNumber, uint8 rollType, Roll const& roll, bool autoPass)
{
WorldPacket data(SMSG_LOOT_ROLL, (8 + 4 + 8 + 4 + 4 + 4 + 1 + 1 + 1));
data << sourceGuid; // guid of the item rolled
@@ -907,7 +909,7 @@ void Group::SendLootRoll(ObjectGuid sourceGuid, ObjectGuid targetGuid, uint8 rol
data << uint32(roll.itemRandomPropId); // Item random property ID
data << uint8(rollNumber); // 0: "Need for: [item name]" > 127: "you passed on: [item name]" Roll number
data << uint8(rollType); // 0: "Need for: [item name]" 0: "You have selected need for [item name] 1: need roll 2: greed roll
data << uint8(0); // 1: "You automatically passed on: %s because you cannot loot that item." - Possibly used in need befor greed
data << uint8(autoPass); // 1: "You automatically passed on: %s because you cannot loot that item."
for (Roll::PlayerVote::const_iterator itr = roll.playerVote.begin(); itr != roll.playerVote.end(); ++itr)
{
@@ -984,6 +986,23 @@ void Group::SendLooter(Creature* creature, Player* groupLooter)
BroadcastPacket(&data, false);
}
bool CanRollOnItem(LootItem const& item, Player const* player, Loot* loot)
{
// Players can't roll on unique items if they already reached the maximum quantity of that item
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(item.itemid);
if (!proto)
return false;
uint32 itemCount = player->GetItemCount(item.itemid);
if ((proto->MaxCount > 0 && static_cast<int32>(itemCount) >= proto->MaxCount) || (player->CanEquipUniqueItem(proto) != EQUIP_ERR_OK))
return false;
if (!item.AllowedForPlayer(player, loot->sourceWorldObjectGUID))
return false;
return true;
}
void Group::GroupLoot(Loot* loot, WorldObject* pLootedObject)
{
std::vector<LootItem>::iterator i;
@@ -1011,23 +1030,20 @@ void Group::GroupLoot(Loot* loot, WorldObject* pLootedObject)
for (GroupReference* itr = GetFirstMember(); itr != nullptr; itr = itr->next())
{
Player* member = itr->GetSource();
if (!member)
if (!member || !member->GetSession())
continue;
if (member->IsAtLootRewardDistance(pLootedObject))
{
if (i->AllowedForPlayer(member, loot->sourceWorldObjectGUID))
{
r->totalPlayersRolling++;
r->totalPlayersRolling++;
if (member->GetPassOnGroupLoot())
{
r->playerVote[member->GetGUID()] = PASS;
r->totalPass++;
// can't broadcast the pass now. need to wait until all rolling players are known.
}
else
r->playerVote[member->GetGUID()] = NOT_EMITED_YET;
RollVote vote = member->GetPassOnGroupLoot() ? PASS : NOT_EMITED_YET;
if (!CanRollOnItem(*i, member, loot))
{
vote = PASS;
++r->totalPass;
}
r->playerVote[member->GetGUID()] = vote;
}
}
@@ -1050,23 +1066,28 @@ void Group::GroupLoot(Loot* loot, WorldObject* pLootedObject)
continue;
if (itr->second == PASS)
SendLootRoll(newitemGUID, p->GetGUID(), 128, ROLL_PASS, *r);
SendLootRoll(newitemGUID, p->GetGUID(), 128, ROLL_PASS, *r, true);
}
}
SendLootStartRoll(60000, pLootedObject->GetMapId(), *r);
RollId.push_back(r);
if (Creature* creature = pLootedObject->ToCreature())
if (r->totalPass == r->totalPlayersRolling)
delete r;
else
{
creature->m_groupLootTimer = 60000;
creature->lootingGroupLowGUID = GetGUID().GetCounter();
}
else if (GameObject* go = pLootedObject->ToGameObject())
{
go->m_groupLootTimer = 60000;
go->lootingGroupLowGUID = GetGUID().GetCounter();
SendLootStartRoll(60000, pLootedObject->GetMapId(), *r);
RollId.push_back(r);
if (Creature* creature = pLootedObject->ToCreature())
{
creature->m_groupLootTimer = 60000;
creature->lootingGroupLowGUID = GetGUID().GetCounter();
}
else if (GameObject* go = pLootedObject->ToGameObject())
{
go->m_groupLootTimer = 60000;
go->lootingGroupLowGUID = GetGUID().GetCounter();
}
}
}
else
@@ -1094,16 +1115,18 @@ void Group::GroupLoot(Loot* loot, WorldObject* pLootedObject)
for (GroupReference* itr = GetFirstMember(); itr != nullptr; itr = itr->next())
{
Player* member = itr->GetSource();
if (!member)
if (!member || !member->GetSession())
continue;
if (member->IsAtLootRewardDistance(pLootedObject))
{
if (i->AllowedForPlayer(member, loot->sourceWorldObjectGUID))
RollVote vote = NOT_EMITED_YET;
if (!CanRollOnItem(*i, member, loot))
{
r->totalPlayersRolling++;
r->playerVote[member->GetGUID()] = NOT_EMITED_YET;
vote = PASS;
++r->totalPass;
}
r->playerVote[member->GetGUID()] = vote;
}
}
@@ -1154,20 +1177,21 @@ void Group::NeedBeforeGreed(Loot* loot, WorldObject* lootedObject)
for (GroupReference* itr = GetFirstMember(); itr != nullptr; itr = itr->next())
{
Player* playerToRoll = itr->GetSource();
if (!playerToRoll)
if (!playerToRoll || !playerToRoll->GetSession())
continue;
if (i->AllowedForPlayer(playerToRoll, loot->sourceWorldObjectGUID) && playerToRoll->IsAtLootRewardDistance(lootedObject))
if (playerToRoll->IsAtGroupRewardDistance(lootedObject))
{
r->totalPlayersRolling++;
if (playerToRoll->GetPassOnGroupLoot())
RollVote vote = playerToRoll->GetPassOnGroupLoot() ? PASS : NOT_EMITED_YET;
if (!CanRollOnItem(*i, playerToRoll, loot))
{
r->playerVote[playerToRoll->GetGUID()] = PASS;
r->totalPass++;
// can't broadcast the pass now. need to wait until all rolling players are known.
vote = PASS;
r->totalPass++; // Can't broadcast the pass now. need to wait until all rolling players are known
}
else
r->playerVote[playerToRoll->GetGUID()] = NOT_EMITED_YET;
r->playerVote[playerToRoll->GetGUID()] = vote;
}
}
@@ -1228,13 +1252,21 @@ void Group::NeedBeforeGreed(Loot* loot, WorldObject* lootedObject)
for (GroupReference* itr = GetFirstMember(); itr != nullptr; itr = itr->next())
{
Player* playerToRoll = itr->GetSource();
if (!playerToRoll)
if (!playerToRoll || !playerToRoll->GetSession())
continue;
if (i->AllowedForPlayer(playerToRoll, loot->sourceWorldObjectGUID) && playerToRoll->IsAtLootRewardDistance(lootedObject))
if (playerToRoll->IsAtGroupRewardDistance(lootedObject))
{
r->totalPlayersRolling++;
r->playerVote[playerToRoll->GetGUID()] = NOT_EMITED_YET;
RollVote vote = playerToRoll->GetPassOnGroupLoot() ? PASS : NOT_EMITED_YET;
if (!CanRollOnItem(*i, playerToRoll, loot))
{
vote = PASS;
r->totalPass++; // Can't broadcast the pass now. need to wait until all rolling players are known
}
r->playerVote[playerToRoll->GetGUID()] = vote;
}
}
@@ -1996,6 +2028,35 @@ GroupJoinBattlegroundResult Group::CanJoinBattlegroundQueue(Battleground const*
if (bgTemplate->isArena() && memberscount != MinPlayerCount)
return ERR_ARENA_TEAM_PARTY_SIZE;
//check against other arena team members
if (isRated)
{
ArenaTeam* arenaTeam = sArenaTeamMgr->GetArenaTeamById(arenaTeamId);
for (auto const& itr : arenaTeam->GetMembers())
{
Player* teamMember = ObjectAccessor::FindConnectedPlayer(itr.Guid);
//are they online and not a member of this current group?
if (teamMember && !IsMember(teamMember->GetGUID()))
{
//are they already in queue for a rated arena?
if (teamMember->InBattlegroundQueueForBattlegroundQueueType(bgQueueTypeId))
{
GroupQueueInfo ginfo;
BattlegroundQueue& queue = sBattlegroundMgr->GetBattlegroundQueue(bgQueueTypeId);
if (queue.GetPlayerGroupInfoData(teamMember->GetGUID(), &ginfo))
{
if (ginfo.IsRated)
return ERR_BATTLEGROUND_JOIN_FAILED;
}
}
//are they currently in an arena match?
Battleground* bg = teamMember->GetBattleground(false);
if (bg && bg->isRated() && bg->GetMinPlayersPerTeam() == MinPlayerCount)
return ERR_BATTLEGROUND_JOIN_FAILED;
}
}
}
return GroupJoinBattlegroundResult(bgTemplate->GetBgTypeID());
}
+1 -1
View File
@@ -288,7 +288,7 @@ public:
bool isRollLootActive() const;
void SendLootStartRoll(uint32 CountDown, uint32 mapid, const Roll& r);
void SendLootStartRollToPlayer(uint32 countDown, uint32 mapId, Player* p, bool canNeed, Roll const& r);
void SendLootRoll(ObjectGuid SourceGuid, ObjectGuid TargetGuid, uint8 RollNumber, uint8 RollType, const Roll& r);
void SendLootRoll(ObjectGuid SourceGuid, ObjectGuid TargetGuid, uint8 RollNumber, uint8 RollType, const Roll& r, bool autoPass = false);
void SendLootRollWon(ObjectGuid SourceGuid, ObjectGuid TargetGuid, uint8 RollNumber, uint8 RollType, const Roll& r);
void SendLootAllPassed(Roll const& roll);
void SendLooter(Creature* creature, Player* pLooter);
@@ -889,6 +889,9 @@ void WorldSession::HandlePlayerLoginFromDB(LoginQueryHolder const& holder)
pCurrChar->TeleportTo(at->target_mapId, at->target_X, at->target_Y, at->target_Z, pCurrChar->GetOrientation());
else
pCurrChar->TeleportTo(pCurrChar->m_homebindMapId, pCurrChar->m_homebindX, pCurrChar->m_homebindY, pCurrChar->m_homebindZ, pCurrChar->GetOrientation());
// Probably a hackfix, but currently the best workaround to prevent character names showing as Unknown after teleport out from instances at login.
pCurrChar->GetSession()->SendNameQueryOpcode(pCurrChar->GetGUID());
}
pCurrChar->SendInitialPacketsAfterAddToMap();
+15 -7
View File
@@ -283,14 +283,22 @@ void WorldSession::HandleMessagechatOpcode(WorldPacket& recvData)
if (msg.empty())
return;
if (ChatHandler(this).ParseCommands(msg.c_str()))
return;
if (!_player->CanSpeak())
if (lang == LANG_ADDON)
{
std::string timeStr = secsToTimeString(m_muteTime - GameTime::GetGameTime().count());
SendNotification(GetAcoreString(LANG_WAIT_BEFORE_SPEAKING), timeStr.c_str());
return;
if (AddonChannelCommandHandler(this).ParseCommands(msg.c_str()))
return;
}
else
{
if (ChatHandler(this).ParseCommands(msg.c_str()))
return;
if (!_player->CanSpeak())
{
std::string timeStr = secsToTimeString(m_muteTime - GameTime::GetGameTime().count());
SendNotification(GetAcoreString(LANG_WAIT_BEFORE_SPEAKING), timeStr.c_str());
return;
}
}
}
+6 -3
View File
@@ -84,9 +84,12 @@ void WorldSession::HandleSetSheathedOpcode(WorldPackets::Combat::SetSheathed& pa
void WorldSession::SendAttackStop(Unit const* enemy)
{
WorldPacket data(SMSG_ATTACKSTOP, (8 + 8 + 4)); // we guess size
WorldPacket data(SMSG_ATTACKSTOP, (8 + 8 + 4)); // we guess size
data << GetPlayer()->GetPackGUID();
data << (enemy ? enemy->GetPackGUID() : PackedGuid()); // must be packed guid
data << uint32(0); // unk, can be 1 also
if (enemy)
{
data << enemy->GetPackGUID(); // must be packed guid
data << enemy->isDead();
}
SendPacket(&data);
}
+1 -1
View File
@@ -3069,7 +3069,7 @@ void InstanceMap::CreateInstanceScript(bool load, std::string data, uint32 compl
bool isOtherAI = false;
sScriptMgr->OnBeforeCreateInstanceScript(this, instance_data, load, data, completedEncounterMask);
sScriptMgr->OnBeforeCreateInstanceScript(this, &instance_data, load, data, completedEncounterMask);
if (instance_data)
isOtherAI = true;
+1 -1
View File
@@ -15,8 +15,8 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Define.h"
#include "QuestDef.h"
#include "Define.h"
#include "SmartEnum.h"
#include <stdexcept>
@@ -279,7 +279,7 @@ void ScriptMgr::OnMapUpdate(Map* map, uint32 diff)
});
}
void ScriptMgr::OnBeforeCreateInstanceScript(InstanceMap* instanceMap, InstanceScript* instanceData, bool load, std::string data, uint32 completedEncounterMask)
void ScriptMgr::OnBeforeCreateInstanceScript(InstanceMap* instanceMap, InstanceScript** instanceData, bool load, std::string data, uint32 completedEncounterMask)
{
ExecuteScript<AllMapScript>([&](AllMapScript* script)
{
@@ -51,7 +51,7 @@ public:
* @param data Contains information about the instance save data
* @param completedEncounterMask Contains information about the completed encouter mask
*/
virtual void OnBeforeCreateInstanceScript(InstanceMap* /*instanceMap*/, InstanceScript* /*instanceData*/, bool /*load*/, std::string /*data*/, uint32 /*completedEncounterMask*/) { }
virtual void OnBeforeCreateInstanceScript(InstanceMap* /*instanceMap*/, InstanceScript** /*instanceData*/, bool /*load*/, std::string /*data*/, uint32 /*completedEncounterMask*/) { }
/**
* @brief This hook called before destroy instance
+1 -1
View File
@@ -605,7 +605,7 @@ public: /* AllGameobjectScript */
void OnGameObjectSaveToDB(GameObject* go);
public: /* AllMapScript */
void OnBeforeCreateInstanceScript(InstanceMap* instanceMap, InstanceScript* instanceData, bool load, std::string data, uint32 completedEncounterMask);
void OnBeforeCreateInstanceScript(InstanceMap* instanceMap, InstanceScript** instanceData, bool load, std::string data, uint32 completedEncounterMask);
void OnDestroyInstance(MapInstanced* mapInstanced, Map* map);
public: /* BGScript */
@@ -2011,7 +2011,7 @@ void AuraEffect::HandleAuraModShapeshift(AuraApplication const* aurApp, uint8 mo
if (modelid > 0)
{
bool allow = true;
if (target->getTransForm())
if (target->getTransForm() && !(target->GetMapId() == 560 /*The Escape From Durnholde*/))
if (SpellInfo const* transformSpellInfo = sSpellMgr->GetSpellInfo(target->getTransForm()))
if (transformSpellInfo->HasAttribute(SPELL_ATTR0_NO_IMMUNITIES) || !transformSpellInfo->IsPositive())
allow = false;
@@ -2143,7 +2143,7 @@ void AuraEffect::HandleAuraModShapeshift(AuraApplication const* aurApp, uint8 mo
if (target->GetTypeId() == TYPEID_PLAYER)
{
SpellShapeshiftEntry const* shapeInfo = sSpellShapeshiftStore.LookupEntry(form);
SpellShapeshiftFormEntry const* shapeInfo = sSpellShapeshiftFormStore.LookupEntry(form);
// 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)
{
+1 -1
View File
@@ -5799,7 +5799,7 @@ SpellCastResult Spell::CheckCast(bool strict)
SpellEffectInfo const* effInfo = &m_spellInfo->Effects[effIndex];
if (effInfo->ApplyAuraName == SPELL_AURA_MOD_SHAPESHIFT)
{
SpellShapeshiftEntry const* shapeShiftEntry = sSpellShapeshiftStore.LookupEntry(effInfo->MiscValue);
SpellShapeshiftFormEntry const* shapeShiftEntry = sSpellShapeshiftFormStore.LookupEntry(effInfo->MiscValue);
if (shapeShiftEntry && (shapeShiftEntry->flags1 & 1) == 0) // unk flag
checkMask |= VEHICLE_SEAT_FLAG_UNCONTROLLED;
break;
+3 -3
View File
@@ -1435,10 +1435,10 @@ SpellCastResult SpellInfo::CheckShapeshift(uint32 form) const
return SPELL_CAST_OK;
bool actAsShifted = false;
SpellShapeshiftEntry const* shapeInfo = nullptr;
SpellShapeshiftFormEntry const* shapeInfo = nullptr;
if (form > 0)
{
shapeInfo = sSpellShapeshiftStore.LookupEntry(form);
shapeInfo = sSpellShapeshiftFormStore.LookupEntry(form);
if (!shapeInfo)
{
LOG_ERROR("spells", "GetErrorAtShapeshiftedCast: unknown shapeshift {}", form);
@@ -2437,7 +2437,7 @@ int32 SpellInfo::CalcPowerCost(Unit const* caster, SpellSchoolMask schoolMask, S
if (AttributesEx4 & SPELL_ATTR4_WEAPON_SPEED_COST_SCALING)
{
uint32 speed = 0;
if (SpellShapeshiftEntry const* ss = sSpellShapeshiftStore.LookupEntry(caster->GetShapeshiftForm()))
if (SpellShapeshiftFormEntry const* ss = sSpellShapeshiftFormStore.LookupEntry(caster->GetShapeshiftForm()))
speed = ss->attackSpeed;
else
{
+5 -4
View File
@@ -165,10 +165,11 @@ void WorldUpdateTime::RecordUpdateTime(Milliseconds gameTimeMs, uint32 diff, uin
{
if (GetMSTimeDiff(_lastRecordTime, gameTimeMs) > _recordUpdateTimeInverval)
{
LOG_INFO("time.update", "Last {} diffs summary with {} players online:", GetDatasetSize(), sessionCount);
LOG_INFO("time.update", " - Mean: {};", GetAverageUpdateTime());
LOG_INFO("time.update", " - Median: {};", GetPercentile(50));
LOG_INFO("time.update", " - Percentiles (95, 99, max): {}, {}, {}.", GetPercentile(95), GetPercentile(99), GetPercentile(100));
LOG_INFO("time.update", "Update time diff: {}ms with {} players online", GetLastUpdateTime(), sessionCount);
LOG_INFO("time.update", "Last {} diffs summary:", GetDatasetSize());
LOG_INFO("time.update", "|- Mean: {}ms", GetAverageUpdateTime());
LOG_INFO("time.update", "|- Median: {}ms", GetPercentile(50));
LOG_INFO("time.update", "|- Percentiles (95, 99, max): {}ms, {}ms, {}ms", GetPercentile(95), GetPercentile(99), GetPercentile(100));
_lastRecordTime = gameTimeMs;
}
}
@@ -15,9 +15,9 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "WardenCheckMgr.h"
#include "Define.h"
#include "SmartEnum.h"
#include "WardenCheckMgr.h"
#include <stdexcept>
namespace Acore::Impl::EnumUtilsImpl