diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/server/database/Database/Implementation/CharacterDatabase.cpp | 3 | ||||
-rw-r--r-- | src/server/database/Database/Implementation/CharacterDatabase.h | 2 | ||||
-rw-r--r-- | src/server/game/Entities/Player/Player.cpp | 109 | ||||
-rw-r--r-- | src/server/game/Entities/Player/Player.h | 7 | ||||
-rw-r--r-- | src/server/game/Entities/Unit/Unit.h | 2 | ||||
-rw-r--r-- | src/server/game/Handlers/MiscHandler.cpp | 33 | ||||
-rw-r--r-- | src/server/game/Server/Packets/MiscPackets.cpp | 5 | ||||
-rw-r--r-- | src/server/game/Server/Packets/MiscPackets.h | 10 | ||||
-rw-r--r-- | src/server/game/Server/Packets/SystemPackets.h | 2 | ||||
-rw-r--r-- | src/server/game/Server/Protocol/Opcodes.cpp | 2 | ||||
-rw-r--r-- | src/server/game/Server/WorldSession.h | 2 | ||||
-rw-r--r-- | src/server/game/World/World.cpp | 80 | ||||
-rw-r--r-- | src/server/game/World/World.h | 16 | ||||
-rw-r--r-- | src/server/scripts/Commands/cs_debug.cpp | 49 | ||||
-rw-r--r-- | src/server/scripts/Spells/spell_generic.cpp | 31 | ||||
-rw-r--r-- | src/server/worldserver/worldserver.conf.dist | 38 |
16 files changed, 368 insertions, 23 deletions
diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp index 0879d50c074..99c2d8ea45a 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp @@ -797,6 +797,9 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_DEL_CHARACTER_AURA_STORED_LOCATION, "DELETE FROM character_aura_stored_location WHERE Guid = ? AND Spell = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_INS_CHARACTER_AURA_STORED_LOCATION, "INSERT INTO character_aura_stored_location (Guid, Spell, MapId, PositionX, PositionY, PositionZ, Orientation) " "VALUES (?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); + + // War mode + PrepareStatement(CHAR_SEL_WAR_MODE_TUNING, "SELECT race, COUNT(guid) FROM characters WHERE ((playerFlags & 0x00000800) = 0x00000800) AND logout_time >= (UNIX_TIMESTAMP() - 604800) GROUP BY race", CONNECTION_SYNCH); } CharacterDatabaseConnection::CharacterDatabaseConnection(MySQLConnectionInfo& connInfo) : MySQLConnection(connInfo) diff --git a/src/server/database/Database/Implementation/CharacterDatabase.h b/src/server/database/Database/Implementation/CharacterDatabase.h index e1862ce8eaf..571cc26183c 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.h +++ b/src/server/database/Database/Implementation/CharacterDatabase.h @@ -665,6 +665,8 @@ enum CharacterDatabaseStatements : uint32 CHAR_DEL_CHARACTER_AURA_STORED_LOCATION, CHAR_INS_CHARACTER_AURA_STORED_LOCATION, + CHAR_SEL_WAR_MODE_TUNING, + MAX_CHARACTERDATABASE_STATEMENTS }; diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 075fb0fd85d..a77afdfd8af 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -2429,6 +2429,11 @@ void Player::GiveLevel(uint8 level) sScriptMgr->OnPlayerLevelChanged(this, oldLevel); } +bool Player::IsMaxLevel() const +{ + return GetLevel() >= m_activePlayerData->MaxLevel; +} + void Player::InitTalentForLevel() { uint8 level = GetLevel(); @@ -7279,6 +7284,8 @@ void Player::UpdateZone(uint32 newZone, uint32 newArea) GetMap()->SendZoneDynamicInfo(newZone, this); + UpdateWarModeAuras(); + UpdateHostileAreaState(zone); if (zone->Flags[0] & AREA_FLAG_CAPITAL) // Is in a capital city @@ -7333,15 +7340,15 @@ void Player::UpdateHostileAreaState(AreaTableEntry const* area) { if (InBattleground() || area->Flags[0] & AREA_FLAG_COMBAT || (area->PvpCombatWorldStateID != -1 && sWorld->getWorldState(area->PvpCombatWorldStateID) != 0)) pvpInfo.IsInHostileArea = true; - else if (sWorld->IsPvPRealm() || (area->Flags[0] & AREA_FLAG_UNK3)) + else if (IsWarModeLocalActive() || (area->Flags[0] & AREA_FLAG_UNK3)) { if (area->Flags[0] & AREA_FLAG_CONTESTED_AREA) - pvpInfo.IsInHostileArea = sWorld->IsPvPRealm(); + pvpInfo.IsInHostileArea = IsWarModeLocalActive(); else { FactionTemplateEntry const* factionTemplate = GetFactionTemplateEntry(); if (!factionTemplate || factionTemplate->FriendGroup & area->FactionGroupMask) - pvpInfo.IsInHostileArea = false; + pvpInfo.IsInHostileArea = false; // friend area are considered hostile if war mode is active else if (factionTemplate->EnemyGroup & area->FactionGroupMask) pvpInfo.IsInHostileArea = true; else @@ -7368,7 +7375,7 @@ void Player::UpdateHostileAreaState(AreaTableEntry const* area) } // Treat players having a quest flagging for PvP as always in hostile area - pvpInfo.IsHostile = pvpInfo.IsInHostileArea || HasPvPForcingQuest(); + pvpInfo.IsHostile = pvpInfo.IsInHostileArea || HasPvPForcingQuest() || IsWarModeLocalActive(); } //If players are too far away from the duel flag... they lose the duel @@ -9214,6 +9221,12 @@ void Player::SendInitWorldStates(uint32 zoneId, uint32 areaId) packet.Worldstates.emplace_back(2491, 15); // NA_UI_GUARDS_LEFT } + // Horde War Mode bonus + packet.Worldstates.emplace_back(17042, 10 + (sWorld->GetWarModeDominantFaction() == TEAM_ALLIANCE ? sWorld->GetWarModeOutnumberedFactionReward() : 0)); + + // Alliance War Mode bonus + packet.Worldstates.emplace_back(17043, 10 + (sWorld->GetWarModeDominantFaction() == TEAM_HORDE ? sWorld->GetWarModeOutnumberedFactionReward() : 0)); + switch (zoneId) { case 1: // Dun Morogh @@ -29069,13 +29082,95 @@ void Player::UpdateAverageItemLevelEquipped() SetAverageItemLevelEquipped(totalItemLevel); } +void Player::SetWarModeDesired(bool enabled) +{ + // Only allow to toggle on when in stormwind/orgrimmar, and to toggle off in any rested place. + // Also disallow when in combat + if ((enabled == IsWarModeDesired()) || IsInCombat() || !HasPlayerFlag(PLAYER_FLAGS_RESTING)) + return; + + if (enabled && !CanEnableWarModeInArea()) + return; + + // Don't allow to chang when aura SPELL_PVP_RULES_ENABLED is on + if (HasAura(SPELL_PVP_RULES_ENABLED)) + return; + + if (enabled) + { + AddPlayerFlag(PLAYER_FLAGS_WAR_MODE_DESIRED); + TogglePvpTalents(true); + SetPvP(true); + } + else + { + RemovePlayerFlag(PLAYER_FLAGS_WAR_MODE_DESIRED); + TogglePvpTalents(false); + SetPvP(false); + } + + UpdateWarModeAuras(); +} + +void Player::SetWarModeLocal(bool enabled) +{ + if (enabled) + AddPlayerLocalFlag(PLAYER_LOCAL_FLAG_WAR_MODE); + else + RemovePlayerLocalFlag(PLAYER_LOCAL_FLAG_WAR_MODE); +} + bool Player::CanEnableWarModeInArea() const { - AreaTableEntry const* area = sAreaTableStore.LookupEntry(GetAreaId()); - if (!area || !IsFriendlyArea(area)) + AreaTableEntry const* zone = sAreaTableStore.LookupEntry(GetZoneId()); + if (!zone || !IsFriendlyArea(zone)) return false; - return area->Flags[1] & AREA_FLAG_2_CAN_ENABLE_WAR_MODE; + AreaTableEntry const* area = sAreaTableStore.LookupEntry(GetAreaId()); + if (!area) + area = zone; + + do + { + if (area->Flags[1] & AREA_FLAG_2_CAN_ENABLE_WAR_MODE) + return true; + + area = sAreaTableStore.LookupEntry(area->ParentAreaID); + } while (area); + + return false; +} + +void Player::UpdateWarModeAuras() +{ + uint32 auraInside = 282559; + uint32 auraOutside = WARMODE_ENLISTED_SPELL_OUTSIDE; + + if (IsWarModeDesired()) + { + if (CanEnableWarModeInArea()) + { + RemovePlayerFlag(PLAYER_FLAGS_WAR_MODE_ACTIVE); + RemoveAurasDueToSpell(auraOutside); + CastSpell(this, auraInside, true); + } + else + { + AddPlayerFlag(PLAYER_FLAGS_WAR_MODE_ACTIVE); + RemoveAurasDueToSpell(auraInside); + CastSpell(this, auraOutside, true); + } + SetWarModeLocal(true); + AddPvpFlag(UNIT_BYTE2_FLAG_PVP); + } + else + { + SetWarModeLocal(false); + RemoveAurasDueToSpell(auraOutside); + RemoveAurasDueToSpell(auraInside); + RemovePlayerFlag(PLAYER_FLAGS_WAR_MODE_ACTIVE); + RemovePvpFlag(UNIT_BYTE2_FLAG_PVP); + } } std::string Player::GetDebugInfo() const diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index b9c536b04ff..b16d0903e1e 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1186,6 +1186,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> void SetXP(uint32 xp); void GiveXP(uint32 xp, Unit* victim, float group_rate=1.0f); void GiveLevel(uint8 level); + bool IsMaxLevel() const; void InitStatsForLevel(bool reapplyMods = false); @@ -2730,7 +2731,13 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> bool IsInFriendlyArea() const; bool IsFriendlyArea(AreaTableEntry const* inArea) const; + void SetWarModeDesired(bool enabled); + bool IsWarModeDesired() const { return HasPlayerFlag(PLAYER_FLAGS_WAR_MODE_DESIRED); } + bool IsWarModeActive() const { return HasPlayerFlag(PLAYER_FLAGS_WAR_MODE_ACTIVE); } + bool IsWarModeLocalActive() const { return HasPlayerLocalFlag(PLAYER_LOCAL_FLAG_WAR_MODE); } + void SetWarModeLocal(bool enabled); bool CanEnableWarModeInArea() const; + void UpdateWarModeAuras(); std::string GetDebugInfo() const override; diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index 668253fe6ca..1de368514a1 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -37,6 +37,8 @@ #define SPELL_DH_DOUBLE_JUMP 196055 #define DISPLAYID_HIDDEN_MOUNT 73200 +#define WARMODE_ENLISTED_SPELL_OUTSIDE 269083 + #define MAX_SPELL_CHARM 4 #define MAX_SPELL_VEHICLE 6 #define MAX_SPELL_POSSESS 8 diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp index fe85091db82..9d94b531ac6 100644 --- a/src/server/game/Handlers/MiscHandler.cpp +++ b/src/server/game/Handlers/MiscHandler.cpp @@ -313,38 +313,43 @@ void WorldSession::HandleLogoutCancelOpcode(WorldPackets::Character::LogoutCance void WorldSession::HandleTogglePvP(WorldPackets::Misc::TogglePvP& /*packet*/) { - if (GetPlayer()->HasPlayerFlag(PLAYER_FLAGS_IN_PVP)) - { - GetPlayer()->RemovePlayerFlag(PLAYER_FLAGS_IN_PVP); - GetPlayer()->AddPlayerFlag(PLAYER_FLAGS_PVP_TIMER); - if (!GetPlayer()->pvpInfo.IsHostile && GetPlayer()->IsPvP()) - GetPlayer()->pvpInfo.EndTimer = GameTime::GetGameTime() + 300; // start toggle-off - } - else + if (!GetPlayer()->HasPlayerFlag(PLAYER_FLAGS_IN_PVP)) { GetPlayer()->AddPlayerFlag(PLAYER_FLAGS_IN_PVP); GetPlayer()->RemovePlayerFlag(PLAYER_FLAGS_PVP_TIMER); if (!GetPlayer()->IsPvP() || GetPlayer()->pvpInfo.EndTimer) GetPlayer()->UpdatePvP(true, true); } -} - -void WorldSession::HandleSetPvP(WorldPackets::Misc::SetPvP& packet) -{ - if (!packet.EnablePVP) + else if (!GetPlayer()->IsWarModeLocalActive()) { GetPlayer()->RemovePlayerFlag(PLAYER_FLAGS_IN_PVP); GetPlayer()->AddPlayerFlag(PLAYER_FLAGS_PVP_TIMER); if (!GetPlayer()->pvpInfo.IsHostile && GetPlayer()->IsPvP()) GetPlayer()->pvpInfo.EndTimer = GameTime::GetGameTime() + 300; // start toggle-off } - else +} + +void WorldSession::HandleSetPvP(WorldPackets::Misc::SetPvP& packet) +{ + if (packet.EnablePVP) { GetPlayer()->AddPlayerFlag(PLAYER_FLAGS_IN_PVP); GetPlayer()->RemovePlayerFlag(PLAYER_FLAGS_PVP_TIMER); if (!GetPlayer()->IsPvP() || GetPlayer()->pvpInfo.EndTimer) GetPlayer()->UpdatePvP(true, true); } + else if (!GetPlayer()->IsWarModeLocalActive()) + { + GetPlayer()->RemovePlayerFlag(PLAYER_FLAGS_IN_PVP); + GetPlayer()->AddPlayerFlag(PLAYER_FLAGS_PVP_TIMER); + if (!GetPlayer()->pvpInfo.IsHostile && GetPlayer()->IsPvP()) + GetPlayer()->pvpInfo.EndTimer = GameTime::GetGameTime() + 300; // start toggle-off + } +} + +void WorldSession::HandleSetWarMode(WorldPackets::Misc::SetWarMode& packet) +{ + _player->SetWarModeDesired(packet.Enable); } void WorldSession::HandlePortGraveyard(WorldPackets::Misc::PortGraveyard& /*packet*/) diff --git a/src/server/game/Server/Packets/MiscPackets.cpp b/src/server/game/Server/Packets/MiscPackets.cpp index b568f0108d8..3d31d29c944 100644 --- a/src/server/game/Server/Packets/MiscPackets.cpp +++ b/src/server/game/Server/Packets/MiscPackets.cpp @@ -612,6 +612,11 @@ void WorldPackets::Misc::SetPvP::Read() EnablePVP = _worldPacket.ReadBit(); } +void WorldPackets::Misc::SetWarMode::Read() +{ + Enable = _worldPacket.ReadBit(); +} + WorldPacket const* WorldPackets::Misc::AccountHeirloomUpdate::Write() { _worldPacket.WriteBit(IsFullUpdate); diff --git a/src/server/game/Server/Packets/MiscPackets.h b/src/server/game/Server/Packets/MiscPackets.h index 2e107094c2d..80a38dbbc94 100644 --- a/src/server/game/Server/Packets/MiscPackets.h +++ b/src/server/game/Server/Packets/MiscPackets.h @@ -801,6 +801,16 @@ namespace WorldPackets bool EnablePVP = false; }; + class SetWarMode final : public ClientPacket + { + public: + SetWarMode(WorldPacket&& packet) : ClientPacket(CMSG_SET_WAR_MODE, std::move(packet)) { } + + void Read() override; + + bool Enable = false; + }; + class AccountHeirloomUpdate final : public ServerPacket { public: diff --git a/src/server/game/Server/Packets/SystemPackets.h b/src/server/game/Server/Packets/SystemPackets.h index 65608acea49..6fe675f18eb 100644 --- a/src/server/game/Server/Packets/SystemPackets.h +++ b/src/server/game/Server/Packets/SystemPackets.h @@ -135,7 +135,7 @@ namespace WorldPackets bool KioskModeEnabled = false; bool CompetitiveModeEnabled = false; bool TokenBalanceEnabled = false; - bool WarModeFeatureEnabled = false; + bool WarModeFeatureEnabled = true; bool ClubsEnabled = false; bool ClubsBattleNetClubTypeAllowed = false; bool ClubsCharacterClubTypeAllowed = false; diff --git a/src/server/game/Server/Protocol/Opcodes.cpp b/src/server/game/Server/Protocol/Opcodes.cpp index ea17f2ffdd7..d9cf29b345c 100644 --- a/src/server/game/Server/Protocol/Opcodes.cpp +++ b/src/server/game/Server/Protocol/Opcodes.cpp @@ -824,7 +824,7 @@ void OpcodeTable::Initialize() DEFINE_HANDLER(CMSG_SET_TRADE_GOLD, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleSetTradeGoldOpcode); DEFINE_HANDLER(CMSG_SET_TRADE_ITEM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleSetTradeItemOpcode); DEFINE_HANDLER(CMSG_SET_USING_PARTY_GARRISON, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::Handle_NULL); - DEFINE_HANDLER(CMSG_SET_WAR_MODE, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::Handle_NULL); + DEFINE_HANDLER(CMSG_SET_WAR_MODE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleSetWarMode); DEFINE_HANDLER(CMSG_SET_WATCHED_FACTION, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleSetWatchedFactionOpcode); DEFINE_HANDLER(CMSG_SHOW_TRADE_SKILL, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::Handle_NULL); DEFINE_HANDLER(CMSG_SIGN_PETITION, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleSignPetition); diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index 2e34e817284..b80248fcbb8 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -509,6 +509,7 @@ namespace WorldPackets class OpeningCinematic; class TogglePvP; class SetPvP; + class SetWarMode; class MountSpecial; class SetTaxiBenchmarkMode; class MountSetFavorite; @@ -1255,6 +1256,7 @@ class TC_GAME_API WorldSession void HandleTogglePvP(WorldPackets::Misc::TogglePvP& packet); void HandleSetPvP(WorldPackets::Misc::SetPvP& packet); + void HandleSetWarMode(WorldPackets::Misc::SetWarMode& packet); void HandleSetSelectionOpcode(WorldPackets::Misc::SetSelection& packet); void HandleStandStateChangeOpcode(WorldPackets::Misc::StandStateChange& packet); diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index c67ca9b99bd..7d8c91d2398 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -156,6 +156,9 @@ World::World() _guidAlert = false; _warnDiff = 0; _warnShutdownTime = GameTime::GetGameTime(); + + _warModeDominantFaction = TEAM_NEUTRAL; + _warModeOutnumberedFactionReward = 0; } /// World destructor @@ -1659,6 +1662,12 @@ void World::LoadConfigSettings(bool reload) // Whether to use LoS from game objects m_bool_configs[CONFIG_CHECK_GOBJECT_LOS] = sConfigMgr->GetBoolDefault("CheckGameObjectLoS", true); + // FactionBalance + m_int_configs[CONFIG_FACTION_BALANCE_LEVEL_CHECK_DIFF] = sConfigMgr->GetIntDefault("Pvp.FactionBalance.LevelCheckDiff", 0); + m_float_configs[CONFIG_CALL_TO_ARMS_5_PCT] = sConfigMgr->GetFloatDefault("Pvp.FactionBalance.Pct5", 0.6f); + m_float_configs[CONFIG_CALL_TO_ARMS_10_PCT] = sConfigMgr->GetFloatDefault("Pvp.FactionBalance.Pct10", 0.7f); + m_float_configs[CONFIG_CALL_TO_ARMS_20_PCT] = sConfigMgr->GetFloatDefault("Pvp.FactionBalance.Pct20", 0.8f); + // call ScriptMgr if we're reloading the configuration if (reload) sScriptMgr->OnConfigLoad(reload); @@ -2462,6 +2471,17 @@ void World::SetInitialWorldSettings() TC_METRIC_EVENT("events", "World initialized", "World initialized in " + std::to_string(startupDuration / 60000) + " minutes " + std::to_string((startupDuration % 60000) / 1000) + " seconds"); } +void World::SetForcedWarModeFactionBalanceState(TeamId team, int32 reward) +{ + _warModeDominantFaction = team; + _warModeOutnumberedFactionReward = reward; +} + +void World::DisableForcedWarModeFactionBalanceState() +{ + UpdateWarModeRewardValues(); +} + void World::LoadAutobroadcasts() { uint32 oldMSTime = getMSTime(); @@ -3440,6 +3460,9 @@ void World::ResetWeeklyQuests() // reselect pools sQuestPoolMgr->ChangeWeeklyQuests(); + // Update faction balance + UpdateWarModeRewardValues(); + // store next reset time time_t now = GameTime::GetGameTime(); time_t next = GetNextWeeklyResetTime(now); @@ -3793,6 +3816,63 @@ void World::RemoveOldCorpses() m_timers[WUPDATE_CORPSES].SetCurrent(m_timers[WUPDATE_CORPSES].GetInterval()); } +void World::UpdateWarModeRewardValues() +{ + std::array<int64, 2> warModeEnabledFaction = { }; + + // Search for characters that have war mode enabled and played during the last week + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_WAR_MODE_TUNING); + stmt->setUInt32(0, PLAYER_FLAGS_WAR_MODE_DESIRED); + stmt->setUInt32(1, PLAYER_FLAGS_WAR_MODE_DESIRED); + if (PreparedQueryResult result = CharacterDatabase.Query(stmt)) + { + do + { + Field* fields = result->Fetch(); + uint8 race = fields[0].GetUInt8(); + if (ChrRacesEntry const* raceEntry = sChrRacesStore.LookupEntry(race)) + { + if (FactionTemplateEntry const* raceFaction = sFactionTemplateStore.AssertEntry(raceEntry->FactionID)) + { + if (raceFaction->FactionGroup & FACTION_MASK_ALLIANCE) + warModeEnabledFaction[TEAM_ALLIANCE] += fields[1].GetInt64(); + else if (raceFaction->FactionGroup & FACTION_MASK_HORDE) + warModeEnabledFaction[TEAM_HORDE] += fields[1].GetInt64(); + } + } + + } while (result->NextRow()); + } + + _warModeDominantFaction = TEAM_NEUTRAL; + _warModeOutnumberedFactionReward = 0; + + if (std::all_of(warModeEnabledFaction.begin(), warModeEnabledFaction.end(), [](int64 val) { return val == 0; })) + return; + + int64 dominantFactionCount = warModeEnabledFaction[TEAM_ALLIANCE]; + TeamId dominantFaction = TEAM_ALLIANCE; + if (warModeEnabledFaction[TEAM_ALLIANCE] < warModeEnabledFaction[TEAM_HORDE]) + { + dominantFactionCount = warModeEnabledFaction[TEAM_HORDE]; + dominantFaction = TEAM_HORDE; + } + + double total = warModeEnabledFaction[TEAM_ALLIANCE] + warModeEnabledFaction[TEAM_HORDE]; + double pct = dominantFactionCount / total; + + if (pct >= sWorld->getFloatConfig(CONFIG_CALL_TO_ARMS_20_PCT)) + _warModeOutnumberedFactionReward = 20; + else if (pct >= sWorld->getFloatConfig(CONFIG_CALL_TO_ARMS_10_PCT)) + _warModeOutnumberedFactionReward = 10; + else if (pct >= sWorld->getFloatConfig(CONFIG_CALL_TO_ARMS_5_PCT)) + _warModeOutnumberedFactionReward = 5; + else + return; + + _warModeDominantFaction = dominantFaction; +} + Realm realm; uint32 GetVirtualRealmAddress() diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h index 40d6ce3c938..2383b0c6d10 100644 --- a/src/server/game/World/World.h +++ b/src/server/game/World/World.h @@ -218,6 +218,9 @@ enum WorldFloatConfigs CONFIG_ARENA_MATCHMAKER_RATING_MODIFIER, CONFIG_RESPAWN_DYNAMICRATE_CREATURE, CONFIG_RESPAWN_DYNAMICRATE_GAMEOBJECT, + CONFIG_CALL_TO_ARMS_5_PCT, + CONFIG_CALL_TO_ARMS_10_PCT, + CONFIG_CALL_TO_ARMS_20_PCT, FLOAT_CONFIG_VALUE_COUNT }; @@ -421,6 +424,7 @@ enum WorldIntConfigs CONFIG_SOCKET_TIMEOUTTIME_ACTIVE, CONFIG_BLACKMARKET_MAXAUCTIONS, CONFIG_BLACKMARKET_UPDATE_PERIOD, + CONFIG_FACTION_BALANCE_LEVEL_CHECK_DIFF, INT_CONFIG_VALUE_COUNT }; @@ -808,6 +812,12 @@ class TC_GAME_API World bool IsGuidWarning() { return _guidWarn; } bool IsGuidAlert() { return _guidAlert; } + // War mode balancing + TeamId GetWarModeDominantFaction() const { return _warModeDominantFaction; } + int32 GetWarModeOutnumberedFactionReward() const { return _warModeOutnumberedFactionReward; } + void SetForcedWarModeFactionBalanceState(TeamId team, int32 reward = 0); + void DisableForcedWarModeFactionBalanceState(); + protected: void _UpdateGameTime(); @@ -931,6 +941,12 @@ class TC_GAME_API World uint32 _warnDiff; time_t _warnShutdownTime; + // War mode balancing + void UpdateWarModeRewardValues(); + + TeamId _warModeDominantFaction; // the team that has higher percentage + int32 _warModeOutnumberedFactionReward; + friend class debug_commandscript; }; diff --git a/src/server/scripts/Commands/cs_debug.cpp b/src/server/scripts/Commands/cs_debug.cpp index 952f5a316c9..6de82fcd0eb 100644 --- a/src/server/scripts/Commands/cs_debug.cpp +++ b/src/server/scripts/Commands/cs_debug.cpp @@ -88,6 +88,10 @@ public: { "spellfail", rbac::RBAC_PERM_COMMAND_DEBUG_SEND_SPELLFAIL, false, &HandleDebugSendSpellFailCommand, "" }, { "playerchoice", rbac::RBAC_PERM_COMMAND_DEBUG_SEND_PLAYER_CHOICE, false, &HandleDebugSendPlayerChoiceCommand, "" }, }; + static std::vector<ChatCommand> debugPvpCommandTable = + { + { "warmode", rbac::RBAC_PERM_COMMAND_DEBUG, false, &HandleDebugWarModeFactionBalanceCommand, "" }, + }; static std::vector<ChatCommand> debugAsanCommandTable = { { "memoryleak", rbac::RBAC_PERM_COMMAND_DEBUG_ASAN, true, &HandleDebugMemoryLeak, "" }, @@ -124,6 +128,7 @@ public: { "conversation" , rbac::RBAC_PERM_COMMAND_DEBUG_CONVERSATION, false, &HandleDebugConversationCommand, "" }, { "worldstate" , rbac::RBAC_PERM_COMMAND_DEBUG, false, &HandleDebugWorldStateCommand, "" }, { "wsexpression" , rbac::RBAC_PERM_COMMAND_DEBUG, false, &HandleDebugWSExpressionCommand, "" }, + { "pvp", rbac::RBAC_PERM_COMMAND_DEBUG, false, nullptr, "", debugPvpCommandTable }, { "dummy", rbac::RBAC_PERM_COMMAND_DEBUG_DUMMY, false, &HandleDebugDummyCommand, "" }, { "asan", rbac::RBAC_PERM_COMMAND_DEBUG_ASAN, true, nullptr, "", debugAsanCommandTable }, { "guidlimits", rbac::RBAC_PERM_COMMAND_DEBUG, true, &HandleDebugGuidLimitsCommand, "" }, @@ -137,6 +142,50 @@ public: return commandTable; } + static bool TryExtractTeamId(std::string const &args, TeamId &outFaction) + { + if ("a" == args || "alliance" == args) + outFaction = TEAM_ALLIANCE; + else if ("h" == args || "horde" == args) + outFaction = TEAM_HORDE; + else if ("n" == args || "neutral" == args) + outFaction = TEAM_NEUTRAL; + else + return false; + + return true; + } + + static bool HandleDebugWarModeFactionBalanceCommand(ChatHandler* handler, Variant<uint32, ExactSequence<'a','l','l','i','a','n','c','e'>, ExactSequence<'h','o','r','d','e'>, ExactSequence<'n','e','u','t','r','a','l'>, ExactSequence<'o','f','f'>> command, Optional<int32> rewardValue) + { + // USAGE: .debug pvp fb <alliance|horde|neutral|off> [pct] + // neutral Sets faction balance off. + // alliance Set faction balance to alliance. + // horde Set faction balance to horde. + // off Reset the faction balance and use the calculated value of it + switch (command.which()) + { + case 0: // workaround for Variant of only ExactSequences not being supported + handler->SendSysMessage(LANG_BAD_VALUE); + handler->SetSentErrorMessage(true); + return false; + case 1: + sWorld->SetForcedWarModeFactionBalanceState(TEAM_ALLIANCE, rewardValue.get_value_or(0)); + break; + case 2: + sWorld->SetForcedWarModeFactionBalanceState(TEAM_HORDE, rewardValue.get_value_or(0)); + break; + case 3: + sWorld->SetForcedWarModeFactionBalanceState(TEAM_NEUTRAL); + break; + case 4: + sWorld->DisableForcedWarModeFactionBalanceState(); + break; + } + + return true; + } + static bool HandleDebugPlayCinematicCommand(ChatHandler* handler, char const* args) { // USAGE: .debug play cinematic #cinematicId diff --git a/src/server/scripts/Spells/spell_generic.cpp b/src/server/scripts/Spells/spell_generic.cpp index b0d1593e4f3..212717fbd14 100644 --- a/src/server/scripts/Spells/spell_generic.cpp +++ b/src/server/scripts/Spells/spell_generic.cpp @@ -44,6 +44,7 @@ #include "SpellPackets.h" #include "SpellScript.h" #include "Vehicle.h" +#include "World.h" class spell_gen_absorb0_hitlimit1 : public AuraScript { @@ -4497,6 +4498,35 @@ class spell_gen_impatient_mind : public AuraScript } }; +// 269083 - Enlisted +// 282559 - Enlisted +class spell_gen_war_mode_enlisted : public AuraScript +{ + PrepareAuraScript(spell_gen_war_mode_enlisted); + + void CalcWarModeBonus(AuraEffect const* /*aurEff*/, int32& amount, bool& /*canBeRecalculated*/) + { + Player* target = GetUnitOwner()->ToPlayer(); + if (!target) + return; + + if (target->GetTeamId() == sWorld->GetWarModeDominantFaction()) + return; + + amount += sWorld->GetWarModeOutnumberedFactionReward(); + } + + void Register() override + { + DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_gen_war_mode_enlisted::CalcWarModeBonus, EFFECT_ALL, SPELL_AURA_MOD_XP_PCT); + DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_gen_war_mode_enlisted::CalcWarModeBonus, EFFECT_ALL, SPELL_AURA_MOD_XP_QUEST_PCT); + DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_gen_war_mode_enlisted::CalcWarModeBonus, EFFECT_ALL, SPELL_AURA_MOD_CURRENCY_GAIN_FROM_SOURCE); + DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_gen_war_mode_enlisted::CalcWarModeBonus, EFFECT_ALL, SPELL_AURA_MOD_MONEY_GAIN); + DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_gen_war_mode_enlisted::CalcWarModeBonus, EFFECT_ALL, SPELL_AURA_MOD_ANIMA_GAIN); + DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_gen_war_mode_enlisted::CalcWarModeBonus, EFFECT_ALL, SPELL_AURA_DUMMY); + } +}; + enum DefenderOfAzerothData { SPELL_DEATH_GATE_TELEPORT_STORMWIND = 316999, @@ -4749,6 +4779,7 @@ void AddSC_generic_spell_scripts() RegisterSpellScript(spell_gen_azgalor_rain_of_fire_hellfire_citadel); RegisterAuraScript(spell_gen_face_rage); RegisterAuraScript(spell_gen_impatient_mind); + RegisterAuraScript(spell_gen_war_mode_enlisted); RegisterSpellScript(spell_defender_of_azeroth_death_gate_selector); RegisterSpellScript(spell_defender_of_azeroth_speak_with_mograine); RegisterSpellScript(spell_summon_battle_pet); diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist index ed70b69d01d..505c5cc1306 100644 --- a/src/server/worldserver/worldserver.conf.dist +++ b/src/server/worldserver/worldserver.conf.dist @@ -40,6 +40,7 @@ # CURRENCIES SETTINGS # PACKET SPOOF PROTECTION SETTINGS # METRIC SETTINGS +# PVP SETTINGS # ################################################################################################### @@ -4286,3 +4287,40 @@ Metric.OverallStatusInterval = 1 # ################################################################################################### + +################################################################################################### +# PVP SETTINGS +# +# +# Pvp.FactionBalance.LevelCheckDiff +# Description: The amount of levels below the maximum level that accounts people for faction balance. +# That is, if max level is 60 and this has a value of 5, all players that are level 55 and above (including) will be included in the check +# Default: 0 + +Pvp.FactionBalance.LevelCheckDiff = 0 + +# Pvp.FactionBalance.Pct5 +# Description: The percentage at which a faction will receive an extra % percentage on their enlisted buff. Value range: [0...1] +# That is, if value is 0.6, and alliance outnumber horde by 60%, horde will get 15% bonus (5% + base 10%) +# Default: 0.6 + +Pvp.FactionBalance.Pct5 = 0.6 + +# Pvp.FactionBalance.Pct10 +# Description: The percentage at which a faction will receive an extra % percentage on their enlisted buff. Value range: [0...1] +# Does not stack with previous pct. Pct5 will not be checked if faction percentage is higher than this value. +# That is, if value is 0.7, and alliance outnumber horde by 70%, horde will get 20% bonus (10% + base 10%) +# Default: 0.7 + +Pvp.FactionBalance.Pct10 = 0.7 + +# Pvp.FactionBalance.Pct20 +# Description: The percentage at which a faction will receive an extra % percentage on their enlisted buff. Value range: [0...1] +# Does not stack with previous pct. Pct10 will not be checked if faction percentage is higher than this value. +# That is, if value is 0.8, and alliance outnumber horde by 80%, horde will get 30% bonus (20% + base 10%) +# Default: 0.8 + +Pvp.FactionBalance.Pct20 = 0.8 + +# +################################################################################################### |