Merge pull request #54 from liyunfan1223/Playerbot_sync

Playerbot sync
This commit is contained in:
Yunfan Li
2025-08-02 16:10:42 +08:00
committed by GitHub
71 changed files with 996 additions and 732 deletions
@@ -1767,6 +1767,16 @@ CleanCharacterDB = 0
PersistentCharacterCleanFlags = 0
#
# ValidateSkillLearnedBySpells
# Description: If enabled, players will lose spells that are invalid for their race/class.
# Default: 1 - (Enabled, enforce valid spells)
# 0 - (Disabled, allow invalid spells)
# Disabling this and then having your character learn spells which require DBC edits can result in the character not being saved in the database
# Disable AT YOUR OWN RISK
ValidateSkillLearnedBySpells = 1
#
###################################################################################################
@@ -3503,6 +3513,14 @@ Wintergrasp.CrashRestartTimer = 10
###################################################################################################
# BATTLEGROUND
#
# Battleground.PrepTime
# Description: Time (in seconds) for battleground preparation phase. Strand of the Ancients will be
# the exception and will always use the default 120 seconds timer, due to its boat timing mechanic.
# Default: 120
Battleground.PrepTime = 120
#
# Battleground.CastDeserter
# Description: Cast Deserter spell at players who leave battlegrounds in progress.
@@ -3768,6 +3786,13 @@ Battleground.EyeOfTheStorm.CapturePoints = 1600
###################################################################################################
# ARENA
#
# Arena.PrepTime
# Description: Time (in seconds) for arena preparation phase.
# Default: 60
Arena.PrepTime = 60
#
# Arena.MaxRatingDifference
# Description: Maximum rating difference between two teams in rated matches.
@@ -617,6 +617,10 @@ void CharacterDatabaseConnection::DoPrepareStatements()
"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);
// world_state
PrepareStatement(CHAR_SEL_WORLD_STATE, "SELECT Id, Data FROM world_state", CONNECTION_SYNCH);
PrepareStatement(CHAR_REP_WORLD_STATE, "REPLACE INTO world_state (Id, Data) VALUES(?, ?)", CONNECTION_ASYNC);
}
CharacterDatabaseConnection::CharacterDatabaseConnection(MySQLConnectionInfo& connInfo) : MySQLConnection(connInfo)
@@ -529,6 +529,9 @@ enum CharacterDatabaseStatements : uint32
CHAR_DELETE_INSTANCE_SAVED_DATA,
CHAR_SANITIZE_INSTANCE_SAVED_DATA,
CHAR_SEL_WORLD_STATE,
CHAR_REP_WORLD_STATE,
MAX_CHARACTERDATABASE_STATEMENTS
};
@@ -103,8 +103,7 @@ void LoginDatabaseConnection::DoPrepareStatements()
PrepareStatement(LOGIN_SEL_CHECK_PASSWORD, "SELECT salt, verifier FROM account WHERE id = ?", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_CHECK_PASSWORD_BY_NAME, "SELECT salt, verifier FROM account WHERE username = ?", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_ACCOUNT_FLAG, "SELECT Flags FROM account WHERE id = ?", CONNECTION_SYNCH);
PrepareStatement(LOGIN_UPD_ADD_ACCOUNT_FLAG, "UPDATE account SET Flags = Flags | ? WHERE id = ?", CONNECTION_ASYNC);
PrepareStatement(LOGIN_UPD_REMOVE_ACCOUNT_FLAG, "UPDATE account SET Flags = Flags &~ ? WHERE id = ?", CONNECTION_ASYNC);
PrepareStatement(LOGIN_UPD_SET_ACCOUNT_FLAG, "UPDATE account SET Flags = ? WHERE id = ?", CONNECTION_ASYNC);
PrepareStatement(LOGIN_SEL_PINFO, "SELECT a.username, aa.gmlevel, a.email, a.reg_mail, a.last_ip, DATE_FORMAT(a.last_login, '%Y-%m-%d %T'), a.mutetime, a.mutereason, a.muteby, a.failed_logins, a.locked, a.OS FROM account a LEFT JOIN account_access aa ON (a.id = aa.id AND (aa.RealmID = ? OR aa.RealmID = -1)) WHERE a.id = ?", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_PINFO_BANS, "SELECT unbandate, bandate = unbandate, bannedby, banreason FROM account_banned WHERE id = ? AND active ORDER BY bandate ASC LIMIT 1", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_GM_ACCOUNTS, "SELECT a.username, aa.gmlevel FROM account a, account_access aa WHERE a.id=aa.id AND aa.gmlevel >= ? AND (aa.realmid = -1 OR aa.realmid = ?)", CONNECTION_SYNCH);
@@ -87,8 +87,7 @@ enum LoginDatabaseStatements : uint32
LOGIN_SEL_CHECK_PASSWORD,
LOGIN_SEL_CHECK_PASSWORD_BY_NAME,
LOGIN_SEL_ACCOUNT_FLAG,
LOGIN_UPD_ADD_ACCOUNT_FLAG,
LOGIN_UPD_REMOVE_ACCOUNT_FLAG,
LOGIN_UPD_SET_ACCOUNT_FLAG,
LOGIN_SEL_PINFO,
LOGIN_SEL_PINFO_BANS,
LOGIN_SEL_GM_ACCOUNTS,
-33
View File
@@ -299,39 +299,6 @@ namespace AccountMgr
return false;
}
bool HasAccountFlag(uint32 accountId, uint32 flag)
{
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_FLAG);
stmt->SetData(0, accountId);
if (PreparedQueryResult result = LoginDatabase.Query(stmt))
{
uint32 flags = (*result)[0].Get<uint32>();
return (flags & flag) != 0;
}
return false;
}
void UpdateAccountFlag(uint32 accountId, uint32 flag, bool remove /*= false*/)
{
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(
remove ? LOGIN_UPD_REMOVE_ACCOUNT_FLAG : LOGIN_UPD_ADD_ACCOUNT_FLAG
);
stmt->SetData(0, flag);
stmt->SetData(1, accountId);
LoginDatabase.Execute(stmt);
}
void ValidateAccountFlags(uint32 accountId, uint32 flags, uint32 security)
{
bool hasGMFlag = (flags & ACCOUNT_FLAG_GM) != 0;
if (IsGMAccount(security) && !hasGMFlag)
UpdateAccountFlag(accountId, ACCOUNT_FLAG_GM);
else if (hasGMFlag && !IsGMAccount(security))
UpdateAccountFlag(accountId, ACCOUNT_FLAG_GM, true);
}
uint32 GetCharactersCount(uint32 accountId)
{
// check character count
-4
View File
@@ -56,10 +56,6 @@ namespace AccountMgr
bool IsGMAccount(uint32 gmlevel);
bool IsAdminAccount(uint32 gmlevel);
bool IsConsoleAccount(uint32 gmlevel);
bool HasAccountFlag(uint32 accountId, uint32 flag);
void UpdateAccountFlag(uint32 accountId, uint32 flag, bool remove = false);
void ValidateAccountFlags(uint32 accountId, uint32 flags, uint32 security);
};
#endif
@@ -155,7 +155,7 @@ void AuctionHouseMgr::SendAuctionWonMail(AuctionEntry* auction, CharacterDatabas
}
}
else
sAuctionMgr->RemoveAItem(auction->item_guid, true, &trans);
RemoveAItem(auction->item_guid, true, &trans);
}
void AuctionHouseMgr::SendAuctionSalePendingMail(AuctionEntry* auction, CharacterDatabaseTransaction trans, bool sendMail)
@@ -256,7 +256,7 @@ void AuctionHouseMgr::SendAuctionExpiredMail(AuctionEntry* auction, CharacterDat
}
}
else
sAuctionMgr->RemoveAItem(auction->item_guid, true, &trans);
RemoveAItem(auction->item_guid, true, &trans);
}
//this function sends mail to old bidder
+48 -8
View File
@@ -195,6 +195,8 @@ Battleground::Battleground()
m_HonorMode = BG_NORMAL;
m_SetupCompleted = false;
StartDelayTimes[BG_STARTING_EVENT_FIRST] = BG_START_DELAY_2M;
StartDelayTimes[BG_STARTING_EVENT_SECOND] = BG_START_DELAY_1M;
StartDelayTimes[BG_STARTING_EVENT_THIRD] = BG_START_DELAY_30S;
@@ -475,10 +477,8 @@ inline void Battleground::_ProcessJoin(uint32 diff)
itr->second->ResetAllPowers();
}
if (!(m_Events & BG_STARTING_EVENT_1))
if (!m_SetupCompleted)
{
m_Events |= BG_STARTING_EVENT_1;
if (!FindBgMap())
{
LOG_ERROR("bg.battleground", "Battleground::_ProcessJoin: map (map id: {}, instance id: {}) is not created!", m_MapId, m_InstanceID);
@@ -494,13 +494,52 @@ inline void Battleground::_ProcessJoin(uint32 diff)
}
StartingEventCloseDoors();
SetStartDelayTime(StartDelayTimes[BG_STARTING_EVENT_FIRST]);
// First start warning - 2 or 1 minute
// Get the configured prep time
uint32 configuredPrepTime;
// Special case for Strand of the Ancients - always use 120 seconds due to boat timing mechanics
if (GetBgTypeID() == BATTLEGROUND_SA)
configuredPrepTime = 120 * IN_MILLISECONDS;
else
configuredPrepTime = isArena() ?
sWorld->getIntConfig(CONFIG_ARENA_PREP_TIME) * IN_MILLISECONDS :
sWorld->getIntConfig(CONFIG_BATTLEGROUND_PREP_TIME) * IN_MILLISECONDS;
SetStartDelayTime(configuredPrepTime);
// Pre-mark events for announcements that should be skipped based on configured prep time
if (configuredPrepTime < StartDelayTimes[BG_STARTING_EVENT_FIRST])
{
// Skip first announcement (120s for BG, 60s for Arena)
m_Events |= BG_STARTING_EVENT_1;
if (configuredPrepTime < StartDelayTimes[BG_STARTING_EVENT_SECOND])
{
// Skip second announcement (60s for BG, 30s for Arena)
m_Events |= BG_STARTING_EVENT_2;
if (configuredPrepTime < StartDelayTimes[BG_STARTING_EVENT_THIRD])
{
// Skip third announcement (30s for BG, 15s for Arena)
m_Events |= BG_STARTING_EVENT_3;
}
}
}
// Mark setup as completed
m_SetupCompleted = true;
}
// First announcement at 120s or 60s (Depending on BG or Arena and configured time)
if (GetStartDelayTime() <= StartDelayTimes[BG_STARTING_EVENT_FIRST] && !(m_Events & BG_STARTING_EVENT_1))
{
m_Events |= BG_STARTING_EVENT_1;
if (StartMessageIds[BG_STARTING_EVENT_FIRST])
SendBroadcastText(StartMessageIds[BG_STARTING_EVENT_FIRST], CHAT_MSG_BG_SYSTEM_NEUTRAL);
}
// After 1 minute or 30 seconds, warning is signaled
// Second announcement at 60s or 30s
else if (GetStartDelayTime() <= StartDelayTimes[BG_STARTING_EVENT_SECOND] && !(m_Events & BG_STARTING_EVENT_2))
{
m_Events |= BG_STARTING_EVENT_2;
@@ -508,7 +547,7 @@ inline void Battleground::_ProcessJoin(uint32 diff)
if (StartMessageIds[BG_STARTING_EVENT_SECOND])
SendBroadcastText(StartMessageIds[BG_STARTING_EVENT_SECOND], CHAT_MSG_BG_SYSTEM_NEUTRAL);
}
// After 30 or 15 seconds, warning is signaled
// Third announcement at 30s or 15s
else if (GetStartDelayTime() <= StartDelayTimes[BG_STARTING_EVENT_THIRD] && !(m_Events & BG_STARTING_EVENT_3))
{
m_Events |= BG_STARTING_EVENT_3;
@@ -543,11 +582,12 @@ inline void Battleground::_ProcessJoin(uint32 diff)
break;
}
}
// Delay expired (after 2 or 1 minute)
// Delay expired (after configured prep time)
else if (GetStartDelayTime() <= 0 && !(m_Events & BG_STARTING_EVENT_4))
{
m_Events |= BG_STARTING_EVENT_4;
// Start the battle
StartingEventOpenDoors();
if (StartMessageIds[BG_STARTING_EVENT_FOURTH])
@@ -615,6 +615,9 @@ protected:
void _ProcessJoin(uint32 diff);
void _CheckSafePositions(uint32 diff);
// Setup completion marker
bool m_SetupCompleted;
// Scorekeeping
BattlegroundScoreMap PlayerScores; // Player scores
// must be implemented in BG subclass
@@ -296,7 +296,6 @@ Creature::Creature(bool isWorldObject): Unit(isWorldObject), MovableMapObject(),
ResetLootMode(); // restore default loot mode
TriggerJustRespawned = false;
m_isTempWorldObject = false;
_focusSpell = nullptr;
m_respawnedTime = time_t(0);
@@ -802,7 +801,7 @@ void Creature::Update(uint32 diff)
// Periodically check if able to move, if not, extend leash timer
if (diff >= m_extendLeashTime)
{
if (!CanFreeMove())
if (HasUnitState(UNIT_STATE_LOST_CONTROL))
UpdateLeashExtensionTime();
m_extendLeashTime = EXTEND_LEASH_CHECK_INTERVAL;
}
@@ -2685,14 +2684,13 @@ bool Creature::CanCreatureAttack(Unit const* victim, bool skipDistCheck) const
if (skipDistCheck)
return true;
float dist = sWorld->getFloatConfig(CONFIG_CREATURE_LEASH_RADIUS);
if (GetCharmerOrOwner())
if (Unit* unit = GetCharmerOrOwner())
{
dist = std::min<float>(GetMap()->GetVisibilityRange() + GetObjectSize() * 2, 150.0f);
return IsWithinDist(victim, dist);
float visibilityDist = std::min<float>(GetMap()->GetVisibilityRange() + GetObjectSize() * 2, DEFAULT_VISIBILITY_DISTANCE);
return victim->IsWithinDist(unit, visibilityDist);
}
float dist = sWorld->getFloatConfig(CONFIG_CREATURE_LEASH_RADIUS);
if (!dist)
return true;
@@ -376,8 +376,6 @@ public:
float m_SightDistance, m_CombatDistance;
bool m_isTempWorldObject; //true when possessed
// Handling caster facing during spellcast
void SetTarget(ObjectGuid guid = ObjectGuid::Empty) override;
void ClearTarget() { SetTarget(); };
+2 -21
View File
@@ -1072,25 +1072,6 @@ void WorldObject::Update(uint32 diff)
sScriptMgr->OnWorldObjectUpdate(this, diff);
}
void WorldObject::SetWorldObject(bool on)
{
if (!IsInWorld())
return;
GetMap()->AddObjectToSwitchList(this, on);
}
bool WorldObject::IsWorldObject() const
{
if (m_isWorldObject)
return true;
if (ToCreature() && ToCreature()->m_isTempWorldObject)
return true;
return false;
}
void WorldObject::setActive(bool on)
{
if (m_isActive == on)
@@ -1944,8 +1925,8 @@ bool WorldObject::CanDetectInvisibilityOf(WorldObject const* obj) const
bool isPermInvisibleCreature = false;
if (Creature const* baseObj = ToCreature())
{
auto auraEffects = baseObj->GetAuraEffectsByType(SPELL_AURA_MOD_INVISIBILITY);
for (auto const effect : auraEffects)
Unit::AuraEffectList const& auraEffects = baseObj->GetAuraEffectsByType(SPELL_AURA_MOD_INVISIBILITY);
for (AuraEffect* const effect : auraEffects)
{
if (SpellInfo const* spell = effect->GetSpellInfo())
{
+1 -3
View File
@@ -635,9 +635,7 @@ public:
[[nodiscard]] bool IsFarVisible() const { return m_isFarVisible; }
[[nodiscard]] bool IsVisibilityOverridden() const { return m_visibilityDistanceOverride.has_value(); }
void SetVisibilityDistanceOverride(VisibilityDistanceType type);
void SetWorldObject(bool apply);
[[nodiscard]] bool IsPermanentWorldObject() const { return m_isWorldObject; }
[[nodiscard]] bool IsWorldObject() const;
[[nodiscard]] bool IsWorldObject() const { return m_isWorldObject; }
[[nodiscard]] bool IsInWintergrasp() const
{
@@ -48,6 +48,8 @@
#define NOMINAL_MELEE_RANGE 5.0f
#define MELEE_RANGE (NOMINAL_MELEE_RANGE - MIN_MELEE_REACH * 2) //center to center for players
#define DEFAULT_COLLISION_HEIGHT 2.03128f // Most common value in dbc
#define LEEWAY_MIN_MOVE_SPEED 4.97f // NYI
#define LEEWAY_BONUS_RANGE 2.66f
// used for creating values for respawn for example
inline uint32 PAIR64_HIPART(uint64 x);
@@ -318,6 +318,8 @@ Player::Player(WorldSession* session): Unit(true), m_mover(this)
m_baseRatingValue[i] = 0;
m_baseSpellPower = 0;
m_baseSpellDamage = 0;
m_baseSpellHealing = 0;
m_baseFeralAP = 0;
m_baseManaRegen = 0;
m_baseHealthRegen = 0;
@@ -3103,6 +3105,9 @@ bool Player::addSpell(uint32 spellId, uint8 addSpecMask, bool updateActive, bool
bool Player::CheckSkillLearnedBySpell(uint32 spellId)
{
if (!sWorld->getBoolConfig(CONFIG_VALIDATE_SKILL_LEARNED_BY_SPELLS))
return true;
SkillLineAbilityMapBounds skill_bounds = sSpellMgr->GetSkillLineAbilityMapBounds(spellId);
uint32 errorSkill = 0;
for (SkillLineAbilityMap::const_iterator sla = skill_bounds.first; sla != skill_bounds.second; ++sla)
@@ -6844,7 +6849,10 @@ void Player::_ApplyItemBonuses(ItemTemplate const* proto, uint8 slot, bool apply
break;
/// @deprecated item mods
case ITEM_MOD_SPELL_HEALING_DONE:
ApplySpellHealingBonus(int32(val), apply);
break;
case ITEM_MOD_SPELL_DAMAGE_DONE:
ApplySpellDamageBonus(int32(val), apply);
break;
}
}
+6
View File
@@ -1954,6 +1954,8 @@ public:
void UpdateAttackPowerAndDamage(bool ranged = false) override;
void UpdateShieldBlockValue();
void ApplySpellPowerBonus(int32 amount, bool apply);
void ApplySpellDamageBonus(int32 amount, bool apply);
void ApplySpellHealingBonus(int32 amount, bool apply);
void UpdateSpellDamageAndHealingBonus();
void ApplyRatingMod(CombatRating cr, int32 value, bool apply);
void UpdateRating(CombatRating cr);
@@ -1972,6 +1974,8 @@ public:
[[nodiscard]] float GetRatingMultiplier(CombatRating cr) const;
[[nodiscard]] float GetRatingBonusValue(CombatRating cr) const;
uint32 GetBaseSpellPowerBonus() { return m_baseSpellPower; }
uint32 GetBaseSpellDamageBonus() { return m_baseSpellDamage; }
uint32 GetBaseSpellHealingBonus() { return m_baseSpellHealing; }
[[nodiscard]] int32 GetSpellPenetrationItemMod() const { return m_spellPenetrationItemMod; }
[[nodiscard]] float GetExpertiseDodgeOrParryReduction(WeaponAttackType attType) const;
@@ -2831,6 +2835,8 @@ protected:
float m_auraBaseMod[BASEMOD_END][MOD_END];
int32 m_baseRatingValue[MAX_COMBAT_RATING];
uint32 m_baseSpellPower;
uint32 m_baseSpellDamage;
uint32 m_baseSpellHealing;
uint32 m_baseFeralAP;
uint32 m_baseManaRegen;
uint32 m_baseHealthRegen;
@@ -1272,7 +1272,7 @@ bool Player::SatisfyQuestDay(Quest const* qInfo, bool msg) const
if (qInfo->IsDFQuest())
{
if (!m_DFQuests.empty())
if (m_DFQuests.find(qInfo->GetQuestId()) != m_DFQuests.end())
return false;
return true;
@@ -4616,8 +4616,15 @@ void Player::ApplyEnchantment(Item* item, EnchantmentSlot slot, bool apply, bool
HandleBaseModValue(SHIELD_BLOCK_VALUE, FLAT_MOD, float(enchant_amount), apply);
LOG_DEBUG("entities.player.items", "+ {} BLOCK_VALUE", enchant_amount);
break;
case ITEM_MOD_SPELL_HEALING_DONE: // deprecated
case ITEM_MOD_SPELL_DAMAGE_DONE: // deprecated
/// @deprecated item mods
case ITEM_MOD_SPELL_HEALING_DONE:
ApplySpellHealingBonus(enchant_amount, apply);
LOG_DEBUG("entities.player.items", "+ {} SPELL_HEALING", enchant_amount);
break;
case ITEM_MOD_SPELL_DAMAGE_DONE:
ApplySpellDamageBonus(enchant_amount, apply);
LOG_DEBUG("entities.player.items", "+ {} SPELL_DAMAGE", enchant_amount);
break;
default:
break;
}
@@ -181,8 +181,8 @@ void Player::Update(uint32 p_time)
m_swingErrorMsg = 1;
}
}
// 120 degrees of radiant range
else if (!HasInArc(2 * M_PI / 3, victim))
// 120 degrees of radiant range, if player is not in boundary radius
else if (!IsWithinBoundaryRadius(victim) && !HasInArc(2 * float(M_PI) / 3, victim))
{
setAttackTimer(BASE_ATTACK, 100);
if (m_swingErrorMsg != 2) // send single time (client auto repeat)
@@ -211,8 +211,8 @@ void Player::Update(uint32 p_time)
{
if (!IsWithinMeleeRange(victim))
setAttackTimer(OFF_ATTACK, 100);
else if (!HasInArc(2 * M_PI / 3, victim))
setAttackTimer(OFF_ATTACK, 100);
else if (!IsWithinBoundaryRadius(victim) && !HasInArc(2 * float(M_PI) / 3, victim))
setAttackTimer(BASE_ATTACK, 100);
else
{
// prevent base and off attack in same time, delay attack at
@@ -174,6 +174,23 @@ void Player::ApplySpellPowerBonus(int32 amount, bool apply)
ApplyModInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + i, amount, apply);
}
void Player::ApplySpellDamageBonus(int32 amount, bool apply)
{
apply = _ModifyUInt32(apply, m_baseSpellDamage, amount);
// For speed just update for client
for (int i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i)
ApplyModInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + i, amount, apply);
}
void Player::ApplySpellHealingBonus(int32 amount, bool apply)
{
apply = _ModifyUInt32(apply, m_baseSpellHealing, amount);
// For speed just update for client
ApplyModUInt32Value(PLAYER_FIELD_MOD_HEALING_DONE_POS, amount, apply);
}
void Player::UpdateSpellDamageAndHealingBonus()
{
// Magic damage modifiers implemented in Unit::SpellDamageBonusDone
+54 -12
View File
@@ -674,6 +674,9 @@ bool Unit::IsWithinMeleeRange(Unit const* obj, float dist) const
float maxdist = dist + GetMeleeRange(obj);
if ((IsPlayer() || obj->IsPlayer()) && HasLeewayMovement() && obj->HasLeewayMovement())
maxdist += LEEWAY_BONUS_RANGE;
return distsq < maxdist * maxdist;
}
@@ -698,6 +701,16 @@ bool Unit::IsWithinRange(Unit const* obj, float dist) const
return distsq <= dist * dist;
}
bool Unit::IsWithinBoundaryRadius(const Unit* obj) const
{
if (!obj || !IsInMap(obj) || !InSamePhase(obj))
return false;
float objBoundaryRadius = std::max(obj->GetBoundaryRadius(), MIN_MELEE_REACH);
return IsInDist(obj, objBoundaryRadius);
}
bool Unit::GetRandomContactPoint(Unit const* obj, float& x, float& y, float& z, bool force) const
{
float combat_reach = GetCombatReach();
@@ -2256,7 +2269,7 @@ void Unit::CalcAbsorbResist(DamageInfo& dmgInfo, bool Splited)
// We're going to call functions which can modify content of the list during iteration over it's elements
// Let's copy the list so we can prevent iterator invalidation
AuraEffectList vSchoolAbsorbCopy(victim->GetAuraEffectsByType(SPELL_AURA_SCHOOL_ABSORB));
vSchoolAbsorbCopy.sort(Acore::AbsorbAuraOrderPred());
std::sort(vSchoolAbsorbCopy.begin(), vSchoolAbsorbCopy.end(), Acore::AbsorbAuraOrderPred());
// absorb without mana cost
for (AuraEffectList::iterator itr = vSchoolAbsorbCopy.begin(); (itr != vSchoolAbsorbCopy.end()) && (dmgInfo.GetDamage() > 0); ++itr)
@@ -2439,7 +2452,7 @@ void Unit::CalcAbsorbResist(DamageInfo& dmgInfo, bool Splited)
// We're going to call functions which can modify content of the list during iteration over it's elements
// Let's copy the list so we can prevent iterator invalidation
AuraEffectList vSplitDamagePctCopy(victim->GetAuraEffectsByType(SPELL_AURA_SPLIT_DAMAGE_PCT));
for (AuraEffectList::iterator itr = vSplitDamagePctCopy.begin(), next; (itr != vSplitDamagePctCopy.end()) && (dmgInfo.GetDamage() > 0); ++itr)
for (AuraEffectList::iterator itr = vSplitDamagePctCopy.begin(); (itr != vSplitDamagePctCopy.end()) && (dmgInfo.GetDamage() > 0); ++itr)
{
// Check if aura was removed during iteration - we don't need to work on such auras
AuraApplication const* aurApp = (*itr)->GetBase()->GetApplicationOfTarget(victim->GetGUID());
@@ -2565,7 +2578,7 @@ void Unit::CalcHealAbsorb(HealInfo& healInfo)
{
uint32 removedAuras = healInfo.GetTarget()->m_removedAurasCount;
auraEff->GetBase()->Remove(AURA_REMOVE_BY_ENEMY_SPELL);
if (removedAuras + 1 < healInfo.GetTarget()->m_removedAurasCount)
if (healInfo.GetTarget()->m_removedAurasCount > removedAuras)
i = vHealAbsorb.begin();
}
}
@@ -4175,6 +4188,15 @@ void Unit::InterruptNonMeleeSpells(bool withDelayed, uint32 spell_id, bool withI
InterruptSpell(CURRENT_CHANNELED_SPELL, true, true, bySelf);
}
Spell* Unit::GetFirstCurrentCastingSpell() const
{
for (uint32 i = 0; i < CURRENT_MAX_SPELL; i++)
if (m_currentSpells[i] && m_currentSpells[i]->GetCastTimeRemaining() > 0)
return m_currentSpells[i];
return nullptr;
}
Spell* Unit::FindCurrentSpellBySpellId(uint32 spell_id) const
{
for (uint32 i = 0; i < CURRENT_MAX_SPELL; i++)
@@ -4732,7 +4754,7 @@ void Unit::_RegisterAuraEffect(AuraEffect* aurEff, bool apply)
if (apply)
m_modAuras[aurEff->GetAuraType()].push_back(aurEff);
else
m_modAuras[aurEff->GetAuraType()].remove(aurEff);
m_modAuras[aurEff->GetAuraType()].erase(std::remove(m_modAuras[aurEff->GetAuraType()].begin(), m_modAuras[aurEff->GetAuraType()].end(), aurEff), m_modAuras[aurEff->GetAuraType()].end());
}
// All aura base removes should go threw this function!
@@ -5158,7 +5180,7 @@ void Unit::RemoveAurasByType(AuraType auraType, ObjectGuid casterGUID, Aura* exc
{
uint32 removedAuras = m_removedAurasCount;
RemoveAura(aurApp);
if (m_removedAurasCount > removedAuras + 1)
if (m_removedAurasCount > removedAuras)
iter = m_modAuras[auraType].begin();
}
}
@@ -10419,8 +10441,29 @@ bool Unit::Attack(Unit* victim, bool meleeAttack)
if (meleeAttack)
AddUnitState(UNIT_STATE_MELEE_ATTACKING);
Unit* owner = GetCharmerOrOwner();
Creature* ownerCreature = owner ? owner->ToCreature() : nullptr;
Creature* controlledCreatureWithSameVictim = nullptr;
if (creature && !m_Controlled.empty())
{
for (ControlSet::iterator itr = m_Controlled.begin(); itr != m_Controlled.end(); ++itr)
{
if ((*itr)->ToCreature() && (*itr)->GetVictim() == victim)
{
controlledCreatureWithSameVictim = (*itr)->ToCreature();
break;
}
}
}
// Share leash timer with controlled unit
if (controlledCreatureWithSameVictim)
creature->SetLastLeashExtensionTimePtr(controlledCreatureWithSameVictim->GetLastLeashExtensionTimePtr());
// Share leash timer with owner
else if (creature && ownerCreature && ownerCreature->GetVictim() == victim)
creature->SetLastLeashExtensionTimePtr(ownerCreature->GetLastLeashExtensionTimePtr());
// Update leash timer when attacking creatures
if (victim->IsCreature())
else if (victim->IsCreature())
victim->ToCreature()->UpdateLeashExtensionTime();
// set position before any AI calls/assistance
@@ -11229,10 +11272,8 @@ Unit* Unit::GetNextRandomRaidMemberOrPet(float radius)
void Unit::AddPlayerToVision(Player* player)
{
if (m_sharedVision.empty())
{
setActive(true);
SetWorldObject(true);
}
m_sharedVision.push_back(player);
player->m_isInSharedVisionOf.insert(this);
}
@@ -11242,11 +11283,10 @@ void Unit::RemovePlayerFromVision(Player* player)
{
m_sharedVision.remove(player);
player->m_isInSharedVisionOf.erase(this);
/// @todo: This isn't right, if a previously active object was set to active with e.g. Mind Vision this will make them no longer active
if (m_sharedVision.empty())
{
setActive(false);
SetWorldObject(false);
}
}
void Unit::RemoveBindSightAuras()
@@ -12011,6 +12051,7 @@ int32 Unit::SpellBaseDamageBonusDone(SpellSchoolMask schoolMask)
{
// Base value
DoneAdvertisedBenefit += ToPlayer()->GetBaseSpellPowerBonus();
DoneAdvertisedBenefit += ToPlayer()->GetBaseSpellDamageBonus();
// Damage bonus from stats
AuraEffectList const& mDamageDoneOfStatPercent = GetAuraEffectsByType(SPELL_AURA_MOD_SPELL_DAMAGE_OF_STAT_PERCENT);
@@ -12773,6 +12814,7 @@ int32 Unit::SpellBaseHealingBonusDone(SpellSchoolMask schoolMask)
{
// Base value
AdvertisedBenefit += ToPlayer()->GetBaseSpellPowerBonus();
AdvertisedBenefit += ToPlayer()->GetBaseSpellHealingBonus();
// Healing bonus from stats
AuraEffectList const& mHealingDoneOfStatPercent = GetAuraEffectsByType(SPELL_AURA_MOD_SPELL_HEALING_OF_STAT_PERCENT);
+10 -1
View File
@@ -633,7 +633,7 @@ public:
typedef std::multimap<AuraStateType, AuraApplication*> AuraStateAurasMap;
typedef std::pair<AuraStateAurasMap::const_iterator, AuraStateAurasMap::const_iterator> AuraStateAurasMapBounds;
typedef std::list<AuraEffect*> AuraEffectList;
typedef std::vector<AuraEffect*> AuraEffectList;
typedef std::list<Aura*> AuraList;
typedef std::list<AuraApplication*> AuraApplicationList;
typedef std::list<DiminishingReturn> Diminishing;
@@ -818,9 +818,11 @@ public:
bool _IsValidAssistTarget(Unit const* target, SpellInfo const* bySpell) const;
// Combat range
[[nodiscard]] float GetBoundaryRadius() const { return m_floatValues[UNIT_FIELD_BOUNDINGRADIUS]; }
[[nodiscard]] float GetCombatReach() const override { return m_floatValues[UNIT_FIELD_COMBATREACH]; }
[[nodiscard]] float GetMeleeReach() const { float reach = m_floatValues[UNIT_FIELD_COMBATREACH]; return reach > MIN_MELEE_REACH ? reach : MIN_MELEE_REACH; }
[[nodiscard]] bool IsWithinRange(Unit const* obj, float dist) const;
bool IsWithinBoundaryRadius(const Unit* obj) const;
bool IsWithinCombatRange(Unit const* obj, float dist2compare) const;
bool IsWithinMeleeRange(Unit const* obj, float dist = 0.f) const;
float GetMeleeRange(Unit const* target) const;
@@ -1492,6 +1494,7 @@ public:
[[nodiscard]] Player* GetSpellModOwner() const;
[[nodiscard]] Spell* GetCurrentSpell(CurrentSpellTypes spellType) const { return m_currentSpells[spellType]; }
[[nodiscard]] Spell* GetCurrentSpell(uint32 spellType) const { return m_currentSpells[spellType]; }
[[nodiscard]] Spell* GetFirstCurrentCastingSpell() const;
[[nodiscard]] Spell* FindCurrentSpellBySpellId(uint32 spell_id) const;
[[nodiscard]] int32 GetCurrentSpellCastTime(uint32 spell_id) const;
@@ -1636,6 +1639,12 @@ public:
UNIT_STATE_ROOT | UNIT_STATE_STUNNED | UNIT_STATE_DISTRACTED) && !GetOwnerGUID();
}
[[nodiscard]] bool HasLeewayMovement() const
{
return m_movementInfo.HasMovementFlag(MOVEMENTFLAG_FORWARD | MOVEMENTFLAG_STRAFE_LEFT | MOVEMENTFLAG_STRAFE_RIGHT | MOVEMENTFLAG_FALLING)
&& !IsWalking();
}
void KnockbackFrom(float x, float y, float speedXY, float speedZ);
void JumpTo(float speedXY, float speedZ, bool forward = true);
void JumpTo(WorldObject* obj, float speedZ);
+1 -1
View File
@@ -1934,7 +1934,7 @@ void GameEventMgr::SetHolidayEventTime(GameEventData& event)
uint32 GameEventMgr::GetHolidayEventId(uint32 holidayId) const
{
auto const& events = sGameEventMgr->GetEventMap();
auto const& events = GetEventMap();
for (auto const& eventEntry : events)
{
+115 -113
View File
@@ -871,7 +871,7 @@ void ObjectMgr::LoadCreatureTemplateAddons()
uint32 entry = fields[0].Get<uint32>();
if (!sObjectMgr->GetCreatureTemplate(entry))
if (!GetCreatureTemplate(entry))
{
LOG_ERROR("sql.sql", "Creature template (Entry: {}) does not exist but has a record in `creature_template_addon`", entry);
continue;
@@ -1501,7 +1501,7 @@ void ObjectMgr::LoadEquipmentTemplates()
uint32 entry = fields[0].Get<uint32>();
if (!sObjectMgr->GetCreatureTemplate(entry))
if (!GetCreatureTemplate(entry))
{
LOG_ERROR("sql.sql", "Creature template (CreatureID: {}) does not exist but has a record in `creature_equip_template`", entry);
continue;
@@ -2455,7 +2455,7 @@ void ObjectMgr::LoadCreatureSparring()
ObjectGuid::LowType spawnId = fields[0].Get<uint32>();
float sparringHealthPct = fields[1].Get<float>();
if (!sObjectMgr->GetCreatureData(spawnId))
if (!GetCreatureData(spawnId))
{
LOG_ERROR("sql.sql", "Entry {} has a record in `creature_sparring` but doesn't exist in `creatures` table");
continue;
@@ -2535,7 +2535,7 @@ ObjectGuid::LowType ObjectMgr::AddGOData(uint32 entry, uint32 mapId, float x, fl
// We use spawn coords to spawn
if (!map->Instanceable() && map->IsGridLoaded(x, y))
{
GameObject* go = sObjectMgr->IsGameObjectStaticTransport(data.id) ? new StaticTransport() : new GameObject();
GameObject* go = IsGameObjectStaticTransport(data.id) ? new StaticTransport() : new GameObject();
if (!go->LoadGameObjectFromDB(spawnId, map))
{
LOG_ERROR("sql.sql", "AddGOData: cannot add gameobject entry {} to map", entry);
@@ -2862,35 +2862,35 @@ void ObjectMgr::LoadItemTemplates()
// 0 1 2 3 4 5 6 7 8 9 10 11 12
QueryResult result = WorldDatabase.Query("SELECT entry, class, subclass, SoundOverrideSubclass, name, displayid, Quality, Flags, FlagsExtra, BuyCount, BuyPrice, SellPrice, InventoryType, "
// 13 14 15 16 17 18 19 20
// 13 14 15 16 17 18 19 20
"AllowableClass, AllowableRace, ItemLevel, RequiredLevel, RequiredSkill, RequiredSkillRank, requiredspell, requiredhonorrank, "
// 21 22 23 24 25 26 27 28
"RequiredCityRank, RequiredReputationFaction, RequiredReputationRank, maxcount, stackable, ContainerSlots, StatsCount, stat_type1, "
// 29 30 31 32 33 34 35 36 37 38
// 21 22 23 24 25 26 27
"RequiredCityRank, RequiredReputationFaction, RequiredReputationRank, maxcount, stackable, ContainerSlots, stat_type1, "
// 28 29 30 31 32 33 34 35 36 37
"stat_value1, stat_type2, stat_value2, stat_type3, stat_value3, stat_type4, stat_value4, stat_type5, stat_value5, stat_type6, "
// 39 40 41 42 43 44 45 46 47
// 38 39 40 41 42 43 44 45 46
"stat_value6, stat_type7, stat_value7, stat_type8, stat_value8, stat_type9, stat_value9, stat_type10, stat_value10, "
// 48 49 50 51 52 53 54 55 56 57 58
// 47 48 49 50 51 52 53 54 55 56 57
"ScalingStatDistribution, ScalingStatValue, dmg_min1, dmg_max1, dmg_type1, dmg_min2, dmg_max2, dmg_type2, armor, holy_res, fire_res, "
// 59 60 61 62 63 64 65 66 67 68
// 58 59 60 61 62 63 64 65 66 67
"nature_res, frost_res, shadow_res, arcane_res, delay, ammo_type, RangedModRange, spellid_1, spelltrigger_1, spellcharges_1, "
// 69 70 71 72 73 74 75
// 68 69 70 71 72 73 74
"spellppmRate_1, spellcooldown_1, spellcategory_1, spellcategorycooldown_1, spellid_2, spelltrigger_2, spellcharges_2, "
// 76 77 78 79 80 81 82
// 75 76 77 78 79 80 81
"spellppmRate_2, spellcooldown_2, spellcategory_2, spellcategorycooldown_2, spellid_3, spelltrigger_3, spellcharges_3, "
// 83 84 85 86 87 88 89
// 82 83 84 85 86 87 88
"spellppmRate_3, spellcooldown_3, spellcategory_3, spellcategorycooldown_3, spellid_4, spelltrigger_4, spellcharges_4, "
// 90 91 92 93 94 95 96
// 89 90 91 92 93 94 95
"spellppmRate_4, spellcooldown_4, spellcategory_4, spellcategorycooldown_4, spellid_5, spelltrigger_5, spellcharges_5, "
// 97 98 99 100 101 102 103 104 105
// 96 97 98 99 100 101 102 103 104
"spellppmRate_5, spellcooldown_5, spellcategory_5, spellcategorycooldown_5, bonding, description, PageText, LanguageID, PageMaterial, "
// 106 107 108 109 110 111 112 113 114 115 116 117
// 105 106 107 108 109 110 111 112 113 114 115 116
"startquest, lockid, Material, sheath, RandomProperty, RandomSuffix, block, itemset, MaxDurability, area, Map, BagFamily, "
// 118 119 120 121 122 123 124 125
// 117 118 119 120 121 122 123 124
"TotemCategory, socketColor_1, socketContent_1, socketColor_2, socketContent_2, socketColor_3, socketContent_3, socketBonus, "
// 126 127 128 129 130 131 132 133
// 125 126 127 128 129 130 131 132
"GemProperties, RequiredDisenchantSkill, ArmorDamageModifier, duration, ItemLimitCategory, HolidayId, ScriptName, DisenchantID, "
// 134 135 136
// 133 134 135 136
"FoodType, minMoneyLoot, maxMoneyLoot, flagsCustom FROM item_template");
if (!result)
@@ -2940,84 +2940,91 @@ void ObjectMgr::LoadItemTemplates()
itemTemplate.MaxCount = fields[24].Get<int32>();
itemTemplate.Stackable = fields[25].Get<int32>();
itemTemplate.ContainerSlots = uint32(fields[26].Get<uint8>());
itemTemplate.StatsCount = uint32(fields[27].Get<uint8>());
for (uint8 i = 0; i < itemTemplate.StatsCount; ++i)
uint8 statsCount = 0;
while (statsCount < MAX_ITEM_PROTO_STATS)
{
itemTemplate.ItemStat[i].ItemStatType = uint32(fields[28 + i * 2].Get<uint8>());
itemTemplate.ItemStat[i].ItemStatValue = fields[29 + i * 2].Get<int32>();
}
uint32 statType = uint32(fields[27 + statsCount * 2].Get<uint8>());
int32 statValue = fields[28 + statsCount * 2].Get<int32>();
if (statType == 0)
break;
itemTemplate.ScalingStatDistribution = uint32(fields[48].Get<uint16>());
itemTemplate.ScalingStatValue = fields[49].Get<int32>();
itemTemplate.ItemStat[statsCount].ItemStatType = statType;
itemTemplate.ItemStat[statsCount].ItemStatValue = statValue;
statsCount++;
}
itemTemplate.StatsCount = statsCount;
itemTemplate.ScalingStatDistribution = uint32(fields[47].Get<uint16>());
itemTemplate.ScalingStatValue = fields[48].Get<int32>();
for (uint8 i = 0; i < MAX_ITEM_PROTO_DAMAGES; ++i)
{
itemTemplate.Damage[i].DamageMin = fields[50 + i * 3].Get<float>();
itemTemplate.Damage[i].DamageMax = fields[51 + i * 3].Get<float>();
itemTemplate.Damage[i].DamageType = uint32(fields[52 + i * 3].Get<uint8>());
itemTemplate.Damage[i].DamageMin = fields[49 + i * 3].Get<float>();
itemTemplate.Damage[i].DamageMax = fields[50 + i * 3].Get<float>();
itemTemplate.Damage[i].DamageType = uint32(fields[51 + i * 3].Get<uint8>());
}
itemTemplate.Armor = fields[56].Get<uint32>();
itemTemplate.HolyRes = fields[57].Get<int32>();
itemTemplate.FireRes = fields[58].Get<int32>();
itemTemplate.NatureRes = fields[59].Get<int32>();
itemTemplate.FrostRes = fields[60].Get<int32>();
itemTemplate.ShadowRes = fields[61].Get<int32>();
itemTemplate.ArcaneRes = fields[62].Get<int32>();
itemTemplate.Delay = uint32(fields[63].Get<uint16>());
itemTemplate.AmmoType = uint32(fields[64].Get<uint8>());
itemTemplate.RangedModRange = fields[65].Get<float>();
itemTemplate.Armor = fields[55].Get<uint32>();
itemTemplate.HolyRes = fields[56].Get<int32>();
itemTemplate.FireRes = fields[57].Get<int32>();
itemTemplate.NatureRes = fields[58].Get<int32>();
itemTemplate.FrostRes = fields[59].Get<int32>();
itemTemplate.ShadowRes = fields[60].Get<int32>();
itemTemplate.ArcaneRes = fields[61].Get<int32>();
itemTemplate.Delay = uint32(fields[62].Get<uint16>());
itemTemplate.AmmoType = uint32(fields[63].Get<uint8>());
itemTemplate.RangedModRange = fields[64].Get<float>();
for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
{
itemTemplate.Spells[i].SpellId = fields[66 + i * 7 ].Get<int32>();
itemTemplate.Spells[i].SpellTrigger = uint32(fields[67 + i * 7].Get<uint8>());
itemTemplate.Spells[i].SpellCharges = int32(fields[68 + i * 7].Get<int16>());
itemTemplate.Spells[i].SpellPPMRate = fields[69 + i * 7].Get<float>();
itemTemplate.Spells[i].SpellCooldown = fields[70 + i * 7].Get<int32>();
itemTemplate.Spells[i].SpellCategory = uint32(fields[71 + i * 7].Get<uint16>());
itemTemplate.Spells[i].SpellCategoryCooldown = fields[72 + i * 7].Get<int32>();
itemTemplate.Spells[i].SpellId = fields[65 + i * 7 ].Get<int32>();
itemTemplate.Spells[i].SpellTrigger = uint32(fields[66 + i * 7].Get<uint8>());
itemTemplate.Spells[i].SpellCharges = int32(fields[67 + i * 7].Get<int16>());
itemTemplate.Spells[i].SpellPPMRate = fields[68 + i * 7].Get<float>();
itemTemplate.Spells[i].SpellCooldown = fields[69 + i * 7].Get<int32>();
itemTemplate.Spells[i].SpellCategory = uint32(fields[70 + i * 7].Get<uint16>());
itemTemplate.Spells[i].SpellCategoryCooldown = fields[71 + i * 7].Get<int32>();
}
itemTemplate.Bonding = uint32(fields[101].Get<uint8>());
itemTemplate.Description = fields[102].Get<std::string>();
itemTemplate.PageText = fields[103].Get<uint32>();
itemTemplate.LanguageID = uint32(fields[104].Get<uint8>());
itemTemplate.PageMaterial = uint32(fields[105].Get<uint8>());
itemTemplate.StartQuest = fields[106].Get<uint32>();
itemTemplate.LockID = fields[107].Get<uint32>();
itemTemplate.Material = int32(fields[108].Get<int8>());
itemTemplate.Sheath = uint32(fields[109].Get<uint8>());
itemTemplate.RandomProperty = fields[110].Get<int32>();
itemTemplate.RandomSuffix = fields[111].Get<int32>();
itemTemplate.Block = fields[112].Get<uint32>();
itemTemplate.ItemSet = fields[113].Get<uint32>();
itemTemplate.MaxDurability = uint32(fields[114].Get<uint16>());
itemTemplate.Area = fields[115].Get<uint32>();
itemTemplate.Map = uint32(fields[116].Get<uint16>());
itemTemplate.BagFamily = fields[117].Get<uint32>();
itemTemplate.TotemCategory = fields[118].Get<uint32>();
itemTemplate.Bonding = uint32(fields[100].Get<uint8>());
itemTemplate.Description = fields[101].Get<std::string>();
itemTemplate.PageText = fields[102].Get<uint32>();
itemTemplate.LanguageID = uint32(fields[103].Get<uint8>());
itemTemplate.PageMaterial = uint32(fields[104].Get<uint8>());
itemTemplate.StartQuest = fields[105].Get<uint32>();
itemTemplate.LockID = fields[106].Get<uint32>();
itemTemplate.Material = int32(fields[107].Get<int8>());
itemTemplate.Sheath = uint32(fields[108].Get<uint8>());
itemTemplate.RandomProperty = fields[109].Get<int32>();
itemTemplate.RandomSuffix = fields[110].Get<int32>();
itemTemplate.Block = fields[111].Get<uint32>();
itemTemplate.ItemSet = fields[112].Get<uint32>();
itemTemplate.MaxDurability = uint32(fields[113].Get<uint16>());
itemTemplate.Area = fields[114].Get<uint32>();
itemTemplate.Map = uint32(fields[115].Get<uint16>());
itemTemplate.BagFamily = fields[116].Get<uint32>();
itemTemplate.TotemCategory = fields[117].Get<uint32>();
for (uint8 i = 0; i < MAX_ITEM_PROTO_SOCKETS; ++i)
{
itemTemplate.Socket[i].Color = uint32(fields[119 + i * 2].Get<uint8>());
itemTemplate.Socket[i].Content = fields[120 + i * 2].Get<uint32>();
itemTemplate.Socket[i].Color = uint32(fields[118 + i * 2].Get<uint8>());
itemTemplate.Socket[i].Content = fields[119 + i * 2].Get<uint32>();
}
itemTemplate.socketBonus = fields[125].Get<uint32>();
itemTemplate.GemProperties = fields[126].Get<uint32>();
itemTemplate.RequiredDisenchantSkill = uint32(fields[127].Get<int16>());
itemTemplate.ArmorDamageModifier = fields[128].Get<float>();
itemTemplate.Duration = fields[129].Get<uint32>();
itemTemplate.ItemLimitCategory = uint32(fields[130].Get<int16>());
itemTemplate.HolidayId = fields[131].Get<uint32>();
itemTemplate.ScriptId = sObjectMgr->GetScriptId(fields[132].Get<std::string>());
itemTemplate.DisenchantID = fields[133].Get<uint32>();
itemTemplate.FoodType = uint32(fields[134].Get<uint8>());
itemTemplate.MinMoneyLoot = fields[135].Get<uint32>();
itemTemplate.MaxMoneyLoot = fields[136].Get<uint32>();
itemTemplate.FlagsCu = ItemFlagsCustom(fields[137].Get<uint32>());
itemTemplate.socketBonus = fields[124].Get<uint32>();
itemTemplate.GemProperties = fields[125].Get<uint32>();
itemTemplate.RequiredDisenchantSkill = uint32(fields[126].Get<int16>());
itemTemplate.ArmorDamageModifier = fields[127].Get<float>();
itemTemplate.Duration = fields[128].Get<uint32>();
itemTemplate.ItemLimitCategory = uint32(fields[129].Get<int16>());
itemTemplate.HolidayId = fields[130].Get<uint32>();
itemTemplate.ScriptId = GetScriptId(fields[131].Get<std::string>());
itemTemplate.DisenchantID = fields[132].Get<uint32>();
itemTemplate.FoodType = uint32(fields[133].Get<uint8>());
itemTemplate.MinMoneyLoot = fields[134].Get<uint32>();
itemTemplate.MaxMoneyLoot = fields[135].Get<uint32>();
itemTemplate.FlagsCu = ItemFlagsCustom(fields[136].Get<uint32>());
// Checks
ItemEntry const* dbcitem = sItemStore.LookupEntry(entry);
@@ -3171,12 +3178,6 @@ void ObjectMgr::LoadItemTemplates()
itemTemplate.ContainerSlots = MAX_BAG_SIZE;
}
if (itemTemplate.StatsCount > MAX_ITEM_PROTO_STATS)
{
LOG_ERROR("sql.sql", "Item (Entry: {}) has too large value in statscount ({}), replace by hardcoded limit ({}).", entry, itemTemplate.StatsCount, MAX_ITEM_PROTO_STATS);
itemTemplate.StatsCount = MAX_ITEM_PROTO_STATS;
}
for (uint8 j = 0; j < itemTemplate.StatsCount; ++j)
{
// for ItemStatValue != 0
@@ -3189,7 +3190,8 @@ void ObjectMgr::LoadItemTemplates()
switch (itemTemplate.ItemStat[j].ItemStatType)
{
case ITEM_MOD_SPELL_HEALING_DONE:
LOG_ERROR("sql.sql", "Item (Entry: {}) has deprecated stat_type{} ({})", entry, j + 1, itemTemplate.ItemStat[j].ItemStatType);
case ITEM_MOD_SPELL_DAMAGE_DONE:
LOG_WARN("sql.sql", "Item (Entry: {}) has deprecated stat_type{} ({})", entry, j + 1, itemTemplate.ItemStat[j].ItemStatType);
break;
default:
break;
@@ -3570,7 +3572,7 @@ void ObjectMgr::LoadItemSetNames()
{
uint32 entry = *itr;
// add data from item_template if available
pProto = sObjectMgr->GetItemTemplate(entry);
pProto = GetItemTemplate(entry);
if (pProto)
{
LOG_ERROR("sql.sql", "Item set part (Entry: {}) does not have entry in `item_set_names`, adding data from `item_template`.", entry);
@@ -3617,13 +3619,13 @@ void ObjectMgr::LoadVehicleTemplateAccessories()
uint8 uiSummonType = fields[4].Get<uint8>();
uint32 uiSummonTimer = fields[5].Get<uint32>();
if (!sObjectMgr->GetCreatureTemplate(uiEntry))
if (!GetCreatureTemplate(uiEntry))
{
LOG_ERROR("sql.sql", "Table `vehicle_template_accessory`: creature template entry {} does not exist.", uiEntry);
continue;
}
if (!sObjectMgr->GetCreatureTemplate(uiAccessory))
if (!GetCreatureTemplate(uiAccessory))
{
LOG_ERROR("sql.sql", "Table `vehicle_template_accessory`: Accessory {} does not exist.", uiAccessory);
continue;
@@ -3673,7 +3675,7 @@ void ObjectMgr::LoadVehicleAccessories()
uint8 uiSummonType = fields[4].Get<uint8>();
uint32 uiSummonTimer = fields[5].Get<uint32>();
if (!sObjectMgr->GetCreatureTemplate(uiAccessory))
if (!GetCreatureTemplate(uiAccessory))
{
LOG_ERROR("sql.sql", "Table `vehicle_accessory`: Accessory {} does not exist.", uiAccessory);
continue;
@@ -3765,7 +3767,7 @@ void ObjectMgr::LoadPetLevelInfo()
Field* fields = result->Fetch();
uint32 creature_id = fields[0].Get<uint32>();
if (!sObjectMgr->GetCreatureTemplate(creature_id))
if (!GetCreatureTemplate(creature_id))
{
LOG_ERROR("sql.sql", "Wrong creature id {} in `pet_levelstats` table, ignoring.", creature_id);
continue;
@@ -4998,7 +5000,7 @@ void ObjectMgr::LoadQuests()
if (qinfo->StartItem)
{
if (!sObjectMgr->GetItemTemplate(qinfo->StartItem))
if (!GetItemTemplate(qinfo->StartItem))
{
LOG_ERROR("sql.sql", "Quest {} has `StartItem` = {} but item with entry {} does not exist, quest can't be done.",
qinfo->GetQuestId(), qinfo->StartItem, qinfo->StartItem);
@@ -5049,7 +5051,7 @@ void ObjectMgr::LoadQuests()
qinfo->SetSpecialFlag(QUEST_SPECIAL_FLAGS_DELIVER);
if (!sObjectMgr->GetItemTemplate(id))
if (!GetItemTemplate(id))
{
LOG_ERROR("sql.sql", "Quest {} has `RequiredItemId{}` = {} but item with entry {} does not exist, quest can't be done.",
qinfo->GetQuestId(), j + 1, id, id);
@@ -5069,7 +5071,7 @@ void ObjectMgr::LoadQuests()
uint32 id = qinfo->ItemDrop[j];
if (id)
{
if (!sObjectMgr->GetItemTemplate(id))
if (!GetItemTemplate(id))
{
LOG_ERROR("sql.sql", "Quest {} has `ItemDrop{}` = {} but item with entry {} does not exist, quest can't be done.",
qinfo->GetQuestId(), j + 1, id, id);
@@ -5090,14 +5092,14 @@ void ObjectMgr::LoadQuests()
for (uint8 j = 0; j < QUEST_OBJECTIVES_COUNT; ++j)
{
int32 id = qinfo->RequiredNpcOrGo[j];
if (id < 0 && !sObjectMgr->GetGameObjectTemplate(-id))
if (id < 0 && !GetGameObjectTemplate(-id))
{
LOG_ERROR("sql.sql", "Quest {} has `RequiredNpcOrGo{}` = {} but gameobject {} does not exist, quest can't be done.",
qinfo->GetQuestId(), j + 1, id, uint32(-id));
qinfo->RequiredNpcOrGo[j] = 0; // quest can't be done for this requirement
}
if (id > 0 && !sObjectMgr->GetCreatureTemplate(id))
if (id > 0 && !GetCreatureTemplate(id))
{
LOG_ERROR("sql.sql", "Quest {} has `RequiredNpcOrGo{}` = {} but creature with entry {} does not exist, quest can't be done.",
qinfo->GetQuestId(), j + 1, id, uint32(id));
@@ -5130,7 +5132,7 @@ void ObjectMgr::LoadQuests()
uint32 id = qinfo->RewardChoiceItemId[j];
if (id)
{
if (!sObjectMgr->GetItemTemplate(id))
if (!GetItemTemplate(id))
{
LOG_ERROR("sql.sql", "Quest {} has `RewardChoiceItemId{}` = {} but item with entry {} does not exist, quest will not reward this item.",
qinfo->GetQuestId(), j + 1, id, id);
@@ -5176,7 +5178,7 @@ void ObjectMgr::LoadQuests()
uint32 id = qinfo->RewardItemId[j];
if (id)
{
if (!sObjectMgr->GetItemTemplate(id))
if (!GetItemTemplate(id))
{
LOG_ERROR("sql.sql", "Quest {} has `RewardItemId{}` = {} but item with entry {} does not exist, quest will not reward this item.",
qinfo->GetQuestId(), j + 1, id, id);
@@ -5760,7 +5762,7 @@ void ObjectMgr::LoadEventScripts()
std::set<uint32> evt_scripts;
// Load all possible script entries from gameobjects
GameObjectTemplateContainer const* gotc = sObjectMgr->GetGameObjectTemplates();
GameObjectTemplateContainer const* gotc = GetGameObjectTemplates();
for (GameObjectTemplateContainer::const_iterator itr = gotc->begin(); itr != gotc->end(); ++itr)
if (uint32 eventId = itr->second.GetEventScriptId())
evt_scripts.insert(eventId);
@@ -6069,7 +6071,7 @@ void ObjectMgr::LoadInstanceTemplate()
instanceTemplate.AllowMount = fields[3].Get<bool>();
instanceTemplate.Parent = uint32(fields[1].Get<uint16>());
instanceTemplate.ScriptId = sObjectMgr->GetScriptId(fields[2].Get<std::string>());
instanceTemplate.ScriptId = GetScriptId(fields[2].Get<std::string>());
_instanceTemplateStore[mapID] = instanceTemplate;
@@ -6515,14 +6517,14 @@ void ObjectMgr::LoadQuestGreetings()
switch (type)
{
case 0: // Creature
if (!sObjectMgr->GetCreatureTemplate(id))
if (!GetCreatureTemplate(id))
{
LOG_ERROR("sql.sql", "Table `quest_greeting`: creature template entry {} does not exist.", id);
continue;
}
break;
case 1: // GameObject
if (!sObjectMgr->GetGameObjectTemplate(id))
if (!GetGameObjectTemplate(id))
{
LOG_ERROR("sql.sql", "Table `quest_greeting`: gameobject template entry {} does not exist.", id);
continue;
@@ -6568,14 +6570,14 @@ void ObjectMgr::LoadQuestGreetingsLocales()
switch (type)
{
case 0: // Creature
if (!sObjectMgr->GetCreatureTemplate(id))
if (!GetCreatureTemplate(id))
{
LOG_ERROR("sql.sql", "Table `quest_greeting_locale`: creature template entry {} does not exist.", id);
continue;
}
break;
case 1: // GameObject
if (!sObjectMgr->GetGameObjectTemplate(id))
if (!GetGameObjectTemplate(id))
{
LOG_ERROR("sql.sql", "Table `quest_greeting_locale`: gameobject template entry {} does not exist.", id);
continue;
@@ -7577,7 +7579,7 @@ void ObjectMgr::LoadGameObjectTemplateAddons()
uint32 entry = fields[0].Get<uint32>();
GameObjectTemplate const* got = sObjectMgr->GetGameObjectTemplate(entry);
GameObjectTemplate const* got = GetGameObjectTemplate(entry);
if (!got)
{
LOG_ERROR("sql.sql",
@@ -8222,7 +8224,7 @@ void ObjectMgr::LoadNPCSpellClickSpells()
// all spellclick data loaded, now we check if there are creatures with NPC_FLAG_SPELLCLICK but with no data
// NOTE: It *CAN* be the other way around: no spellclick flag but with spellclick data, in case of creature-only vehicle accessories
CreatureTemplateContainer const* ctc = sObjectMgr->GetCreatureTemplates();
CreatureTemplateContainer const* ctc = GetCreatureTemplates();
for (CreatureTemplateContainer::const_iterator itr = ctc->begin(); itr != ctc->end(); ++itr)
{
if ((itr->second.npcflag & UNIT_NPC_FLAG_SPELLCLICK) && _spellClickInfoStore.find(itr->second.Entry) == _spellClickInfoStore.end())
@@ -8747,7 +8749,7 @@ void ObjectMgr::LoadGameObjectForQuests()
{
uint32 oldMSTime = getMSTime();
if (sObjectMgr->GetGameObjectTemplates()->empty())
if (GetGameObjectTemplates()->empty())
{
LOG_WARN("server.loading", ">> Loaded 0 GameObjects for quests");
LOG_INFO("server.loading", " ");
@@ -8757,7 +8759,7 @@ void ObjectMgr::LoadGameObjectForQuests()
uint32 count = 0;
// collect GO entries for GO that must activated
GameObjectTemplateContainer* gotc = const_cast<GameObjectTemplateContainer*>(sObjectMgr->GetGameObjectTemplates());
GameObjectTemplateContainer* gotc = const_cast<GameObjectTemplateContainer*>(GetGameObjectTemplates());
for (GameObjectTemplateContainer::iterator itr = gotc->begin(); itr != gotc->end(); ++itr)
{
itr->second.IsForQuests = false;
@@ -9666,7 +9668,7 @@ bool ObjectMgr::RemoveVendorItem(uint32 entry, uint32 item, bool persist /*= tru
bool ObjectMgr::IsVendorItemValid(uint32 vendor_entry, uint32 item_id, int32 maxcount, uint32 incrtime, uint32 ExtendedCost, Player* player, std::set<uint32>* /*skip_vendors*/, uint32 /*ORnpcflag*/) const
{
/*
CreatureTemplate const* cInfo = sObjectMgr->GetCreatureTemplate(vendor_entry);
CreatureTemplate const* cInfo = GetCreatureTemplate(vendor_entry);
if (!cInfo)
{
if (player)
@@ -10040,7 +10042,7 @@ void ObjectMgr::LoadCreatureClassLevelStats()
++count;
} while (result->NextRow());
CreatureTemplateContainer const* ctc = sObjectMgr->GetCreatureTemplates();
CreatureTemplateContainer const* ctc = GetCreatureTemplates();
for (CreatureTemplateContainer::const_iterator itr = ctc->begin(); itr != ctc->end(); ++itr)
{
for (uint16 lvl = itr->second.minlevel; lvl <= itr->second.maxlevel; ++lvl)
@@ -10148,9 +10150,9 @@ void ObjectMgr::LoadFactionChangeQuests()
uint32 alliance = fields[0].Get<uint32>();
uint32 horde = fields[1].Get<uint32>();
if (!sObjectMgr->GetQuestTemplate(alliance))
if (!GetQuestTemplate(alliance))
LOG_ERROR("sql.sql", "Quest {} (alliance_id) referenced in `player_factionchange_quests` does not exist, pair skipped!", alliance);
else if (!sObjectMgr->GetQuestTemplate(horde))
else if (!GetQuestTemplate(horde))
LOG_ERROR("sql.sql", "Quest {} (horde_id) referenced in `player_factionchange_quests` does not exist, pair skipped!", horde);
else
FactionChangeQuests[alliance] = horde;
+1 -1
View File
@@ -52,7 +52,7 @@ class ObjectGuid;
#define MAP_HALFSIZE (MAP_SIZE/2)
// Creature used instead pet to simplify *::Visit templates (not required duplicate code for Creature->Pet case)
typedef TYPELIST_5(GameObject, Player, Creature/*pets*/, Corpse/*resurrectable*/, DynamicObject/*farsight target*/) AllWorldObjectTypes;
typedef TYPELIST_4(GameObject, Player, Creature/*pets*/, Corpse/*resurrectable*/) AllWorldObjectTypes;
typedef TYPELIST_4(GameObject, Creature/*except pets*/, DynamicObject, Corpse/*Bones*/) AllGridObjectTypes;
typedef TYPELIST_5(Creature, GameObject, DynamicObject, Pet, Corpse) AllMapStoredObjectTypes;
+2 -8
View File
@@ -84,20 +84,14 @@ void GridObjectLoader::LoadAllCellsInGrid()
LoadGameObjects(cell_guids.gameobjects, _map);
LoadCreatures(cell_guids.creatures, _map);
if (std::unordered_set<Corpse*> const* corpses = _map->GetCorpsesInCell(_grid.GetId()))
if (std::unordered_set<Corpse*> const* corpses = _map->GetCorpsesInGrid(_grid.GetId()))
{
for (Corpse* corpse : *corpses)
{
if (corpse->IsInGrid())
continue;
CellCoord cellCoord = Acore::ComputeCellCoord(corpse->GetPositionX(), corpse->GetPositionY());
Cell cell(cellCoord);
if (corpse->IsWorldObject())
_grid.AddWorldObject(cell.CellX(), cell.CellY(), corpse);
else
_grid.AddGridObject(cell.CellX(), cell.CellY(), corpse);
AddObjectHelper<Corpse>(_map, corpse);
}
}
}
+14 -3
View File
@@ -16,7 +16,9 @@
*/
#include "ServerMailMgr.h"
#include "AccountMgr.h"
#include "AchievementMgr.h"
#include "Common.h"
#include "DatabaseEnv.h"
#include "Item.h"
#include "Log.h"
@@ -240,21 +242,28 @@ void ServerMailMgr::LoadMailServerTemplatesConditions()
case ServerMailConditionType::Faction:
if (conditionValue < TEAM_ALLIANCE || conditionValue > TEAM_HORDE)
{
LOG_ERROR("sql.sql", "Table `mail_server_template_conditions` has conditionType 'Faction' with invalid conditionValue ({}) for templateID {}, skipped.", conditionState, templateID);
LOG_ERROR("sql.sql", "Table `mail_server_template_conditions` has conditionType 'Faction' with invalid conditionValue ({}) for templateID {}, skipped.", conditionValue, templateID);
continue;
}
break;
case ServerMailConditionType::Race:
if (conditionValue & ~RACEMASK_ALL_PLAYABLE)
{
LOG_ERROR("sql.sql", "Table `mail_server_template_conditions` has conditionType 'Race' with invalid conditionValue ({}) for templateID {}, skipped.", conditionState, templateID);
LOG_ERROR("sql.sql", "Table `mail_server_template_conditions` has conditionType 'Race' with invalid conditionValue ({}) for templateID {}, skipped.", conditionValue, templateID);
continue;
}
break;
case ServerMailConditionType::Class:
if (conditionValue & ~CLASSMASK_ALL_PLAYABLE)
{
LOG_ERROR("sql.sql", "Table `mail_server_template_conditions` has conditionType 'Class' with invalid conditionValue ({}) for templateID {}, skipped.", conditionState, templateID);
LOG_ERROR("sql.sql", "Table `mail_server_template_conditions` has conditionType 'Class' with invalid conditionValue ({}) for templateID {}, skipped.", conditionValue, templateID);
continue;
}
break;
case ServerMailConditionType::AccountFlags:
if ((conditionValue & ~ACCOUNT_FLAGS_ALL) != 0)
{
LOG_ERROR("sql.sql", "Table `mail_server_template_conditions` has conditionType 'AccountFlags' with invalid conditionValue ({}) for templateID {}, skipped.", conditionValue, templateID);
continue;
}
break;
@@ -344,6 +353,8 @@ bool ServerMailCondition::CheckCondition(Player* player) const
return (player->getRaceMask() & value) != 0;
case ServerMailConditionType::Class:
return (player->getClassMask() & value) != 0;
case ServerMailConditionType::AccountFlags:
return player->GetSession()->HasAccountFlag(value);
default:
[[unlikely]] LOG_ERROR("server.mail", "Unknown server mail condition type '{}'", static_cast<uint32>(type));
return false;
+3 -1
View File
@@ -53,6 +53,7 @@ enum class ServerMailConditionType : uint8
Faction = 6, ///< Requires the player to be a part of a specific faction. Horde/Alliance.
Race = 7, ///< Requires the player to be a specific race.
Class = 8, ///< Requires the player to be a specific class.
AccountFlags = 9, ///< Requires the player to have a specific AccountFlag (bit)
};
/**
@@ -67,7 +68,8 @@ constexpr std::pair<std::string_view, ServerMailConditionType> ServerMailConditi
{ "Reputation", ServerMailConditionType::Reputation },
{ "Faction", ServerMailConditionType::Faction },
{ "Race", ServerMailConditionType::Race },
{ "Class", ServerMailConditionType::Class }
{ "Class", ServerMailConditionType::Class },
{ "AccountFlags", ServerMailConditionType::AccountFlags }
};
/**
+12 -122
View File
@@ -175,81 +175,6 @@ void Map::AddToGrid(Corpse* obj, Cell const& cell)
}
}
template<class T>
void Map::SwitchGridContainers(T* /*obj*/, bool /*on*/)
{
}
template<>
void Map::SwitchGridContainers(Creature* obj, bool on)
{
ASSERT(!obj->IsPermanentWorldObject());
CellCoord p = Acore::ComputeCellCoord(obj->GetPositionX(), obj->GetPositionY());
if (!p.IsCoordValid())
{
LOG_ERROR("maps", "Map::SwitchGridContainers: Object {} has invalid coordinates X:{} Y:{} grid cell [{}:{}]",
obj->GetGUID().ToString(), obj->GetPositionX(), obj->GetPositionY(), p.x_coord, p.y_coord);
return;
}
Cell cell(p);
if (!IsGridLoaded(GridCoord(cell.data.Part.grid_x, cell.data.Part.grid_y)))
return;
LOG_DEBUG("maps", "Switch object {} from grid[{}, {}] {}", obj->GetGUID().ToString(), cell.GridX(), cell.GridY(), on);
MapGridType* grid = GetMapGrid(cell.GridX(), cell.GridY());
ASSERT(grid);
obj->RemoveFromGrid(); //This step is not really necessary but we want to do ASSERT in remove/add
if (on)
{
grid->AddWorldObject(cell.CellX(), cell.CellY(), obj);
AddWorldObject(obj);
}
else
{
grid->AddGridObject(cell.CellX(), cell.CellY(), obj);
RemoveWorldObject(obj);
}
obj->m_isTempWorldObject = on;
}
template<>
void Map::SwitchGridContainers(GameObject* obj, bool on)
{
ASSERT(!obj->IsPermanentWorldObject());
CellCoord p = Acore::ComputeCellCoord(obj->GetPositionX(), obj->GetPositionY());
if (!p.IsCoordValid())
{
LOG_ERROR("maps", "Map::SwitchGridContainers: Object {} has invalid coordinates X:{} Y:{} grid cell [{}:{}]",
obj->GetGUID().ToString(), obj->GetPositionX(), obj->GetPositionY(), p.x_coord, p.y_coord);
return;
}
Cell cell(p);
if (!IsGridLoaded(GridCoord(cell.data.Part.grid_x, cell.data.Part.grid_y)))
return;
//LOG_DEBUG(LOG_FILTER_MAPS, "Switch object {} from grid[{}, {}] {}", obj->GetGUID().ToString(), cell.data.Part.grid_x, cell.data.Part.grid_y, on);
MapGridType* grid = GetMapGrid(cell.GridX(), cell.GridY());
ASSERT(grid);
obj->RemoveFromGrid(); //This step is not really necessary but we want to do ASSERT in remove/add
if (on)
{
grid->AddWorldObject(cell.CellX(), cell.CellY(), obj);
AddWorldObject(obj);
}
else
{
grid->AddGridObject(cell.CellX(), cell.CellY(), obj);
RemoveWorldObject(obj);
}
}
template<class T>
void Map::DeleteFromWorld(T* obj)
{
@@ -1108,7 +1033,7 @@ void Map::UnloadAll()
_transports.clear();
for (auto& cellCorpsePair : _corpsesByCell)
for (auto& cellCorpsePair : _corpsesByGrid)
{
for (Corpse* corpse : cellCorpsePair.second)
{
@@ -1118,7 +1043,7 @@ void Map::UnloadAll()
}
}
_corpsesByCell.clear();
_corpsesByGrid.clear();
_corpsesByPlayer.clear();
_corpseBones.clear();
}
@@ -1849,49 +1774,8 @@ void Map::AddObjectToRemoveList(WorldObject* obj)
//LOG_DEBUG("maps", "Object ({}) added to removing list.", obj->GetGUID().ToString());
}
void Map::AddObjectToSwitchList(WorldObject* obj, bool on)
{
ASSERT(obj->GetMapId() == GetId() && obj->GetInstanceId() == GetInstanceId());
// i_objectsToSwitch is iterated only in Map::RemoveAllObjectsInRemoveList() and it uses
// the contained objects only if IsCreature() , so we can return in all other cases
if (!obj->IsCreature() && !obj->IsGameObject())
return;
std::map<WorldObject*, bool>::iterator itr = i_objectsToSwitch.find(obj);
if (itr == i_objectsToSwitch.end())
i_objectsToSwitch.insert(itr, std::make_pair(obj, on));
else if (itr->second != on)
i_objectsToSwitch.erase(itr);
else
ABORT();
}
void Map::RemoveAllObjectsInRemoveList()
{
while (!i_objectsToSwitch.empty())
{
std::map<WorldObject*, bool>::iterator itr = i_objectsToSwitch.begin();
WorldObject* obj = itr->first;
bool on = itr->second;
i_objectsToSwitch.erase(itr);
if (!obj->IsPermanentWorldObject())
{
switch (obj->GetTypeId())
{
case TYPEID_UNIT:
SwitchGridContainers<Creature>(obj->ToCreature(), on);
break;
case TYPEID_GAMEOBJECT:
SwitchGridContainers<GameObject>(obj->ToGameObject(), on);
break;
default:
break;
}
}
}
//LOG_DEBUG("maps", "Object remover 1 check.");
while (!i_objectsToRemove.empty())
{
std::unordered_set<WorldObject*>::iterator itr = i_objectsToRemove.begin();
@@ -1929,8 +1813,6 @@ void Map::RemoveAllObjectsInRemoveList()
break;
}
}
//LOG_DEBUG("maps", "Object remover 2 check.");
}
uint32 Map::GetPlayersCountExceptGMs() const
@@ -1972,6 +1854,12 @@ void Map::AddToActive(GameObject* d)
AddToActiveHelper(d);
}
template<>
void Map::AddToActive(Corpse* /*c*/)
{
// do nothing for corpses
}
template<class T>
void Map::RemoveFromActive(T* obj)
{
@@ -2791,7 +2679,8 @@ void Map::AddCorpse(Corpse* corpse)
{
corpse->SetMap(this);
_corpsesByCell[corpse->GetCellCoord().GetId()].insert(corpse);
GridCoord const gridCoord = Acore::ComputeGridCoord(corpse->GetPositionX(), corpse->GetPositionY());
_corpsesByGrid[gridCoord.GetId()].insert(corpse);
if (corpse->GetType() != CORPSE_BONES)
_corpsesByPlayer[corpse->GetOwnerGUID()] = corpse;
else
@@ -2801,6 +2690,7 @@ void Map::AddCorpse(Corpse* corpse)
void Map::RemoveCorpse(Corpse* corpse)
{
ASSERT(corpse);
GridCoord const gridCoord = Acore::ComputeGridCoord(corpse->GetPositionX(), corpse->GetPositionY());
corpse->DestroyForNearbyPlayers();
if (corpse->IsInGrid())
@@ -2811,7 +2701,7 @@ void Map::RemoveCorpse(Corpse* corpse)
corpse->ResetMap();
}
_corpsesByCell[corpse->GetCellCoord().GetId()].erase(corpse);
_corpsesByGrid[gridCoord.GetId()].erase(corpse);
if (corpse->GetType() != CORPSE_BONES)
_corpsesByPlayer.erase(corpse->GetOwnerGUID());
else
+4 -7
View File
@@ -306,7 +306,6 @@ public:
}
void AddObjectToRemoveList(WorldObject* obj);
void AddObjectToSwitchList(WorldObject* obj, bool on);
virtual void DelayedUpdate(const uint32 diff);
void resetMarkedCells() { marked_cells.reset(); }
@@ -336,7 +335,6 @@ public:
template<class T>
void RemoveFromActive(T* obj);
template<class T> void SwitchGridContainers(T* obj, bool on);
CreatureGroupHolderType CreatureGroupHolder;
void UpdateIteratorBack(Player* player);
@@ -361,10 +359,10 @@ public:
typedef std::unordered_multimap<ObjectGuid::LowType, GameObject*> GameObjectBySpawnIdContainer;
GameObjectBySpawnIdContainer& GetGameObjectBySpawnIdStore() { return _gameobjectBySpawnIdStore; }
[[nodiscard]] std::unordered_set<Corpse*> const* GetCorpsesInCell(uint32 cellId) const
[[nodiscard]] std::unordered_set<Corpse*> const* GetCorpsesInGrid(uint32 gridId) const
{
auto itr = _corpsesByCell.find(cellId);
if (itr != _corpsesByCell.end())
auto itr = _corpsesByGrid.find(gridId);
if (itr != _corpsesByGrid.end())
return &itr->second;
return nullptr;
@@ -577,7 +575,6 @@ private:
bool i_scriptLock;
std::unordered_set<WorldObject*> i_objectsToRemove;
std::map<WorldObject*, bool> i_objectsToSwitch;
std::unordered_set<WorldObject*> i_worldObjects;
typedef std::multimap<time_t, ScriptAction> ScriptScheduleMap;
@@ -634,7 +631,7 @@ private:
MapStoredObjectTypesContainer _objectsStore;
CreatureBySpawnIdContainer _creatureBySpawnIdStore;
GameObjectBySpawnIdContainer _gameobjectBySpawnIdStore;
std::unordered_map<uint32/*cellId*/, std::unordered_set<Corpse*>> _corpsesByCell;
std::unordered_map<uint32/*gridId*/, std::unordered_set<Corpse*>> _corpsesByGrid;
std::unordered_map<ObjectGuid, Corpse*> _corpsesByPlayer;
std::unordered_set<Corpse*> _corpseBones;
+1 -1
View File
@@ -223,7 +223,7 @@ Map::EnterState MapMgr::PlayerCannotEnter(uint32 mapid, Player* player, bool log
{
uint32 destInstId = sInstanceSaveMgr->PlayerGetDestinationInstanceId(player, mapid, targetDifficulty);
if (destInstId)
if (Map* boundMap = sMapMgr->FindMap(mapid, destInstId))
if (Map* boundMap = FindMap(mapid, destInstId))
if (Map::EnterState denyReason = boundMap->CannotEnter(player, loginCheck))
return denyReason;
}
+28
View File
@@ -18,7 +18,9 @@
#include "MapUpdater.h"
#include "DatabaseEnv.h"
#include "LFGMgr.h"
#include "Log.h"
#include "Map.h"
#include "MapMgr.h"
#include "Metric.h"
class UpdateRequest
@@ -52,6 +54,27 @@ private:
uint32 s_diff;
};
class MapPreloadRequest : public UpdateRequest
{
public:
MapPreloadRequest(uint32 mapId, MapUpdater& updater)
: _mapId(mapId), _updater(updater)
{
}
void call() override
{
Map* map = sMapMgr->CreateBaseMap(_mapId);
LOG_INFO("server.loading", ">> Loading All Grids For Map {} ({})", map->GetId(), map->GetMapName());
map->LoadAllGrids();
_updater.update_finished();
}
private:
uint32 _mapId;
MapUpdater& _updater;
};
class LFGUpdateRequest : public UpdateRequest
{
public:
@@ -120,6 +143,11 @@ void MapUpdater::schedule_update(Map& map, uint32 diff, uint32 s_diff)
schedule_task(new MapUpdateRequest(map, *this, diff, s_diff));
}
void MapUpdater::schedule_map_preload(uint32 mapid)
{
schedule_task(new MapPreloadRequest(mapid, *this));
}
void MapUpdater::schedule_lfg_update(uint32 diff)
{
schedule_task(new LFGUpdateRequest(*this, diff));
+1
View File
@@ -35,6 +35,7 @@ public:
void schedule_task(UpdateRequest* request);
void schedule_update(Map& map, uint32 diff, uint32 s_diff);
void schedule_map_preload(uint32 mapid);
void schedule_lfg_update(uint32 diff);
void wait();
void activate(std::size_t num_threads);
+4 -4
View File
@@ -91,7 +91,7 @@ GraveyardStruct const* Graveyard::GetDefaultGraveyard(TeamId teamId)
ALLIANCE_GRAVEYARD = 4, // Westfall
};
return sGraveyard->GetGraveyard(teamId == TEAM_HORDE ? HORDE_GRAVEYARD : ALLIANCE_GRAVEYARD);
return GetGraveyard(teamId == TEAM_HORDE ? HORDE_GRAVEYARD : ALLIANCE_GRAVEYARD);
}
GraveyardStruct const* Graveyard::GetClosestGraveyard(Player* player, TeamId teamId, bool nearCorpse)
@@ -100,7 +100,7 @@ GraveyardStruct const* Graveyard::GetClosestGraveyard(Player* player, TeamId tea
sScriptMgr->OnPlayerBeforeChooseGraveyard(player, teamId, nearCorpse, graveyardOverride);
if (graveyardOverride)
{
return sGraveyard->GetGraveyard(graveyardOverride);
return GetGraveyard(graveyardOverride);
}
WorldLocation loc = player->GetWorldLocation();
@@ -187,7 +187,7 @@ GraveyardStruct const* Graveyard::GetClosestGraveyard(uint32 mapId, float x, flo
for (; range.first != range.second; ++range.first)
{
GraveyardData const& graveyardLink = range.first->second;
GraveyardStruct const* entry = sGraveyard->GetGraveyard(graveyardLink.safeLocId);
GraveyardStruct const* entry = GetGraveyard(graveyardLink.safeLocId);
if (!entry)
{
LOG_ERROR("sql.sql", "Table `graveyard_zone` has record for not existing `game_graveyard` table {}, skipped.", graveyardLink.safeLocId);
@@ -393,7 +393,7 @@ void Graveyard::LoadGraveyardZones()
uint32 team = fields[2].Get<uint16>();
TeamId teamId = team == 0 ? TEAM_NEUTRAL : (team == ALLIANCE ? TEAM_ALLIANCE : TEAM_HORDE);
GraveyardStruct const* entry = sGraveyard->GetGraveyard(safeLocId);
GraveyardStruct const* entry = GetGraveyard(safeLocId);
if (!entry)
{
LOG_ERROR("sql.sql", "Table `graveyard_zone` has a record for not existing `game_graveyard` table {}, skipped.", safeLocId);
+5 -1
View File
@@ -217,7 +217,11 @@ enum AcoreStrings
LANG_INVALID_GAMEOBJECT_TYPE = 176,
LANG_GAMEOBJECT_DAMAGED = 177,
LANG_GRID_POSITION = 178,
// 179-185 used in other client versions
LANG_ACCOUNT_FLAGS_PINFO = 179,
// Free 180-185
LANG_TRANSPORT_POSITION = 186,
LANG_PROFANITY_NAME = 187,
LANG_2FA_SECRET_TOO_LONG = 188,
@@ -72,15 +72,27 @@ bool ChaseMovementGenerator<T>::DoUpdate(T* owner, uint32 time_diff)
return false;
Creature* cOwner = owner->ToCreature();
bool isStoppedBecauseOfCasting = cOwner && cOwner->IsMovementPreventedByCasting();
// the owner might be unable to move (rooted or casting), or we have lost the target, pause movement
if (owner->HasUnitState(UNIT_STATE_NOT_MOVE) || HasLostTarget(owner) || (cOwner && cOwner->IsMovementPreventedByCasting()))
if (owner->HasUnitState(UNIT_STATE_NOT_MOVE) || HasLostTarget(owner) || isStoppedBecauseOfCasting)
{
owner->StopMoving();
_lastTargetPosition.reset();
if (cOwner)
{
cOwner->UpdateLeashExtensionTime();
if (isStoppedBecauseOfCasting)
{
// Don't reset leash timer if it's a spell like Shoot with a short cast time.
/// @todo: Research how it should actually work.
Spell *spell = cOwner->GetFirstCurrentCastingSpell();
bool spellHasLongCast = spell && spell->GetCastTime() > 1 * SECOND * IN_MILLISECONDS;
if (spellHasLongCast)
cOwner->UpdateLeashExtensionTime();
}
else
cOwner->UpdateLeashExtensionTime();
cOwner->SetCannotReachTarget();
}
return true;
+26 -1
View File
@@ -104,7 +104,7 @@ bool WorldSessionFilter::Process(WorldPacket* packet)
}
/// WorldSession constructor
WorldSession::WorldSession(uint32 id, std::string&& name, std::shared_ptr<WorldSocket> sock, AccountTypes sec, uint8 expansion,
WorldSession::WorldSession(uint32 id, std::string&& name, uint32 accountFlags, std::shared_ptr<WorldSocket> sock, AccountTypes sec, uint8 expansion,
time_t mute_time, LocaleConstant locale, uint32 recruiter, bool isARecruiter, bool skipQueue, uint32 TotalTime, bool isBot) :
m_muteTime(mute_time),
m_timeOutTime(0),
@@ -116,6 +116,7 @@ WorldSession::WorldSession(uint32 id, std::string&& name, std::shared_ptr<WorldS
_skipQueue(skipQueue),
_accountId(id),
_accountName(std::move(name)),
_accountFlags(accountFlags),
m_expansion(expansion),
m_total_time(TotalTime),
_logoutTime(0),
@@ -182,6 +183,30 @@ WorldSession::~WorldSession()
LoginDatabase.Execute("UPDATE account SET online = 0 WHERE id = {};", GetAccountId()); // One-time query
}
void WorldSession::UpdateAccountFlag(uint32 flag, bool remove /*= flase*/)
{
if (remove)
_accountFlags &= ~flag;
else
_accountFlags |= flag;
// Async update
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_SET_ACCOUNT_FLAG);
stmt->SetData(0, _accountFlags);
stmt->SetData(1, GetAccountId());
LoginDatabase.Execute(stmt);
}
void WorldSession::ValidateAccountFlags()
{
bool hasGMFlag = HasAccountFlag(ACCOUNT_FLAG_GM);
if (IsGMAccount() && !hasGMFlag)
UpdateAccountFlag(ACCOUNT_FLAG_GM);
else if (hasGMFlag && !IsGMAccount())
UpdateAccountFlag(ACCOUNT_FLAG_GM, true);
}
bool WorldSession::IsGMAccount() const
{
return GetSecurity() >= SEC_GAMEMASTER;
+7 -2
View File
@@ -348,10 +348,14 @@ struct PacketCounter
class WorldSession
{
public:
WorldSession(uint32 id, std::string&& name, std::shared_ptr<WorldSocket> sock, AccountTypes sec, uint8 expansion, time_t mute_time, LocaleConstant locale,
uint32 recruiter, bool isARecruiter, bool skipQueue, uint32 TotalTime, bool isBot = false);
WorldSession(uint32 id, std::string&& name, uint32 accountFlags, std::shared_ptr<WorldSocket> sock, AccountTypes sec, uint8 expansion, time_t mute_time, LocaleConstant locale, uint32 recruiter, bool isARecruiter, bool skipQueue, uint32 TotalTime, bool is_bot = false);
~WorldSession();
uint32 GetAccountFlags() const { return _accountFlags; }
bool HasAccountFlag(uint32 flag) const { return (_accountFlags & flag) != 0; }
void UpdateAccountFlag(uint32 flag, bool remove = false);
void ValidateAccountFlags();
bool IsGMAccount() const;
bool PlayerLoading() const { return m_playerLoading; }
@@ -1188,6 +1192,7 @@ private:
bool _skipQueue;
uint32 _accountId;
std::string _accountName;
uint32 _accountFlags;
uint8 m_expansion;
uint32 m_total_time;
+3 -3
View File
@@ -698,8 +698,6 @@ void WorldSocket::HandleAuthSessionCallback(std::shared_ptr<AuthSession> authSes
LoginDatabase.Execute(stmt);
AccountMgr::ValidateAccountFlags(account.Id, account.Flags, account.Security);
// At this point, we can safely hook a successful login
sScriptMgr->OnAccountLogin(account.Id);
@@ -707,7 +705,7 @@ void WorldSocket::HandleAuthSessionCallback(std::shared_ptr<AuthSession> authSes
sScriptMgr->OnLastIpUpdate(account.Id, address);
_worldSession = new WorldSession(account.Id, std::move(authSession->Account), shared_from_this(), account.Security,
_worldSession = new WorldSession(account.Id, std::move(authSession->Account), account.Flags, shared_from_this(), account.Security,
account.Expansion, account.MuteTime, account.Locale, account.Recruiter, account.IsRectuiter, account.Security ? true : false, account.TotalTime);
_worldSession->ReadAddonsInfo(authSession->AddonInfo);
@@ -718,6 +716,8 @@ void WorldSocket::HandleAuthSessionCallback(std::shared_ptr<AuthSession> authSes
_worldSession->InitWarden(account.SessionKey, account.OS);
}
_worldSession->ValidateAccountFlags();
sWorldSessionMgr->AddSession(_worldSession);
AsyncRead();
+16 -19
View File
@@ -2238,21 +2238,15 @@ void Spell::SearchChainTargets(std::list<WorldObject*>& targets, uint32 chainTar
if (isBouncingFar)
searchRadius *= chainTargets;
WorldObject* chainSource = m_spellInfo->HasAttribute(SPELL_ATTR2_CHAIN_FROM_CASTER) ? m_caster : target;
std::list<WorldObject*> tempTargets;
SearchAreaTargets(tempTargets, searchRadius, target, m_caster, objectType, selectType, condList);
SearchAreaTargets(tempTargets, searchRadius, chainSource, m_caster, objectType, selectType, condList);
tempTargets.remove(target);
// remove targets which are always invalid for chain spells
// for some spells allow only chain targets in front of caster (swipe for example)
if (!isBouncingFar)
{
for (std::list<WorldObject*>::iterator itr = tempTargets.begin(); itr != tempTargets.end();)
{
std::list<WorldObject*>::iterator checkItr = itr++;
if (!m_caster->HasInArc(static_cast<float>(M_PI), *checkItr))
tempTargets.erase(checkItr);
}
}
tempTargets.remove_if([this](WorldObject* target) { return !m_caster->HasInArc(static_cast<float>(M_PI), target); });
while (chainTargets)
{
@@ -2267,7 +2261,7 @@ void Spell::SearchChainTargets(std::list<WorldObject*>& targets, uint32 chainTar
if (Unit* unit = (*itr)->ToUnit())
{
uint32 deficit = unit->GetMaxHealth() - unit->GetHealth();
if (deficit > maxHPDeficit && target->IsWithinDist(unit, jumpRadius) && target->IsWithinLOSInMap(unit, VMAP::ModelIgnoreFlags::M2))
if (deficit > maxHPDeficit && chainSource->IsWithinDist(unit, jumpRadius) && chainSource->IsWithinLOSInMap(unit, VMAP::ModelIgnoreFlags::M2))
{
foundItr = itr;
maxHPDeficit = deficit;
@@ -2282,19 +2276,22 @@ void Spell::SearchChainTargets(std::list<WorldObject*>& targets, uint32 chainTar
{
if (foundItr == tempTargets.end())
{
if ((!isBouncingFar || target->IsWithinDist(*itr, jumpRadius)) && target->IsWithinLOSInMap(*itr, VMAP::ModelIgnoreFlags::M2))
if ((!isBouncingFar || chainSource->IsWithinDist(*itr, jumpRadius)) && chainSource->IsWithinLOSInMap(*itr, VMAP::ModelIgnoreFlags::M2))
foundItr = itr;
}
else if (target->GetDistanceOrder(*itr, *foundItr) && target->IsWithinLOSInMap(*itr, VMAP::ModelIgnoreFlags::M2))
else if (chainSource->GetDistanceOrder(*itr, *foundItr) && chainSource->IsWithinLOSInMap(*itr, VMAP::ModelIgnoreFlags::M2))
foundItr = itr;
}
}
// not found any valid target - chain ends
if (foundItr == tempTargets.end())
break;
target = *foundItr;
if (!m_spellInfo->HasAttribute(SPELL_ATTR2_CHAIN_FROM_CASTER))
chainSource = *foundItr;
targets.push_back(*foundItr);
tempTargets.erase(foundItr);
targets.push_back(target);
--chainTargets;
}
}
@@ -7112,7 +7109,7 @@ SpellCastResult Spell::CheckRange(bool strict)
if (range_type == SPELL_RANGE_MELEE)
{
float real_max_range = max_range;
if (!m_caster->IsCreature() && m_caster->isMoving() && target->isMoving() && !m_caster->IsWalking() && !target->IsWalking())
if (!m_caster->IsCreature() && m_caster->HasLeewayMovement() && target->HasLeewayMovement())
real_max_range -= MIN_MELEE_REACH; // Because of lag, we can not check too strictly here (is only used if both caster and target are moving)
else
real_max_range -= 2 * MIN_MELEE_REACH;
@@ -7129,7 +7126,7 @@ SpellCastResult Spell::CheckRange(bool strict)
return SPELL_FAILED_TOO_CLOSE;
}
if (m_caster->IsPlayer() && (m_spellInfo->FacingCasterFlags & SPELL_FACING_FLAG_INFRONT) && !m_caster->HasInArc(static_cast<float>(M_PI), target))
if (m_caster->IsPlayer() && (m_spellInfo->FacingCasterFlags & SPELL_FACING_FLAG_INFRONT) && !m_caster->HasInArc(static_cast<float>(M_PI), target) && !m_caster->IsWithinBoundaryRadius(target))
return SPELL_FAILED_UNIT_NOT_INFRONT;
}
@@ -9040,7 +9037,7 @@ namespace Acore
case TARGET_CHECK_PARTY:
if (unitTarget->IsTotem())
return false;
if (unitTarget->IsGuardian())
if (unitTarget->IsGuardian() && !unitTarget->IsControllableGuardian())
return false;
if (!_caster->_IsValidAssistTarget(unitTarget, _spellInfo))
return false;
@@ -9054,7 +9051,7 @@ namespace Acore
case TARGET_CHECK_RAID:
if (unitTarget->IsTotem())
return false;
if (unitTarget->IsGuardian())
if (unitTarget->IsGuardian() && !unitTarget->IsControllableGuardian())
return false;
if (!_caster->_IsValidAssistTarget(unitTarget, _spellInfo))
return false;
@@ -9132,7 +9129,7 @@ namespace Acore
}
else
{
if (!_caster->isInFront(target, _coneAngle))
if (!_caster->IsWithinBoundaryRadius(target->ToUnit()) && !_caster->isInFront(target, _coneAngle))
return false;
}
return WorldObjectSpellAreaTargetCheck::operator ()(target);
+1 -1
View File
@@ -2731,7 +2731,7 @@ void Spell::EffectAddFarsight(SpellEffIndex effIndex)
// Remove old farsight if exist
bool updateViewerVisibility = m_caster->RemoveDynObject(m_spellInfo->Id);
DynamicObject* dynObj = new DynamicObject(true);
DynamicObject* dynObj = new DynamicObject(false);
if (!dynObj->CreateDynamicObject(m_caster->GetMap()->GenerateLowGuid<HighGuid::DynamicObject>(), m_caster, m_spellInfo->Id, *destTarget, radius, DYNAMIC_OBJECT_FARSIGHT_FOCUS))
{
delete dynObj;
@@ -592,6 +592,12 @@ void SpellMgr::LoadSpellInfoCorrections()
spellInfo->AttributesEx3 &= ~SPELL_ATTR3_SUPPRESS_CASTER_PROCS;
});
// Vindication
ApplySpellFix({ 67, 26017}, [](SpellInfo* spellInfo)
{
spellInfo->Effects[EFFECT_0].MiscValue = 0;
});
// Arcane Missiles
ApplySpellFix({ 5143, 5144, 5145, 8416, 8417, 10211, 10212, 25345, 27075, 38699, 38704, 42843, 42846 }, [](SpellInfo* spellInfo)
{
+5 -5
View File
@@ -1614,7 +1614,7 @@ void SpellMgr::LoadSpellTargetPositions()
}
if (found)
{
if (!sSpellMgr->GetSpellTargetPosition(i))
if (!GetSpellTargetPosition(i))
LOG_DEBUG("spells.aura", "Spell (ID: {}) does not have record in `spell_target_position`", i);
}
}*/
@@ -2348,7 +2348,7 @@ void SpellMgr::LoadPetLevelupSpellMap()
LOG_INFO("server.loading", " ");
}
bool LoadPetDefaultSpells_helper(CreatureTemplate const* cInfo, PetDefaultSpellsEntry& petDefSpells)
static bool LoadPetDefaultSpells_helper(CreatureTemplate const* cInfo, PetDefaultSpellsEntry& petDefSpells)
{
// skip empty list;
bool have_spell = false;
@@ -3448,9 +3448,9 @@ void SpellMgr::LoadSpellInfoCustomAttributes()
spellInfo->_InitializeExplicitTargetMask();
if (sSpellMgr->HasSpellCooldownOverride(spellInfo->Id))
if (HasSpellCooldownOverride(spellInfo->Id))
{
SpellCooldownOverride spellOverride = sSpellMgr->GetSpellCooldownOverride(spellInfo->Id);
SpellCooldownOverride spellOverride = GetSpellCooldownOverride(spellInfo->Id);
if (spellInfo->RecoveryTime != spellOverride.RecoveryTime)
{
@@ -3497,7 +3497,7 @@ void SpellMgr::LoadSpellInfoCustomAttributes()
case SPELL_AURA_PERIODIC_TRIGGER_SPELL:
case SPELL_AURA_PERIODIC_TRIGGER_SPELL_FROM_CLIENT:
case SPELL_AURA_PERIODIC_TRIGGER_SPELL_WITH_VALUE:
if (SpellInfo const* triggerSpell = sSpellMgr->GetSpellInfo(spellInfo->Effects[j].TriggerSpell))
if (SpellInfo const* triggerSpell = GetSpellInfo(spellInfo->Effects[j].TriggerSpell))
{
overrideAttr = true;
if (triggerSpell->AttributesCu & SPELL_ATTR0_CU_BINARY_SPELL)
+1 -1
View File
@@ -286,7 +286,7 @@ void TicketMgr::ResetTickets()
{
uint32 ticketId = itr->second->GetId();
++itr;
sTicketMgr->RemoveTicket(ticketId);
RemoveTicket(ticketId);
}
else
++itr;
+26 -18
View File
@@ -218,10 +218,10 @@ void World::LoadConfigSettings(bool reload)
//visibility on continents
_maxVisibleDistanceOnContinents = sConfigMgr->GetOption<float>("Visibility.Distance.Continents", DEFAULT_VISIBILITY_DISTANCE);
if (_maxVisibleDistanceOnContinents < 45 * sWorld->getRate(RATE_CREATURE_AGGRO))
if (_maxVisibleDistanceOnContinents < 45 * getRate(RATE_CREATURE_AGGRO))
{
LOG_ERROR("server.loading", "Visibility.Distance.Continents can't be less max aggro radius {}", 45 * sWorld->getRate(RATE_CREATURE_AGGRO));
_maxVisibleDistanceOnContinents = 45 * sWorld->getRate(RATE_CREATURE_AGGRO);
LOG_ERROR("server.loading", "Visibility.Distance.Continents can't be less max aggro radius {}", 45 * getRate(RATE_CREATURE_AGGRO));
_maxVisibleDistanceOnContinents = 45 * getRate(RATE_CREATURE_AGGRO);
}
else if (_maxVisibleDistanceOnContinents > MAX_VISIBILITY_DISTANCE)
{
@@ -231,10 +231,10 @@ void World::LoadConfigSettings(bool reload)
//visibility in instances
_maxVisibleDistanceInInstances = sConfigMgr->GetOption<float>("Visibility.Distance.Instances", DEFAULT_VISIBILITY_INSTANCE);
if (_maxVisibleDistanceInInstances < 45 * sWorld->getRate(RATE_CREATURE_AGGRO))
if (_maxVisibleDistanceInInstances < 45 * getRate(RATE_CREATURE_AGGRO))
{
LOG_ERROR("server.loading", "Visibility.Distance.Instances can't be less max aggro radius {}", 45 * sWorld->getRate(RATE_CREATURE_AGGRO));
_maxVisibleDistanceInInstances = 45 * sWorld->getRate(RATE_CREATURE_AGGRO);
LOG_ERROR("server.loading", "Visibility.Distance.Instances can't be less max aggro radius {}", 45 * getRate(RATE_CREATURE_AGGRO));
_maxVisibleDistanceInInstances = 45 * getRate(RATE_CREATURE_AGGRO);
}
else if (_maxVisibleDistanceInInstances > MAX_VISIBILITY_DISTANCE)
{
@@ -244,10 +244,10 @@ void World::LoadConfigSettings(bool reload)
//visibility in BG/Arenas
_maxVisibleDistanceInBGArenas = sConfigMgr->GetOption<float>("Visibility.Distance.BGArenas", DEFAULT_VISIBILITY_BGARENAS);
if (_maxVisibleDistanceInBGArenas < 45 * sWorld->getRate(RATE_CREATURE_AGGRO))
if (_maxVisibleDistanceInBGArenas < 45 * getRate(RATE_CREATURE_AGGRO))
{
LOG_ERROR("server.loading", "Visibility.Distance.BGArenas can't be less max aggro radius {}", 45 * sWorld->getRate(RATE_CREATURE_AGGRO));
_maxVisibleDistanceInBGArenas = 45 * sWorld->getRate(RATE_CREATURE_AGGRO);
LOG_ERROR("server.loading", "Visibility.Distance.BGArenas can't be less max aggro radius {}", 45 * getRate(RATE_CREATURE_AGGRO));
_maxVisibleDistanceInBGArenas = 45 * getRate(RATE_CREATURE_AGGRO);
}
else if (_maxVisibleDistanceInBGArenas > MAX_VISIBILITY_DISTANCE)
{
@@ -1001,7 +1001,7 @@ void World::SetInitialWorldSettings()
sScriptMgr->OnBeforeWorldInitialized();
if (sWorld->getBoolConfig(CONFIG_PRELOAD_ALL_NON_INSTANCED_MAP_GRIDS))
if (getBoolConfig(CONFIG_PRELOAD_ALL_NON_INSTANCED_MAP_GRIDS))
{
LOG_INFO("server.loading", "Loading All Grids For All Non-Instanced Maps...");
@@ -1011,15 +1011,23 @@ void World::SetInitialWorldSettings()
if (mapEntry && !mapEntry->Instanceable())
{
Map* map = sMapMgr->CreateBaseMap(mapEntry->MapID);
if (map)
if (sMapMgr->GetMapUpdater()->activated())
sMapMgr->GetMapUpdater()->schedule_map_preload(mapEntry->MapID);
else
{
LOG_INFO("server.loading", ">> Loading All Grids For Map {}", map->GetId());
map->LoadAllGrids();
Map* map = sMapMgr->CreateBaseMap(mapEntry->MapID);
if (map)
{
LOG_INFO("server.loading", ">> Loading All Grids For Map {}", map->GetId());
map->LoadAllGrids();
}
}
}
}
if (sMapMgr->GetMapUpdater()->activated())
sMapMgr->GetMapUpdater()->wait();
}
uint32 startupDuration = GetMSTimeDiffToNow(startupBegin);
@@ -1191,7 +1199,7 @@ void World::Update(uint32 diff)
}
/// <li> Clean logs table
if (sWorld->getIntConfig(CONFIG_LOGDB_CLEARTIME) > 0) // if not enabled, ignore the timer
if (getIntConfig(CONFIG_LOGDB_CLEARTIME) > 0) // if not enabled, ignore the timer
{
if (_timers[WUPDATE_CLEANDB].Passed())
{
@@ -1200,7 +1208,7 @@ void World::Update(uint32 diff)
_timers[WUPDATE_CLEANDB].Reset();
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_OLD_LOGS);
stmt->SetData(0, sWorld->getIntConfig(CONFIG_LOGDB_CLEARTIME));
stmt->SetData(0, getIntConfig(CONFIG_LOGDB_CLEARTIME));
stmt->SetData(1, uint32(currentGameTime.count()));
LoginDatabase.Execute(stmt);
}
@@ -1217,7 +1225,7 @@ void World::Update(uint32 diff)
sMapMgr->Update(diff);
}
if (sWorld->getBoolConfig(CONFIG_AUTOBROADCAST))
if (getBoolConfig(CONFIG_AUTOBROADCAST))
{
if (_timers[WUPDATE_AUTOBROADCAST].Passed())
{
+3
View File
@@ -160,6 +160,7 @@ void WorldConfig::BuildConfigCache()
SetConfigValue<uint32>(CONFIG_INTERVAL_SAVE, "PlayerSaveInterval", 900000);
SetConfigValue<uint32>(CONFIG_INTERVAL_DISCONNECT_TOLERANCE, "DisconnectToleranceInterval", 0);
SetConfigValue<bool>(CONFIG_STATS_SAVE_ONLY_ON_LOGOUT, "PlayerSave.Stats.SaveOnlyOnLogout", true);
SetConfigValue<bool>(CONFIG_VALIDATE_SKILL_LEARNED_BY_SPELLS, "ValidateSkillLearnedBySpells", true);
SetConfigValue<uint32>(CONFIG_MIN_LEVEL_STAT_SAVE, "PlayerSave.Stats.MinLevel", 0, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value < MAX_LEVEL; }, "< MAX_LEVEL");
@@ -406,6 +407,7 @@ void WorldConfig::BuildConfigCache()
SetConfigValue<float>(CONFIG_LISTEN_RANGE_TEXTEMOTE, "ListenRange.TextEmote", 40.0f);
SetConfigValue<float>(CONFIG_LISTEN_RANGE_YELL, "ListenRange.Yell", 300.0f);
SetConfigValue<uint32>(CONFIG_BATTLEGROUND_PREP_TIME, "Battleground.PrepTime", 120);
SetConfigValue<uint32>(CONFIG_BATTLEGROUND_OVERRIDE_LOWLEVELS_MINPLAYERS, "Battleground.Override.LowLevels.MinPlayers", 0);
SetConfigValue<bool>(CONFIG_BATTLEGROUND_DISABLE_QUEST_SHARE_IN_BG, "Battleground.DisableQuestShareInBG", false);
SetConfigValue<bool>(CONFIG_BATTLEGROUND_DISABLE_READY_CHECK_IN_BG, "Battleground.DisableReadyCheckInBG", false);
@@ -436,6 +438,7 @@ void WorldConfig::BuildConfigCache()
SetConfigValue<uint32>(CONFIG_BATTLEGROUND_ALTERAC_REP_ONBOSSDEATH, "Battleground.Alterac.ReputationOnBossDeath", 350);
SetConfigValue<uint32>(CONFIG_BATTLEGROUND_EYEOFTHESTORM_CAPTUREPOINTS, "Battleground.EyeOfTheStorm.CapturePoints", 1600);
SetConfigValue<uint32>(CONFIG_ARENA_PREP_TIME, "Arena.PrepTime", 60);
SetConfigValue<uint32>(CONFIG_ARENA_MAX_RATING_DIFFERENCE, "Arena.MaxRatingDifference", 150);
SetConfigValue<uint32>(CONFIG_ARENA_RATING_DISCARD_TIMER, "Arena.RatingDiscardTimer", 600000);
SetConfigValue<uint32>(CONFIG_ARENA_PREV_OPPONENTS_DISCARD_TIMER, "Arena.PreviousOpponentsDiscardTimer", 120000);
+3
View File
@@ -254,6 +254,7 @@ enum ServerConfigs
CONFIG_DEATH_SICKNESS_LEVEL,
CONFIG_INSTANT_LOGOUT,
CONFIG_DISABLE_BREATHING,
CONFIG_BATTLEGROUND_PREP_TIME,
CONFIG_BATTLEGROUND_OVERRIDE_LOWLEVELS_MINPLAYERS,
CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_SPAM_DELAY,
CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_TIMER,
@@ -274,6 +275,7 @@ enum ServerConfigs
CONFIG_BATTLEGROUND_ALTERAC_REP_ONBOSSDEATH,
CONFIG_BATTLEGROUND_EYEOFTHESTORM_CAPTUREPOINTS,
CONFIG_WINTERGRASP_ENABLE,
CONFIG_ARENA_PREP_TIME,
CONFIG_ARENA_MAX_RATING_DIFFERENCE,
CONFIG_ARENA_RATING_DISCARD_TIMER,
CONFIG_ARENA_PREV_OPPONENTS_DISCARD_TIMER,
@@ -472,6 +474,7 @@ enum ServerConfigs
RATE_MISS_CHANCE_MULTIPLIER_TARGET_CREATURE,
RATE_MISS_CHANCE_MULTIPLIER_TARGET_PLAYER,
CONFIG_NEW_CHAR_STRING,
CONFIG_VALIDATE_SKILL_LEARNED_BY_SPELLS,
MAX_NUM_SERVER_CONFIGS
};
+7 -3
View File
@@ -47,7 +47,9 @@ WorldState::~WorldState()
void WorldState::Load()
{
QueryResult result = CharacterDatabase.Query("SELECT Id, Data FROM world_state");
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_WORLD_STATE);
PreparedQueryResult result = CharacterDatabase.Query(stmt);
if (result)
{
do
@@ -205,8 +207,10 @@ void WorldState::Save(WorldStateSaveIds saveId)
void WorldState::SaveHelper(std::string& stringToSave, WorldStateSaveIds saveId)
{
CharacterDatabase.Execute("DELETE FROM world_state WHERE Id='{}'", saveId);
CharacterDatabase.Execute("INSERT INTO world_state(Id,Data) VALUES('{}','{}')", saveId, stringToSave.data());
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_WORLD_STATE);
stmt->SetData(0, saveId);
stmt->SetData(1, stringToSave);
CharacterDatabase.Execute(stmt);
}
bool WorldState::IsConditionFulfilled(uint32 conditionId, uint32 state) const
+53
View File
@@ -22,6 +22,7 @@
#include "CharacterCache.h"
#include "Chat.h"
#include "CommandScript.h"
#include "Common.h"
#include "GameGraveyard.h"
#include "GameTime.h"
#include "GridNotifiers.h"
@@ -57,6 +58,49 @@
constexpr auto SPELL_STUCK = 7355;
constexpr auto SPELL_FREEZE = 9454;
struct AccountFlagText
{
AccountFlag flag;
std::string text;
};
AccountFlagText const accountFlagText[MAX_ACCOUNT_FLAG] =
{
{ ACCOUNT_FLAG_GM, "ACCOUNT_FLAG_GM" },
{ ACCOUNT_FLAG_NOKICK, "ACCOUNT_FLAG_NOKICK" },
{ ACCOUNT_FLAG_COLLECTOR, "ACCOUNT_FLAG_COLLECTOR" },
{ ACCOUNT_FLAG_TRIAL, "ACCOUNT_FLAG_TRIAL" },
{ ACCOUNT_FLAG_CANCELLED, "ACCOUNT_FLAG_CANCELLED" },
{ ACCOUNT_FLAG_IGR, "ACCOUNT_FLAG_IGR" },
{ ACCOUNT_FLAG_WHOLESALER, "ACCOUNT_FLAG_WHOLESALER" },
{ ACCOUNT_FLAG_PRIVILEGED, "ACCOUNT_FLAG_PRIVILEGED" },
{ ACCOUNT_FLAG_EU_FORBID_ELV, "ACCOUNT_FLAG_EU_FORBID_ELV" },
{ ACCOUNT_FLAG_EU_FORBID_BILLING, "ACCOUNT_FLAG_EU_FORBID_BILLING" },
{ ACCOUNT_FLAG_RESTRICTED, "ACCOUNT_FLAG_RESTRICTED" },
{ ACCOUNT_FLAG_REFERRAL, "ACCOUNT_FLAG_REFERRAL" },
{ ACCOUNT_FLAG_BLIZZARD, "ACCOUNT_FLAG_BLIZZARD" },
{ ACCOUNT_FLAG_RECURRING_BILLING, "ACCOUNT_FLAG_RECURRING_BILLING" },
{ ACCOUNT_FLAG_NOELECTUP, "ACCOUNT_FLAG_NOELECTUP" },
{ ACCOUNT_FLAG_KR_CERTIFICATE, "ACCOUNT_FLAG_KR_CERTIFICATE" },
{ ACCOUNT_FLAG_EXPANSION_COLLECTOR, "ACCOUNT_FLAG_EXPANSION_COLLECTOR" },
{ ACCOUNT_FLAG_DISABLE_VOICE, "ACCOUNT_FLAG_DISABLE_VOICE" },
{ ACCOUNT_FLAG_DISABLE_VOICE_SPEAK, "ACCOUNT_FLAG_DISABLE_VOICE_SPEAK" },
{ ACCOUNT_FLAG_REFERRAL_RESURRECT, "ACCOUNT_FLAG_REFERRAL_RESURRECT" },
{ ACCOUNT_FLAG_EU_FORBID_CC, "ACCOUNT_FLAG_EU_FORBID_CC" },
{ ACCOUNT_FLAG_OPENBETA_DELL, "ACCOUNT_FLAG_OPENBETA_DELL" },
{ ACCOUNT_FLAG_PROPASS, "ACCOUNT_FLAG_PROPASS" },
{ ACCOUNT_FLAG_PROPASS_LOCK, "ACCOUNT_FLAG_PROPASS_LOCK" },
{ ACCOUNT_FLAG_PENDING_UPGRADE, "ACCOUNT_FLAG_PENDING_UPGRADE" },
{ ACCOUNT_FLAG_RETAIL_FROM_TRIAL, "ACCOUNT_FLAG_RETAIL_FROM_TRIAL" },
{ ACCOUNT_FLAG_EXPANSION2_COLLECTOR, "ACCOUNT_FLAG_EXPANSION2_COLLECTOR" },
{ ACCOUNT_FLAG_OVERMIND_LINKED, "ACCOUNT_FLAG_OVERMIND_LINKED" },
{ ACCOUNT_FLAG_DEMOS, "ACCOUNT_FLAG_DEMOS" },
{ ACCOUNT_FLAG_DEATH_KNIGHT_OK, "ACCOUNT_FLAG_DEATH_KNIGHT_OK" },
{ ACCOUNT_FLAG_S2_REQUIRE_IGR, "ACCOUNT_FLAG_S2_REQUIRE_IGR" },
{ ACCOUNT_FLAG_S2_TRIAL, "ACCOUNT_FLAG_S2_TRIAL" },
// { ACCOUNT_FLAG_S2_RESTRICTED, "ACCOUNT_FLAG_S2_RESTRICTED" }
};
std::string const GetLocalizeCreatureName(Creature* creature, LocaleConstant locale)
{
auto creatureTemplate = sObjectMgr->GetCreatureTemplate(creature->GetEntry());
@@ -2193,6 +2237,15 @@ public:
// Output V. LANG_PINFO_ACC_ACCOUNT
handler->PSendSysMessage(LANG_PINFO_ACC_ACCOUNT, userName, accId, security);
if (playerTarget)
{
uint32 accountFlags = playerTarget->GetSession()->GetAccountFlags();
handler->PSendSysMessage(LANG_ACCOUNT_FLAGS_PINFO);
for (uint8 i = 0; i < MAX_ACCOUNT_FLAG; i++)
if (accountFlags & static_cast<uint32>(accountFlagText[i].flag))
handler->PSendSysMessage(LANG_SUBCMDS_LIST_ENTRY, accountFlagText[i].text);
}
// Output VI. LANG_PINFO_ACC_LASTLOGIN
handler->PSendSysMessage(LANG_PINFO_ACC_LASTLOGIN, lastLogin, failedLogins);
@@ -692,6 +692,7 @@ public:
// Start Event
Start(true, false);
SetDespawnAtEnd(false);
SetDespawnAtFar(false);
ScheduleNextEvent(currentEvent, 9000);
break;
@@ -31,14 +31,7 @@ enum Spells
SPELL_REMOVE_MUSHROOM_POWER = 57283,
// Mushroom
SPELL_HEALTHY_MUSHROOM_POTENT_FUNGUS = 56648,
SPELL_POISONOUS_MUSHROOM_POISON_CLOUD = 57061,
SPELL_POISONOUS_MUSHROOM_VISUAL_AURA = 56741,
SPELL_POISONOUS_MUSHROOM_VISUAL_AREA = 61566, // Self
SPELL_HEALTHY_MUSHROOM_VISUAL_AURA = 56740,
SPELL_PUTRID_MUSHROOM = 31690,
SPELL_GROW = 57059,
SPELL_SHRINK = 31691,
SPELL_HEALTHY_MUSHROOM_POTENT_FUNGUS = 56648
};
enum Creatures
@@ -94,13 +87,12 @@ Position const MushroomPositions[MAX_MUSHROOMS_COUNT] =
struct boss_amanitar : public BossAI
{
boss_amanitar(Creature* creature) : BossAI(creature, DATA_AMANITAR), mushroomsSummoned(false) { }
boss_amanitar(Creature* creature) : BossAI(creature, DATA_AMANITAR) { }
void Reset() override
{
_Reset();
_mushroomsDeque.clear();
mushroomsSummoned = false;
}
void JustEngagedWith(Unit* /*attacker*/) override
@@ -110,7 +102,7 @@ struct boss_amanitar : public BossAI
}, 10s, 15s);
ScheduleTimedEvent(10s, 14s, [&] {
DoCastVictim(SPELL_BASH, false);
DoCastVictim(SPELL_BASH);
}, 15s, 20s);
ScheduleTimedEvent(15s, 20s, [&] {
@@ -153,10 +145,10 @@ struct boss_amanitar : public BossAI
instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_MINI);
}
void SummonedCreatureDies(Creature* summon, Unit* killer) override
void SummonedCreatureDespawn(Creature* summon) override
{
_mushroomsDeque.push_back(summon->GetPosition());
BossAI::SummonedCreatureDies(summon, killer);
BossAI::SummonedCreatureDespawn(summon);
}
void EnterEvadeMode(EvadeReason why) override
@@ -167,7 +159,6 @@ struct boss_amanitar : public BossAI
private:
std::deque<Position> _mushroomsDeque;
bool mushroomsSummoned;
void SummonMushroom(Position const& pos)
{
@@ -175,98 +166,6 @@ private:
}
};
struct npc_amanitar_mushrooms : public ScriptedAI
{
npc_amanitar_mushrooms(Creature* pCreature) : ScriptedAI(pCreature)
{
me->SetCombatMovement(false);
//TODO: this prolly needs to be done in database
pCreature->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
pCreature->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
pCreature->SetRegeneratingHealth(false);
}
// Disabled events
void JustEngagedWith(Unit* /*who*/) override {}
void AttackStart(Unit* /*victim*/) override {}
void EnterEvadeMode(EvadeReason /*why*/) override {}
void Reset() override
{
me->SetReactState(REACT_PASSIVE);
DoCastSelf(SPELL_PUTRID_MUSHROOM);
if (me->GetEntry() == NPC_POISONOUS_MUSHROOM)
{
DoCastSelf(SPELL_POISONOUS_MUSHROOM_VISUAL_AURA, true);
}
else
{
DoCastSelf(SPELL_HEALTHY_MUSHROOM_VISUAL_AURA, true);
}
events.ScheduleEvent(EVENT_GROW, 800ms);
if (me->GetEntry() == NPC_POISONOUS_MUSHROOM)
{
events.ScheduleEvent(EVENT_CHECK_PLAYER, 250ms);
}
}
void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damagetype*/, SpellSchoolMask /*damageSchoolMask*/) override
{
if (me->GetEntry() == NPC_HEALTHY_MUSHROOM && damage >= me->GetHealth())
{
DoCastSelf(SPELL_HEALTHY_MUSHROOM_POTENT_FUNGUS, true);
}
}
void UpdateAI(uint32 diff) override
{
if (events.Empty())
return;
events.Update(diff);
while (uint32 const eventId = events.ExecuteEvent())
{
switch (eventId)
{
case EVENT_GROW:
{
DoCastSelf(SPELL_GROW);
break;
}
case EVENT_CHECK_PLAYER:
{
if (Player* plr = me->SelectNearestPlayer(2.0f))
{
plr->RemoveAurasDueToSpell(SPELL_HEALTHY_MUSHROOM_POTENT_FUNGUS);
DoCastSelf(SPELL_POISONOUS_MUSHROOM_VISUAL_AREA);
DoCastSelf(SPELL_POISONOUS_MUSHROOM_POISON_CLOUD);
DoCastSelf(SPELL_SHRINK);
events.ScheduleEvent(EVENT_KILLSELF, 4s);
}
else
{
events.Repeat(250ms);
}
break;
}
case EVENT_KILLSELF:
{
me->DisappearAndDie();
break;
}
}
}
}
private:
EventMap events;
};
// 57283 Remove Mushroom Power
class spell_amanitar_remove_mushroom_power : public AuraScript
{
@@ -274,8 +173,7 @@ class spell_amanitar_remove_mushroom_power : public AuraScript
void HandleApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
{
if (Unit* target = GetTarget())
target->RemoveAurasDueToSpell(SPELL_HEALTHY_MUSHROOM_POTENT_FUNGUS);
GetTarget()->RemoveAurasDueToSpell(SPELL_HEALTHY_MUSHROOM_POTENT_FUNGUS);
}
void Register() override
@@ -287,8 +185,5 @@ class spell_amanitar_remove_mushroom_power : public AuraScript
void AddSC_boss_amanitar()
{
RegisterAhnKahetCreatureAI(boss_amanitar);
RegisterAhnKahetCreatureAI(npc_amanitar_mushrooms);
// Spells
RegisterSpellScript(spell_amanitar_remove_mushroom_power);
}
@@ -56,6 +56,8 @@ enum Misc
MAX_EMBRACE_DMG_H = 40000,
SUMMON_GROUP_TRIGGERS = 0,
GROUP_COMBAT_ABILITIES = 1,
};
enum Actions
@@ -64,15 +66,6 @@ enum Actions
ACTION_SPHERE,
};
enum Event
{
EVENT_PRINCE_FLAME_SPHERES = 1,
EVENT_PRINCE_VANISH,
EVENT_PRINCE_BLOODTHIRST,
EVENT_PRINCE_VANISH_RUN,
EVENT_PRINCE_RESCHEDULE,
};
enum Yells
{
//SAY_SPHERE_ACTIVATED = 0,
@@ -178,9 +171,12 @@ private:
struct boss_taldaram : public BossAI
{
boss_taldaram(Creature* pCreature) : BossAI(pCreature, DATA_PRINCE_TALDARAM),
vanishDamage(0)
boss_taldaram(Creature* pCreature) : BossAI(pCreature, DATA_PRINCE_TALDARAM), vanishDamage(0)
{
scheduler.SetValidator([this]
{
return !me->HasUnitState(UNIT_STATE_CASTING);
});
}
void InitializeAI() override
@@ -193,19 +189,16 @@ struct boss_taldaram : public BossAI
me->SetImmuneToAll(true);
me->SetDisableGravity(true);
me->SetHover(true);
if (!me->HasAura(SPELL_BEAM_VISUAL))
{
DoCastSelf(SPELL_BEAM_VISUAL, true);
}
me->SummonCreatureGroup(SUMMON_GROUP_TRIGGERS);
return;
}
if (instance->GetPersistentData(DATA_TELDRAM_SPHERE1) == DONE && instance->GetPersistentData(DATA_TELDRAM_SPHERE2) == DONE)
{
DoAction(ACTION_REMOVE_PRISON_AT_RESET);
}
}
void Reset() override
@@ -213,7 +206,45 @@ struct boss_taldaram : public BossAI
_Reset();
vanishDamage = 0;
vanishTarget_GUID.Clear();
me->SetReactState(REACT_AGGRESSIVE);
ScheduleHealthCheckEvent({ 75, 50, 25 }, [&] {
scheduler.Schedule(1s, [this](TaskContext)
{
Talk(SAY_VANISH);
DoCastSelf(SPELL_VANISH);
me->SetReactState(REACT_PASSIVE);
me->GetMotionMaster()->Clear();
DoStopAttack();
scheduler.CancelGroup(GROUP_COMBAT_ABILITIES);
});
});
}
void OnAuraRemove(AuraApplication* auraApp, AuraRemoveMode mode) override
{
if (auraApp->GetBase()->GetId() == SPELL_VANISH && mode == AURA_REMOVE_BY_EXPIRE)
{
scheduler.Schedule(2500ms, [this](TaskContext)
{
if (Unit* vanishTarget = SelectTarget(SelectTargetMethod::Random, 0, 100.0f, true))
{
vanishDamage = 0;
DoCast(vanishTarget, SPELL_SHADOWSTEP);
DoCast(vanishTarget, SPELL_EMBRACE_OF_THE_VAMPYR);
}
me->m_Events.AddEventAtOffset([&] {
if (!scheduler.IsGroupScheduled(GROUP_COMBAT_ABILITIES))
{
ScheduleCombatEvents();
me->SetReactState(REACT_AGGRESSIVE);
me->ResumeChasingVictim();
}
}, 20s);
});
}
}
void DoAction(int32 action) override
@@ -256,20 +287,20 @@ struct boss_taldaram : public BossAI
}
}
void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damageType*/, SpellSchoolMask /*school*/) override
void DamageTaken(Unit* attacker, uint32& damage, DamageEffectType damageType, SpellSchoolMask school) override
{
if (vanishTarget_GUID)
BossAI::DamageTaken(attacker, damage, damageType, school);
if (me->FindCurrentSpellBySpellId(SPELL_EMBRACE_OF_THE_VAMPYR))
{
if (me->FindCurrentSpellBySpellId(SPELL_EMBRACE_OF_THE_VAMPYR))
vanishDamage += damage;
if (vanishDamage >= DUNGEON_MODE<uint32>(MAX_EMBRACE_DMG, MAX_EMBRACE_DMG_H))
{
vanishDamage += damage;
if (vanishDamage >= DUNGEON_MODE<uint32>(MAX_EMBRACE_DMG, MAX_EMBRACE_DMG_H))
{
ScheduleCombatEvents();
me->CastStop();
vanishTarget_GUID.Clear();
vanishDamage = 0;
}
ScheduleCombatEvents();
me->CastStop();
me->ResumeChasingVictim();
me->SetReactState(REACT_AGGRESSIVE);
vanishDamage = 0;
}
}
}
@@ -283,17 +314,9 @@ struct boss_taldaram : public BossAI
void KilledUnit(Unit* victim) override
{
if (!victim->IsPlayer())
{
return;
}
Talk(SAY_SLAY);
if (vanishTarget_GUID && victim->GetGUID() == vanishTarget_GUID)
{
vanishTarget_GUID.Clear();
vanishDamage = 0;
}
}
void JustEngagedWith(Unit* /*who*/) override
@@ -309,9 +332,7 @@ struct boss_taldaram : public BossAI
void SpellHitTarget(Unit* /*target*/, SpellInfo const* spellInfo) override
{
if (spellInfo->Id == SPELL_CONJURE_FLAME_SPHERE)
{
summons.DoAction(ACTION_SPHERE);
}
}
void JustSummoned(Creature* summon) override
@@ -324,9 +345,7 @@ struct boss_taldaram : public BossAI
case NPC_FLAME_SPHERE_3:
{
if (npc_taldaram_flamesphere* summonAI = dynamic_cast<npc_taldaram_flamesphere*>(summon->AI()))
{
summonAI->SetVictimPos(victimSperePos);
}
break;
}
@@ -338,130 +357,27 @@ struct boss_taldaram : public BossAI
}
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
{
return;
}
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
{
return;
}
while (uint32 const eventId = events.ExecuteEvent())
{
switch (eventId)
{
case EVENT_PRINCE_BLOODTHIRST:
{
DoCastVictim(SPELL_BLOODTHIRST);
events.Repeat(10s);
break;
}
case EVENT_PRINCE_FLAME_SPHERES:
{
if (Unit* victim = me->GetVictim())
{
DoCast(victim, SPELL_CONJURE_FLAME_SPHERE);
victimSperePos = victim->GetPosition();
}
if (!events.GetNextEventTime(EVENT_PRINCE_VANISH))
{
events.RescheduleEvent(EVENT_PRINCE_VANISH, 14s);
}
else
{
// Make sure that Vanish won't get triggered at same time as sphere summon
events.DelayEvents(4s);
}
events.Repeat(15s);
break;
}
case EVENT_PRINCE_VANISH:
{
//Count alive players
uint8 count = 0;
std::list<HostileReference*> const t_list = me->GetThreatMgr().GetThreatList();
if (!t_list.empty())
{
for (HostileReference const* reference : t_list)
{
if (reference)
{
Unit const* pTarget = ObjectAccessor::GetUnit(*me, reference->getUnitGuid());
if (pTarget && pTarget->IsPlayer() && pTarget->IsAlive())
{
++count;
}
}
}
}
// He only vanishes if there are 3 or more alive players
if (count > 2)
{
Talk(SAY_VANISH);
DoCastSelf(SPELL_VANISH, false);
if (Unit* pEmbraceTarget = SelectTarget(SelectTargetMethod::Random, 0, 100, true))
{
vanishTarget_GUID = pEmbraceTarget->GetGUID();
}
events.CancelEvent(EVENT_PRINCE_FLAME_SPHERES);
events.CancelEvent(EVENT_PRINCE_BLOODTHIRST);
events.ScheduleEvent(EVENT_PRINCE_VANISH_RUN, 2499ms);
}
break;
}
case EVENT_PRINCE_VANISH_RUN:
{
if (Unit* _vanishTarget = ObjectAccessor::GetUnit(*me, vanishTarget_GUID))
{
vanishDamage = 0;
DoCast(_vanishTarget, SPELL_SHADOWSTEP);
me->CastSpell(_vanishTarget, SPELL_EMBRACE_OF_THE_VAMPYR, false);
me->RemoveAura(SPELL_VANISH);
}
events.ScheduleEvent(EVENT_PRINCE_RESCHEDULE, 20s);
break;
}
case EVENT_PRINCE_RESCHEDULE:
{
ScheduleCombatEvents();
break;
}
}
if (me->HasUnitState(UNIT_STATE_CASTING))
{
return;
}
}
if (me->IsVisible())
{
DoMeleeAttackIfReady();
}
}
private:
Position victimSperePos;
ObjectGuid vanishTarget_GUID;
uint32 vanishDamage;
void ScheduleCombatEvents()
{
events.Reset();
events.RescheduleEvent(EVENT_PRINCE_FLAME_SPHERES, 10s);
events.RescheduleEvent(EVENT_PRINCE_BLOODTHIRST, 10s);
vanishTarget_GUID.Clear();
scheduler.Schedule(10s, GROUP_COMBAT_ABILITIES, [this](TaskContext context)
{
DoCastVictim(SPELL_BLOODTHIRST);
context.Repeat(15s);
}).Schedule(10s, GROUP_COMBAT_ABILITIES, [this](TaskContext context)
{
if (Unit* victim = me->GetVictim())
{
DoCast(victim, SPELL_CONJURE_FLAME_SPHERE);
victimSperePos = victim->GetPosition();
}
context.Repeat(15s);
});
vanishDamage = 0;
}
};
+31 -37
View File
@@ -74,6 +74,9 @@ enum DeathKnightSpells
SPELL_DK_UNHOLY_PRESENCE_TRIGGERED = 49772,
SPELL_DK_WILL_OF_THE_NECROPOLIS_TALENT_R1 = 49189,
SPELL_DK_WILL_OF_THE_NECROPOLIS_AURA_R1 = 52284,
SPELL_DK_ICY_TALONS_TALENT_R1 = 50880,
SPELL_DK_CRYPT_FEVER_R1 = 50508,
SPELL_DK_EBON_PLAGUE_R1 = 51726,
// Risen Ally
SPELL_DK_RAISE_ALLY = 46619,
SPELL_DK_THRASH = 47480,
@@ -342,7 +345,7 @@ class spell_dk_death_and_decay_aura : public AuraScript
if (GetCaster() && GetTarget())
{
int32 basePoints0 = aurEff->GetAmount();
GetCaster()->CastCustomSpell(GetTarget(), SPELL_DK_DEATH_AND_DECAY_TRIGGER, &basePoints0, nullptr, nullptr, true, 0, aurEff);
GetCaster()->CastCustomSpell(GetTarget(), SPELL_DK_DEATH_AND_DECAY_TRIGGER, &basePoints0, nullptr, nullptr, false, 0, aurEff);
}
}
@@ -1746,7 +1749,8 @@ class spell_dk_pestilence : public SpellScript
{
SPELL_DK_GLYPH_OF_DISEASE,
SPELL_DK_BLOOD_PLAGUE,
SPELL_DK_FROST_FEVER
SPELL_DK_FROST_FEVER,
SPELL_DK_ICY_TALONS_TALENT_R1
});
}
@@ -1758,46 +1762,36 @@ class spell_dk_pestilence : public SpellScript
if (!target)
return;
if (target != hitUnit || caster->GetAura(SPELL_DK_GLYPH_OF_DISEASE))
// Spread on others
if (target != hitUnit)
{
// xinef: checked in target selection
//if (!m_targets.GetUnitTarget()->IsWithinLOSInMap(unitTarget))
// return;
// And spread them on target
// Blood Plague
if (Aura* disOld = target->GetAura(SPELL_DK_BLOOD_PLAGUE, caster->GetGUID()))
if (AuraEffect* effOld = disOld->GetEffect(EFFECT_0))
{
float pctMods = effOld->GetPctMods();
float crit = effOld->GetCritChance();
caster->CastSpell(hitUnit, SPELL_DK_BLOOD_PLAGUE, true);
if (Aura* disNew = hitUnit->GetAura(SPELL_DK_BLOOD_PLAGUE, caster->GetGUID()))
if (AuraEffect* effNew = disNew->GetEffect(EFFECT_0))
{
effNew->SetPctMods(pctMods);
effNew->SetCritChance(crit);
effNew->SetAmount(effNew->CalculateAmount(effNew->GetCaster()));
}
}
if (target->GetAura(SPELL_DK_BLOOD_PLAGUE, caster->GetGUID()))
caster->CastSpell(hitUnit, SPELL_DK_BLOOD_PLAGUE, true);
// Frost Fever
if (Aura* disOld = target->GetAura(SPELL_DK_FROST_FEVER, caster->GetGUID()))
if (AuraEffect* effOld = disOld->GetEffect(EFFECT_0))
{
float pctMods = effOld->GetPctMods();
float crit = effOld->GetCritChance();
caster->CastSpell(hitUnit, SPELL_DK_FROST_FEVER, true);
if (target->GetAura(SPELL_DK_FROST_FEVER, caster->GetGUID()))
caster->CastSpell(hitUnit, SPELL_DK_FROST_FEVER, true);
}
// Refresh on target
else if (caster->GetAura(SPELL_DK_GLYPH_OF_DISEASE))
{
// Blood Plague
if (Aura* disease = target->GetAura(SPELL_DK_BLOOD_PLAGUE, caster->GetGUID()))
disease->RefreshDuration();
if (Aura* disNew = hitUnit->GetAura(SPELL_DK_FROST_FEVER, caster->GetGUID()))
if (AuraEffect* effNew = disNew->GetEffect(EFFECT_0))
{
effNew->SetPctMods(pctMods);
effNew->SetCritChance(crit);
effNew->SetAmount(effNew->CalculateAmount(effNew->GetCaster()));
}
}
// Frost Fever
if (Aura* disease = target->GetAura(SPELL_DK_FROST_FEVER, caster->GetGUID()))
{
disease->RefreshDuration();
if (Aura const* talons = caster->GetAuraOfRankedSpell(SPELL_DK_ICY_TALONS_TALENT_R1))
caster->CastSpell(caster, talons->GetSpellInfo()->Effects[EFFECT_0].TriggerSpell, true);
}
if (Aura* disease = target->GetAuraOfRankedSpell(SPELL_DK_EBON_PLAGUE_R1, caster->GetGUID()))
disease->RefreshDuration();
else if (Aura* disease = target->GetAuraOfRankedSpell(SPELL_DK_CRYPT_FEVER_R1, caster->GetGUID()))
disease->RefreshDuration();
}
}
+56
View File
@@ -60,6 +60,7 @@ enum DruidSpells
SPELL_DRUID_ENRAGE = 5229,
SPELL_DRUID_ENRAGED_DEFENSE = 70725,
SPELL_DRUID_ITEM_T10_FERAL_4P_BONUS = 70726,
SPELL_DRUID_MOONGLADE_2P_BONUS = 37286
};
enum DruidIcons
@@ -1194,6 +1195,60 @@ class spell_dru_moonkin_form_passive_proc : public AuraScript
}
};
// -774 - Rejuvenation
class spell_dru_rejuvenation_moonglade_2_set : public AuraScript
{
PrepareAuraScript(spell_dru_rejuvenation_moonglade_2_set);
bool Validate(SpellInfo const* /*spellInfo*/) override
{
return ValidateSpellInfo({ SPELL_DRUID_MOONGLADE_2P_BONUS });
}
bool Load() override
{
_casterGUID.Clear();
return true;
}
void OnApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
{
if (Player* caster = ObjectAccessor::FindPlayer(GetCasterGUID()))
if (caster->HasAura(SPELL_DRUID_MOONGLADE_2P_BONUS))
{
Player* target = GetTarget()->ToPlayer();
if (!target)
return;
_casterGUID = GetCasterGUID();
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(SPELL_DRUID_MOONGLADE_2P_BONUS);
target->ApplyRatingMod(CR_DODGE, spellInfo->Effects[EFFECT_0].CalcValue(), true); // 35 rating
}
}
void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
{
if (_casterGUID)
{
Player* target = GetTarget()->ToPlayer();
if (!target)
return;
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(SPELL_DRUID_MOONGLADE_2P_BONUS);
target->ApplyRatingMod(CR_DODGE, spellInfo->Effects[EFFECT_0].CalcValue(), false); // 35 rating
}
}
void Register() override
{
AfterEffectApply += AuraEffectApplyFn(spell_dru_rejuvenation_moonglade_2_set::OnApply, EFFECT_0, SPELL_AURA_PERIODIC_HEAL, AURA_EFFECT_HANDLE_REAL);
AfterEffectRemove += AuraEffectRemoveFn(spell_dru_rejuvenation_moonglade_2_set::OnRemove, EFFECT_0, SPELL_AURA_PERIODIC_HEAL, AURA_EFFECT_HANDLE_REAL);
}
private:
ObjectGuid _casterGUID;
};
void AddSC_druid_spell_scripts()
{
RegisterSpellScript(spell_dru_bear_form_passive);
@@ -1229,4 +1284,5 @@ void AddSC_druid_spell_scripts()
RegisterSpellScript(spell_dru_t10_restoration_4p_bonus);
RegisterSpellScript(spell_dru_wild_growth);
RegisterSpellScript(spell_dru_moonkin_form_passive_proc);
RegisterSpellScript(spell_dru_rejuvenation_moonglade_2_set);
}
+1 -1
View File
@@ -910,7 +910,7 @@ class spell_warr_heroic_strike : public SpellScript
Unit* target = GetHitUnit();
if (!target)
return;
std::list<AuraEffect*> AuraEffectList = target->GetAuraEffectsByType(SPELL_AURA_MOD_DECREASE_SPEED);
Unit::AuraEffectList const& AuraEffectList = target->GetAuraEffectsByType(SPELL_AURA_MOD_DECREASE_SPEED);
bool bonusDamage = false;
for (AuraEffect* eff : AuraEffectList)
{
+1 -1
View File
@@ -465,7 +465,7 @@ enum SpellAttr2 : uint32
SPELL_ATTR2_ALWAYS_CAST_AS_UNIT = 0x00000200, // TITLE Unknown attribute 9@Attr2
SPELL_ATTR2_SPECIAL_TAMING_FLAG = 0x00000400, // TITLE Unknown attribute 10@Attr2 DESCRIPTION Related to taming?
SPELL_ATTR2_NO_TARGET_PER_SECOND_COST = 0x00000800, // TITLE Health Funnel
SPELL_ATTR2_CHAIN_FROM_CASTER = 0x00001000, // TITLE Unknown attribute 12@Attr2 DESCRIPTION Cleave, Heart Strike, Maul, Sunder Armor, Swipe
SPELL_ATTR2_CHAIN_FROM_CASTER = 0x00001000, // TITLE Chain from caster DESCRIPTION Cleave, Heart Strike, Maul, Sunder Armor, Swipe
SPELL_ATTR2_ENCHANT_OWN_ITEM_ONLY = 0x00002000, // TITLE Enchant persists when entering arena
SPELL_ATTR2_ALLOW_WHILE_INVISIBLE = 0x00004000, // TITLE Unknown attribute 14@Attr2
SPELL_ATTR2_DO_NOT_CONSUME_IF_GAINED_DURING_CAST = 0x00008000, // TITLE Unused attribute 15@Attr2 DESCRIPTION not set in 3.3.5a