Merge branch 'master' into Playerbot

This commit is contained in:
Yunfan Li
2024-01-04 19:22:20 +08:00
57 changed files with 1900 additions and 551 deletions
@@ -604,9 +604,9 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_DEL_CHAR_SETTINGS, "DELETE FROM character_settings WHERE guid = ?", CONNECTION_ASYNC);
// Instance saved data. Stores the states of gameobjects in instances to be loaded on server start
PrepareStatement(CHAR_SELECT_INSTANCE_SAVED_DATA, "SELECT id, guid, state FROM instance_saved_go_state_data", CONNECTION_SYNCH);
PrepareStatement(CHAR_UPDATE_INSTANCE_SAVED_DATA, "UPDATE instance_saved_go_state_data SET state = ? WHERE guid = ? AND id = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_INSERT_INSTANCE_SAVED_DATA, "INSERT INTO instance_saved_go_state_data (id, guid, state) VALUES (?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_SELECT_INSTANCE_SAVED_DATA, "SELECT guid, state FROM instance_saved_go_state_data WHERE id = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_INSERT_INSTANCE_SAVED_DATA, "INSERT INTO instance_saved_go_state_data (id, guid, state) VALUES (?, ?, ?)"
"ON DUPLICATE KEY UPDATE state = VALUES(state)", CONNECTION_ASYNC);
PrepareStatement(CHAR_DELETE_INSTANCE_SAVED_DATA, "DELETE FROM instance_saved_go_state_data WHERE id = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_SANITIZE_INSTANCE_SAVED_DATA, "DELETE FROM instance_saved_go_state_data WHERE id NOT IN (SELECT instance.id FROM instance)", CONNECTION_ASYNC);
}
@@ -517,7 +517,6 @@ enum CharacterDatabaseStatements : uint32
CHAR_DEL_CHAR_SETTINGS,
CHAR_SELECT_INSTANCE_SAVED_DATA,
CHAR_UPDATE_INSTANCE_SAVED_DATA,
CHAR_INSERT_INSTANCE_SAVED_DATA,
CHAR_DELETE_INSTANCE_SAVED_DATA,
CHAR_SANITIZE_INSTANCE_SAVED_DATA,
+35 -21
View File
@@ -500,6 +500,7 @@ void SmartAI::CheckConditions(const uint32 diff)
void SmartAI::UpdateAI(uint32 diff)
{
bool hasVictim = UpdateVictim();
CheckConditions(diff);
GetScript()->OnUpdate(diff);
UpdatePath(diff);
@@ -535,7 +536,7 @@ void SmartAI::UpdateAI(uint32 diff)
return;
}
if (!UpdateVictim())
if (!hasVictim)
return;
if (mCanAutoAttack)
@@ -827,14 +828,14 @@ void SmartAI::AttackStart(Unit* who)
// xinef: dont allow charmed npcs to act on their own
if (me->HasUnitFlag(UNIT_FLAG_POSSESSED))
{
if (who && mCanAutoAttack)
me->Attack(who, true);
if (who)
me->Attack(who, mCanAutoAttack);
return;
}
if (who && me->Attack(who, me->IsWithinMeleeRange(who)))
{
if (mCanCombatMove || GetScript()->GetMaxCombatDist())
if (mCanCombatMove)
{
SetRun(mRun);
MovementGeneratorType type = me->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_ACTIVE);
@@ -843,8 +844,8 @@ void SmartAI::AttackStart(Unit* who)
me->GetMotionMaster()->MovementExpired();
me->StopMoving();
}
float range = GetScript()->GetCasterActualDist() > 0.f ? GetScript()->GetCasterActualDist() : GetScript()->GetActualCombatDist();
me->GetMotionMaster()->MoveChase(who, range > 0.f ? ChaseRange(range) : std::optional<ChaseRange>());
me->GetMotionMaster()->MoveChase(who);
}
}
}
@@ -1019,21 +1020,8 @@ void SmartAI::sQuestReward(Player* player, Quest const* quest, uint32 opt)
GetScript()->ProcessEventsFor(SMART_EVENT_REWARD_QUEST, player, quest->GetQuestId(), opt);
}
void SmartAI::SetForcedCombatMove(float dist)
void SmartAI::SetCombatMove(bool on, float chaseRange)
{
if (!me->GetVictim())
return;
SetRun(mRun);
me->GetMotionMaster()->MoveChase(me->GetVictim(), dist);
}
void SmartAI::SetCombatMove(bool on)
{
// Xinef: Fix Combat Movement
if (GetScript()->GetMaxCombatDist()/* || GetScript()->GetCasterMaxDist()*/) // Xinef: we only need this hack for old caster movement system
return;
if (mCanCombatMove == on)
return;
@@ -1049,7 +1037,7 @@ void SmartAI::SetCombatMove(bool on)
if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == IDLE_MOTION_TYPE)
{
SetRun(mRun);
me->GetMotionMaster()->MoveChase(me->GetVictim());
me->GetMotionMaster()->MoveChase(me->GetVictim(), chaseRange);
me->CastStop();
}
}
@@ -1115,6 +1103,32 @@ void SmartAI::StopFollow(bool complete)
GetScript()->ProcessEventsFor(SMART_EVENT_FOLLOW_COMPLETED, player);
}
void SmartAI::MoveAway(float distance)
{
if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == POINT_MOTION_TYPE)
return;
mCanCombatMove = false;
if (!IsAIControlled())
return;
if (!HasEscortState(SMART_ESCORT_ESCORTING))
{
if (me->GetVictim())
{
me->StopMoving();
if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == CHASE_MOTION_TYPE)
me->GetMotionMaster()->Clear(false);
float x, y, z;
me->GetClosePoint(x, y, z, me->GetObjectSize(), distance, M_PI);
if (me->GetVictim()->IsWithinLOS(x, y, z))
me->GetMotionMaster()->MovePoint(SMART_RANDOM_POINT, x, y, z);
}
}
}
void SmartAI::SetScript9(SmartScriptHolder& e, uint32 entry, Unit* invoker)
{
if (invoker)
+2 -2
View File
@@ -64,10 +64,11 @@ public:
bool IsEscorted() override { return (mEscortState & SMART_ESCORT_ESCORTING); }
void RemoveEscortState(uint32 uiEscortState) { mEscortState &= ~uiEscortState; }
void SetAutoAttack(bool on) { mCanAutoAttack = on; }
void SetCombatMove(bool on);
void SetCombatMove(bool on, float chaseRange = 0.0f);
bool CanCombatMove() { return mCanCombatMove; }
void SetFollow(Unit* target, float dist = 0.0f, float angle = 0.0f, uint32 credit = 0, uint32 end = 0, uint32 creditType = 0, bool aliveState = true);
void StopFollow(bool complete);
void MoveAway(float distance);
void SetScript9(SmartScriptHolder& e, uint32 entry, Unit* invoker);
SmartScript* GetScript() { return &mScript; }
@@ -205,7 +206,6 @@ public:
// Xinef
void SetWPPauseTimer(uint32 time) { mWPPauseTimer = time; }
void SetForcedCombatMove(float dist);
private:
bool mIsCharmed;
+126 -83
View File
@@ -56,15 +56,8 @@ SmartScript::SmartScript()
mTemplate = SMARTAI_TEMPLATE_BASIC;
mScriptType = SMART_SCRIPT_TYPE_CREATURE;
isProcessingTimedActionList = false;
// Xinef: Fix Combat Movement
mActualCombatDist = 0;
mMaxCombatDist = 0;
smartCasterActualDist = 0.0f;
smartCasterMaxDist = 0.0f;
smartCasterPowerType = POWER_MANA;
mCurrentPriority = 0;
mEventSortingRequired = false;
_allowPhaseReset = true;
}
@@ -87,14 +80,16 @@ void SmartScript::OnReset()
InitTimer((*i));
(*i).runOnce = false;
}
if ((*i).priority != SmartScriptHolder::DEFAULT_PRIORITY)
{
(*i).priority = SmartScriptHolder::DEFAULT_PRIORITY;
mEventSortingRequired = true;
}
}
ProcessEventsFor(SMART_EVENT_RESET);
mLastInvoker.Clear();
mCounterList.clear();
// Xinef: Fix Combat Movement
RestoreMaxCombatDist();
RestoreCasterMaxDist();
}
void SmartScript::ProcessEventsFor(SMART_EVENT e, Unit* unit, uint32 var0, uint32 var1, bool bvar, SpellInfo const* spell, GameObject* gob)
@@ -118,14 +113,18 @@ void SmartScript::ProcessEventsFor(SMART_EVENT e, Unit* unit, uint32 var0, uint3
void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, uint32 var1, bool bvar, SpellInfo const* spell, GameObject* gob)
{
e.runOnce = true;//used for repeat check
//calc random
if (e.event.event_chance < 100 && e.event.event_chance)
if (e.event.event_chance < 100 && e.event.event_chance && !(e.event.event_flags & SMART_EVENT_FLAG_TEMP_IGNORE_CHANCE_ROLL))
{
uint32 rnd = urand(1, 100);
if (e.event.event_chance <= rnd)
return;
}
e.runOnce = true;//used for repeat check
// Remove SMART_EVENT_FLAG_TEMP_IGNORE_CHANCE_ROLL flag after processing roll chances as it's not needed anymore
e.event.event_flags &= ~SMART_EVENT_FLAG_TEMP_IGNORE_CHANCE_ROLL;
if (unit)
mLastInvoker = unit->GetGUID();
@@ -589,6 +588,8 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
if (e.action.cast.targetsLimit)
Acore::Containers::RandomResize(targets, e.action.cast.targetsLimit);
bool failedSpellCast = false, successfulSpellCast = false;
for (WorldObject* target : targets)
{
// may be nullptr
@@ -621,23 +622,40 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
if (e.action.cast.castFlags & SMARTCAST_INTERRUPT_PREVIOUS)
me->InterruptNonMeleeSpells(false);
// Flag usable only if caster has max dist set.
if ((e.action.cast.castFlags & SMARTCAST_COMBAT_MOVE) && GetCasterMaxDist() > 0.0f && me->GetMaxPower(GetCasterPowerType()) > 0)
{
// Check mana case only and operate movement accordingly, LoS and range is checked in targetet movement generator.
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(e.action.cast.spell);
int32 currentPower = me->GetPower(GetCasterPowerType());
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(e.action.cast.spell);
float distanceToTarget = me->GetDistance(target->ToUnit());
float spellMaxRange = me->GetSpellMaxRangeForTarget(target->ToUnit(), spellInfo);
float spellMinRange = me->GetSpellMinRangeForTarget(target->ToUnit(), spellInfo);
float meleeRange = me->GetMeleeRange(target->ToUnit());
if ((spellInfo && (currentPower < spellInfo->CalcPowerCost(me, spellInfo->GetSchoolMask()) || me->IsSpellProhibited(spellInfo->GetSchoolMask()))) || me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED))
{
SetCasterActualDist(0);
CAST_AI(SmartAI, me->AI())->SetForcedCombatMove(0);
}
else if (GetCasterActualDist() == 0.0f && me->GetPowerPct(GetCasterPowerType()) > 30.0f)
{
RestoreCasterMaxDist();
CAST_AI(SmartAI, me->AI())->SetForcedCombatMove(GetCasterActualDist());
}
bool isWithinLOSInMap = me->IsWithinLOSInMap(target->ToUnit());
bool isWithinMeleeRange = distanceToTarget <= meleeRange;
bool isRangedAttack = spellMaxRange > NOMINAL_MELEE_RANGE;
bool isTargetRooted = target->ToUnit()->HasUnitState(UNIT_STATE_ROOT);
// To prevent running back and forth when OOM, we must have more than 10% mana.
bool canCastSpell = me->GetPowerPct(POWER_MANA) > 10.0f && spellInfo->CalcPowerCost(me, spellInfo->GetSchoolMask()) < (int32)me->GetPower(POWER_MANA) && !me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED);
// If target is rooted we move out of melee range before casting, but not further than spell max range.
if (isWithinLOSInMap && isWithinMeleeRange && isRangedAttack && isTargetRooted && canCastSpell)
{
failedSpellCast = true; // Mark spellcast as failed so we can retry it later
float minDistance = std::max(meleeRange, spellMinRange) - distanceToTarget + NOMINAL_MELEE_RANGE;
CAST_AI(SmartAI, me->AI())->MoveAway(std::min(minDistance, spellMaxRange));
continue;
}
// Let us not try to cast spell if we know it is going to fail anyway. Stick to chasing and continue.
if (distanceToTarget > spellMaxRange && isWithinLOSInMap)
{
failedSpellCast = true;
CAST_AI(SmartAI, me->AI())->SetCombatMove(true, std::max(spellMaxRange - NOMINAL_MELEE_RANGE, 0.0f));
continue;
}
else if (distanceToTarget < spellMinRange || !isWithinLOSInMap)
{
failedSpellCast = true;
CAST_AI(SmartAI, me->AI())->SetCombatMove(true);
continue;
}
TriggerCastFlags triggerFlags = TRIGGERED_NONE;
@@ -649,11 +667,38 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
triggerFlags = TRIGGERED_FULL_MASK;
}
me->CastSpell(target->ToUnit(), e.action.cast.spell, triggerFlags);
SpellCastResult result = me->CastSpell(target->ToUnit(), e.action.cast.spell, triggerFlags);
bool spellCastFailed = (result != SPELL_CAST_OK && result != SPELL_FAILED_SPELL_IN_PROGRESS);
if (e.action.cast.castFlags & SMARTCAST_COMBAT_MOVE)
{
// 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.
if (result == SPELL_FAILED_OUT_OF_RANGE)
// if we are just out of range, we only chase until we are back in spell range.
CAST_AI(SmartAI, me->AI())->SetCombatMove(true, std::max(spellMaxRange - NOMINAL_MELEE_RANGE, 0.0f));
else
// if spell fail for any other reason, we chase to melee range, or stay where we are if spellcast was successful.
CAST_AI(SmartAI, me->AI())->SetCombatMove(spellCastFailed);
}
if (spellCastFailed)
failedSpellCast = true;
else
successfulSpellCast = true;
LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_CAST: Unit {} casts spell {} on target {} with castflags {}",
me->GetGUID().ToString(), e.action.cast.spell, target->GetGUID().ToString(), e.action.cast.castFlags);
}
}
// If there is at least 1 failed cast and no successful casts at all, retry again on next loop
if (failedSpellCast && !successfulSpellCast)
{
RetryLater(e, true);
// Don't execute linked events
return;
}
break;
}
case SMART_ACTION_SELF_CAST:
@@ -864,15 +909,8 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
if (!IsSmart())
break;
// Xinef: Fix Combat Movement
bool move = e.action.combatMove.move;
if (move && GetMaxCombatDist() && e.GetEventType() == SMART_EVENT_MANA_PCT)
{
SetActualCombatDist(0);
CAST_AI(SmartAI, me->AI())->SetForcedCombatMove(0);
}
else
CAST_AI(SmartAI, me->AI())->SetCombatMove(move);
CAST_AI(SmartAI, me->AI())->SetCombatMove(move);
LOG_DEBUG("sql.sql", "SmartScript::ProcessAction:: SMART_ACTION_ALLOW_COMBAT_MOVEMENT: Creature {} bool on = {}",
me->GetGUID().ToString(), e.action.combatMove.move);
break;
@@ -2425,17 +2463,6 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
break;
}
case SMART_ACTION_SET_CASTER_COMBAT_DIST:
{
if (e.action.casterDistance.reset)
RestoreCasterMaxDist();
else
SetCasterActualDist(e.action.casterDistance.dist);
if (me->GetVictim() && me->GetMotionMaster()->GetCurrentMovementGeneratorType() == CHASE_MOTION_TYPE)
me->GetMotionMaster()->MoveChase(me->GetVictim(), GetCasterActualDist());
break;
}
case SMART_ACTION_SET_SIGHT_DIST:
{
for (WorldObject* const target : targets)
@@ -4577,7 +4604,7 @@ void SmartScript::UpdateTimer(SmartScriptHolder& e, uint32 const diff)
{
if (me && me->HasUnitState(UNIT_STATE_CASTING))
{
e.timer = 1200;
RaisePriority(e);
return;
}
}
@@ -4635,6 +4662,18 @@ void SmartScript::UpdateTimer(SmartScriptHolder& e, uint32 const diff)
break;
}
}
if (e.priority != SmartScriptHolder::DEFAULT_PRIORITY)
{
// Reset priority to default one only if the event hasn't been rescheduled again to next loop
if (e.timer > 1)
{
// Re-sort events if this was moved to the top of the queue
mEventSortingRequired = true;
// Reset priority to default one
e.priority = SmartScriptHolder::DEFAULT_PRIORITY;
}
}
}
else
e.timer -= diff;
@@ -4663,6 +4702,12 @@ void SmartScript::OnUpdate(uint32 const diff)
InstallEvents();//before UpdateTimers
if (mEventSortingRequired)
{
SortEvents(mEvents);
mEventSortingRequired = false;
}
for (SmartAIEventList::iterator i = mEvents.begin(); i != mEvents.end(); ++i)
UpdateTimer(*i, diff);
@@ -4718,6 +4763,33 @@ void SmartScript::OnUpdate(uint32 const diff)
}
}
void SmartScript::SortEvents(SmartAIEventList& events)
{
std::sort(events.begin(), events.end());
}
void SmartScript::RaisePriority(SmartScriptHolder& e)
{
e.timer = 1200;
// Change priority only if it's set to default, otherwise keep the current order of events
if (e.priority == SmartScriptHolder::DEFAULT_PRIORITY)
{
e.priority = mCurrentPriority++;
mEventSortingRequired = true;
}
}
void SmartScript::RetryLater(SmartScriptHolder& e, bool ignoreChanceRoll)
{
RaisePriority(e);
// This allows to retry the action later without rolling again the chance roll (which might fail and end up not executing the action)
if (ignoreChanceRoll)
e.event.event_flags |= SMART_EVENT_FLAG_TEMP_IGNORE_CHANCE_ROLL;
e.runOnce = false;
}
void SmartScript::FillScript(SmartAIEventList e, WorldObject* obj, AreaTrigger const* at)
{
(void)at; // ensure that the variable is referenced even if extra logs are disabled in order to pass compiler checks
@@ -4822,37 +4894,8 @@ void SmartScript::OnInitialize(WorldObject* obj, AreaTrigger const* at)
GetScript();//load copy of script
uint32 maxDisableDist = 0;
uint32 minEnableDist = 0;
for (SmartAIEventList::iterator i = mEvents.begin(); i != mEvents.end(); ++i)
{
InitTimer((*i));//calculate timers for first time use
if (i->GetEventType() == SMART_EVENT_RANGE && i->GetActionType() == SMART_ACTION_ALLOW_COMBAT_MOVEMENT)
{
if (i->action.combatMove.move == 1 && i->event.minMaxRepeat.rangeMin > minEnableDist)
minEnableDist = i->event.minMaxRepeat.rangeMin;
else if (i->action.combatMove.move == 0 && (i->event.minMaxRepeat.rangeMax < maxDisableDist || maxDisableDist == 0))
maxDisableDist = i->event.minMaxRepeat.rangeMax;
}
// Xinef: if smartcast combat move flag is present
if (i->GetActionType() == SMART_ACTION_CAST && (i->action.cast.castFlags & SMARTCAST_COMBAT_MOVE))
{
if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(i->action.cast.spell))
{
float maxRange = spellInfo->GetMaxRange(spellInfo->IsPositive());
float minRange = spellInfo->GetMinRange(spellInfo->IsPositive());
if (maxRange > 0 && minRange <= maxRange)
{
smartCasterMaxDist = minRange + ((maxRange - minRange) * 0.65f);
smartCasterPowerType = (Powers)spellInfo->PowerType;
}
}
}
}
if (maxDisableDist > 0 && minEnableDist >= maxDisableDist)
mMaxCombatDist = uint32(maxDisableDist + ((minEnableDist - maxDisableDist) / 2));
for (SmartScriptHolder& event : mEvents)
InitTimer(event);//calculate timers for first time use
ProcessEventsFor(SMART_EVENT_AI_INIT);
InstallEvents();
+6 -22
View File
@@ -216,19 +216,6 @@ public:
typedef std::unordered_map<uint32, uint32> CounterMap;
CounterMap mCounterList;
// Xinef: Fix Combat Movement
void SetActualCombatDist(uint32 dist) { mActualCombatDist = dist; }
void RestoreMaxCombatDist() { mActualCombatDist = mMaxCombatDist; }
uint32 GetActualCombatDist() const { return mActualCombatDist; }
uint32 GetMaxCombatDist() const { return mMaxCombatDist; }
// Xinef: SmartCasterAI, replace above
void SetCasterActualDist(float dist) { smartCasterActualDist = dist; }
void RestoreCasterMaxDist() { smartCasterActualDist = smartCasterMaxDist; }
Powers GetCasterPowerType() const { return smartCasterPowerType; }
float GetCasterActualDist() const { return smartCasterActualDist; }
float GetCasterMaxDist() const { return smartCasterMaxDist; }
bool AllowPhaseReset() const { return _allowPhaseReset; }
void SetPhaseReset(bool allow) { _allowPhaseReset = allow; }
@@ -241,6 +228,10 @@ private:
void SetPhase(uint32 p);
bool IsInPhase(uint32 p) const;
void SortEvents(SmartAIEventList& events);
void RaisePriority(SmartScriptHolder& e);
void RetryLater(SmartScriptHolder& e, bool ignoreChanceRoll = false);
SmartAIEventList mEvents;
SmartAIEventList mInstallEvents;
SmartAIEventList mTimedActionList;
@@ -262,15 +253,8 @@ private:
uint32 mLastTextID;
uint32 mTalkerEntry;
bool mUseTextTimer;
// Xinef: Fix Combat Movement
uint32 mActualCombatDist;
uint32 mMaxCombatDist;
// Xinef: SmartCasterAI, replace above in future
uint32 smartCasterActualDist;
uint32 smartCasterMaxDist;
Powers smartCasterPowerType;
uint32 mCurrentPriority;
bool mEventSortingRequired;
// Xinef: misc
bool _allowPhaseReset;
@@ -755,7 +755,6 @@ bool SmartAIMgr::CheckUnusedActionParams(SmartScriptHolder const& e)
case SMART_ACTION_EXIT_VEHICLE: return NO_PARAMS;
case SMART_ACTION_SET_UNIT_MOVEMENT_FLAGS: return sizeof(SmartAction::movementFlag);
case SMART_ACTION_SET_COMBAT_DISTANCE: return sizeof(SmartAction::combatDistance);
case SMART_ACTION_SET_CASTER_COMBAT_DIST: return sizeof(SmartAction::casterDistance);
case SMART_ACTION_FALL: return NO_PARAMS;
case SMART_ACTION_SET_EVENT_FLAG_RESET: return sizeof(SmartAction::setActive);
case SMART_ACTION_STOP_MOTION: return sizeof(SmartAction::stopMotion);
@@ -1918,7 +1917,6 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
case SMART_ACTION_EXIT_VEHICLE:
case SMART_ACTION_SET_UNIT_MOVEMENT_FLAGS:
case SMART_ACTION_SET_COMBAT_DISTANCE:
case SMART_ACTION_SET_CASTER_COMBAT_DIST:
case SMART_ACTION_SET_SIGHT_DIST:
case SMART_ACTION_FLEE:
case SMART_ACTION_ADD_THREAT:
@@ -26,6 +26,7 @@
#include "Spell.h"
#include "SpellMgr.h"
#include "Unit.h"
#include <limits>
typedef uint32 SAIBool;
@@ -686,7 +687,7 @@ enum SMART_ACTION
SMART_ACTION_EXIT_VEHICLE = 203, // none
SMART_ACTION_SET_UNIT_MOVEMENT_FLAGS = 204, // flags
SMART_ACTION_SET_COMBAT_DISTANCE = 205, // combatDistance
SMART_ACTION_SET_CASTER_COMBAT_DIST = 206, // followDistance, resetToMax
// UNUSED = 206,
SMART_ACTION_SET_HOVER = 207, // 0/1
SMART_ACTION_ADD_IMMUNITY = 208, // type, id, value
SMART_ACTION_REMOVE_IMMUNITY = 209, // type, id, value
@@ -1859,7 +1860,10 @@ enum SmartEventFlags
SMART_EVENT_FLAG_WHILE_CHARMED = 0x200, // Event occurs even if AI owner is charmed
SMART_EVENT_FLAG_DIFFICULTY_ALL = (SMART_EVENT_FLAG_DIFFICULTY_0 | SMART_EVENT_FLAG_DIFFICULTY_1 | SMART_EVENT_FLAG_DIFFICULTY_2 | SMART_EVENT_FLAG_DIFFICULTY_3),
SMART_EVENT_FLAGS_ALL = (SMART_EVENT_FLAG_NOT_REPEATABLE | SMART_EVENT_FLAG_DIFFICULTY_ALL | SMART_EVENT_FLAG_RESERVED_5 | SMART_EVENT_FLAG_RESERVED_6 | SMART_EVENT_FLAG_DEBUG_ONLY | SMART_EVENT_FLAG_DONT_RESET | SMART_EVENT_FLAG_WHILE_CHARMED)
SMART_EVENT_FLAGS_ALL = (SMART_EVENT_FLAG_NOT_REPEATABLE | SMART_EVENT_FLAG_DIFFICULTY_ALL | SMART_EVENT_FLAG_RESERVED_5 | SMART_EVENT_FLAG_RESERVED_6 | SMART_EVENT_FLAG_DEBUG_ONLY | SMART_EVENT_FLAG_DONT_RESET | SMART_EVENT_FLAG_WHILE_CHARMED),
// Temp flags, used only at runtime, never stored in DB
SMART_EVENT_FLAG_TEMP_IGNORE_CHANCE_ROLL = 0x40000000, //Event occurs no matter what roll_chance_i(e.event.event_chance) returns.
};
enum SmartCastFlags
@@ -1889,7 +1893,7 @@ enum SmartFollowType
struct SmartScriptHolder
{
SmartScriptHolder() : entryOrGuid(0), source_type(SMART_SCRIPT_TYPE_CREATURE)
, event_id(0), link(0), event(), action(), target(), timer(0), active(false), runOnce(false)
, event_id(0), link(0), event(), action(), target(), timer(0), priority(DEFAULT_PRIORITY), active(false), runOnce(false)
, enableTimed(false) {}
int32 entryOrGuid;
@@ -1908,9 +1912,18 @@ public:
uint32 GetTargetType() const { return (uint32)target.type; }
uint32 timer;
uint32 priority;
bool active;
bool runOnce;
bool enableTimed;
// Default comparision operator using priority field as first ordering field
bool operator<(SmartScriptHolder const& other) const
{
return std::tie(priority, entryOrGuid, source_type, event_id, link) < std::tie(other.priority, other.entryOrGuid, other.source_type, other.event_id, other.link);
}
static constexpr uint32 DEFAULT_PRIORITY = std::numeric_limits<uint32>::max();
};
typedef std::unordered_map<uint32, WayPoint*> WPPath;
@@ -335,21 +335,22 @@ bool GameObject::Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, u
if (IsInstanceGameobject())
{
switch (GetStateSavedOnInstance())
if (InstanceScript* instance = GetInstanceScript())
{
case 0:
SetGoState(GO_STATE_READY);
SwitchDoorOrButton(true);
break;
case 1:
SetGoState(GO_STATE_READY);
break;
case 2:
SetGoState(GO_STATE_ACTIVE_ALTERNATIVE);
break;
default:
SetGoState(go_state);
break;
switch (uint8 state = instance->GetStoredGameObjectState(GetSpawnId()))
{
case 0:
SetGoState(GO_STATE_READY);
SwitchDoorOrButton(true);
break;
case 1:
case 2:
SetGoState((GOState)state);
break;
default:
SetGoState(go_state);
break;
}
}
}
else
@@ -2505,21 +2506,13 @@ void GameObject::SetGoState(GOState state)
* save it's state on the database to be loaded properly
* on server restart or crash.
*/
if (IsInstanceGameobject() && IsAbleToSaveOnDb())
if (IsInstanceGameobject() && IsAllowedToSaveToDB())
{
// Save the gameobject state on the Database
if (!FindStateSavedOnInstance())
{
SaveInstanceData(GameobjectStateToInt(&state));
}
else
{
UpdateInstanceData(GameobjectStateToInt(&state));
}
SaveStateToDB();
}
}
bool GameObject::IsInstanceGameobject()
bool GameObject::IsInstanceGameobject() const
{
// Avoid checking for unecessary gameobjects whose
// states don't matter for the dungeon progression
@@ -2538,7 +2531,7 @@ bool GameObject::IsInstanceGameobject()
return false;
}
bool GameObject::ValidateGameobjectType()
bool GameObject::ValidateGameobjectType() const
{
switch (m_goInfo->type)
{
@@ -2553,7 +2546,7 @@ bool GameObject::ValidateGameobjectType()
}
}
uint8 GameObject::GameobjectStateToInt(GOState* state)
uint8 GameObject::GameobjectStateToInt(GOState* state) const
{
uint8 m_state = 3;
@@ -2578,71 +2571,24 @@ uint8 GameObject::GameobjectStateToInt(GOState* state)
return m_state;
}
bool GameObject::IsAbleToSaveOnDb()
{
return m_saveStateOnDb;
}
void GameObject::UpdateSaveToDb(bool enable)
{
m_saveStateOnDb = enable;
if (enable)
{
SavingStateOnDB();
}
}
void GameObject::SavingStateOnDB()
void GameObject::SaveStateToDB()
{
if (IsInstanceGameobject())
{
GOState param = GetGoState();
if (!FindStateSavedOnInstance())
if (InstanceScript* instance = GetInstanceScript())
{
SaveInstanceData(GameobjectStateToInt(&param));
GOState param = GetGoState();
instance->StoreGameObjectState(GetSpawnId(), GameobjectStateToInt(&param));
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INSERT_INSTANCE_SAVED_DATA);
stmt->SetData(0, GetInstanceId());
stmt->SetData(1, GetSpawnId());
stmt->SetData(2, GameobjectStateToInt(&param));
CharacterDatabase.Execute(stmt);
}
}
}
void GameObject::SaveInstanceData(uint8 state)
{
uint32 id = GetInstanceId();
uint32 guid = GetSpawnId();
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INSERT_INSTANCE_SAVED_DATA);
stmt->SetData(0, id);
stmt->SetData(1, guid);
stmt->SetData(2, state);
CharacterDatabase.Execute(stmt);
sObjectMgr->NewInstanceSavedGameobjectState(id, guid, state);
}
void GameObject::UpdateInstanceData(uint8 state)
{
uint32 id = GetInstanceId();
uint32 guid = GetSpawnId();
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPDATE_INSTANCE_SAVED_DATA);
stmt->SetData(0, state);
stmt->SetData(1, guid);
stmt->SetData(2, id);
CharacterDatabase.Execute(stmt);
sObjectMgr->SetInstanceSavedGameobjectState(id, guid, state);
}
uint8 GameObject::GetStateSavedOnInstance()
{
return sObjectMgr->GetInstanceSavedGameobjectState(GetInstanceId(), GetSpawnId());
}
bool GameObject::FindStateSavedOnInstance()
{
return sObjectMgr->FindInstanceSavedGameobjectState(GetInstanceId(), GetSpawnId());
}
void GameObject::SetDisplayId(uint32 displayid)
{
SetUInt32Value(GAMEOBJECT_DISPLAYID, displayid);
@@ -350,25 +350,21 @@ public:
static std::unordered_map<int, goEventFlag> gameObjectToEventFlag; // Gameobject -> event flag
void SaveInstanceData(uint8 state);
void UpdateInstanceData(uint8 state);
bool FindStateSavedOnInstance();
bool ValidateGameobjectType();
uint8 GetStateSavedOnInstance();
bool IsInstanceGameobject();
uint8 GameobjectStateToInt(GOState* state);
[[nodiscard]] bool ValidateGameobjectType() const;
[[nodiscard]] bool IsInstanceGameobject() const;
[[nodiscard]] uint8 GameobjectStateToInt(GOState* state) const;
/* A check to verify if this object is available to be saved on the DB when
* a state change occurs
*/
bool IsAbleToSaveOnDb();
[[nodiscard]] bool IsAllowedToSaveToDB() const { return m_saveStateOnDb; };
/* Enable or Disable the ability to save on the database this gameobject's state
* whenever it changes
*/
void UpdateSaveToDb(bool enable);
void AllowSaveToDB(bool enable) { m_saveStateOnDb = enable; };
void SavingStateOnDB();
void SaveStateToDB();
std::string GetDebugInfo() const override;
protected:
+8
View File
@@ -105,6 +105,14 @@ void Pet::AddToWorld()
if (GetOwnerGUID().IsPlayer())
{
if (Player* owner = GetOwner())
{
if (getPetType() == SUMMON_PET && owner->getClass() == CLASS_WARLOCK)
{
owner->SetLastPetSpell(GetUInt32Value(UNIT_CREATED_BY_SPELL));
}
}
sScriptMgr->OnPetAddToWorld(this);
}
}
+18 -16
View File
@@ -2524,6 +2524,11 @@ void Player::GiveLevel(uint8 level)
sScriptMgr->OnPlayerLevelChanged(this, oldLevel);
}
bool Player::IsMaxLevel() const
{
return GetLevel() >= GetUInt32Value(PLAYER_FIELD_MAX_LEVEL);
}
void Player::InitTalentForLevel()
{
uint32 talentPointsForLevel = CalculateTalentsPoints();
@@ -14166,24 +14171,21 @@ void Player::ResummonPetTemporaryUnSummonedIfAny()
bool Player::CanResummonPet(uint32 spellid)
{
switch (getClass())
if (getClass() == CLASS_DEATH_KNIGHT)
{
case CLASS_DEATH_KNIGHT:
if (CanSeeDKPet())
return true;
else if (spellid == 52150) //Raise Dead
return false;
break;
case CLASS_MAGE:
if (HasSpell(31687) && HasAura(70937)) //Has [Summon Water Elemental] spell and [Glyph of Eternal Water].
return true;
break;
case CLASS_HUNTER:
case CLASS_WARLOCK:
if (CanSeeDKPet())
return true;
break;
default:
break;
else if (spellid == 52150) // Raise Dead
return false;
}
else if (getClass() == CLASS_MAGE)
{
if (HasSpell(31687) && HasAura(70937)) //Has [Summon Water Elemental] spell and [Glyph of Eternal Water].
return true;
}
else if (getClass() == CLASS_HUNTER)
{
return true;
}
return HasSpell(spellid);
+1
View File
@@ -1686,6 +1686,7 @@ public:
void SetFreeTalentPoints(uint32 points);
bool resetTalents(bool noResetCost = false);
[[nodiscard]] uint32 resetTalentsCost() const;
bool IsMaxLevel() const;
void InitTalentForLevel();
void BuildPlayerTalentsInfoData(WorldPacket* data);
void BuildPetTalentsInfoData(WorldPacket* data);
@@ -2352,7 +2352,7 @@ void Player::SendQuestReward(Quest const* quest, uint32 XP)
WorldPacket data(SMSG_QUESTGIVER_QUEST_COMPLETE, (4 + 4 + 4 + 4 + 4));
data << uint32(questid);
if (GetLevel() < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
if (!IsMaxLevel())
{
data << uint32(XP);
data << uint32(quest->GetRewOrReqMoney(GetLevel()));
+18 -25
View File
@@ -4468,19 +4468,12 @@ void Unit::_ApplyAura(AuraApplication* aurApp, uint8 effMask)
SpellInfo const* spellInfo = aura->GetSpellInfo();
if (AuraStateType aState = spellInfo->GetAuraState())
{
if (aState != AURA_STATE_CONFLAGRATE)
{
// Sting (hunter's pet ability), Faerie Fire (druid versions)
if (aState == AURA_STATE_FAERIE_FIRE)
aurApp->GetTarget()->RemoveAurasByType(SPELL_AURA_MOD_STEALTH);
uint32 aStateMask = (1 << (aState - 1));
// force update so the new caster registers it
if ((aStateMask & PER_CASTER_AURA_STATE_MASK) && HasFlag(UNIT_FIELD_AURASTATE, aStateMask))
ForceValuesUpdateAtIndex(UNIT_FIELD_AURASTATE);
else
ModifyAuraState(aState, true);
}
else if (caster)
{
ConflagrateAuraStateDelayEvent* pEvent = new ConflagrateAuraStateDelayEvent(this, caster->GetGUID());
m_Events.AddEvent(pEvent, m_Events.CalculateTime(700)); // intended 700ms delay before allowing to cast conflagrate
}
}
if (aurApp->GetRemoveMode())
@@ -4575,9 +4568,19 @@ void Unit::_UnapplyAura(AuraApplicationMap::iterator& i, AuraRemoveMode removeMo
ToTotem()->setDeathState(DeathState::JustDied);
}
// Remove aurastates only if were not found
if (!auraStateFound)
ModifyAuraState(auraState, false);
// Remove aurastates only if needed and were not found
if (auraState)
{
if (!auraStateFound)
ModifyAuraState(auraState, false);
else
{
// update for casters, some shouldn't 'see' the aura state
uint32 aStateMask = (1 << (auraState - 1));
if ((aStateMask & PER_CASTER_AURA_STATE_MASK) != 0)
ForceValuesUpdateAtIndex(UNIT_FIELD_AURASTATE);
}
}
aura->HandleAuraSpecificMods(aurApp, caster, false, false);
@@ -20762,16 +20765,6 @@ int32 Unit::CalculateAOEDamageReduction(int32 damage, uint32 schoolMask, Unit* c
return damage;
}
bool ConflagrateAuraStateDelayEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/)
{
if (m_owner->IsInWorld())
if (m_owner->GetAuraEffect(SPELL_AURA_PERIODIC_DAMAGE, SPELLFAMILY_WARLOCK, 0x4, 0, 0, m_casterGUID) || // immolate
m_owner->GetAuraEffect(SPELL_AURA_PERIODIC_DAMAGE, SPELLFAMILY_WARLOCK, 0, 0, 0x2, m_casterGUID)) // shadowflame
m_owner->ModifyAuraState(AURA_STATE_CONFLAGRATE, true);
return true;
}
void Unit::ExecuteDelayedUnitRelocationEvent()
{
this->RemoveFromNotify(NOTIFY_VISIBILITY_CHANGED);
-11
View File
@@ -2674,17 +2674,6 @@ namespace Acore
};
}
class ConflagrateAuraStateDelayEvent : public BasicEvent
{
public:
ConflagrateAuraStateDelayEvent(Unit* owner, ObjectGuid casterGUID) : BasicEvent(), m_owner(owner), m_casterGUID(casterGUID) { }
bool Execute(uint64 e_time, uint32 p_time) override;
private:
Unit* m_owner;
ObjectGuid m_casterGUID;
};
class RedirectSpellEvent : public BasicEvent
{
public:
-66
View File
@@ -10135,72 +10135,6 @@ uint32 ObjectMgr::GetQuestMoneyReward(uint8 level, uint32 questMoneyDifficulty)
return 0;
}
void ObjectMgr::LoadInstanceSavedGameobjectStateData()
{
uint32 oldMSTime = getMSTime();
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SELECT_INSTANCE_SAVED_DATA);
PreparedQueryResult result = CharacterDatabase.Query(stmt);
if (!result)
{
// There's no gameobject with this GUID saved on the DB
LOG_INFO("sql.sql", ">> Loaded 0 Instance saved gameobject state data. DB table `instance_saved_go_state_data` is empty.");
return;
}
Field* fields;
uint32 count = 0;
do
{
fields = result->Fetch();
GameobjectInstanceSavedStateList.push_back({ fields[0].Get<uint32>(), fields[1].Get<uint32>(), fields[2].Get<unsigned short>() });
count++;
} while (result->NextRow());
LOG_INFO("server.loading", ">> Loaded {} instance saved gameobject state data in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
LOG_INFO("server.loading", " ");
}
uint8 ObjectMgr::GetInstanceSavedGameobjectState(uint32 id, uint32 guid)
{
for (auto it = GameobjectInstanceSavedStateList.begin(); it != GameobjectInstanceSavedStateList.end(); it++)
{
if (it->m_guid == guid && it->m_instance == id)
{
return it->m_state;
}
}
return 3; // Any state higher than 2 to get the default state
}
bool ObjectMgr::FindInstanceSavedGameobjectState(uint32 id, uint32 guid)
{
for (auto it = GameobjectInstanceSavedStateList.begin(); it != GameobjectInstanceSavedStateList.end(); it++)
{
if (it->m_guid == guid && it->m_instance == id)
{
return true;
}
}
return false;
}
void ObjectMgr::SetInstanceSavedGameobjectState(uint32 id, uint32 guid, uint8 state)
{
for (auto it = GameobjectInstanceSavedStateList.begin(); it != GameobjectInstanceSavedStateList.end(); it++)
{
if (it->m_guid == guid && it->m_instance == id)
{
it->m_state = state;
}
}
}
void ObjectMgr::NewInstanceSavedGameobjectState(uint32 id, uint32 guid, uint8 state)
{
GameobjectInstanceSavedStateList.push_back({ id, guid, state });
}
void ObjectMgr::SendServerMail(Player* player, uint32 id, uint32 reqLevel, uint32 reqPlayTime, uint32 rewardMoneyA, uint32 rewardMoneyH, uint32 rewardItemA, uint32 rewardItemCountA, uint32 rewardItemH, uint32 rewardItemCountH, std::string subject, std::string body, uint8 active) const
{
if (active)
-6
View File
@@ -1445,12 +1445,6 @@ public:
[[nodiscard]] uint32 GetQuestMoneyReward(uint8 level, uint32 questMoneyDifficulty) const;
void SendServerMail(Player* player, uint32 id, uint32 reqLevel, uint32 reqPlayTime, uint32 rewardMoneyA, uint32 rewardMoneyH, uint32 rewardItemA, uint32 rewardItemCountA, uint32 rewardItemH, uint32 rewardItemCountH, std::string subject, std::string body, uint8 active) const;
void LoadInstanceSavedGameobjectStateData();
bool FindInstanceSavedGameobjectState(uint32 id, uint32 guid);
uint8 GetInstanceSavedGameobjectState(uint32 id, uint32 guid);
void SetInstanceSavedGameobjectState(uint32 id, uint32 guid, uint8 state);
void NewInstanceSavedGameobjectState(uint32 id, uint32 guid, uint8 state);
private:
// first free id for selected id type
uint32 _auctionId; // pussywizard: accessed by a single thread
@@ -371,10 +371,6 @@ void WorldSession::HandleAuctionSellItem(WorldPacket& recvData)
item2->SetState(ITEM_CHANGED, _player);
_player->ItemRemovedQuestCheck(item2->GetEntry(), count[j]);
item2->SendUpdateToPlayer(_player);
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
item2->SaveToDB(trans);
CharacterDatabase.CommitTransaction(trans);
}
}
+4 -1
View File
@@ -188,7 +188,10 @@ void WorldSession::HandleLfgPlayerLockInfoRequestOpcode(WorldPacket& /*recvData*
uint8 playerLevel = GetPlayer() ? GetPlayer()->GetLevel() : 0;
data << uint8(done);
data << uint32(quest->GetRewOrReqMoney(playerLevel));
data << uint32(quest->XPValue(playerLevel));
if (!GetPlayer()->IsMaxLevel())
data << uint32(quest->XPValue(playerLevel));
else
data << uint32(0);
data << uint32(0);
data << uint32(0);
data << uint8(quest->GetRewItemsCount());
@@ -740,6 +740,26 @@ void InstanceScript::SendEncounterUnit(uint32 type, Unit* unit /*= nullptr*/, ui
instance->SendToPlayers(&data);
}
void InstanceScript::LoadInstanceSavedGameobjectStateData()
{
_objectStateMap.clear();
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SELECT_INSTANCE_SAVED_DATA);
stmt->SetData(0, instance->GetInstanceId());
if (PreparedQueryResult result = CharacterDatabase.Query(stmt))
{
Field* fields;
do
{
fields = result->Fetch();
StoreGameObjectState(fields[0].Get<uint32>(), fields[1].Get<uint8>());
} while (result->NextRow());
}
}
std::string InstanceScript::GetBossStateName(uint8 state)
{
// See enum EncounterState in InstanceScript.h
@@ -762,6 +782,18 @@ std::string InstanceScript::GetBossStateName(uint8 state)
}
}
uint8 InstanceScript::GetStoredGameObjectState(ObjectGuid::LowType spawnId) const
{
auto i = _objectStateMap.find(spawnId);
if (i != _objectStateMap.end())
{
return i->second;
}
return 3; // Any state higher than 2 to get the default state for the object we are loading.
}
bool InstanceHasScript(WorldObject const* obj, char const* scriptName)
{
if (InstanceMap* instance = obj->GetMap()->ToInstanceMap())
@@ -136,6 +136,7 @@ typedef std::pair<DoorInfoMap::const_iterator, DoorInfoMap::const_iterator> Door
typedef std::map<uint32 /*entry*/, MinionInfo> MinionInfoMap;
typedef std::map<uint32 /*type*/, ObjectGuid /*guid*/> ObjectGuidMap;
typedef std::map<uint32 /*entry*/, uint32 /*type*/> ObjectInfoMap;
typedef std::map<ObjectGuid::LowType /*spawnId*/, uint8 /*state*/> ObjectStateMap;
class InstanceScript : public ZoneScript
{
@@ -265,6 +266,12 @@ public:
// Allows executing code using all creatures registered in the instance script as minions
void DoForAllMinions(uint32 id, std::function<void(Creature*)> exec);
//
void StoreGameObjectState(ObjectGuid::LowType spawnId, uint8 state) { _objectStateMap[spawnId] = state; };
[[nodiscard]] uint8 GetStoredGameObjectState(ObjectGuid::LowType spawnId) const;
void LoadInstanceSavedGameobjectStateData();
TaskScheduler scheduler;
protected:
void SetHeaders(std::string const& dataHeaders);
@@ -311,6 +318,7 @@ private:
ObjectInfoMap _creatureInfo;
ObjectInfoMap _gameObjectInfo;
ObjectGuidMap _objectGuids;
ObjectStateMap _objectStateMap;
uint32 completedEncounters; // completed encounter mask, bit indexes are DungeonEncounter.dbc boss numbers, used for packets
std::unordered_set<uint32> _activatedAreaTriggers;
};
+2
View File
@@ -3096,6 +3096,8 @@ void InstanceMap::CreateInstanceScript(bool load, std::string data, uint32 compl
if (data != "")
instance_data->Load(data.c_str());
}
instance_data->LoadInstanceSavedGameobjectStateData();
}
/*
@@ -107,7 +107,7 @@ bool ChaseMovementGenerator<T>::DoUpdate(T* owner, uint32 time_diff)
if (i_recalculateTravel && PositionOkay(owner, target, _movingTowards ? maxTarget : Optional<float>(), angle))
{
if (owner->HasUnitState(UNIT_STATE_CHASE_MOVE) && !target->isMoving() && !mutualChase)
if ((owner->HasUnitState(UNIT_STATE_CHASE_MOVE) && !target->isMoving() && !mutualChase) || _range)
{
i_recalculateTravel = false;
i_path = nullptr;
@@ -134,11 +134,11 @@ bool ChaseMovementGenerator<T>::DoUpdate(T* owner, uint32 time_diff)
}
// if the target moved, we have to consider whether to adjust
if (!_lastTargetPosition || target->GetPosition() != _lastTargetPosition.value() || mutualChase != _mutualChase)
if (!_lastTargetPosition || target->GetPosition() != _lastTargetPosition.value() || mutualChase != _mutualChase || !owner->IsWithinLOSInMap(target))
{
_lastTargetPosition = target->GetPosition();
_mutualChase = mutualChase;
if (owner->HasUnitState(UNIT_STATE_CHASE_MOVE) || !PositionOkay(owner, target, target->isMoving() ? maxTarget : maxRange, angle))
if (owner->HasUnitState(UNIT_STATE_CHASE_MOVE) || !PositionOkay(owner, target, maxTarget, angle))
{
// can we get to the target?
if (cOwner && !target->isInAccessiblePlaceFor(cOwner))
@@ -150,10 +150,10 @@ bool ChaseMovementGenerator<T>::DoUpdate(T* owner, uint32 time_diff)
}
// figure out which way we want to move
float tarX, tarY, tarZ;
target->GetPosition(tarX, tarY, tarZ);
float x, y, z;
target->GetPosition(x, y, z);
bool withinRange = owner->IsInDist(target, maxRange);
bool withinLOS = owner->IsWithinLOS(tarX, tarY, tarZ);
bool withinLOS = owner->IsWithinLOS(x, y, z);
bool moveToward = !(withinRange && withinLOS);
// make a new path if we have to...
@@ -181,14 +181,12 @@ bool ChaseMovementGenerator<T>::DoUpdate(T* owner, uint32 time_diff)
additionalRange = owner->GetExactDistSq(target) < G3D::square(speed) ? 0 : speed;
}
float x, y, z;
bool shortenPath;
// if we want to move toward the target and there's no fixed angle...
if (moveToward && !angle)
{
// ...we'll pathfind to the center, then shorten the path
target->GetPosition(x, y, z);
shortenPath = true;
}
else
@@ -214,7 +212,7 @@ bool ChaseMovementGenerator<T>::DoUpdate(T* owner, uint32 time_diff)
}
if (shortenPath)
i_path->ShortenPathUntilDist(G3D::Vector3(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ()), maxTarget);
i_path->ShortenPathUntilDist(G3D::Vector3(x, y, z), maxTarget);
if (cOwner)
{
-4
View File
@@ -385,10 +385,6 @@ void Spell::EffectSchoolDMG(SpellEffIndex effIndex)
damage += damage / 4;
}
}
// Immolate - hidden delay for conflagrate
else if (m_spellInfo->SpellFamilyFlags[0] & 0x4)
{
}
// Conflagrate - consumes Immolate or Shadowflame
else if (m_spellInfo->TargetAuraState == AURA_STATE_CONFLAGRATE)
{
+1
View File
@@ -612,6 +612,7 @@ public:
virtual void SetRealmName(std::string name) = 0;
virtual void RemoveOldCorpses() = 0;
virtual SQLQueryHolderCallback& AddQueryHolderCallback(SQLQueryHolderCallback&& callback) = 0;
virtual void DoForAllOnlinePlayers(std::function<void(Player*)> exec) = 0;
};
#endif //AZEROTHCORE_IWORLD_H
+17 -3
View File
@@ -1615,9 +1615,6 @@ void World::SetInitialWorldSettings()
LOG_INFO("server.loading", "Loading Instance Template...");
sObjectMgr->LoadInstanceTemplate();
LOG_INFO("server.loading", "Loading Instance Saved Gameobject State Data...");
sObjectMgr->LoadInstanceSavedGameobjectStateData();
LOG_INFO("server.loading", "Loading Character Cache...");
sCharacterCache->LoadCharacterCacheStorage();
@@ -3273,6 +3270,23 @@ void World::RemoveOldCorpses()
_timers[WUPDATE_CORPSES].SetCurrent(_timers[WUPDATE_CORPSES].GetInterval());
}
void World::DoForAllOnlinePlayers(std::function<void(Player*)> exec)
{
std::shared_lock lock(*HashMapHolder<Player>::GetLock());
for (auto const& it : ObjectAccessor::GetPlayers())
{
if (Player* player = it.second)
{
if (!player->IsInWorld())
{
continue;
}
exec(player);
}
}
}
bool World::IsPvPRealm() const
{
return getIntConfig(CONFIG_GAME_TYPE) == REALM_TYPE_PVP || getIntConfig(CONFIG_GAME_TYPE) == REALM_TYPE_RPPVP || getIntConfig(CONFIG_GAME_TYPE) == REALM_TYPE_FFA_PVP;
+2
View File
@@ -352,6 +352,8 @@ public:
void RemoveOldCorpses() override;
void DoForAllOnlinePlayers(std::function<void(Player*)> exec) override;
protected:
void _UpdateGameTime();
// callback for UpdateRealmCharacters
+3 -4
View File
@@ -290,10 +290,9 @@ public:
stmt->SetData(0, uint16(atLogin));
CharacterDatabase.Execute(stmt);
std::shared_lock<std::shared_mutex> lock(*HashMapHolder<Player>::GetLock());
HashMapHolder<Player>::MapType const& plist = ObjectAccessor::GetPlayers();
for (auto itr = plist.begin(); itr != plist.end(); ++itr)
itr->second->SetAtLoginFlag(atLogin);
sWorld->DoForAllOnlinePlayers([&] (Player* player){
player->SetAtLoginFlag(atLogin);
});
return true;
}
@@ -23,13 +23,11 @@
#include "GridNotifiers.h"
#include "InstanceMapScript.h"
#include "InstanceScript.h"
#include "ObjectDefines.h"
#include "ObjectMgr.h"
#include "ScriptedCreature.h"
#include "SpellScript.h"
#include "SpellScriptLoader.h"
#include "blackrock_spire.h"
#include "GridNotifiersImpl.h"
uint32 const DragonspireMobs[3] = { NPC_BLACKHAND_DREADWEAVER, NPC_BLACKHAND_SUMMONER, NPC_BLACKHAND_VETERAN };
@@ -15,7 +15,6 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Containers.h"
#include "CreatureScript.h"
#include "ObjectAccessor.h"
#include "ScriptedCreature.h"
@@ -46,17 +46,17 @@ public:
case GO_DOOR_LEVER_2:
case GO_DOOR_LEVER_3:
case GO_CANNON:
gameobject->UpdateSaveToDb(true);
gameobject->AllowSaveToDB(true);
break;
case GO_FACTORY_DOOR:
gameobject->UpdateSaveToDb(true);
gameobject->AllowSaveToDB(true);
// GoState (Door opened) is restored during GO creation, but we need to set LootState to prevent Lever from closing it again
if (_encounters[TYPE_RHAHK_ZOR] == DONE)
gameobject->SetLootState(GO_ACTIVATED);
break;
case GO_IRON_CLAD_DOOR:
gameobject->UpdateSaveToDb(true);
if (gameobject->GetStateSavedOnInstance() == GO_STATE_ACTIVE)
gameobject->AllowSaveToDB(true);
if (GetStoredGameObjectState(gameobject->GetSpawnId()) == GO_STATE_ACTIVE)
{
gameobject->DespawnOrUnsummon();
}
@@ -62,7 +62,7 @@ public:
case GO_CAVE_IN_2:
case GO_WORKSHOP_DOOR:
case GO_FINAL_CHAMBER_DOOR:
gameobject->UpdateSaveToDb(true);
gameobject->AllowSaveToDB(true);
break;
}
}
@@ -18,7 +18,6 @@
#include "SpellInfo.h"
#include "SpellScript.h"
#include "SpellScriptLoader.h"
#include "Unit.h"
class spell_q12779_an_end_to_all_things : public SpellScript
{
@@ -538,7 +538,7 @@ public:
void DamageTaken(Unit* /*doneBy*/, uint32& damage, DamageEffectType, SpellSchoolMask) override
{
if (!canResurrectCheck && damage >= me->GetHealth())
if ((!canResurrectCheck || canResurrect) && damage >= me->GetHealth())
damage = me->GetHealth() - 1;
}
@@ -65,7 +65,7 @@ public:
GateKirtonosGUID = go->GetGUID();
break;
case GO_DOOR_OPENED_WITH_KEY:
go->UpdateSaveToDb(true);
go->AllowSaveToDB(true);
break;
case GO_GATE_GANDLING_DOWN_NORTH:
GandlingGatesGUID[0] = go->GetGUID();
@@ -178,69 +178,69 @@ public:
case GO_HALL_OF_HIGH_COMMAND:
case GO_GAUNTLET_DOOR_1:
case GO_GAUNTLET_DOOR_2:
go->UpdateSaveToDb(true);
go->AllowSaveToDB(true);
break;
case GO_ZIGGURAT_DOORS1:
go->UpdateSaveToDb(true);
go->AllowSaveToDB(true);
_zigguratDoorsGUID1 = go->GetGUID();
if (GetData(TYPE_ZIGGURAT1) >= 1)
go->SetGoState(GO_STATE_ACTIVE);
break;
case GO_ZIGGURAT_DOORS2:
go->UpdateSaveToDb(true);
go->AllowSaveToDB(true);
_zigguratDoorsGUID2 = go->GetGUID();
if (GetData(TYPE_ZIGGURAT2) >= 1)
go->SetGoState(GO_STATE_ACTIVE);
break;
case GO_ZIGGURAT_DOORS3:
go->UpdateSaveToDb(true);
go->AllowSaveToDB(true);
_zigguratDoorsGUID3 = go->GetGUID();
if (GetData(TYPE_ZIGGURAT3) >= 1)
go->SetGoState(GO_STATE_ACTIVE);
break;
case GO_GAUNTLET_GATE:
go->UpdateSaveToDb(true);
go->AllowSaveToDB(true);
_gauntletGateGUID = go->GetGUID();
if (_zigguratState1 == 2 && _zigguratState2 == 2 && _zigguratState3 == 2)
go->SetGoState(GO_STATE_ACTIVE);
break;
case GO_SLAUGTHER_GATE:
go->UpdateSaveToDb(true);
go->AllowSaveToDB(true);
_slaughterGateGUID = go->GetGUID();
if (_zigguratState1 == 2 && _zigguratState2 == 2 && _zigguratState3 == 2)
go->SetGoState(GO_STATE_ACTIVE);
break;
case GO_ZIGGURAT_DOORS4:
go->UpdateSaveToDb(true);
go->AllowSaveToDB(true);
_zigguratDoorsGUID4 = go->GetGUID();
if (_slaughterProgress == 4)
go->SetGoState(GO_STATE_ACTIVE);
break;
case GO_ZIGGURAT_DOORS5:
go->UpdateSaveToDb(true);
go->AllowSaveToDB(true);
_zigguratDoorsGUID5 = go->GetGUID();
if (_slaughterProgress == 4)
go->SetGoState(GO_STATE_ACTIVE);
break;
case GO_SLAUGHTER_GATE_SIDE:
go->UpdateSaveToDb(true);
go->AllowSaveToDB(true);
if (_slaughterProgress >= 2)
go->SetGoState(GO_STATE_ACTIVE);
break;
case GO_PORT_TRAP_GATE_1:
go->UpdateSaveToDb(true);
go->AllowSaveToDB(true);
_trapGatesGUIDs[0] = go->GetGUID();
break;
case GO_PORT_TRAP_GATE_2:
go->UpdateSaveToDb(true);
go->AllowSaveToDB(true);
_trapGatesGUIDs[1] = go->GetGUID();
break;
case GO_PORT_TRAP_GATE_3:
go->UpdateSaveToDb(true);
go->AllowSaveToDB(true);
_trapGatesGUIDs[2] = go->GetGUID();
break;
case GO_PORT_TRAP_GATE_4:
go->UpdateSaveToDb(true);
go->AllowSaveToDB(true);
_trapGatesGUIDs[3] = go->GetGUID();
break;
}
@@ -23,7 +23,6 @@
#include "SpellScript.h"
#include "SpellScriptLoader.h"
#include "TaskScheduler.h"
#include "WaypointMgr.h"
#include "zulgurub.h"
enum Says
@@ -20,7 +20,6 @@ Blasted_Lands
Quest support: 3628. Teleporter to Rise of the Defiler.
*/
#include "CreatureScript.h"
#include "Group.h"
#include "Player.h"
#include "ScriptedCreature.h"
@@ -18,7 +18,6 @@
#include "CreatureScript.h"
#include "Player.h"
#include "ScriptedCreature.h"
#include "ScriptedGossip.h"
/*######
## NPC 9836: Maredis Firestar
@@ -20,7 +20,6 @@
#include "Player.h"
#include "ScriptedCreature.h"
#include "ScriptedEscortAI.h"
#include "ScriptedGossip.h"
/*######
## npc_ranger_lilatha
+19 -38
View File
@@ -1014,44 +1014,29 @@ class spell_midsummer_fling_torch : public SpellScript
bool handled;
bool Load() override { handled = false; return true; }
SpellCastResult CheckCast()
{
GetCaster()->GetCreaturesWithEntryInRange(_crList, 100.0f, NPC_TORCH_TARGET);
if (_crList.empty())
{
return SPELL_FAILED_NOT_HERE;
}
return SPELL_CAST_OK;
}
void ThrowNextTorch(Unit* caster)
{
uint8 rand = urand(0, _crList.size() - 1);
Position pos;
pos.Relocate(0.0f, 0.0f, 0.0f);
for (std::list<Creature*>::const_iterator itr = _crList.begin(); itr != _crList.end(); ++itr, --rand)
{
if (caster->GetDistance(*itr) < 5)
{
if (!rand)
rand++;
continue;
}
Creature* bunny = caster->FindNearestCreature(NPC_TORCH_TARGET, 100.0f);
if (!rand)
{
pos.Relocate(*itr);
break;
}
}
if (!bunny)
return;
// we have any pos
if (pos.GetPositionX())
{
caster->CastSpell(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), SPELL_FLING_TORCH, true);
caster->CastSpell(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), SPELL_TORCH_SHADOW, true);
}
// targets are located on a circle with fixed radius around the target bunny
// first target is chosen randomly anywhere on the circle
// next target is chosen on the opposite half of the circle
// so a minimum flight duration of the torch is guaranteed
float angle = 0.0f;
if (GetSpellInfo()->Id == SPELL_FLING_TORCH_DUMMY)
angle = frand(-1.0f * M_PI, 1.0f * M_PI); // full circle
else
angle = frand(-0.5f * M_PI, 0.5f * M_PI); // half circle
Position pos = bunny->GetPosition();
pos.SetOrientation(caster->GetPosition().GetAbsoluteAngle(pos));
pos.RelocatePolarOffset(angle, 8.0f); // radius is sniffed value
caster->CastSpell(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), SPELL_FLING_TORCH, true);
caster->CastSpell(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), SPELL_TORCH_SHADOW, true);
}
void HandleFinish()
@@ -1108,15 +1093,11 @@ class spell_midsummer_fling_torch : public SpellScript
void Register() override
{
AfterCast += SpellCastFn(spell_midsummer_fling_torch::HandleFinish);
OnCheckCast += SpellCheckCastFn(spell_midsummer_fling_torch::CheckCast);
if (m_scriptSpellId == SPELL_JUGGLE_TORCH)
{
OnEffectHitTarget += SpellEffectFn(spell_midsummer_fling_torch::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
}
}
private:
std::list<Creature*> _crList;
};
enum eJuggle
@@ -59,19 +59,12 @@ enum Misc
struct boss_high_astromancer_solarian : public BossAI
{
boss_high_astromancer_solarian(Creature* creature) : BossAI(creature, DATA_ASTROMANCER)
{
scheduler.SetValidator([this]
{
return !me->HasUnitState(UNIT_STATE_CASTING);
});
}
boss_high_astromancer_solarian(Creature* creature) : BossAI(creature, DATA_ASTROMANCER) { }
void Reset() override
{
BossAI::Reset();
me->SetModelVisible(true);
_visible = true;
ScheduleHealthCheckEvent(20, [&]{
scheduler.CancelAll();
@@ -131,7 +124,6 @@ struct boss_high_astromancer_solarian : public BossAI
context.Repeat(33900ms, 48100ms);
}).Schedule(52100ms, [this](TaskContext context)
{
_visible = false;
me->SetReactState(REACT_PASSIVE);
me->SetModelVisible(false);
scheduler.DelayAll(21s);
@@ -158,7 +150,6 @@ struct boss_high_astromancer_solarian : public BossAI
});
}).Schedule(20s, [this](TaskContext)
{
_visible = true;
me->SetReactState(REACT_AGGRESSIVE);
Talk(SAY_SUMMON2);
summons.DoForAllSummons([&](WorldObject* summon)
@@ -204,7 +195,7 @@ struct boss_high_astromancer_solarian : public BossAI
scheduler.Update(diff);
if (_visible)
if (me->GetReactState() == REACT_AGGRESSIVE)
{
DoMeleeAttackIfReady();
}
@@ -223,76 +214,52 @@ struct boss_high_astromancer_solarian : public BossAI
{
return me->GetDistance2d(432.59f, -371.93f) > 105.0f;
}
private:
bool _visible;
};
class spell_astromancer_wrath_of_the_astromancer : public SpellScriptLoader
class spell_astromancer_wrath_of_the_astromancer : public AuraScript
{
public:
spell_astromancer_wrath_of_the_astromancer() : SpellScriptLoader("spell_astromancer_wrath_of_the_astromancer") { }
PrepareAuraScript(spell_astromancer_wrath_of_the_astromancer);
class spell_astromancer_wrath_of_the_astromancer_AuraScript : public AuraScript
void AfterRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
{
PrepareAuraScript(spell_astromancer_wrath_of_the_astromancer_AuraScript);
if (GetTargetApplication()->GetRemoveMode() != AURA_REMOVE_BY_EXPIRE)
return;
void AfterRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
{
if (GetTargetApplication()->GetRemoveMode() != AURA_REMOVE_BY_EXPIRE)
return;
Unit* target = GetUnitOwner();
target->CastSpell(target, GetSpellInfo()->Effects[EFFECT_1].CalcValue(), false);
}
Unit* target = GetUnitOwner();
target->CastSpell(target, GetSpellInfo()->Effects[EFFECT_1].CalcValue(), false);
}
void Register() override
{
AfterEffectRemove += AuraEffectRemoveFn(spell_astromancer_wrath_of_the_astromancer_AuraScript::AfterRemove, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL, AURA_EFFECT_HANDLE_REAL);
}
};
AuraScript* GetAuraScript() const override
void Register() override
{
return new spell_astromancer_wrath_of_the_astromancer_AuraScript();
AfterEffectRemove += AuraEffectRemoveFn(spell_astromancer_wrath_of_the_astromancer::AfterRemove, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL, AURA_EFFECT_HANDLE_REAL);
}
};
class spell_astromancer_solarian_transform : public SpellScriptLoader
class spell_astromancer_solarian_transform : public AuraScript
{
public:
spell_astromancer_solarian_transform() : SpellScriptLoader("spell_astromancer_solarian_transform") { }
PrepareAuraScript(spell_astromancer_solarian_transform);
class spell_astromancer_solarian_transform_AuraScript : public AuraScript
void OnApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
{
PrepareAuraScript(spell_astromancer_solarian_transform_AuraScript);
GetUnitOwner()->HandleStatModifier(UnitMods(UNIT_MOD_ARMOR), TOTAL_PCT, 400.0f, true);
}
void OnApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
{
GetUnitOwner()->HandleStatModifier(UnitMods(UNIT_MOD_ARMOR), TOTAL_PCT, 400.0f, true);
}
void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
{
GetUnitOwner()->HandleStatModifier(UnitMods(UNIT_MOD_ARMOR), TOTAL_PCT, 400.0f, false);
}
void Register() override
{
OnEffectApply += AuraEffectApplyFn(spell_astromancer_solarian_transform_AuraScript::OnApply, EFFECT_0, SPELL_AURA_TRANSFORM, AURA_EFFECT_HANDLE_REAL);
OnEffectRemove += AuraEffectRemoveFn(spell_astromancer_solarian_transform_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_TRANSFORM, AURA_EFFECT_HANDLE_REAL);
}
};
AuraScript* GetAuraScript() const override
void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
{
return new spell_astromancer_solarian_transform_AuraScript();
GetUnitOwner()->HandleStatModifier(UnitMods(UNIT_MOD_ARMOR), TOTAL_PCT, 400.0f, false);
}
void Register() override
{
OnEffectApply += AuraEffectApplyFn(spell_astromancer_solarian_transform::OnApply, EFFECT_0, SPELL_AURA_TRANSFORM, AURA_EFFECT_HANDLE_REAL);
OnEffectRemove += AuraEffectRemoveFn(spell_astromancer_solarian_transform::OnRemove, EFFECT_0, SPELL_AURA_TRANSFORM, AURA_EFFECT_HANDLE_REAL);
}
};
void AddSC_boss_high_astromancer_solarian()
{
RegisterTheEyeAI(boss_high_astromancer_solarian);
new spell_astromancer_wrath_of_the_astromancer();
new spell_astromancer_solarian_transform();
RegisterSpellScript(spell_astromancer_wrath_of_the_astromancer);
RegisterSpellScript(spell_astromancer_solarian_transform);
}
@@ -245,49 +245,6 @@ public:
}
};
/*######
## at_sholazar_waygate
######*/
enum Waygate
{
SPELL_SHOLAZAR_TO_UNGORO_TELEPORT = 52056,
SPELL_UNGORO_TO_SHOLAZAR_TELEPORT = 52057,
AT_SHOLAZAR = 5046,
AT_UNGORO = 5047,
QUEST_THE_MAKERS_OVERLOOK = 12613,
QUEST_THE_MAKERS_PERCH = 12559,
QUEST_MEETING_A_GREAT_ONE = 13956,
};
class AreaTrigger_at_sholazar_waygate : public AreaTriggerScript
{
public:
AreaTrigger_at_sholazar_waygate() : AreaTriggerScript("at_sholazar_waygate") { }
bool OnTrigger(Player* player, AreaTrigger const* trigger) override
{
if (!player->isDead() && (player->GetQuestStatus(QUEST_MEETING_A_GREAT_ONE) != QUEST_STATUS_NONE ||
(player->GetQuestStatus(QUEST_THE_MAKERS_OVERLOOK) == QUEST_STATUS_REWARDED && player->GetQuestStatus(QUEST_THE_MAKERS_PERCH) == QUEST_STATUS_REWARDED)))
{
switch (trigger->entry)
{
case AT_SHOLAZAR:
player->CastSpell(player, SPELL_SHOLAZAR_TO_UNGORO_TELEPORT, true);
break;
case AT_UNGORO:
player->CastSpell(player, SPELL_UNGORO_TO_SHOLAZAR_TELEPORT, true);
break;
}
}
return false;
}
};
/*######
## at_nats_landing
######*/
@@ -490,7 +447,6 @@ void AddSC_areatrigger_scripts()
new AreaTrigger_at_stormwright_shelf();
new AreaTrigger_at_scent_larkorwi();
new AreaTrigger_at_last_rites();
new AreaTrigger_at_sholazar_waygate();
new AreaTrigger_at_nats_landing();
new AreaTrigger_at_sentry_point();
new AreaTrigger_at_brewfest();
+1
View File
@@ -120,6 +120,7 @@ public:
MOCK_METHOD(std::string const&, GetRealmName, (), (const));
MOCK_METHOD(void, SetRealmName, (std::string name), ());
MOCK_METHOD(void, RemoveOldCorpses, ());
MOCK_METHOD(void, DoForAllOnlinePlayers, (std::function<void(Player*)> exec));
};
#pragma GCC diagnostic pop