Merge branch 'azerothcore:master' into test-staging

This commit is contained in:
kadeshar
2026-04-24 23:01:48 +02:00
committed by GitHub
33 changed files with 413 additions and 148 deletions
+4
View File
@@ -358,6 +358,10 @@ bool CreatureAI::UpdateVictim()
return false;
}
// Charmed creatures: the charmer controls target selection, don't interfere
if (me->IsCharmed())
return me->GetVictim() != nullptr;
if (!me->HasReactState(REACT_PASSIVE))
{
if (Unit* victim = me->SelectVictim())
+1 -1
View File
@@ -911,7 +911,7 @@ void SmartAI::AttackStart(Unit* who)
return;
}
if (who && me->Attack(who, me->IsWithinMeleeRange(who)))
if (who && me->Attack(who, mCanAutoAttack))
{
if (!me->HasUnitState(UNIT_STATE_NO_COMBAT_MOVEMENT))
{
@@ -2054,6 +2054,8 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
case SMART_ACTION_WORLD_SCRIPT:
case SMART_ACTION_SET_GOSSIP_MENU:
case SMART_ACTION_SUMMON_GAMEOBJECT_GROUP:
case SMART_ACTION_SPAWN_SPAWNGROUP:
case SMART_ACTION_DESPAWN_SPAWNGROUP:
break;
default:
LOG_ERROR("sql.sql", "SmartAIMgr: Not handled action_type({}), event_type({}), Entry {} SourceType {} Event {}, skipped.", e.GetActionType(), e.GetEventType(), e.entryOrGuid, e.GetScriptType(), e.event_id);
@@ -1118,7 +1118,7 @@ void BattlegroundSA::UpdateDemolisherSpawns()
BG_SA_NpcSpawnlocs[i][2], BG_SA_NpcSpawnlocs[i][3]);
Demolisher->SetVisible(true);
Demolisher->Respawn();
Demolisher->Respawn(true);
DemoliserRespawnList.erase(i);
}
}
@@ -591,6 +591,12 @@ bool Condition::Meets(ConditionSourceInfo& sourceInfo)
condMeets = unit->IsCharmed();
break;
}
case CONDITION_UNIT_IN_COMBAT:
{
if (Unit* unit = object->ToUnit())
condMeets = unit->IsInCombat();
break;
}
case CONDITION_WORLD_SCRIPT:
{
condMeets = sWorldState->IsConditionFulfilled(ConditionValue1, ConditionValue2);
@@ -804,6 +810,9 @@ uint32 Condition::GetSearcherTypeMaskForCondition()
case CONDITION_CHARMED:
mask |= GRID_MAP_TYPE_MASK_CREATURE | GRID_MAP_TYPE_MASK_PLAYER;
break;
case CONDITION_UNIT_IN_COMBAT:
mask |= GRID_MAP_TYPE_MASK_CREATURE | GRID_MAP_TYPE_MASK_PLAYER;
break;
case CONDITION_PLAYER_QUEUED_RANDOM_DUNGEON:
mask |= GRID_MAP_TYPE_MASK_PLAYER;
break;
@@ -834,6 +843,7 @@ uint32 Condition::GetMaxAvailableConditionTargets()
case CONDITION_SOURCE_TYPE_SPELL_CLICK_EVENT:
case CONDITION_SOURCE_TYPE_GOSSIP_MENU:
case CONDITION_SOURCE_TYPE_GOSSIP_MENU_OPTION:
case CONDITION_SOURCE_TYPE_GOSSIP_HELLO:
case CONDITION_SOURCE_TYPE_NPC_VENDOR:
case CONDITION_SOURCE_TYPE_SPELL_PROC:
case CONDITION_SOURCE_TYPE_OBJECT_VISIBILITY:
@@ -2624,6 +2634,7 @@ bool ConditionMgr::isConditionTypeValid(Condition* cond)
case CONDITION_TAXI:
case CONDITION_IN_WATER:
case CONDITION_CHARMED:
case CONDITION_UNIT_IN_COMBAT:
default:
break;
}
+2 -1
View File
@@ -89,8 +89,9 @@ enum ConditionTypes
CONDITION_WORLD_SCRIPT = 103, // conditionId state 0 true if WorldState::IsConditionFulfilled returns true
CONDITION_AI_DATA = 104, // dataId value 0 true if AI::GetData returns value
CONDITION_PLAYER_QUEUED_RANDOM_DUNGEON = 105, // checkDifficulty difficulty 0 true if player is queued for a random dungeon via RDF
CONDITION_UNIT_IN_COMBAT = 106, // 0 0 0 true if unit is engaged in combat
CONDITION_AC_END = 106 // placeholder
CONDITION_AC_END = 107 // placeholder
};
/*! Documentation on implementing a new ConditionSourceType:
@@ -67,7 +67,7 @@
KillRewarder::KillRewarder(Player* killer, Unit* victim, bool isBattleGround) :
// 1. Initialize internal variables to default values.
_killer(killer), _victim(victim), _group(killer->GetGroup()),
_groupRate(1.0f), _maxNotGrayMember(nullptr), _count(0), _aliveSumLevel(0), _sumLevel(0), _xp(0),
_groupRate(1.0f), _maxNotGrayMember(nullptr), _maxNotGrayMemberLevel(0), _count(0), _aliveSumLevel(0), _sumLevel(0), _xp(0),
_isFullXP(false), _maxLevel(0), _isBattleGround(isBattleGround), _isPvP(false)
{
// mark the credit as pvp if victim is player
@@ -89,7 +89,7 @@ void KillRewarder::_InitGroupData()
if (Player* member = itr->GetSource())
if ((_killer == member || member->IsAtGroupRewardDistance(_victim)))
{
const uint8 lvl = member->GetLevel();
const uint8 lvl = _GetPlayerLevel(member);
if (member->IsAlive())
{
// 2.1. _count - number of alive group members within reward distance;
@@ -104,9 +104,10 @@ void KillRewarder::_InitGroupData()
// 2.4. _maxNotGrayMember - maximum level of alive group member within reward distance,
// for whom victim is not gray;
uint32 grayLevel = Acore::XP::GetGrayLevel(lvl);
if (_victim->GetLevel() > grayLevel && (!_maxNotGrayMember || _maxNotGrayMember->GetLevel() < lvl))
if (_victim->GetLevel() > grayLevel && (!_maxNotGrayMember || _maxNotGrayMemberLevel < lvl))
{
_maxNotGrayMember = member;
_maxNotGrayMemberLevel = lvl;
}
}
// 2.5. _sumLevel - sum of levels of group members within reward distance;
@@ -114,7 +115,7 @@ void KillRewarder::_InitGroupData()
}
// 2.6. _isFullXP - flag identifying that for all group members victim is not gray,
// so 100% XP will be rewarded (50% otherwise).
_isFullXP = _maxNotGrayMember && (_maxLevel == _maxNotGrayMember->GetLevel());
_isFullXP = _maxNotGrayMember && (_maxLevel == _maxNotGrayMemberLevel);
}
else
_count = 1;
@@ -153,7 +154,7 @@ void KillRewarder::_RewardXP(Player* player, float rate)
// * set to 0 if player's level is more than maximum level of not gray member;
// * cut XP in half if _isFullXP is false.
if (_maxNotGrayMember && player->IsAlive() &&
_maxNotGrayMember->GetLevel() >= player->GetLevel())
_maxNotGrayMemberLevel >= _GetPlayerLevel(player))
xp = _isFullXP ?
uint32(xp * rate) : // Reward FULL XP if all group members are not gray.
uint32(xp * rate / 2) + 1; // Reward only HALF of XP if some of group members are gray.
@@ -208,8 +209,8 @@ void KillRewarder::_RewardPlayer(Player* player, bool isDungeon)
// Give reputation and kill credit only in PvE.
if (!_isPvP || _isBattleGround)
{
float xpRate = _group ? _groupRate * float(player->GetLevel()) / _aliveSumLevel : /*Personal rate is 100%.*/ 1.0f; // Group rate depends on the sum of levels.
sScriptMgr->OnPlayerRewardKillRewarder(player, this, isDungeon, xpRate); // Personal rate is 100%.
float xpRate = _group ? _groupRate * float(_GetPlayerLevel(player)) / _aliveSumLevel : /*Personal rate is 100%.*/ 1.0f; // Group rate depends on the sum of levels.
sScriptMgr->OnPlayerRewardKillRewarder(player, this, isDungeon, xpRate); // Personal rate is 100%.
if (_xp)
{
@@ -264,6 +265,13 @@ void KillRewarder::_RewardGroup()
}
}
uint8 KillRewarder::_GetPlayerLevel(Player const* player)
{
uint8 level = player->GetLevel();
sScriptMgr->OnPlayerBeforeGetLevelForXPGain(player, level);
return level;
}
void KillRewarder::Reward()
{
// 3. Reward killer (and group, if necessary).
@@ -43,12 +43,14 @@ private:
void _RewardKillCredit(Player* player);
void _RewardPlayer(Player* player, bool isDungeon);
void _RewardGroup();
uint8 _GetPlayerLevel(Player const* player);
Player* _killer;
Unit* _victim;
Group* _group;
float _groupRate;
Player* _maxNotGrayMember;
uint8 _maxNotGrayMemberLevel;
uint32 _count;
uint32 _aliveSumLevel;
uint32 _sumLevel;
+7 -3
View File
@@ -2370,6 +2370,7 @@ void Player::GiveXP(uint32 xp, Unit* victim, float group_rate, bool isLFGReward)
}
uint8 level = GetLevel();
sScriptMgr->OnPlayerBeforeGetLevelForXPGain(this, level);
// Favored experience increase START
uint32 zone = GetZoneId();
@@ -5784,17 +5785,20 @@ void Player::CheckAreaExploreAndOutdoor()
if (areaEntry->area_level > 0)
{
if (GetLevel() >= sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
uint8 playerLevel = GetLevel();
sScriptMgr->OnPlayerBeforeGetLevelForXPGain(this, playerLevel);
if (playerLevel >= sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
{
SendExplorationExperience(areaId, 0);
}
else
{
int32 diff = int32(GetLevel()) - areaEntry->area_level;
int32 diff = int32(playerLevel) - areaEntry->area_level;
uint32 XP = 0;
if (diff < -5)
{
XP = uint32(sObjectMgr->GetBaseXP(GetLevel() + 5) * sWorld->getRate(RATE_XP_EXPLORE));
XP = uint32(sObjectMgr->GetBaseXP(playerLevel + 5) * sWorld->getRate(RATE_XP_EXPLORE));
}
else if (diff > 5)
{
@@ -1435,8 +1435,11 @@ bool Player::TakeQuestSourceItem(uint32 questId, bool msg)
uint32 Player::CalculateQuestRewardXP(Quest const* quest)
{
uint8 level = GetLevel();
sScriptMgr->OnPlayerBeforeGetLevelForXPGain(this, level);
// apply world quest rate
uint32 xp = uint32(quest->XPValue(GetLevel()) * GetQuestRate(quest->IsDFQuest()));
uint32 xp = uint32(quest->XPValue(level) * GetQuestRate(quest->IsDFQuest()));
// handle SPELL_AURA_MOD_XP_QUEST_PCT auras
xp *= GetTotalAuraMultiplier(SPELL_AURA_MOD_XP_QUEST_PCT);
@@ -26,6 +26,73 @@ constexpr double DEG_TO_RAD = PI / 180.0;
// Helper: sin/cos in degrees
inline double sind(double deg) { return std::sin(deg * DEG_TO_RAD); }
// mktime() rejects pre-1970 dates on MSVC; these helpers normalize without it.
static bool IsLeapYear(int year)
{
return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
}
static int DaysInMonth(int year, int month)
{
static int const table[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
return (month == 2 && IsLeapYear(year)) ? 29 : table[month - 1];
}
static void NormalizeDate(std::tm& date)
{
int year = date.tm_year + 1900;
int month = date.tm_mon + 1;
int day = date.tm_mday;
while (month > 12)
{
month -= 12;
++year;
}
while (month < 1)
{
month += 12;
--year;
}
while (day > DaysInMonth(year, month))
{
day -= DaysInMonth(year, month);
++month;
if (month > 12)
{
month = 1;
++year;
}
}
while (day < 1)
{
--month;
if (month < 1)
{
month = 12;
--year;
}
day += DaysInMonth(year, month);
}
date.tm_year = year - 1900;
date.tm_mon = month - 1;
date.tm_mday = day;
// Zeller's congruence; remap 0=Sat to POSIX 0=Sun.
int zy = year;
int zm = month;
if (zm < 3)
{
zm += 12;
--zy;
}
int const K = zy % 100;
int const J = zy / 100;
int const h = (day + (13 * (zm + 1)) / 5 + K + K / 4 + J / 4 + 5 * J) % 7;
date.tm_wday = (h + 6) % 7;
}
// Static holiday rules configuration
static const std::vector<HolidayRule> HolidayRules = {
// Lunar Festival: Chinese New Year - 1 day (event starts day before CNY)
@@ -104,7 +171,7 @@ std::tm HolidayDateCalculator::CalculateEasterSunday(int year)
result.tm_year = year - 1900;
result.tm_mon = month - 1;
result.tm_mday = day;
mktime(&result); // Normalize and fill in other fields
NormalizeDate(result);
return result;
}
@@ -116,7 +183,7 @@ std::tm HolidayDateCalculator::CalculateNthWeekday(int year, int month, Weekday
date.tm_year = year - 1900;
date.tm_mon = month - 1;
date.tm_mday = 1;
mktime(&date);
NormalizeDate(date);
// Find first occurrence of the target weekday
int const daysUntilWeekday = (static_cast<int>(weekday) - date.tm_wday + 7) % 7;
@@ -125,7 +192,7 @@ std::tm HolidayDateCalculator::CalculateNthWeekday(int year, int month, Weekday
// Move to nth occurrence
date.tm_mday += (n - 1) * 7;
mktime(&date); // Normalize (handles month overflow)
NormalizeDate(date);
return date;
}
@@ -136,13 +203,13 @@ std::tm HolidayDateCalculator::CalculateWeekdayOnOrAfter(int year, int month, in
date.tm_year = year - 1900;
date.tm_mon = month - 1;
date.tm_mday = day;
mktime(&date);
NormalizeDate(date);
// Find days until the target weekday (0 if already on that day)
int const daysUntilWeekday = (static_cast<int>(weekday) - date.tm_wday + 7) % 7;
date.tm_mday += daysUntilWeekday;
mktime(&date); // Normalize
NormalizeDate(date);
return date;
}
@@ -288,7 +355,7 @@ std::tm HolidayDateCalculator::CalculateLunarNewYear(int year)
result.tm_year = cnyYear - 1900;
result.tm_mon = cnyMonth - 1;
result.tm_mday = cnyDay;
mktime(&result);
NormalizeDate(result);
return result;
}
}
@@ -298,7 +365,7 @@ std::tm HolidayDateCalculator::CalculateLunarNewYear(int year)
fallback.tm_year = year - 1900;
fallback.tm_mon = 0; // January
fallback.tm_mday = 25;
mktime(&fallback);
NormalizeDate(fallback);
return fallback;
}
@@ -354,7 +421,7 @@ std::tm HolidayDateCalculator::CalculateAutumnEquinox(int year)
result.tm_year = eqYear - 1900;
result.tm_mon = eqMonth - 1;
result.tm_mday = eqDay;
mktime(&result);
NormalizeDate(result);
return result;
}
@@ -408,7 +475,7 @@ std::tm HolidayDateCalculator::CalculateWinterSolstice(int year)
result.tm_year = solYear - 1900;
result.tm_mon = solMonth - 1;
result.tm_mday = solDay;
mktime(&result);
NormalizeDate(result);
return result;
}
@@ -423,7 +490,7 @@ std::tm HolidayDateCalculator::CalculateHolidayDate(const HolidayRule& rule, int
result.tm_year = year - 1900;
result.tm_mon = rule.month - 1;
result.tm_mday = rule.day;
mktime(&result);
NormalizeDate(result);
break;
}
case HolidayCalculationType::NTH_WEEKDAY:
@@ -432,7 +499,7 @@ std::tm HolidayDateCalculator::CalculateHolidayDate(const HolidayRule& rule, int
if (rule.offset != 0)
{
result.tm_mday += rule.offset;
mktime(&result); // Normalize
NormalizeDate(result);
}
break;
}
@@ -440,7 +507,7 @@ std::tm HolidayDateCalculator::CalculateHolidayDate(const HolidayRule& rule, int
{
result = CalculateEasterSunday(year);
result.tm_mday += rule.offset;
mktime(&result); // Normalize
NormalizeDate(result);
break;
}
case HolidayCalculationType::LUNAR_NEW_YEAR:
@@ -449,7 +516,7 @@ std::tm HolidayDateCalculator::CalculateHolidayDate(const HolidayRule& rule, int
if (rule.offset != 0)
{
result.tm_mday += rule.offset;
mktime(&result); // Normalize
NormalizeDate(result);
}
break;
}
@@ -459,7 +526,7 @@ std::tm HolidayDateCalculator::CalculateHolidayDate(const HolidayRule& rule, int
if (rule.offset != 0)
{
result.tm_mday += rule.offset;
mktime(&result); // Normalize
NormalizeDate(result);
}
break;
}
@@ -469,7 +536,7 @@ std::tm HolidayDateCalculator::CalculateHolidayDate(const HolidayRule& rule, int
if (rule.offset != 0)
{
result.tm_mday += rule.offset;
mktime(&result); // Normalize
NormalizeDate(result);
}
break;
}
@@ -479,7 +546,7 @@ std::tm HolidayDateCalculator::CalculateHolidayDate(const HolidayRule& rule, int
if (rule.offset != 0)
{
result.tm_mday += rule.offset;
mktime(&result); // Normalize
NormalizeDate(result);
}
break;
}
@@ -571,7 +638,7 @@ std::vector<uint32_t> HolidayDateCalculator::GetDarkmoonFaireDates(int locationO
if (dayOffset != 0)
{
date.tm_mday += dayOffset;
mktime(&date); // Normalize
NormalizeDate(date);
}
dates.push_back(PackDate(date));
}
+9 -3
View File
@@ -23,6 +23,7 @@
#include "ObjectMgr.h"
#include "Opcodes.h"
#include "Player.h"
#include "ScriptMgr.h"
#include "WorldPacket.h"
#include "WorldSession.h"
@@ -188,10 +189,13 @@ void WorldSession::HandleLfgPlayerLockInfoRequestOpcode(WorldPacket& /*recvData*
if (quest)
{
uint8 playerLevel = GetPlayer() ? GetPlayer()->GetLevel() : 0;
uint8 playerLevelForXP = playerLevel;
sScriptMgr->OnPlayerBeforeGetLevelForXPGain(GetPlayer(), playerLevelForXP);
data << uint8(done);
data << uint32(quest->GetRewOrReqMoney(playerLevel));
if (!GetPlayer()->IsMaxLevel())
data << uint32(quest->XPValue(playerLevel));
if (playerLevelForXP < GetPlayer()->GetUInt32Value(PLAYER_FIELD_MAX_LEVEL))
data << uint32(quest->XPValue(playerLevelForXP));
else
data << uint32(0);
data << uint32(0);
@@ -479,6 +483,8 @@ void WorldSession::SendLfgPlayerReward(lfg::LfgPlayerRewardData const& rewardDat
uint8 itemNum = rewardData.quest->GetRewItemsCount();
uint8 playerLevel = GetPlayer() ? GetPlayer()->GetLevel() : 0;
uint8 playerLevelForXP = playerLevel;
sScriptMgr->OnPlayerBeforeGetLevelForXPGain(GetPlayer(), playerLevelForXP);
WorldPacket data(SMSG_LFG_PLAYER_REWARD, 4 + 4 + 1 + 4 + 4 + 4 + 4 + 4 + 1 + itemNum * (4 + 4 + 4));
data << uint32(rewardData.rdungeonEntry); // Random Dungeon Finished
@@ -486,7 +492,7 @@ void WorldSession::SendLfgPlayerReward(lfg::LfgPlayerRewardData const& rewardDat
data << uint8(rewardData.done);
data << uint32(1);
data << uint32(rewardData.quest->GetRewOrReqMoney(playerLevel));
data << uint32(rewardData.quest->XPValue(playerLevel));
data << uint32(rewardData.quest->XPValue(playerLevelForXP));
data << uint32(0);
data << uint32(0);
data << uint8(itemNum);
+4 -1
View File
@@ -21,6 +21,7 @@
#include "Creature.h"
#include "Log.h"
#include "Player.h"
#include "ScriptMgr.h"
#include "World.h"
uint32 Acore::XP::BaseGain(uint8 pl_level, uint8 mob_level, ContentLevels content)
@@ -79,7 +80,9 @@ uint32 Acore::XP::Gain(Player* player, Unit* unit, bool isBattleGround /*= false
{
float xpMod = 1.0f;
gain = BaseGain(player->GetLevel(), unit->GetLevel(), GetContentLevelsForMapAndZone(unit->GetMapId(), unit->GetZoneId()));
uint8 playerLevel = player->GetLevel();
sScriptMgr->OnPlayerBeforeGetLevelForXPGain(player, playerLevel);
gain = BaseGain(playerLevel, unit->GetLevel(), GetContentLevelsForMapAndZone(unit->GetMapId(), unit->GetZoneId()));
if (gain && creature)
{
@@ -18,6 +18,9 @@
#include "PlayerScript.h"
#include "ScriptMgr.h"
#include "ScriptMgrMacros.h"
#include "World.h"
#include <algorithm>
void ScriptMgr::OnPlayerBeforeDurabilityRepair(Player* player, ObjectGuid npcGUID, ObjectGuid itemGUID, float& discountMod, uint8 guildBank)
{
@@ -936,6 +939,12 @@ void ScriptMgr::OnPlayerLearnTaxiNode(Player const* player, uint32 nodeId)
CALL_ENABLED_HOOKS(PlayerScript, PLAYERHOOK_ON_LEARN_TAXI_NODE, script->OnPlayerLearnTaxiNode(player, nodeId));
}
void ScriptMgr::OnPlayerBeforeGetLevelForXPGain(Player const* player, uint8& level)
{
CALL_ENABLED_HOOKS(PlayerScript, PLAYERHOOK_ON_BEFORE_GET_LEVEL_FOR_XP_GAIN, script->OnPlayerBeforeGetLevelForXPGain(player, level));
level = std::clamp(level, uint8(1), uint8(sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)));
}
PlayerScript::PlayerScript(const char* name, std::vector<uint16> enabledHooks)
: ScriptObject(name, PLAYERHOOK_END)
{
@@ -217,6 +217,7 @@ enum PlayerHook
PLAYERHOOK_ON_GIVE_REPUTATION,
PLAYERHOOK_ON_GET_REPUTATION_PRICE_DISCOUNT,
PLAYERHOOK_ON_LEARN_TAXI_NODE,
PLAYERHOOK_ON_BEFORE_GET_LEVEL_FOR_XP_GAIN,
PLAYERHOOK_END
};
@@ -836,6 +837,14 @@ public:
* @param nodeId The id of the learned taxi node
*/
virtual void OnPlayerLearnTaxiNode(Player const* /*player*/, uint32 /*nodeId*/) {}
/**
* @brief This hook is called when XP is calculated for the player, and is used to modify the player level used in the XP formulas.
*
* @param player Contains information about the Player
* @param level The level that should be used for XP gain calculations
*/
virtual void OnPlayerBeforeGetLevelForXPGain(Player const* /*player*/, uint8& /*level*/) {}
};
#endif
@@ -15,13 +15,14 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ServerScript.h"
#include "IoContext.h"
#include "ScriptMgr.h"
#include "ScriptMgrMacros.h"
#include "ServerScript.h"
void ScriptMgr::OnNetworkStart()
void ScriptMgr::OnNetworkStart(Acore::Asio::IoContext& ioContext)
{
CALL_ENABLED_HOOKS(ServerScript, SERVERHOOK_ON_NETWORK_START, script->OnNetworkStart());
CALL_ENABLED_HOOKS(ServerScript, SERVERHOOK_ON_NETWORK_START, script->OnNetworkStart(ioContext));
}
void ScriptMgr::OnNetworkStop()
@@ -18,6 +18,7 @@
#ifndef SCRIPT_OBJECT_SERVER_SCRIPT_H_
#define SCRIPT_OBJECT_SERVER_SCRIPT_H_
#include "IoContext.h"
#include "ScriptObject.h"
#include <vector>
#include <memory> // NOTE: this import is NEEDED (even though some IDEs report it as unused)
@@ -41,6 +42,7 @@ protected:
public:
// Called when reactive socket I/O is started (WorldSocketMgr).
virtual void OnNetworkStart() { }
virtual void OnNetworkStart(Acore::Asio::IoContext& /*ioContext*/) { OnNetworkStart(); }
// Called when reactive I/O is stopped.
virtual void OnNetworkStop() { }
+2 -1
View File
@@ -173,7 +173,7 @@ public: /* SpellScriptLoader */
void CreateSpellScriptLoaders(uint32 spellId, std::vector<std::pair<SpellScriptLoader*, std::multimap<uint32, uint32>::iterator>>& scriptVector);
public: /* ServerScript */
void OnNetworkStart();
void OnNetworkStart(Acore::Asio::IoContext& ioContext);
void OnNetworkStop();
void OnSocketOpen(std::shared_ptr<WorldSocket> const& socket);
void OnSocketClose(std::shared_ptr<WorldSocket> const& socket);
@@ -488,6 +488,7 @@ public: /* PlayerScript */
void OnPlayerGetReputationPriceDiscount(Player const* player, Creature const* creature, float& discount);
void OnPlayerGetReputationPriceDiscount(Player const* player, FactionTemplateEntry const* factionTemplate, float& discount);
void OnPlayerLearnTaxiNode(Player const* player, uint32 nodeId);
void OnPlayerBeforeGetLevelForXPGain(Player const* player, uint8& level);
// Anti cheat
void AnticheatSetCanFlybyServer(Player* player, bool apply);
+1 -1
View File
@@ -70,7 +70,7 @@ bool WorldSocketMgr::StartWorldNetwork(Acore::Asio::IoContext& ioContext, std::s
_acceptor->AsyncAcceptWithCallback<&WorldSocketMgr::OnSocketAccept>();
sScriptMgr->OnNetworkStart();
sScriptMgr->OnNetworkStart(ioContext);
return true;
}
@@ -5184,6 +5184,12 @@ void SpellMgr::LoadSpellInfoCorrections()
spellInfo->InterruptFlags &= ~SPELL_INTERRUPT_FLAG_INTERRUPT;
});
// Twilight Torment
ApplySpellFix({ 57935, 58835 }, [](SpellInfo* spellInfo)
{
spellInfo->ProcCharges = 0;
});
for (uint32 i = 0; i < GetSpellInfoStoreSize(); ++i)
{
SpellInfo* spellInfo = mSpellInfoMap[i];
@@ -129,6 +129,13 @@ struct boss_bjarngrim : public npc_escortAI
AddWaypoint(7, 1262.0f, -26.9f, 33.5f, 0);
}
void JustRespawned() override
{
npc_escortAI::JustRespawned();
me->SetWalk(true);
Start(true, ObjectGuid::Empty, nullptr, false, true);
}
void Reset() override
{
events.Reset();
@@ -457,113 +457,6 @@ public:
}
};
enum Aledis
{
SAY_CHALLENGE = 0,
SAY_DEFEATED = 1,
EVENT_TALK = 1,
EVENT_ATTACK = 2,
EVENT_EVADE = 3,
EVENT_FIREBALL = 4,
EVENT_FROSTNOVA = 5,
SPELL_FIREBALL = 20823,
SPELL_FROSTNOVA = 11831,
};
struct npc_magister_aledis : public ScriptedAI
{
npc_magister_aledis(Creature* creature) : ScriptedAI(creature) { }
void StartFight(Player* player)
{
me->Dismount();
me->SetFacingToObject(player);
me->RemoveNpcFlag(UNIT_NPC_FLAG_GOSSIP);
_playerGUID = player->GetGUID();
_events.ScheduleEvent(EVENT_TALK, 2s);
}
void Reset() override
{
me->RestoreFaction();
me->RemoveNpcFlag(UNIT_NPC_FLAG_QUESTGIVER);
me->SetNpcFlag(UNIT_NPC_FLAG_GOSSIP);
me->SetImmuneToPC(true);
}
void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damageType*/, SpellSchoolMask /*spellInfo = nullptr*/) override
{
if (damage > me->GetHealth() || me->HealthBelowPctDamaged(20, damage))
{
damage = 0;
_events.Reset();
me->RestoreFaction();
me->RemoveAllAuras();
me->GetThreatMgr().ClearAllThreat();
me->CombatStop(true);
me->SetNpcFlag(UNIT_NPC_FLAG_QUESTGIVER);
me->SetImmuneToPC(true);
Talk(SAY_DEFEATED);
_events.ScheduleEvent(EVENT_EVADE, 1min);
}
}
void UpdateAI(uint32 diff) override
{
_events.Update(diff);
while (uint32 eventId = _events.ExecuteEvent())
{
switch (eventId)
{
case EVENT_TALK:
Talk(SAY_CHALLENGE);
_events.ScheduleEvent(EVENT_ATTACK, 2s);
break;
case EVENT_ATTACK:
me->SetImmuneToPC(false);
me->SetFaction(FACTION_MONSTER);
if (Player* player = ObjectAccessor::GetPlayer(*me, _playerGUID))
{
AttackStart(player);
}
_events.ScheduleEvent(EVENT_FIREBALL, 1ms);
_events.ScheduleEvent(EVENT_FROSTNOVA, 5s);
break;
case EVENT_FIREBALL:
DoCast(SPELL_FIREBALL);
_events.ScheduleEvent(EVENT_FIREBALL, 10s);
break;
case EVENT_FROSTNOVA:
DoCastAOE(SPELL_FROSTNOVA);
_events.ScheduleEvent(EVENT_FROSTNOVA, 20s);
break;
case EVENT_EVADE:
EnterEvadeMode();
break;
}
}
if (UpdateVictim())
{
DoMeleeAttackIfReady();
}
}
void sGossipSelect(Player* player, uint32 /*sender*/, uint32 /*action*/) override
{
CloseGossipMenuFor(player);
me->StopMoving();
StartFight(player);
}
private:
EventMap _events;
ObjectGuid _playerGUID;
};
enum Beacon
{
NPC_STONESCHYE_WHELP = 16927,
@@ -645,6 +538,5 @@ void AddSC_hellfire_peninsula()
new npc_fel_guard_hound();
new go_beacon();
RegisterCreatureAI(npc_magister_aledis);
RegisterGameObjectAI(go_magtheridons_head);
}
@@ -329,6 +329,12 @@ struct npc_training_dummy : NullCreatureAI
return;
_combatTimer[attacker->GetGUID()] = 5s;
// Pet attacks engage the owner via propagation without firing
// JustEnteredCombat here, so track the owner's timer too.
if (Unit* owner = attacker->GetCharmerOrOwner())
if (me->GetCombatManager().IsInCombatWith(owner))
_combatTimer[owner->GetGUID()] = 5s;
}
void UpdateAI(uint32 diff) override