feat(Scripts): Add hooks to allow modules to override arena rating updates

Add two new ArenaScript hooks to prevent module conflicts:

- OnBeforeArenaTeamMemberUpdate: Allows modules to skip core's
  MemberWon/MemberLost rating calculations by returning true

- CanSaveArenaStatsForMember: Allows modules to prevent core from
  writing to character_arena_stats table by returning false

Hook locations:
- Arena.cpp: Wrap MemberWon/MemberLost calls (lines 358, 366)
- ArenaTeam.cpp: Wrap character_arena_stats DB write (line 969)

Use case: Modules implementing alternative rating systems (like Glicko-2)
can now completely override core arena ratings without database conflicts
or redundant calculations.

When hooks return default values (false/true respectively), core behavior
is unchanged, maintaining backward compatibility.
This commit is contained in:
blinkysc 2025-11-05 14:04:27 -06:00
parent 3ad5e97206
commit 758b74e805
5 changed files with 35 additions and 8 deletions

View File

@ -355,12 +355,18 @@ void Arena::EndBattleground(TeamId winnerTeamId)
player->CastSpell(player, SPELL_LAST_MAN_STANDING, true);
}
winnerArenaTeam->MemberWon(player, loserMatchmakerRating, winnerMatchmakerChange);
if (!sScriptMgr->OnBeforeArenaTeamMemberUpdate(winnerArenaTeam, player, true, loserMatchmakerRating, winnerMatchmakerChange))
{
winnerArenaTeam->MemberWon(player, loserMatchmakerRating, winnerMatchmakerChange);
}
}
}
else
{
loserArenaTeam->MemberLost(player, winnerMatchmakerRating, loserMatchmakerChange);
if (!sScriptMgr->OnBeforeArenaTeamMemberUpdate(loserArenaTeam, player, false, winnerMatchmakerRating, loserMatchmakerChange))
{
loserArenaTeam->MemberLost(player, winnerMatchmakerRating, loserMatchmakerChange);
}
// Arena lost => reset the win_rated_arena having the "no_lose" condition
player->ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_CONDITION_NO_LOSE, 0);

View File

@ -966,12 +966,15 @@ void ArenaTeam::SaveToDB(bool forceMemberSave)
stmt->SetData(6, itr->Guid.GetCounter());
trans->Append(stmt);
stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_CHARACTER_ARENA_STATS);
stmt->SetData(0, itr->Guid.GetCounter());
stmt->SetData(1, GetSlot());
stmt->SetData(2, itr->MatchMakerRating);
stmt->SetData(3, itr->MaxMMR);
trans->Append(stmt);
if (sScriptMgr->CanSaveArenaStatsForMember(this, itr->Guid))
{
stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_CHARACTER_ARENA_STATS);
stmt->SetData(0, itr->Guid.GetCounter());
stmt->SetData(1, GetSlot());
stmt->SetData(2, itr->MatchMakerRating);
stmt->SetData(3, itr->MaxMMR);
trans->Append(stmt);
}
}
CharacterDatabase.CommitTransaction(trans);

View File

@ -44,6 +44,16 @@ void ScriptMgr::OnArenaStart(Battleground* bg)
CALL_ENABLED_HOOKS(ArenaScript, ARENAHOOK_ON_ARENA_START, script->OnArenaStart(bg));
}
bool ScriptMgr::OnBeforeArenaTeamMemberUpdate(ArenaTeam* team, Player* player, bool won, uint32 opponentMatchmakerRating, int32 matchmakerChange)
{
CALL_ENABLED_BOOLEAN_HOOKS(ArenaScript, ARENAHOOK_ON_BEFORE_TEAM_MEMBER_UPDATE, script->OnBeforeArenaTeamMemberUpdate(team, player, won, opponentMatchmakerRating, matchmakerChange));
}
bool ScriptMgr::CanSaveArenaStatsForMember(ArenaTeam* team, ObjectGuid playerGuid)
{
CALL_ENABLED_BOOLEAN_HOOKS(ArenaScript, ARENAHOOK_CAN_SAVE_ARENA_STATS_FOR_MEMBER, !script->CanSaveArenaStatsForMember(team, playerGuid));
}
ArenaScript::ArenaScript(const char* name, std::vector<uint16> enabledHooks)
: ScriptObject(name, ARENAHOOK_END)
{

View File

@ -29,6 +29,8 @@ enum ArenaHook
ARENAHOOK_CAN_SAVE_TO_DB,
ARENAHOOK_ON_BEFORE_CHECK_WIN_CONDITION,
ARENAHOOK_ON_ARENA_START,
ARENAHOOK_ON_BEFORE_TEAM_MEMBER_UPDATE,
ARENAHOOK_CAN_SAVE_ARENA_STATS_FOR_MEMBER,
ARENAHOOK_END
};
@ -51,6 +53,10 @@ public:
[[nodiscard]] virtual bool CanSaveToDB(ArenaTeam* /*team*/) { return true; }
virtual void OnArenaStart(Battleground* /* bg */) { };
[[nodiscard]] virtual bool OnBeforeArenaTeamMemberUpdate(ArenaTeam* /*team*/, Player* /*player*/, bool /*won*/, uint32 /*opponentMatchmakerRating*/, int32 /*matchmakerChange*/) { return false; }
[[nodiscard]] virtual bool CanSaveArenaStatsForMember(ArenaTeam* /*team*/, ObjectGuid /*playerGuid*/) { return true; }
};
#endif

View File

@ -660,6 +660,8 @@ public: /* ArenaScript */
bool CanSaveToDB(ArenaTeam* team);
bool OnBeforeArenaCheckWinConditions(Battleground* const bg);
void OnArenaStart(Battleground* const bg);
bool OnBeforeArenaTeamMemberUpdate(ArenaTeam* team, Player* player, bool won, uint32 opponentMatchmakerRating, int32 matchmakerChange);
bool CanSaveArenaStatsForMember(ArenaTeam* team, ObjectGuid playerGuid);
public: /* MiscScript */