From 4c922933dec7f2c12583b8dc6af3980de34fab5b Mon Sep 17 00:00:00 2001 From: hondacrx Date: Fri, 7 Jan 2022 21:49:50 -0500 Subject: [PATCH] Core/Player: Initial War Mode support Port From (https://github.com/TrinityCore/TrinityCore/commit/b821a729733db0d3742b4aefe05e5a8305724f66) --- Source/Framework/Constants/PlayerConst.cs | 3 + Source/Framework/Constants/SharedConst.cs | 4 + .../Database/Databases/CharacterDatabase.cs | 5 + Source/Game/Entities/Player/Player.Map.cs | 10 +- Source/Game/Entities/Player/Player.cs | 110 +++++++++++++++++- Source/Game/Handlers/MiscHandler.cs | 38 +++--- Source/Game/Networking/Packets/MiscPackets.cs | 12 ++ Source/Game/Server/WorldManager.cs | 84 +++++++++++++ Source/Scripts/Spells/Generic.cs | 27 +++++ Source/WorldServer/WorldServer.conf.dist | 37 ++++++ 10 files changed, 304 insertions(+), 26 deletions(-) diff --git a/Source/Framework/Constants/PlayerConst.cs b/Source/Framework/Constants/PlayerConst.cs index 9a41f2806..231407cd6 100644 --- a/Source/Framework/Constants/PlayerConst.cs +++ b/Source/Framework/Constants/PlayerConst.cs @@ -74,6 +74,9 @@ namespace Framework.Constants public const uint MaxAzeriteItemKnowledgeLevel = 30; public const uint PlayerConditionIdUnlockedAzeriteEssences = 69048; public const uint SpellIdHeartEssenceActionBarOverride = 298554; + + //Warmode + public const uint WarmodeEnlistedSpellOutside = 269083; } public struct MoneyConstants diff --git a/Source/Framework/Constants/SharedConst.cs b/Source/Framework/Constants/SharedConst.cs index 7219be3e6..71be01b8e 100644 --- a/Source/Framework/Constants/SharedConst.cs +++ b/Source/Framework/Constants/SharedConst.cs @@ -1419,6 +1419,9 @@ namespace Framework.Constants CalculateCreatureZoneAreaData, CalculateGameobjectZoneAreaData, CalendarDeleteOldEventsHour, + CallToArms5Pct, + CallToArms10Pct, + CallToArms20Pct, CastUnstuck, CharacterCreatingDisableAlliedRaceAchievementRequirement, CharacterCreatingDisabled, @@ -1488,6 +1491,7 @@ namespace Framework.Constants EnableSinfoLogin, EventAnnounce, Expansion, + FactionBalanceLevelCheckDiff, FeatureSystemBpayStoreEnabled, FeatureSystemCharacterUndeleteCooldown, FeatureSystemCharacterUndeleteEnabled, diff --git a/Source/Framework/Database/Databases/CharacterDatabase.cs b/Source/Framework/Database/Databases/CharacterDatabase.cs index 3adef5c7d..f9fe04696 100644 --- a/Source/Framework/Database/Databases/CharacterDatabase.cs +++ b/Source/Framework/Database/Databases/CharacterDatabase.cs @@ -785,6 +785,9 @@ namespace Framework.Database PrepareStatement(CharStatements.DEL_CHARACTER_AURA_STORED_LOCATIONS_BY_GUID, "DELETE FROM character_aura_stored_location WHERE Guid = ?"); PrepareStatement(CharStatements.DEL_CHARACTER_AURA_STORED_LOCATION, "DELETE FROM character_aura_stored_location WHERE Guid = ? AND Spell = ?"); PrepareStatement(CharStatements.INS_CHARACTER_AURA_STORED_LOCATION, "INSERT INTO character_aura_stored_location (Guid, Spell, MapId, PositionX, PositionY, PositionZ, Orientation) VALUES (?, ?, ?, ?, ?, ?, ?)"); + + // War mode + PrepareStatement(CharStatements.SEL_WAR_MODE_TUNING, "SELECT race, COUNT(guid) FROM characters WHERE ((playerFlags & 0x00000800) = 0x00000800) AND logout_time >= (UNIX_TIMESTAMP() - 604800) GROUP BY race"); } } @@ -1428,6 +1431,8 @@ namespace Framework.Database DEL_CHARACTER_AURA_STORED_LOCATION, INS_CHARACTER_AURA_STORED_LOCATION, + SEL_WAR_MODE_TUNING, + MAX_CHARACTERDATABASE_STATEMENTS } } diff --git a/Source/Game/Entities/Player/Player.Map.cs b/Source/Game/Entities/Player/Player.Map.cs index f184e2a29..ec26686df 100644 --- a/Source/Game/Entities/Player/Player.Map.cs +++ b/Source/Game/Entities/Player/Player.Map.cs @@ -192,6 +192,8 @@ namespace Game.Entities GetMap().SendZoneDynamicInfo(newZone, this); + UpdateWarModeAuras(); + UpdateHostileAreaState(zone); if (zone.HasFlag(AreaFlags.Capital)) // Is in a capital city @@ -247,15 +249,15 @@ namespace Game.Entities { if (InBattleground() || area.HasFlag(AreaFlags.Combat) || (area.PvpCombatWorldStateID != -1 && Global.WorldMgr.GetWorldState((WorldStates)area.PvpCombatWorldStateID) != 0)) pvpInfo.IsInHostileArea = true; - else if (Global.WorldMgr.IsPvPRealm() || area.HasFlag(AreaFlags.Unk3)) + else if (IsWarModeLocalActive() || area.HasFlag(AreaFlags.Unk3)) { if (area.HasFlag(AreaFlags.ContestedArea)) - pvpInfo.IsInHostileArea = Global.WorldMgr.IsPvPRealm(); + pvpInfo.IsInHostileArea = IsWarModeLocalActive(); else { FactionTemplateRecord factionTemplate = GetFactionTemplateEntry(); if (factionTemplate == null || factionTemplate.FriendGroup.HasAnyFlag(area.FactionGroupMask)) - pvpInfo.IsInHostileArea = false; + pvpInfo.IsInHostileArea = false; // friend area are considered hostile if war mode is active else if (factionTemplate.EnemyGroup.HasAnyFlag(area.FactionGroupMask)) pvpInfo.IsInHostileArea = true; else @@ -282,7 +284,7 @@ namespace Game.Entities } // Treat players having a quest flagging for PvP as always in hostile area - pvpInfo.IsHostile = pvpInfo.IsInHostileArea || HasPvPForcingQuest(); + pvpInfo.IsHostile = pvpInfo.IsInHostileArea || HasPvPForcingQuest() || IsWarModeLocalActive(); } public ZonePVPTypeOverride GetOverrideZonePVPType() { return (ZonePVPTypeOverride)(uint)m_activePlayerData.OverrideZonePVPType; } diff --git a/Source/Game/Entities/Player/Player.cs b/Source/Game/Entities/Player/Player.cs index a0c5b6b39..3010ebd09 100644 --- a/Source/Game/Entities/Player/Player.cs +++ b/Source/Game/Entities/Player/Player.cs @@ -2887,6 +2887,12 @@ namespace Game.Entities packet.AddState(2491, 0xF); // 9 } + // Horde War Mode bonus + packet.AddState(17042, 10 + (Global.WorldMgr.GetWarModeDominantFaction() == TeamId.Alliance ? Global.WorldMgr.GetWarModeOutnumberedFactionReward() : 0)); + + // Alliance War Mode bonus + packet.AddState(17043, 10 + (Global.WorldMgr.GetWarModeDominantFaction() == TeamId.Horde ? Global.WorldMgr.GetWarModeOutnumberedFactionReward() : 0)); + // insert switch (zoneid) { @@ -5068,15 +5074,101 @@ namespace Game.Entities return true; } + public void 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(PlayerFlags.Resting)) + return; + + if (enabled && !CanEnableWarModeInArea()) + return; + + // Don't allow to chang when aura SPELL_PVP_RULES_ENABLED is on + if (HasAura(PlayerConst.SpellPvpRulesEnabled)) + return; + + if (enabled) + { + AddPlayerFlag(PlayerFlags.WarModeDesired); + TogglePvpTalents(true); + SetPvP(true); + } + else + { + RemovePlayerFlag(PlayerFlags.WarModeDesired); + TogglePvpTalents(false); + SetPvP(false); + } + + UpdateWarModeAuras(); + } + + void SetWarModeLocal(bool enabled) + { + if (enabled) + AddPlayerLocalFlag(PlayerLocalFlags.WarMode); + else + RemovePlayerLocalFlag(PlayerLocalFlags.WarMode); + } + public bool CanEnableWarModeInArea() { - var area = CliDB.AreaTableStorage.LookupByKey(GetAreaId()); - if (area == null || !IsFriendlyArea(area)) + var zone = CliDB.AreaTableStorage.LookupByKey(GetZoneId()); + if (zone == null || !IsFriendlyArea(zone)) return false; - return area.HasFlag2(AreaFlags2.CanEnableWarMode); + var area = CliDB.AreaTableStorage.LookupByKey(GetAreaId()); + if (area == null) + area = zone; + + do + { + if ((area.Flags[1] & (uint)AreaFlags2.CanEnableWarMode) != 0) + return true; + + area = CliDB.AreaTableStorage.LookupByKey(area.ParentAreaID); + } while (area != null); + + return false; } - + + void UpdateWarModeAuras() + { + uint auraInside = 282559; + uint auraOutside = PlayerConst.WarmodeEnlistedSpellOutside; + + if (IsWarModeDesired()) + { + if (CanEnableWarModeInArea()) + { + RemovePlayerFlag(PlayerFlags.WarModeActive); + RemoveAurasDueToSpell(auraOutside); + CastSpell(this, auraInside, true); + } + else + { + AddPlayerFlag(PlayerFlags.WarModeActive); + RemoveAurasDueToSpell(auraInside); + CastSpell(this, auraOutside, true); + } + SetWarModeLocal(true); + AddPvpFlag(UnitPVPStateFlags.PvP); + } + else + { + SetWarModeLocal(false); + RemoveAurasDueToSpell(auraOutside); + RemoveAurasDueToSpell(auraInside); + RemovePlayerFlag(PlayerFlags.WarModeActive); + RemovePvpFlag(UnitPVPStateFlags.PvP); + } + } + + bool IsWarModeDesired() { return HasPlayerFlag(PlayerFlags.WarModeDesired); } + bool IsWarModeActive() { return HasPlayerFlag(PlayerFlags.WarModeActive); } + public bool IsWarModeLocalActive() { return HasPlayerLocalFlag(PlayerLocalFlags.WarMode); } + // Used in triggers for check "Only to targets that grant experience or honor" req public bool IsHonorOrXPTarget(Unit victim) { @@ -5350,6 +5442,12 @@ namespace Game.Entities } public bool IsAFK() { return HasPlayerFlag(PlayerFlags.AFK); } public bool IsDND() { return HasPlayerFlag(PlayerFlags.DND); } + + public bool IsMaxLevel() + { + return GetLevel() >= m_activePlayerData.MaxLevel; + } + public ChatFlags GetChatFlags() { ChatFlags tag = ChatFlags.None; @@ -7504,11 +7602,11 @@ namespace Game.Entities public bool CanTameExoticPets() { return IsGameMaster() || HasAuraType(AuraType.AllowTamePetType); } - void SendAttackSwingDeadTarget() { SendPacket(new AttackSwingError(AttackSwingErr.DeadTarget)); } void SendAttackSwingCantAttack() { SendPacket(new AttackSwingError(AttackSwingErr.CantAttack)); } + public void SendAttackSwingCancelAttack() { SendPacket(new CancelCombat()); } + void SendAttackSwingDeadTarget() { SendPacket(new AttackSwingError(AttackSwingErr.DeadTarget)); } public void SendAttackSwingNotInRange() { SendPacket(new AttackSwingError(AttackSwingErr.NotInRange)); } void SendAttackSwingBadFacingAttack() { SendPacket(new AttackSwingError(AttackSwingErr.BadFacing)); } - public void SendAttackSwingCancelAttack() { SendPacket(new CancelCombat()); } public void SendAutoRepeatCancel(Unit target) { CancelAutoRepeat cancelAutoRepeat = new(); diff --git a/Source/Game/Handlers/MiscHandler.cs b/Source/Game/Handlers/MiscHandler.cs index f39812148..a7b44d04f 100644 --- a/Source/Game/Handlers/MiscHandler.cs +++ b/Source/Game/Handlers/MiscHandler.cs @@ -506,39 +506,45 @@ namespace Game [WorldPacketHandler(ClientOpcodes.TogglePvp)] void HandleTogglePvP(TogglePvP packet) { - if (GetPlayer().HasPlayerFlag(PlayerFlags.InPVP)) - { - GetPlayer().RemovePlayerFlag(PlayerFlags.InPVP); - GetPlayer().AddPlayerFlag(PlayerFlags.PVPTimer); - if (!GetPlayer().pvpInfo.IsHostile && GetPlayer().IsPvP()) - GetPlayer().pvpInfo.EndTimer = GameTime.GetGameTime() + 300; // start toggle-off - } - else + if (!GetPlayer().HasPlayerFlag(PlayerFlags.InPVP)) { GetPlayer().AddPlayerFlag(PlayerFlags.InPVP); GetPlayer().RemovePlayerFlag(PlayerFlags.PVPTimer); if (!GetPlayer().IsPvP() || GetPlayer().pvpInfo.EndTimer != 0) GetPlayer().UpdatePvP(true, true); } + else if (!GetPlayer().IsWarModeLocalActive()) + { + GetPlayer().RemovePlayerFlag(PlayerFlags.InPVP); + GetPlayer().AddPlayerFlag(PlayerFlags.PVPTimer); + if (!GetPlayer().pvpInfo.IsHostile && GetPlayer().IsPvP()) + GetPlayer().pvpInfo.EndTimer = GameTime.GetGameTime() + 300; // start toggle-off + } } [WorldPacketHandler(ClientOpcodes.SetPvp)] void HandleSetPvP(SetPvP packet) { - if (!packet.EnablePVP) - { - GetPlayer().RemovePlayerFlag(PlayerFlags.InPVP); - GetPlayer().AddPlayerFlag(PlayerFlags.PVPTimer); - if (!GetPlayer().pvpInfo.IsHostile && GetPlayer().IsPvP()) - GetPlayer().pvpInfo.EndTimer = GameTime.GetGameTime() + 300; // start toggle-off - } - else + if (packet.EnablePVP) { GetPlayer().AddPlayerFlag(PlayerFlags.InPVP); GetPlayer().RemovePlayerFlag(PlayerFlags.PVPTimer); if (!GetPlayer().IsPvP() || GetPlayer().pvpInfo.EndTimer != 0) GetPlayer().UpdatePvP(true, true); } + else if (!GetPlayer().IsWarModeLocalActive()) + { + GetPlayer().RemovePlayerFlag(PlayerFlags.InPVP); + GetPlayer().AddPlayerFlag(PlayerFlags.PVPTimer); + if (!GetPlayer().pvpInfo.IsHostile && GetPlayer().IsPvP()) + GetPlayer().pvpInfo.EndTimer = GameTime.GetGameTime() + 300; // start toggle-off + } + } + + [WorldPacketHandler(ClientOpcodes.SetWarMode)] + void HandleSetWarMode(SetWarMode packet) + { + _player.SetWarModeDesired(packet.Enable); } [WorldPacketHandler(ClientOpcodes.FarSight)] diff --git a/Source/Game/Networking/Packets/MiscPackets.cs b/Source/Game/Networking/Packets/MiscPackets.cs index 3aa451263..e27475cb8 100644 --- a/Source/Game/Networking/Packets/MiscPackets.cs +++ b/Source/Game/Networking/Packets/MiscPackets.cs @@ -1135,6 +1135,18 @@ namespace Game.Networking.Packets public bool EnablePVP; } + class SetWarMode : ClientPacket + { + public SetWarMode(WorldPacket packet) : base(packet) { } + + public override void Read() + { + Enable = _worldPacket.HasBit(); + } + + public bool Enable; + } + class AccountHeirloomUpdate : ServerPacket { public AccountHeirloomUpdate() : base(ServerOpcodes.AccountHeirloomUpdate, ConnectionType.Instance) { } diff --git a/Source/Game/Server/WorldManager.cs b/Source/Game/Server/WorldManager.cs index 28d819cb6..521e767d2 100644 --- a/Source/Game/Server/WorldManager.cs +++ b/Source/Game/Server/WorldManager.cs @@ -1243,6 +1243,18 @@ namespace Game Log.outInfo(LogFilter.ServerLoading, @"VMap data directory is: {0}\vmaps", GetDataPath()); } + void SetForcedWarModeFactionBalanceState(int team, int reward) + { + _warModeDominantFaction = team; + _warModeOutnumberedFactionReward = reward; + } + + void DisableForcedWarModeFactionBalanceState() + { + UpdateWarModeRewardValues(); + } + + public void LoadAutobroadcasts() { uint oldMSTime = Time.GetMSTime(); @@ -2001,6 +2013,9 @@ namespace Game // reselect pools Global.QuestPoolMgr.ChangeDailyQuests(); + // Update faction balance + UpdateWarModeRewardValues(); + // store next reset time long now = GameTime.GetGameTime(); long next = GetNextDailyResetTime(now); @@ -2450,6 +2465,67 @@ namespace Game m_timers[WorldTimers.Corpses].SetCurrent(m_timers[WorldTimers.Corpses].GetInterval()); } + void UpdateWarModeRewardValues() + { + long[] warModeEnabledFaction = new long[2]; + + // Search for characters that have war mode enabled and played during the last week + PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_WAR_MODE_TUNING); + stmt.AddValue(0, (uint)PlayerFlags.WarModeDesired); + stmt.AddValue(1, (uint)PlayerFlags.WarModeDesired); + + SQLResult result = DB.Characters.Query(stmt); + if (!result.IsEmpty()) + { + do + { + byte race = result.Read(0); + + var raceEntry = CliDB.ChrRacesStorage.LookupByKey(race); + if (raceEntry != null) + { + var raceFaction = CliDB.FactionTemplateStorage.LookupByKey(raceEntry.FactionID); + if (raceFaction != null) + { + if ((raceFaction.FactionGroup & (byte)FactionMasks.Alliance) != 0) + warModeEnabledFaction[TeamId.Alliance] += result.Read(1); + else if ((raceFaction.FactionGroup & (byte)FactionMasks.Horde) != 0) + warModeEnabledFaction[TeamId.Horde] += result.Read(1); + } + } + + } while (result.NextRow()); + } + + _warModeDominantFaction = TeamId.Neutral; + _warModeOutnumberedFactionReward = 0; + + if (warModeEnabledFaction.All(val => val == 0)) + return; + + long dominantFactionCount = warModeEnabledFaction[TeamId.Alliance]; + int dominantFaction = TeamId.Alliance; + if (warModeEnabledFaction[TeamId.Alliance] < warModeEnabledFaction[TeamId.Horde]) + { + dominantFactionCount = warModeEnabledFaction[TeamId.Horde]; + dominantFaction = TeamId.Horde; + } + + double total = warModeEnabledFaction[TeamId.Alliance] + warModeEnabledFaction[TeamId.Horde]; + double pct = dominantFactionCount / total; + + if (pct >= WorldConfig.GetFloatValue(WorldCfg.CallToArms20Pct)) + _warModeOutnumberedFactionReward = 20; + else if (pct >= WorldConfig.GetFloatValue(WorldCfg.CallToArms10Pct)) + _warModeOutnumberedFactionReward = 10; + else if (pct >= WorldConfig.GetFloatValue(WorldCfg.CallToArms5Pct)) + _warModeOutnumberedFactionReward = 5; + else + return; + + _warModeDominantFaction = dominantFaction; + } + public uint GetVirtualRealmAddress() { return _realm.Id.GetAddress(); @@ -2477,6 +2553,10 @@ namespace Game public bool IsGuidWarning() { return _guidWarn; } public bool IsGuidAlert() { return _guidAlert; } + // War mode balancing + public int GetWarModeDominantFaction() { return _warModeDominantFaction; } + public int GetWarModeOutnumberedFactionReward() { return _warModeOutnumberedFactionReward; } + public WorldUpdateTime GetWorldUpdateTime() { return _worldUpdateTime; } #region Fields @@ -2550,6 +2630,10 @@ namespace Game bool _guidAlert; uint _warnDiff; long _warnShutdownTime; + + // War mode balancing + int _warModeDominantFaction; // the team that has higher percentage + int _warModeOutnumberedFactionReward; #endregion } diff --git a/Source/Scripts/Spells/Generic.cs b/Source/Scripts/Spells/Generic.cs index 3560689e1..9e519dd8c 100644 --- a/Source/Scripts/Spells/Generic.cs +++ b/Source/Scripts/Spells/Generic.cs @@ -4082,6 +4082,33 @@ namespace Scripts.Spells.Generic } } + // 269083 - Enlisted + [Script] // 282559 - Enlisted + class spell_gen_war_mode_enlisted : AuraScript + { + void CalcWarModeBonus(AuraEffect aurEff, ref int amount, ref bool canBeRecalculated) + { + Player target = GetUnitOwner().ToPlayer(); + if (target == null) + return; + + if (target.GetTeamId() == Global.WorldMgr.GetWarModeDominantFaction()) + return; + + amount += Global.WorldMgr.GetWarModeOutnumberedFactionReward(); + } + + public override void Register() + { + DoEffectCalcAmount.Add(new EffectCalcAmountHandler(CalcWarModeBonus, SpellConst.EffectAll, AuraType.ModXpPct)); + DoEffectCalcAmount.Add(new EffectCalcAmountHandler(CalcWarModeBonus, SpellConst.EffectAll, AuraType.ModXpQuestPct)); + DoEffectCalcAmount.Add(new EffectCalcAmountHandler(CalcWarModeBonus, SpellConst.EffectAll, AuraType.ModCurrencyGainFromSource)); + DoEffectCalcAmount.Add(new EffectCalcAmountHandler(CalcWarModeBonus, SpellConst.EffectAll, AuraType.ModMoneyGain)); + DoEffectCalcAmount.Add(new EffectCalcAmountHandler(CalcWarModeBonus, SpellConst.EffectAll, AuraType.ModAnimaGain)); + DoEffectCalcAmount.Add(new EffectCalcAmountHandler(CalcWarModeBonus, SpellConst.EffectAll, AuraType.Dummy)); + } + } + [Script] class spell_defender_of_azeroth_death_gate_selector : SpellScript { diff --git a/Source/WorldServer/WorldServer.conf.dist b/Source/WorldServer/WorldServer.conf.dist index 8fec848e0..5d832ec42 100644 --- a/Source/WorldServer/WorldServer.conf.dist +++ b/Source/WorldServer/WorldServer.conf.dist @@ -3832,5 +3832,42 @@ PacketSpoof.BanMode = 0 PacketSpoof.BanDuration = 86400 +# +################################################################################################### + +################################################################################################### +# 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 + # ################################################################################################### \ No newline at end of file