mirror of
https://github.com/mod-playerbots/azerothcore-wotlk.git
synced 2026-01-29 08:33:47 +00:00
Merge branch 'master' into Playerbot
This commit is contained in:
@@ -30,13 +30,20 @@ char const* localeNames[TOTAL_LOCALES] =
|
||||
"ruRU"
|
||||
};
|
||||
|
||||
bool IsLocaleValid(std::string const& locale)
|
||||
{
|
||||
for (int i = 0; i < TOTAL_LOCALES; ++i)
|
||||
if (locale == localeNames[i])
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
LocaleConstant GetLocaleByName(const std::string& name)
|
||||
{
|
||||
for (uint32 i = 0; i < TOTAL_LOCALES; ++i)
|
||||
if (name == localeNames[i])
|
||||
{
|
||||
return LocaleConstant(i);
|
||||
}
|
||||
|
||||
return LOCALE_enUS; // including enGB case
|
||||
}
|
||||
|
||||
@@ -83,6 +83,7 @@ enum LocaleConstant
|
||||
|
||||
AC_COMMON_API extern char const* localeNames[TOTAL_LOCALES];
|
||||
|
||||
AC_COMMON_API bool IsLocaleValid(std::string const& locale);
|
||||
AC_COMMON_API LocaleConstant GetLocaleByName(const std::string& name);
|
||||
AC_COMMON_API const std::string GetNameByLocaleConstant(LocaleConstant localeConstant);
|
||||
AC_COMMON_API void CleanStringForMysqlQuery(std::string& str);
|
||||
|
||||
@@ -116,10 +116,11 @@ void LoginDatabaseConnection::DoPrepareStatements()
|
||||
PrepareStatement(LOGIN_SEL_REALMLIST_SECURITY_LEVEL, "SELECT allowedSecurityLevel from realmlist WHERE id = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(LOGIN_DEL_ACCOUNT, "DELETE FROM account WHERE id = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(LOGIN_SEL_AUTOBROADCAST, "SELECT id, weight, text FROM autobroadcast WHERE realmid = ? OR realmid = -1", CONNECTION_SYNCH);
|
||||
PrepareStatement(LOGIN_SEL_AUTOBROADCAST_LOCALIZED, "SELECT id, locale, text FROM autobroadcast_locale WHERE realmid = ? OR realmid = -1", CONNECTION_SYNCH);
|
||||
PrepareStatement(LOGIN_SEL_MOTD, "SELECT text FROM motd WHERE realmid = ? OR realmid = -1 ORDER BY realmid DESC", CONNECTION_SYNCH);
|
||||
PrepareStatement(LOGIN_SEL_MOTD_LOCALE, "SELECT locale, text FROM motd_localized WHERE realmid = ? OR realmid = -1 ORDER BY realmid DESC", CONNECTION_SYNCH);
|
||||
PrepareStatement(LOGIN_INS_MOTD, "INSERT INTO motd (realmid, text) VALUES (?, ?) ON DUPLICATE KEY UPDATE text = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(LOGIN_INS_MOTD_LOCALE, "INSERT INTO motd_localized (realmid, locale, text) VALUES(?, ?, ?) ON DUPLICATE KEY UPDATE text = ?;", CONNECTION_ASYNC);
|
||||
PrepareStatement(LOGIN_REP_MOTD, "REPLACE INTO motd (realmid, text) VALUES (?, ?)", CONNECTION_ASYNC);
|
||||
PrepareStatement(LOGIN_REP_MOTD_LOCALE, "REPLACE INTO motd_localized (realmid, locale, text) VALUES (?, ?, ?)", CONNECTION_ASYNC);
|
||||
PrepareStatement(LOGIN_INS_ACCOUNT_MUTE, "INSERT INTO account_muted VALUES (?, UNIX_TIMESTAMP(), ?, ?, ?)", CONNECTION_ASYNC);
|
||||
PrepareStatement(LOGIN_SEL_ACCOUNT_MUTE_INFO, "SELECT mutedate, mutetime, mutereason, mutedby FROM account_muted WHERE guid = ? ORDER BY mutedate ASC", CONNECTION_SYNCH);
|
||||
PrepareStatement(LOGIN_DEL_ACCOUNT_MUTED, "DELETE FROM account_muted WHERE guid = ?", CONNECTION_ASYNC);
|
||||
|
||||
@@ -98,10 +98,11 @@ enum LoginDatabaseStatements : uint32
|
||||
LOGIN_SEL_REALMLIST_SECURITY_LEVEL,
|
||||
LOGIN_DEL_ACCOUNT,
|
||||
LOGIN_SEL_AUTOBROADCAST,
|
||||
LOGIN_SEL_AUTOBROADCAST_LOCALIZED,
|
||||
LOGIN_SEL_MOTD,
|
||||
LOGIN_SEL_MOTD_LOCALE,
|
||||
LOGIN_INS_MOTD,
|
||||
LOGIN_INS_MOTD_LOCALE,
|
||||
LOGIN_REP_MOTD,
|
||||
LOGIN_REP_MOTD_LOCALE,
|
||||
LOGIN_SEL_LAST_ATTEMPT_IP,
|
||||
LOGIN_SEL_LAST_IP,
|
||||
LOGIN_INS_ALDL_IP_LOGGING,
|
||||
|
||||
@@ -42,7 +42,6 @@ void AutobroadcastMgr::LoadAutobroadcasts()
|
||||
if (!result)
|
||||
{
|
||||
LOG_WARN("autobroadcast", ">> Loaded 0 autobroadcasts definitions. DB table `autobroadcast` is empty for this realm!");
|
||||
LOG_INFO("autobroadcast", " ");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -54,35 +53,66 @@ void AutobroadcastMgr::LoadAutobroadcasts()
|
||||
_announceType = AnnounceType::World;
|
||||
}
|
||||
|
||||
uint32 count = 0;
|
||||
do
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
uint8 textId = fields[0].Get<uint8>();
|
||||
|
||||
ObjectMgr::AddLocaleString(fields[2].Get<std::string>(), DEFAULT_LOCALE, _autobroadcasts[textId]);
|
||||
_autobroadcastsWeights[textId] = fields[1].Get<uint8>();
|
||||
|
||||
} while (result->NextRow());
|
||||
|
||||
LOG_INFO("server.loading", ">> Loaded {} Autobroadcast Definitions in {} ms", _autobroadcasts.size(), GetMSTimeDiffToNow(oldMSTime));
|
||||
}
|
||||
|
||||
void AutobroadcastMgr::LoadAutobroadcastsLocalized()
|
||||
{
|
||||
uint32 oldMSTime = getMSTime();
|
||||
uint32 realmId = sConfigMgr->GetOption<int32>("RealmID", 0);
|
||||
|
||||
if (_autobroadcasts.empty())
|
||||
return;
|
||||
|
||||
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_AUTOBROADCAST_LOCALIZED);
|
||||
stmt->SetData(0, realmId);
|
||||
PreparedQueryResult result = LoginDatabase.Query(stmt);
|
||||
|
||||
if (!result)
|
||||
{
|
||||
LOG_WARN("server.loading", ">> Loaded 0 localized autobroadcasts definitions. DB table `autobroadcast_localized` is empty for this realm!");
|
||||
LOG_INFO("server.loading", " ");
|
||||
return;
|
||||
}
|
||||
|
||||
uint8 count = 0;
|
||||
|
||||
do
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
uint8 id = fields[0].Get<uint8>();
|
||||
uint8 textId = fields[0].Get<uint8>();
|
||||
LocaleConstant locale = GetLocaleByName(fields[1].Get<std::string>());
|
||||
|
||||
_autobroadcasts[id] = fields[2].Get<std::string>();
|
||||
_autobroadcastsWeights[id] = fields[1].Get<uint8>();
|
||||
if (locale == DEFAULT_LOCALE || ObjectMgr::GetLocaleString(_autobroadcasts[textId], DEFAULT_LOCALE).empty())
|
||||
continue;
|
||||
|
||||
++count;
|
||||
ObjectMgr::AddLocaleString(fields[2].Get<std::string>(), locale, _autobroadcasts[textId]);
|
||||
count++;
|
||||
} while (result->NextRow());
|
||||
|
||||
LOG_INFO("autobroadcast", ">> Loaded {} Autobroadcast Definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
|
||||
LOG_INFO("autobroadcast", " ");
|
||||
LOG_INFO("server.loading", ">> Loaded {} Localized Autobroadcast Definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
|
||||
LOG_INFO("server.loading", " ");
|
||||
}
|
||||
|
||||
void AutobroadcastMgr::SendAutobroadcasts()
|
||||
{
|
||||
if (_autobroadcasts.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
uint32 weight = 0;
|
||||
uint8 textId = 0;
|
||||
AutobroadcastsWeightMap selectionWeights;
|
||||
|
||||
std::string msg;
|
||||
|
||||
for (AutobroadcastsWeightMap::const_iterator it = _autobroadcastsWeights.begin(); it != _autobroadcastsWeights.end(); ++it)
|
||||
{
|
||||
if (it->second)
|
||||
@@ -101,42 +131,78 @@ void AutobroadcastMgr::SendAutobroadcasts()
|
||||
weight += it->second;
|
||||
if (selectedWeight < weight)
|
||||
{
|
||||
msg = _autobroadcasts[it->first];
|
||||
textId = it->first;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
msg = _autobroadcasts[urand(0, _autobroadcasts.size())];
|
||||
textId = urand(0, _autobroadcasts.size());
|
||||
}
|
||||
|
||||
switch (_announceType)
|
||||
{
|
||||
case AnnounceType::World:
|
||||
SendWorldAnnouncement(msg);
|
||||
SendWorldAnnouncement(textId);
|
||||
break;
|
||||
case AnnounceType::Notification:
|
||||
SendNotificationAnnouncement(msg);
|
||||
SendNotificationAnnouncement(textId);
|
||||
break;
|
||||
case AnnounceType::Both:
|
||||
SendWorldAnnouncement(msg);
|
||||
SendNotificationAnnouncement(msg);
|
||||
SendWorldAnnouncement(textId);
|
||||
SendNotificationAnnouncement(textId);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
LOG_DEBUG("autobroadcast", "AutobroadcastMgr::SendAutobroadcasts: '{}'", msg);
|
||||
LOG_DEBUG("autobroadcast", "AutobroadcastMgr::SendAutobroadcasts: '{}'", textId);
|
||||
}
|
||||
|
||||
void AutobroadcastMgr::SendWorldAnnouncement(std::string msg)
|
||||
void AutobroadcastMgr::SendWorldAnnouncement(uint8 textId)
|
||||
{
|
||||
ChatHandler(nullptr).SendWorldTextOptional(LANG_AUTO_BROADCAST, ANNOUNCER_FLAG_DISABLE_AUTOBROADCAST, msg.data());
|
||||
// Send localized messages to all sessions
|
||||
ChatHandler(nullptr).DoForAllValidSessions([&](Player* player)
|
||||
{
|
||||
// Get player's locale
|
||||
LocaleConstant locale = player->GetSession()->GetSessionDbLocaleIndex();
|
||||
|
||||
if (!_autobroadcasts.empty())
|
||||
return;
|
||||
|
||||
std::string_view localizedMessage = ObjectMgr::GetLocaleString(_autobroadcasts[textId], locale);
|
||||
|
||||
// Check if there is a localized message if not use default one.
|
||||
if (localizedMessage.empty())
|
||||
localizedMessage = ObjectMgr::GetLocaleString(_autobroadcasts[textId], DEFAULT_LOCALE);
|
||||
|
||||
// Send the localized or fallback message
|
||||
ChatHandler(player->GetSession()).SendWorldTextOptional(localizedMessage, ANNOUNCER_FLAG_DISABLE_AUTOBROADCAST);
|
||||
});
|
||||
}
|
||||
|
||||
void AutobroadcastMgr::SendNotificationAnnouncement(std::string msg)
|
||||
void AutobroadcastMgr::SendNotificationAnnouncement(uint8 textId)
|
||||
{
|
||||
WorldPacket data(SMSG_NOTIFICATION, (msg.size() + 1));
|
||||
data << msg.data();
|
||||
sWorld->SendGlobalMessage(&data);
|
||||
ChatHandler(nullptr).DoForAllValidSessions([&](Player* player)
|
||||
{
|
||||
// Retrieve player's locale
|
||||
LocaleConstant locale = player->GetSession()->GetSessionDbLocaleIndex();
|
||||
|
||||
if (!_autobroadcasts.count(textId))
|
||||
return;
|
||||
|
||||
// Get localized message
|
||||
std::string_view localizedMessage = ObjectMgr::GetLocaleString(_autobroadcasts[textId], locale);
|
||||
|
||||
// Check if there is a localized message if not use default one.
|
||||
if (localizedMessage.empty())
|
||||
localizedMessage = ObjectMgr::GetLocaleString(_autobroadcasts[textId], DEFAULT_LOCALE);
|
||||
|
||||
// Prepare the WorldPacket
|
||||
WorldPacket data(SMSG_NOTIFICATION, (localizedMessage.size() + 1));
|
||||
data << localizedMessage;
|
||||
|
||||
// Send packet to the player
|
||||
player->GetSession()->SendPacket(&data);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include "Common.h"
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
enum class AnnounceType : uint8
|
||||
{
|
||||
@@ -34,17 +35,18 @@ public:
|
||||
static AutobroadcastMgr* instance();
|
||||
|
||||
void LoadAutobroadcasts();
|
||||
void LoadAutobroadcastsLocalized();
|
||||
void SendAutobroadcasts();
|
||||
|
||||
private:
|
||||
void SendWorldAnnouncement(std::string msg);
|
||||
void SendNotificationAnnouncement(std::string msg);
|
||||
void SendWorldAnnouncement(uint8 textId);
|
||||
void SendNotificationAnnouncement(uint8 textId);
|
||||
|
||||
typedef std::map<uint8, std::string> AutobroadcastsMap;
|
||||
typedef std::map<uint8, std::vector<std::string>> AutobroadcastsMap;
|
||||
typedef std::map<uint8, uint8> AutobroadcastsWeightMap;
|
||||
|
||||
AutobroadcastsMap _autobroadcasts;
|
||||
AutobroadcastsWeightMap _autobroadcastsWeights;
|
||||
AutobroadcastsMap _autobroadcasts; // autobroadcast messages
|
||||
AutobroadcastsWeightMap _autobroadcastsWeights; // Weights for each message
|
||||
|
||||
AnnounceType _announceType;
|
||||
};
|
||||
|
||||
@@ -273,7 +273,7 @@ Creature::Creature(bool isWorldObject): Unit(isWorldObject), MovableMapObject(),
|
||||
m_transportCheckTimer(1000), lootPickPocketRestoreTime(0), m_combatPulseTime(0), m_combatPulseDelay(0), m_reactState(REACT_AGGRESSIVE), m_defaultMovementType(IDLE_MOTION_TYPE),
|
||||
m_spawnId(0), m_equipmentId(0), m_originalEquipmentId(0), m_AlreadyCallAssistance(false),
|
||||
m_AlreadySearchedAssistance(false), m_regenHealth(true), m_regenPower(true), m_AI_locked(false), m_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL), m_originalEntry(0), m_moveInLineOfSightDisabled(false), m_moveInLineOfSightStrictlyDisabled(false),
|
||||
m_homePosition(), m_transportHomePosition(), m_creatureInfo(nullptr), m_creatureData(nullptr), m_detectionDistance(20.0f), m_waypointID(0), m_path_id(0), m_formation(nullptr), m_lastLeashExtensionTime(nullptr), m_cannotReachTimer(0),
|
||||
m_homePosition(), m_transportHomePosition(), m_creatureInfo(nullptr), m_creatureData(nullptr), m_detectionDistance(20.0f),_sparringPct(0.0f), m_waypointID(0), m_path_id(0), m_formation(nullptr), m_lastLeashExtensionTime(nullptr), m_cannotReachTimer(0),
|
||||
_isMissingSwimmingFlagOutOfCombat(false), m_assistanceTimer(0), _playerDamageReq(0), _damagedByPlayer(false), _isCombatMovementAllowed(true)
|
||||
{
|
||||
m_regenTimer = CREATURE_REGEN_INTERVAL;
|
||||
@@ -608,6 +608,8 @@ bool Creature::UpdateEntry(uint32 Entry, const CreatureData* data, bool changele
|
||||
SetCanModifyStats(true);
|
||||
UpdateAllStats();
|
||||
|
||||
LoadSparringPct();
|
||||
|
||||
// checked and error show at loading templates
|
||||
if (FactionTemplateEntry const* factionTemplate = sFactionTemplateStore.LookupEntry(cInfo->faction))
|
||||
{
|
||||
@@ -1189,6 +1191,7 @@ bool Creature::Create(ObjectGuid::LowType guidlow, Map* map, uint32 phaseMask, u
|
||||
}
|
||||
|
||||
LoadCreaturesAddon();
|
||||
LoadSparringPct();
|
||||
|
||||
//! Need to be called after LoadCreaturesAddon - MOVEMENTFLAG_HOVER is set there
|
||||
m_positionZ += GetHoverHeight();
|
||||
@@ -2025,6 +2028,8 @@ void Creature::setDeathState(DeathState state, bool despawn)
|
||||
|
||||
Motion_Initialize();
|
||||
LoadCreaturesAddon(true);
|
||||
LoadSparringPct();
|
||||
|
||||
if (GetCreatureData() && GetPhaseMask() != GetCreatureData()->phaseMask)
|
||||
SetPhaseMask(GetCreatureData()->phaseMask, false);
|
||||
}
|
||||
@@ -2798,6 +2803,18 @@ bool Creature::LoadCreaturesAddon(bool reload)
|
||||
return true;
|
||||
}
|
||||
|
||||
void Creature::LoadSparringPct()
|
||||
{
|
||||
ObjectGuid::LowType spawnId = GetSpawnId();
|
||||
auto const& sparringData = sObjectMgr->GetSparringData();
|
||||
|
||||
auto itr = sparringData.find(spawnId);
|
||||
if (itr != sparringData.end() && !itr->second.empty())
|
||||
{
|
||||
_sparringPct = itr->second[0];
|
||||
}
|
||||
}
|
||||
|
||||
/// Send a message to LocalDefense channel for players opposition team in the zone
|
||||
void Creature::SendZoneUnderAttackMessage(Player* attacker)
|
||||
{
|
||||
|
||||
@@ -187,6 +187,9 @@ public:
|
||||
void UpdateAttackPowerAndDamage(bool ranged = false) override;
|
||||
void CalculateMinMaxDamage(WeaponAttackType attType, bool normalized, bool addTotalPct, float& minDamage, float& maxDamage, uint8 damageIndex) override;
|
||||
|
||||
void LoadSparringPct();
|
||||
[[nodiscard]] float GetSparringPct() const { return _sparringPct; }
|
||||
|
||||
bool HasWeapon(WeaponAttackType type) const override;
|
||||
bool HasWeaponForAttack(WeaponAttackType type) const override { return (Unit::HasWeaponForAttack(type) && HasWeapon(type)); }
|
||||
void SetCanDualWield(bool value) override;
|
||||
@@ -483,6 +486,8 @@ protected:
|
||||
float m_detectionDistance;
|
||||
uint16 m_LootMode; // bitmask, default LOOT_MODE_DEFAULT, determines what loot will be lootable
|
||||
|
||||
float _sparringPct;
|
||||
|
||||
[[nodiscard]] bool IsInvisibleDueToDespawn() const override;
|
||||
bool CanAlwaysSee(WorldObject const* obj) const override;
|
||||
bool IsAlwaysDetectableFor(WorldObject const* seer) const override;
|
||||
|
||||
@@ -1032,6 +1032,17 @@ uint32 Unit::DealDamage(Unit* attacker, Unit* victim, uint32 damage, CleanDamage
|
||||
}
|
||||
}
|
||||
|
||||
// Sparring
|
||||
if (victim->CanSparringWith(attacker))
|
||||
{
|
||||
if (damage >= victim->GetHealth())
|
||||
damage = 0;
|
||||
|
||||
uint32 sparringHealth = victim->GetHealth() * (victim->ToCreature()->GetSparringPct() / 100);
|
||||
if (victim->GetHealth() - damage <= sparringHealth)
|
||||
damage = 0;
|
||||
}
|
||||
|
||||
if (health <= damage)
|
||||
{
|
||||
LOG_DEBUG("entities.unit", "DealDamage: victim just died");
|
||||
@@ -2635,6 +2646,10 @@ void Unit::AttackerStateUpdate(Unit* victim, WeaponAttackType attType /*= BASE_A
|
||||
Unit::DealDamageMods(victim, damageInfo.damages[i].damage, &damageInfo.damages[i].absorb);
|
||||
}
|
||||
|
||||
// Related to sparring system. Allow attack animations even if there are no damages
|
||||
if (victim->CanSparringWith(damageInfo.attacker))
|
||||
damageInfo.HitInfo |= HITINFO_FAKE_DAMAGE;
|
||||
|
||||
SendAttackStateUpdate(&damageInfo);
|
||||
|
||||
//TriggerAurasProcOnEvent(damageInfo);
|
||||
@@ -3954,6 +3969,24 @@ void Unit::_UpdateAutoRepeatSpell()
|
||||
}
|
||||
}
|
||||
|
||||
bool Unit::CanSparringWith(Unit const* attacker) const
|
||||
{
|
||||
if (!IsCreature() || IsCharmedOwnedByPlayerOrPlayer())
|
||||
return false;
|
||||
|
||||
if (!attacker)
|
||||
return false;
|
||||
|
||||
if (!attacker->IsCreature() || attacker->IsCharmedOwnedByPlayerOrPlayer())
|
||||
return false;
|
||||
|
||||
if (Creature const* creature = ToCreature())
|
||||
if (!creature->GetSparringPct())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Unit::SetCurrentCastedSpell(Spell* pSpell)
|
||||
{
|
||||
ASSERT(pSpell); // nullptr may be never passed here, use InterruptSpell or InterruptNonMeleeSpells
|
||||
|
||||
@@ -726,6 +726,9 @@ public:
|
||||
void RemoveUnitFlag2(UnitFlags2 flags) { RemoveFlag(UNIT_FIELD_FLAGS_2, flags); }
|
||||
void ReplaceAllUnitFlags2(UnitFlags2 flags) { SetUInt32Value(UNIT_FIELD_FLAGS_2, flags); }
|
||||
|
||||
void SetEmoteState(Emote emoteState) { SetUInt32Value(UNIT_NPC_EMOTESTATE, emoteState); } /// @brief Sets emote state (looping emote). Emotes available in SharedDefines.h
|
||||
void ClearEmoteState() { SetEmoteState(EMOTE_ONESHOT_NONE); } /// @brief Clears emote state (looping emote)
|
||||
|
||||
// NPC flags
|
||||
NPCFlags GetNpcFlags() const { return NPCFlags(GetUInt32Value(UNIT_NPC_FLAGS)); }
|
||||
bool HasNpcFlag(NPCFlags flags) const { return HasFlag(UNIT_NPC_FLAGS, flags) != 0; }
|
||||
@@ -2044,6 +2047,8 @@ protected:
|
||||
|
||||
void _UpdateAutoRepeatSpell();
|
||||
|
||||
bool CanSparringWith(Unit const* attacker) const; ///@brief: Check if unit is eligible for sparring damages. Work only if attacker and victim are creatures.
|
||||
|
||||
bool IsAlwaysVisibleFor(WorldObject const* seer) const override;
|
||||
bool IsAlwaysDetectableFor(WorldObject const* seer) const override;
|
||||
|
||||
|
||||
@@ -2298,6 +2298,42 @@ void ObjectMgr::LoadCreatures()
|
||||
LOG_INFO("server.loading", " ");
|
||||
}
|
||||
|
||||
void ObjectMgr::LoadCreatureSparring()
|
||||
{
|
||||
uint32 oldMSTime = getMSTime();
|
||||
|
||||
QueryResult result = WorldDatabase.Query("SELECT GUID, SparringPCT FROM creature_sparring");
|
||||
|
||||
if (!result)
|
||||
{
|
||||
LOG_WARN("server.loading", ">> Loaded 0 sparring data. DB table `creature_sparring` is empty.");
|
||||
LOG_INFO("server.loading", " ");
|
||||
return;
|
||||
}
|
||||
|
||||
uint32 count = 0;
|
||||
do
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
|
||||
ObjectGuid::LowType spawnId = fields[0].Get<uint32>();
|
||||
float sparringHealthPct = fields[1].Get<float>();
|
||||
|
||||
if (!sObjectMgr->GetCreatureData(spawnId))
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Entry {} has a record in `creature_sparring` but doesn't exist in `creatures` table");
|
||||
continue;
|
||||
}
|
||||
|
||||
_creatureSparringStore[spawnId].push_back(sparringHealthPct);
|
||||
|
||||
++count;
|
||||
} while (result->NextRow());
|
||||
|
||||
LOG_INFO("server.loading", ">> Loaded {} sparring data in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
|
||||
LOG_INFO("server.loading", " ");
|
||||
}
|
||||
|
||||
void ObjectMgr::AddCreatureToGrid(ObjectGuid::LowType guid, CreatureData const* data)
|
||||
{
|
||||
uint8 mask = data->spawnMask;
|
||||
|
||||
@@ -751,6 +751,8 @@ public:
|
||||
|
||||
typedef std::map<uint32, uint32> CharacterConversionMap;
|
||||
|
||||
typedef std::unordered_map<ObjectGuid::LowType, std::vector<float>> CreatureSparringContainer;
|
||||
|
||||
GameObjectTemplate const* GetGameObjectTemplate(uint32 entry);
|
||||
bool IsGameObjectStaticTransport(uint32 entry);
|
||||
[[nodiscard]] GameObjectTemplateContainer const* GetGameObjectTemplates() const { return &_gameObjectTemplateStore; }
|
||||
@@ -1029,6 +1031,7 @@ public:
|
||||
void LoadCreatureQuestItems();
|
||||
void LoadTempSummons();
|
||||
void LoadCreatures();
|
||||
void LoadCreatureSparring();
|
||||
void LoadLinkedRespawn();
|
||||
bool SetCreatureLinkedRespawn(ObjectGuid::LowType guid, ObjectGuid::LowType linkedGuid);
|
||||
void LoadCreatureAddons();
|
||||
@@ -1202,6 +1205,9 @@ public:
|
||||
if (itr == _creatureDataStore.end()) return nullptr;
|
||||
return &itr->second;
|
||||
}
|
||||
|
||||
[[nodiscard]] CreatureSparringContainer const& GetSparringData() const { return _creatureSparringStore; }
|
||||
|
||||
CreatureData& NewOrExistCreatureData(ObjectGuid::LowType spawnId) { return _creatureDataStore[spawnId]; }
|
||||
void DeleteCreatureData(ObjectGuid::LowType spawnId);
|
||||
[[nodiscard]] ObjectGuid GetLinkedRespawnGuid(ObjectGuid guid) const
|
||||
@@ -1527,6 +1533,8 @@ private:
|
||||
PageTextContainer _pageTextStore;
|
||||
InstanceTemplateContainer _instanceTemplateStore;
|
||||
|
||||
CreatureSparringContainer _creatureSparringStore;
|
||||
|
||||
private:
|
||||
void LoadScripts(ScriptsType type);
|
||||
void LoadQuestRelationsHelper(QuestRelations& map, std::string const& table, bool starter, bool go);
|
||||
|
||||
@@ -749,6 +749,9 @@ void WorldSession::HandleAuctionListItems(WorldPacket& recvData)
|
||||
if (usable)
|
||||
{
|
||||
AuctionHouseUsablePlayerInfo usablePlayerInfo;
|
||||
usablePlayerInfo.classMask = GetPlayer()->getClassMask();
|
||||
usablePlayerInfo.raceMask = GetPlayer()->getRaceMask();
|
||||
usablePlayerInfo.level = GetPlayer()->GetLevel();
|
||||
|
||||
SkillStatusMap const& skillMap = GetPlayer()->GetSkillStatusMap();
|
||||
for (auto const& pair : skillMap)
|
||||
|
||||
@@ -111,7 +111,8 @@ enum AcoreStrings
|
||||
LANG_RBAC_PERM_REVOKED_NOT_IN_LIST = 79,
|
||||
LANG_PVPSTATS = 80,
|
||||
LANG_PVPSTATS_DISABLED = 81,
|
||||
// Free 82 - 86
|
||||
LANG_GENERIC_TWO_CURLIES_WITH_COLON = 82,
|
||||
// Free 83 - 86
|
||||
|
||||
LANG_UNKNOWN_ERROR = 87,
|
||||
LANG_2FA_COMMANDS_NOT_SETUP = 88,
|
||||
|
||||
@@ -39,11 +39,6 @@ MotdMgr* MotdMgr::instance()
|
||||
return &instance;
|
||||
}
|
||||
|
||||
bool MotdMgr::IsValidLocale(std::string const& locale) {
|
||||
// Use std::find to search for the locale in the array
|
||||
return std::find(std::begin(localeNames), std::end(localeNames), locale) != std::end(localeNames);
|
||||
}
|
||||
|
||||
void MotdMgr::SetMotd(std::string motd, LocaleConstant locale)
|
||||
{
|
||||
// scripts may change motd
|
||||
@@ -53,31 +48,76 @@ void MotdMgr::SetMotd(std::string motd, LocaleConstant locale)
|
||||
MotdPackets[locale] = CreateWorldPacket(motd);
|
||||
}
|
||||
|
||||
void MotdMgr::CreateWorldPackages()
|
||||
{
|
||||
for (auto const& [locale, motd] : MotdMap)
|
||||
// Store the constructed packet in MotdPackets with the locale as the key
|
||||
MotdPackets[locale] = CreateWorldPacket(motd);
|
||||
}
|
||||
|
||||
void MotdMgr::LoadMotd()
|
||||
{
|
||||
uint32 oldMSTime = getMSTime();
|
||||
|
||||
uint32 realmId = sConfigMgr->GetOption<int32>("RealmID", 0);
|
||||
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_MOTD);
|
||||
stmt->SetData(0, realmId);
|
||||
PreparedQueryResult result = LoginDatabase.Query(stmt);
|
||||
|
||||
// Load the main motd for the realm and assign it to enUS if available
|
||||
std::string motd = LoadDefaultMotd(realmId);
|
||||
if (result)
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
std::string motd = fields[0].Get<std::string>();
|
||||
|
||||
// Check if motd was loaded; if not, set default only for enUS
|
||||
if (motd.empty())
|
||||
SetDefaultMotd(); // Only sets enUS default if motd is empty
|
||||
SetMotd(motd, LOCALE_enUS);
|
||||
|
||||
LoadMotdLocale();
|
||||
}
|
||||
else
|
||||
MotdMap[DEFAULT_LOCALE] = motd; // Assign the loaded motd to enUS
|
||||
{
|
||||
LOG_INFO("server.loading", ">> Loaded 0 motd definitions. DB table `motd` is empty for this realm!");
|
||||
LOG_INFO("server.loading", ">> Loaded 0 motd locale definitions. DB table `motd` needs an entry to be able to load DB table `motd_locale`!");
|
||||
LOG_INFO("server.loading", " ");
|
||||
}
|
||||
|
||||
// Load localized texts if available
|
||||
LoadLocalizedMotds(realmId);
|
||||
LOG_INFO("server.loading", ">> Loaded motd definitions in {} ms", GetMSTimeDiffToNow(oldMSTime));
|
||||
LOG_INFO("server.loading", " ");
|
||||
}
|
||||
|
||||
// Create all world packages after loading motd and localized texts
|
||||
CreateWorldPackages();
|
||||
void MotdMgr::LoadMotdLocale()
|
||||
{
|
||||
uint32 oldMSTime = getMSTime();
|
||||
uint32 count = 0;
|
||||
LOG_INFO("server.loading", "Loading Motd locale...");
|
||||
|
||||
uint32 realmId = sConfigMgr->GetOption<int32>("RealmID", 0);
|
||||
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_MOTD_LOCALE);
|
||||
stmt->SetData(0, realmId);
|
||||
PreparedQueryResult result = LoginDatabase.Query(stmt);
|
||||
|
||||
if (result)
|
||||
{
|
||||
do
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
// fields[0] is the locale string and fields[1] is the localized motd text
|
||||
std::string locale = fields[0].Get<std::string>();
|
||||
std::string localizedText = fields[1].Get<std::string>();
|
||||
|
||||
if (!IsLocaleValid(locale))
|
||||
{
|
||||
LOG_ERROR("server.loading", "DB table `motd_localized` has invalid locale ({}), skipped.", locale);
|
||||
continue;
|
||||
}
|
||||
|
||||
LocaleConstant localeId = GetLocaleByName(locale);
|
||||
if (localeId == LOCALE_enUS)
|
||||
continue;
|
||||
|
||||
SetMotd(localizedText, localeId);
|
||||
++count;
|
||||
} while (result->NextRow());
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_INFO("server.loading", ">> Loaded 0 motd locale definitions. DB table `motd_localized` is empty for this realm!");
|
||||
LOG_INFO("server.loading", " ");
|
||||
}
|
||||
|
||||
LOG_INFO("server.loading", ">> Loaded {} motd locale definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
|
||||
}
|
||||
|
||||
char const* MotdMgr::GetMotd(LocaleConstant locale)
|
||||
@@ -87,7 +127,7 @@ char const* MotdMgr::GetMotd(LocaleConstant locale)
|
||||
if (it != MotdMap.end())
|
||||
return it->second.c_str();
|
||||
|
||||
return MotdMap[DEFAULT_LOCALE].c_str(); // Fallback to enUS if locale is not found
|
||||
return MotdMap[LOCALE_enUS].c_str(); // Fallback to enUS if locale is not found
|
||||
}
|
||||
|
||||
WorldPacket const* MotdMgr::GetMotdPacket(LocaleConstant locale)
|
||||
@@ -97,79 +137,21 @@ WorldPacket const* MotdMgr::GetMotdPacket(LocaleConstant locale)
|
||||
if (it != MotdPackets.end())
|
||||
return &it->second;
|
||||
|
||||
return &MotdPackets[DEFAULT_LOCALE]; // Fallback to enUS if locale is not found
|
||||
return &MotdPackets[LOCALE_enUS]; // Fallback to enUS if locale is not found
|
||||
}
|
||||
|
||||
std::string MotdMgr::LoadDefaultMotd(uint32 realmId)
|
||||
WorldPacket MotdMgr::CreateWorldPacket(std::string motd)
|
||||
{
|
||||
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_MOTD);
|
||||
stmt->SetData(0, realmId);
|
||||
PreparedQueryResult result = LoginDatabase.Query(stmt);
|
||||
// Create a new WorldPacket for this locale
|
||||
WorldPacket data(SMSG_MOTD); // new in 2.0.1
|
||||
|
||||
if (result)
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
return fields[0].Get<std::string>(); // Return the main motd if found
|
||||
}
|
||||
|
||||
return ""; // Return empty string if no motd found
|
||||
}
|
||||
|
||||
void MotdMgr::SetDefaultMotd()
|
||||
{
|
||||
std::string motd = /* fctlsup << //0x338// "63"+"cx""d2"+"1e""dd"+"cx""ds"+"ce""dd"+"ce""7D"+ << */
|
||||
/*"d3"+"ce"*/ std::string("@|") + "cf" +/*"as"+"k4"*/"fF" + "F4" +/*"d5"+"f3"*/"A2" + "DT"/*"F4"+"Az"*/ + "hi" + "s "
|
||||
motd = /* fctlsup << //0x338// "63"+"cx""d2"+"1e""dd"+"cx""ds"+"ce""dd"+"ce""7D"+ << */ motd
|
||||
/*"d3"+"ce"*/ + "@|" + "cf" +/*"as"+"k4"*/"fF" + "F4" +/*"d5"+"f3"*/"A2" + "DT"/*"F4"+"Az"*/ + "hi" + "s "
|
||||
/*"fd"+"hy"*/ + "se" + "rv" +/*"nh"+"k3"*/"er" + " r" +/*"x1"+"A2"*/"un" + "s "/*"F2"+"Ay"*/ + "on" + " Az"
|
||||
/*"xs"+"5n"*/ + "er" + "ot" +/*"xs"+"A2"*/"hC" + "or" +/*"a4"+"f3"*/"e|" + "r "/*"f2"+"A2"*/ + "|c" + "ff"
|
||||
/*"5g"+"A2"*/ + "3C" + "E7" +/*"k5"+"AX"*/"FF" + "ww" +/*"sx"+"Gj"*/"w." + "az"/*"a1"+"vf"*/ + "er" + "ot"
|
||||
/*"ds"+"sx"*/ + "hc" + "or" +/*"F4"+"k5"*/"e." + "or" +/*"po"+"xs"*/"g|r"/*"F4"+"p2"+"o4"+"A2"+"i2"*/;
|
||||
|
||||
MotdMap[DEFAULT_LOCALE] = motd;
|
||||
|
||||
// Log that no motd was found and a default is being used for enUS
|
||||
LOG_WARN("server.loading", ">> Loaded 0 motd definitions. DB table `motd` is empty for this realm!");
|
||||
LOG_INFO("server.loading", " ");
|
||||
}
|
||||
|
||||
void MotdMgr::LoadLocalizedMotds(uint32 realmId) {
|
||||
// First, check if base MOTD exists
|
||||
LoginDatabasePreparedStatement* baseStmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_MOTD);
|
||||
baseStmt->SetData(0, realmId);
|
||||
PreparedQueryResult baseResult = LoginDatabase.Query(baseStmt);
|
||||
|
||||
if (!baseResult)
|
||||
{
|
||||
LOG_ERROR("server.loading", "No base MOTD found for realm {}. Localized MOTDs will not be loaded.", realmId);
|
||||
return;
|
||||
}
|
||||
|
||||
// Now load localized versions
|
||||
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_MOTD_LOCALE);
|
||||
stmt->SetData(0, realmId);
|
||||
PreparedQueryResult result = LoginDatabase.Query(stmt);
|
||||
|
||||
if (result)
|
||||
{
|
||||
do {
|
||||
Field* fields = result->Fetch();
|
||||
// fields[0] is the locale string and fields[1] is the localized motd text
|
||||
std::string localizedText = fields[1].Get<std::string>();
|
||||
// Convert locale string to LocaleConstant
|
||||
LocaleConstant localeId = GetLocaleByName(fields[0].Get<std::string>());
|
||||
|
||||
if (localeId == DEFAULT_LOCALE)
|
||||
continue;
|
||||
|
||||
MotdMap[localeId] = localizedText;
|
||||
} while (result->NextRow());
|
||||
}
|
||||
}
|
||||
|
||||
WorldPacket MotdMgr::CreateWorldPacket(std::string const& motd)
|
||||
{
|
||||
// Create a new WorldPacket for this locale
|
||||
WorldPacket data(SMSG_MOTD); // new in 2.0.1
|
||||
|
||||
// Tokenize the motd string by '@'
|
||||
std::vector<std::string_view> motdTokens = Acore::Tokenize(motd, '@', true);
|
||||
data << uint32(motdTokens.size()); // line count
|
||||
|
||||
@@ -29,9 +29,6 @@ class AC_GAME_API MotdMgr
|
||||
public:
|
||||
static MotdMgr* instance();
|
||||
|
||||
/// Converts the localized string to world packages
|
||||
void CreateWorldPackages();
|
||||
|
||||
/// Set a new Message of the Day
|
||||
void SetMotd(std::string motd, LocaleConstant locale);
|
||||
|
||||
@@ -44,18 +41,12 @@ public:
|
||||
/// Returns the current motd packet for the given locale
|
||||
WorldPacket const* GetMotdPacket(LocaleConstant locale);
|
||||
|
||||
// Checks if string is valid locale
|
||||
bool IsValidLocale(std::string const& locale);
|
||||
|
||||
private:
|
||||
// Loads the default motd from the motd table
|
||||
std::string LoadDefaultMotd(uint32 realmId);
|
||||
// Loads all available localized motd for the realm
|
||||
void LoadLocalizedMotds(uint32 realmId);
|
||||
// Sets the default mode if none is found in the database
|
||||
void SetDefaultMotd();
|
||||
void LoadMotdLocale();
|
||||
|
||||
// Create a worldpacket for a given motd localization
|
||||
WorldPacket CreateWorldPacket(std::string const& motd);
|
||||
WorldPacket CreateWorldPacket(std::string motd);
|
||||
};
|
||||
|
||||
#define sMotdMgr MotdMgr::instance()
|
||||
|
||||
@@ -1761,6 +1761,9 @@ void World::SetInitialWorldSettings()
|
||||
LOG_INFO("server.loading", "Loading Creature Data...");
|
||||
sObjectMgr->LoadCreatures();
|
||||
|
||||
LOG_INFO("server.loading", "Loading Creature sparring...");
|
||||
sObjectMgr->LoadCreatureSparring();
|
||||
|
||||
LOG_INFO("server.loading", "Loading Temporary Summon Data...");
|
||||
sObjectMgr->LoadTempSummons(); // must be after LoadCreatureTemplates() and LoadGameObjectTemplates()
|
||||
|
||||
@@ -2029,6 +2032,7 @@ void World::SetInitialWorldSettings()
|
||||
///- Load AutoBroadCast
|
||||
LOG_INFO("server.loading", "Loading Autobroadcasts...");
|
||||
sAutobroadcastMgr->LoadAutobroadcasts();
|
||||
sAutobroadcastMgr->LoadAutobroadcastsLocalized();
|
||||
|
||||
///- Load Motd
|
||||
LOG_INFO("server.loading", "Loading Motd...");
|
||||
|
||||
@@ -297,11 +297,9 @@ public:
|
||||
// Display the 'Message of the day' for the realm
|
||||
static bool HandleServerMotdCommand(ChatHandler* handler)
|
||||
{
|
||||
LocaleConstant localeConstant = DEFAULT_LOCALE;
|
||||
if (Player* player = handler->GetPlayer())
|
||||
localeConstant = player->GetSession()->GetSessionDbLocaleIndex();
|
||||
|
||||
handler->PSendSysMessage(LANG_MOTD_CURRENT, sMotdMgr->GetMotd(localeConstant));
|
||||
handler->PSendSysMessage(LANG_MOTD_CURRENT);
|
||||
for (uint32 i = 0; i < TOTAL_LOCALES; ++i)
|
||||
handler->PSendSysMessage(LANG_GENERIC_TWO_CURLIES_WITH_COLON, GetNameByLocaleConstant(LocaleConstant(i)), sMotdMgr->GetMotd(LocaleConstant(i)));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -533,7 +531,7 @@ public:
|
||||
}
|
||||
|
||||
// Define the 'Message of the day' for the realm
|
||||
static bool HandleServerSetMotdCommand(ChatHandler* handler, Optional<int32> realmId, Optional<std::string> locale, Tail motd)
|
||||
static bool HandleServerSetMotdCommand(ChatHandler* handler, Optional<int32> realmId, std::string locale, Tail motd)
|
||||
{
|
||||
std::wstring wMotd = std::wstring();
|
||||
std::string strMotd = std::string();
|
||||
@@ -542,39 +540,21 @@ public:
|
||||
if (!realmId)
|
||||
realmId = static_cast<int32>(realm.Id.Realm);
|
||||
|
||||
// Determine the locale; default to "enUS" if not provided
|
||||
LocaleConstant localeConstant;
|
||||
if (IsLocaleValid(locale))
|
||||
localeConstant = GetLocaleByName(locale);
|
||||
else
|
||||
{
|
||||
handler->SendErrorMessage("locale ({}) is not valid. Valid locales: enUS, koKR, frFR, deDE, zhCN, zhWE, esES, esMX, ruRU.", locale);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (motd.empty())
|
||||
return false;
|
||||
|
||||
// Convert Tail (motd) to std::string
|
||||
std::ostringstream motdStream;
|
||||
motdStream << motd;
|
||||
std::string motdString = motdStream.str(); // Convert Tail to std::string
|
||||
// Determine the locale; default to "enUS" if not provided
|
||||
LocaleConstant localeConstant = DEFAULT_LOCALE;
|
||||
if (locale.has_value())
|
||||
{
|
||||
if (sMotdMgr->IsValidLocale(locale.value()))
|
||||
{
|
||||
localeConstant = GetLocaleByName(locale.value());
|
||||
}
|
||||
else
|
||||
{
|
||||
motdStream.str("");
|
||||
motdStream << locale.value() << " " << motd;
|
||||
motdString = motdStream.str();
|
||||
localeConstant = DEFAULT_LOCALE;
|
||||
locale = GetNameByLocaleConstant(localeConstant);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Set to default locale string
|
||||
localeConstant = DEFAULT_LOCALE;
|
||||
locale = GetNameByLocaleConstant(localeConstant);
|
||||
}
|
||||
|
||||
// Convert the concatenated motdString to UTF-8 and ensure encoding consistency
|
||||
if (!Utf8toWStr(motdString, wMotd))
|
||||
if (!Utf8toWStr(motd, wMotd))
|
||||
return false;
|
||||
|
||||
if (!WStrToUtf8(wMotd, strMotd))
|
||||
@@ -583,34 +563,29 @@ public:
|
||||
// Start a transaction for the database operations
|
||||
LoginDatabaseTransaction trans = LoginDatabase.BeginTransaction();
|
||||
|
||||
if (localeConstant == DEFAULT_LOCALE)
|
||||
if (localeConstant == LOCALE_enUS)
|
||||
{
|
||||
// Insert or update in the main motd table for enUS
|
||||
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_MOTD);
|
||||
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_REP_MOTD);
|
||||
stmt->SetData(0, realmId.value()); // realmId for insertion
|
||||
stmt->SetData(1, strMotd); // motd text for insertion
|
||||
stmt->SetData(2, strMotd); // motd text for ON DUPLICATE KEY UPDATE
|
||||
trans->Append(stmt);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Insert or update in the motd_localized table for other locales
|
||||
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_MOTD_LOCALE);
|
||||
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_REP_MOTD_LOCALE);
|
||||
stmt->SetData(0, realmId.value()); // realmId for insertion
|
||||
stmt->SetData(1, locale.value()); // locale for insertion
|
||||
stmt->SetData(1, locale); // locale for insertion
|
||||
stmt->SetData(2, strMotd); // motd text for insertion
|
||||
stmt->SetData(3, strMotd); // motd text for ON DUPLICATE KEY UPDATE
|
||||
trans->Append(stmt);
|
||||
}
|
||||
|
||||
// Commit the transaction & update db
|
||||
LoginDatabase.CommitTransaction(trans);
|
||||
|
||||
// Update the in-memory maps for the current realm. Otherwise, do not update
|
||||
if (realmId == -1 || realmId == static_cast<int32>(realm.Id.Realm))
|
||||
sMotdMgr->SetMotd(strMotd, localeConstant);
|
||||
|
||||
handler->PSendSysMessage(LANG_MOTD_NEW, realmId.value(), locale.value(), strMotd);
|
||||
sMotdMgr->SetMotd(strMotd, localeConstant);
|
||||
handler->PSendSysMessage(LANG_MOTD_NEW, realmId.value(), locale, strMotd);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -52,7 +52,12 @@ enum Spells
|
||||
SPELL_GRAVITY_LAPSE_FLY = 44227,
|
||||
SPELL_GRAVITY_LAPSE_DOT = 44226,
|
||||
SPELL_GRAVITY_LAPSE_CHANNEL = 44251,
|
||||
SPELL_POWER_FEEDBACK = 44233
|
||||
SPELL_POWER_FEEDBACK = 44233,
|
||||
SPELL_CLEAR_FLIGHT = 44232, // Does nothing currently
|
||||
|
||||
SPELL_EMOTE_EXCLAMATION = 48348,
|
||||
SPELL_EMOTE_POINT = 48349,
|
||||
SPELL_EMOTE_ROAR = 48350
|
||||
};
|
||||
|
||||
enum Misc
|
||||
@@ -67,19 +72,13 @@ enum Misc
|
||||
|
||||
struct boss_felblood_kaelthas : public BossAI
|
||||
{
|
||||
boss_felblood_kaelthas(Creature* creature) : BossAI(creature, DATA_KAELTHAS)
|
||||
{
|
||||
_hasDoneIntro = false;
|
||||
}
|
||||
boss_felblood_kaelthas(Creature* creature) : BossAI(creature, DATA_KAELTHAS) { }
|
||||
|
||||
void Reset() override
|
||||
{
|
||||
BossAI::Reset();
|
||||
_OOCScheduler.CancelAll();
|
||||
_gravityLapseCounter = 0;
|
||||
me->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_INTERRUPT_CAST, false);
|
||||
me->SetImmuneToAll(false);
|
||||
summons.DespawnAll();
|
||||
|
||||
ScheduleHealthCheckEvent(50, [&]{
|
||||
me->CastStop();
|
||||
@@ -101,12 +100,6 @@ struct boss_felblood_kaelthas : public BossAI
|
||||
});
|
||||
}
|
||||
|
||||
void JustSummoned(Creature* summon) override
|
||||
{
|
||||
BossAI::JustSummoned(summon);
|
||||
summon->SetReactState(REACT_PASSIVE);
|
||||
}
|
||||
|
||||
void GravityLapseSequence(bool firstTime)
|
||||
{
|
||||
Talk(firstTime ? SAY_GRAVITY_LAPSE : SAY_RECAST_GRAVITY);
|
||||
@@ -132,12 +125,6 @@ struct boss_felblood_kaelthas : public BossAI
|
||||
});
|
||||
}
|
||||
|
||||
void InitializeAI() override
|
||||
{
|
||||
BossAI::InitializeAI();
|
||||
me->SetImmuneToAll(true);
|
||||
}
|
||||
|
||||
void JustDied(Unit* killer) override
|
||||
{
|
||||
BossAI::JustDied(killer);
|
||||
@@ -168,20 +155,32 @@ struct boss_felblood_kaelthas : public BossAI
|
||||
}, 50s);
|
||||
}
|
||||
|
||||
void MoveInLineOfSight(Unit* who) override
|
||||
void DoAction(int32 actionId) override
|
||||
{
|
||||
if (!_hasDoneIntro && me->IsWithinDistInMap(who, 40.0f) && who->IsPlayer())
|
||||
if (actionId == DATA_KAEL_INTRO)
|
||||
{
|
||||
Talk(SAY_AGGRO);
|
||||
Talk(SAY_AGGRO_2, 20s);
|
||||
_hasDoneIntro = true;
|
||||
_OOCScheduler.Schedule(35s, [this](TaskContext){
|
||||
me->SetReactState(REACT_AGGRESSIVE);
|
||||
me->SetImmuneToAll(false);
|
||||
me->SetInCombatWithZone();
|
||||
});
|
||||
uint32 counter = instance->GetPersistentData(DATA_KAEL_INTRO);
|
||||
instance->StorePersistentData(DATA_KAEL_INTRO, ++counter);
|
||||
|
||||
if (counter == 6 && !me->IsInCombat())
|
||||
{
|
||||
me->SetEmoteState(EMOTE_STATE_TALK);
|
||||
Talk(SAY_AGGRO);
|
||||
|
||||
me->m_Events.AddEventAtOffset([&] {
|
||||
me->HandleEmoteCommand(EMOTE_ONESHOT_LAUGH_NO_SHEATHE);
|
||||
}, 15s);
|
||||
|
||||
Talk(SAY_AGGRO_2, 20s);
|
||||
me->SetImmuneToAll(true);
|
||||
|
||||
me->m_Events.AddEventAtOffset([&] {
|
||||
me->ClearEmoteState();
|
||||
me->SetReactState(REACT_AGGRESSIVE);
|
||||
me->SetImmuneToAll(false);
|
||||
}, 35s);
|
||||
}
|
||||
}
|
||||
BossAI::MoveInLineOfSight(who);
|
||||
}
|
||||
|
||||
void DamageTaken(Unit* attacker, uint32& damage, DamageEffectType damagetype, SpellSchoolMask damageSchoolMask) override
|
||||
@@ -191,19 +190,35 @@ struct boss_felblood_kaelthas : public BossAI
|
||||
damage = me->GetHealth() - 1;
|
||||
if (me->isRegeneratingHealth())
|
||||
{
|
||||
me->CombatStop();
|
||||
me->CastStop();
|
||||
me->SetRegeneratingHealth(false);
|
||||
me->SetUnitFlag(UNIT_FLAG_DISABLE_MOVE);
|
||||
me->SetImmuneToAll(true);
|
||||
me->SetStandState(UNIT_STAND_STATE_KNEEL);
|
||||
me->CombatStop();
|
||||
me->SetReactState(REACT_PASSIVE);
|
||||
LapseAction(ACTION_REMOVE_FLY);
|
||||
scheduler.CancelAll();
|
||||
_OOCScheduler.Schedule(6s, [this](TaskContext){
|
||||
me->KillSelf();
|
||||
});
|
||||
summons.DespawnAll();
|
||||
|
||||
Talk(SAY_DEATH);
|
||||
DoCastSelf(SPELL_EMOTE_EXCLAMATION);
|
||||
|
||||
me->m_Events.AddEventAtOffset([&] {
|
||||
DoCastSelf(SPELL_EMOTE_POINT);
|
||||
}, 3s);
|
||||
|
||||
me->m_Events.AddEventAtOffset([&] {
|
||||
DoCastSelf(SPELL_EMOTE_ROAR);
|
||||
}, 7s);
|
||||
|
||||
me->m_Events.AddEventAtOffset([&] {
|
||||
DoCastSelf(SPELL_EMOTE_ROAR);
|
||||
DoCastSelf(SPELL_CLEAR_FLIGHT);
|
||||
}, 9s);
|
||||
|
||||
me->m_Events.AddEventAtOffset([&] {
|
||||
me->KillSelf();
|
||||
}, 11s);
|
||||
}
|
||||
}
|
||||
BossAI::DamageTaken(attacker, damage, damagetype, damageSchoolMask);
|
||||
@@ -214,6 +229,9 @@ struct boss_felblood_kaelthas : public BossAI
|
||||
_gravityLapseCounter = 0;
|
||||
me->GetMap()->DoForAllPlayers([&](Player* player)
|
||||
{
|
||||
if (player->IsGameMaster())
|
||||
return;
|
||||
|
||||
if (action == ACTION_TELEPORT_PLAYERS)
|
||||
DoCast(player, SPELL_GRAVITY_LAPSE_PLAYER + _gravityLapseCounter, true);
|
||||
else if (action == ACTION_KNOCKUP)
|
||||
@@ -228,15 +246,7 @@ struct boss_felblood_kaelthas : public BossAI
|
||||
++_gravityLapseCounter;
|
||||
});
|
||||
}
|
||||
|
||||
void UpdateAI(uint32 diff) override
|
||||
{
|
||||
_OOCScheduler.Update(diff);
|
||||
BossAI::UpdateAI(diff);
|
||||
}
|
||||
private:
|
||||
TaskScheduler _OOCScheduler;
|
||||
bool _hasDoneIntro;
|
||||
uint8 _gravityLapseCounter;
|
||||
};
|
||||
|
||||
|
||||
@@ -48,6 +48,7 @@ DoorData const doorData[] =
|
||||
{ GO_SELIN_ENCOUNTER_DOOR, DATA_SELIN_FIREHEART, DOOR_TYPE_ROOM },
|
||||
{ GO_VEXALLUS_DOOR, DATA_VEXALLUS, DOOR_TYPE_PASSAGE },
|
||||
{ GO_DELRISSA_DOOR, DATA_DELRISSA, DOOR_TYPE_PASSAGE },
|
||||
{ GO_KAEL_DOOR, DATA_KAELTHAS, DOOR_TYPE_ROOM },
|
||||
{ 0, 0, DOOR_TYPE_ROOM } // END
|
||||
};
|
||||
|
||||
@@ -64,6 +65,7 @@ public:
|
||||
{
|
||||
SetHeaders(DataHeader);
|
||||
SetBossNumber(MAX_ENCOUNTER);
|
||||
SetPersistentDataCount(MAX_PERSISTENT_DATA);
|
||||
LoadObjectData(creatureData, gameobjectData);
|
||||
LoadDoorData(doorData);
|
||||
LoadSummonData(summonerData);
|
||||
|
||||
@@ -32,7 +32,11 @@ enum MTData
|
||||
MAX_ENCOUNTER = 4,
|
||||
|
||||
DATA_KALECGOS = 5,
|
||||
DATA_ESCAPE_ORB = 6
|
||||
DATA_ESCAPE_ORB = 6,
|
||||
|
||||
// Persistent data
|
||||
DATA_KAEL_INTRO = 0,
|
||||
MAX_PERSISTENT_DATA = 1
|
||||
};
|
||||
|
||||
enum MTCreatures
|
||||
@@ -40,8 +44,8 @@ enum MTCreatures
|
||||
NPC_DELRISSA = 24560,
|
||||
NPC_FEL_CRYSTAL = 24722,
|
||||
NPC_KAEL_THAS = 24664,
|
||||
NPC_PHOENIX = 21362,
|
||||
NPC_PHOENIX_EGG = 21364,
|
||||
NPC_PHOENIX = 24674,
|
||||
NPC_PHOENIX_EGG = 24675,
|
||||
NPC_KALECGOS = 24844
|
||||
};
|
||||
|
||||
|
||||
@@ -189,10 +189,12 @@ struct npc_madrigosa : public NullCreatureAI
|
||||
{
|
||||
if (param == ACTION_START_EVENT)
|
||||
{
|
||||
me->NearTeleportTo(1570.97f, 725.51f, 79.77f, 3.82f);
|
||||
me->SetDisableGravity(true);
|
||||
me->SetStandState(UNIT_STAND_STATE_STAND);
|
||||
me->RemoveDynamicFlag(UNIT_DYNFLAG_DEAD);
|
||||
me->NearTeleportTo(1570.97f, 725.51f, 79.77f, 3.82f);
|
||||
me->SendMovementFlagUpdate();
|
||||
|
||||
events.ScheduleEvent(EVENT_MAD_1, 2000);
|
||||
}
|
||||
else if (param == ACTION_SPAWN_FELMYST)
|
||||
@@ -212,9 +214,9 @@ struct npc_madrigosa : public NullCreatureAI
|
||||
brutallus->SetReactState(REACT_PASSIVE);
|
||||
brutallus->setActive(true);
|
||||
}
|
||||
me->GetMotionMaster()->MovePoint(1, 1477.94f, 643.22f, 21.21f);
|
||||
me->GetMotionMaster()->MoveTakeoff(1, 1477.94f, 643.22f, 21.21f);
|
||||
me->AddUnitState(UNIT_STATE_NO_ENVIRONMENT_UPD);
|
||||
events.ScheduleEvent(EVENT_MAD_2, 6000);
|
||||
events.ScheduleEvent(EVENT_MAD_2, 4000);
|
||||
break;
|
||||
case EVENT_MAD_2:
|
||||
Talk(SAY_MAD_1);
|
||||
|
||||
@@ -333,11 +333,11 @@ struct boss_felmyst : public BossAI
|
||||
|
||||
void UpdateAI(uint32 diff) override
|
||||
{
|
||||
scheduler.Update(diff);
|
||||
|
||||
if (!UpdateVictim())
|
||||
return;
|
||||
|
||||
scheduler.Update(diff);
|
||||
|
||||
if (!me->HasUnitMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY))
|
||||
DoMeleeAttackIfReady();
|
||||
}
|
||||
|
||||
@@ -61,7 +61,6 @@ enum Spells
|
||||
enum Misc
|
||||
{
|
||||
EVENT_SPELL_BERSERK = 1,
|
||||
|
||||
GROUP_DELAY = 1
|
||||
};
|
||||
|
||||
@@ -103,6 +102,7 @@ struct boss_gurtogg_bloodboil : public BossAI
|
||||
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 1, 40.0f, true))
|
||||
{
|
||||
me->RemoveAurasByType(SPELL_AURA_MOD_TAUNT);
|
||||
me->RemoveAurasDueToSpell(SPELL_ACIDIC_WOUND);
|
||||
DoCastSelf(SPELL_FEL_RAGE_SELF, true);
|
||||
DoCast(target, SPELL_FEL_RAGE_TARGET, true);
|
||||
DoCast(target, SPELL_FEL_RAGE_2, true);
|
||||
@@ -118,6 +118,10 @@ struct boss_gurtogg_bloodboil : public BossAI
|
||||
DoCastVictim(SPELL_CHARGE);
|
||||
}, 2s);
|
||||
|
||||
me->m_Events.AddEventAtOffset([&] {
|
||||
DoCastSelf(SPELL_ACIDIC_WOUND, true);
|
||||
}, 28s);
|
||||
|
||||
scheduler.DelayGroup(GROUP_DELAY, 30s);
|
||||
}
|
||||
}, 90s);
|
||||
@@ -139,7 +143,7 @@ struct boss_gurtogg_bloodboil : public BossAI
|
||||
return !who->IsImmunedToDamage(SPELL_SCHOOL_MASK_ALL) && !who->HasUnitState(UNIT_STATE_CONFUSED);
|
||||
}
|
||||
|
||||
void KilledUnit(Unit* /*victim*/) override
|
||||
void KilledUnit(Unit* /*victim*/) override
|
||||
{
|
||||
if (!_recentlySpoken)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user