diff --git a/Source/ACE.Server/Entity/AllegianceNode.cs b/Source/ACE.Server/Entity/AllegianceNode.cs index dea29a2db7..f833c4d88d 100644 --- a/Source/ACE.Server/Entity/AllegianceNode.cs +++ b/Source/ACE.Server/Entity/AllegianceNode.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using ACE.Common; using ACE.Entity; using ACE.Server.Managers; using ACE.Server.WorldObjects; @@ -117,5 +118,67 @@ public void OnLevelUp() vassal.Player.ExistedBeforeAllegianceXpChanges = true; } } + + /// + /// Returns the amount of realtime sworn to patron + /// + public TimeSpan CalculateRealTime() + { + if (Patron == null) + return TimeSpan.Zero; + + // TODO: figure out better handling for legacy players + // with AllegianceSwearTimestamp null + var now = Time.GetUnixTime(); + var timestamp = Player.AllegianceSwearTimestamp ?? now; + + return TimeSpan.FromSeconds(now - timestamp); + } + + /// + /// Returns the amount of gametime sworn to patron + /// + public TimeSpan CalculateGameTime() + { + if (Patron == null) + return TimeSpan.Zero; + + // TODO: figure out better handling for legacy players + // with AllegianceSwearAge null + var current_age = Player.Age ?? 0; + var swear_age = Player.AllegianceSwearAge ?? current_age; + + return TimeSpan.FromSeconds(current_age - swear_age); + } + + /// + /// Returns the average amount of realtime vassals sworn + /// + public TimeSpan CalculateRealTime_VassalAvg() + { + if (!HasVassals) + return TimeSpan.Zero; + + var total = TimeSpan.Zero; + foreach (var vassal in Vassals.Values) + total += vassal.CalculateRealTime(); + + return total / Vassals.Count; + } + + /// + /// Returns the average amount of gametime vassals sworn + /// + public TimeSpan CalculateGameTime_VassalAvg() + { + if (!HasVassals) + return TimeSpan.Zero; + + var total = TimeSpan.Zero; + foreach (var vassal in Vassals.Values) + total += vassal.CalculateGameTime(); + + return total / Vassals.Count; + } } } diff --git a/Source/ACE.Server/Entity/IPlayer.cs b/Source/ACE.Server/Entity/IPlayer.cs index dbea99b50f..6fd02a1ba6 100644 --- a/Source/ACE.Server/Entity/IPlayer.cs +++ b/Source/ACE.Server/Entity/IPlayer.cs @@ -78,6 +78,12 @@ public interface IPlayer int? HouseRentTimestamp { get; set; } + int? Age { get; set; } + + int? AllegianceSwearAge { get; set; } + + double? AllegianceSwearTimestamp { get; set; } + uint GetCurrentLoyalty(); diff --git a/Source/ACE.Server/Entity/OfflinePlayer.cs b/Source/ACE.Server/Entity/OfflinePlayer.cs index 83ce5e8c0c..f284d0ebbd 100644 --- a/Source/ACE.Server/Entity/OfflinePlayer.cs +++ b/Source/ACE.Server/Entity/OfflinePlayer.cs @@ -327,6 +327,25 @@ public int? AllegianceOfficerRank set { if (!value.HasValue) RemoveProperty(PropertyInt.AllegianceOfficerRank); else SetProperty(PropertyInt.AllegianceOfficerRank, value.Value); } } + public int? Age + { + get => GetProperty(PropertyInt.Age); + set { if (!value.HasValue) RemoveProperty(PropertyInt.Age); else SetProperty(PropertyInt.Age, value.Value); } + } + + public int? AllegianceSwearAge + { + get => GetProperty(PropertyInt.AllegianceSwearTimestamp); + set { if (!value.HasValue) RemoveProperty(PropertyInt.AllegianceSwearTimestamp); else SetProperty(PropertyInt.AllegianceSwearTimestamp, value.Value); } + } + + public double? AllegianceSwearTimestamp + { + get => GetProperty(PropertyFloat.AllegianceSwearTimestamp); + set { if (!value.HasValue) RemoveProperty(PropertyFloat.AllegianceSwearTimestamp); else SetProperty(PropertyFloat.AllegianceSwearTimestamp, value.Value); } + + } + /// /// This flag indicates if a player can pass up allegiance XP /// diff --git a/Source/ACE.Server/Managers/AllegianceManager.cs b/Source/ACE.Server/Managers/AllegianceManager.cs index f55f65767c..a05eb45f23 100644 --- a/Source/ACE.Server/Managers/AllegianceManager.cs +++ b/Source/ACE.Server/Managers/AllegianceManager.cs @@ -169,14 +169,16 @@ public static void RemoveCache(Allegiance allegiance) public static float SkillCap = 291.0f; /// - /// The maximum amount of realtime hours sworn to patron + /// The maximum amount of realtime sworn to patron + /// before reaching cap (retail default: 730 days / 2 years) /// - public static float RealCap = 730.0f; + public static TimeSpan RealCap = TimeSpan.FromDays(730); /// - /// The maximum amount of in-game hours sworn to patron + /// The maximum amount of gametime sworn to patron + /// before reaching cap (retail default: 720 hours / 1 month) /// - public static float GameCap = 720.0f; + public static TimeSpan GameCap = TimeSpan.FromHours(720); public static void PassXP(AllegianceNode vassalNode, ulong amount, bool direct) { @@ -246,19 +248,29 @@ public static void PassXP(AllegianceNode vassalNode, ulong amount, bool direct) var loyalty = Math.Min(vassal.GetCurrentLoyalty(), SkillCap); var leadership = Math.Min(patron.GetCurrentLeadership(), SkillCap); - var timeReal = Math.Min(RealCap, RealCap); - var timeGame = Math.Min(GameCap, GameCap); + //var realtime = vassalNode.CalculateRealTime(); + //var gametime = vassalNode.CalculateGameTime(); + var realtime = RealCap; + var gametime = GameCap; - var timeRealAvg = Math.Min(RealCap, RealCap); - var timeGameAvg = Math.Min(GameCap, GameCap); + var timeReal = Math.Min(realtime.TotalDays, RealCap.TotalDays); + var timeGame = Math.Min(gametime.TotalHours, GameCap.TotalHours); + + //var realtime_vassal = patronNode.CalculateRealTime_VassalAvg(); + //var gametime_vassal = patronNode.CalculateGameTime_VassalAvg(); + var realtime_vassal = RealCap; + var gametime_vassal = GameCap; + + var timeRealAvg = Math.Min(realtime_vassal.TotalDays, RealCap.TotalDays); + var timeGameAvg = Math.Min(gametime_vassal.TotalHours, GameCap.TotalHours); var vassalFactor = Math.Min(0.25f * patronNode.TotalVassals, 1.0f); var factor1 = direct ? 50.0f : 16.0f; var factor2 = direct ? 22.5f : 8.0f; - var generated = (factor1 + factor2 * (loyalty / SkillCap) * (1.0f + (timeReal / RealCap) * (timeGame / GameCap))) * 0.01f; - var received = (factor1 + factor2 * (leadership / SkillCap) * (1.0f + vassalFactor * (timeRealAvg / RealCap) * (timeGameAvg / GameCap))) * 0.01f; + var generated = (factor1 + factor2 * (loyalty / SkillCap) * (1.0f + (timeReal / RealCap.TotalDays) * (timeGame / GameCap.TotalHours))) * 0.01f; + var received = (factor1 + factor2 * (leadership / SkillCap) * (1.0f + vassalFactor * (timeRealAvg / RealCap.TotalDays) * (timeGameAvg / GameCap.TotalHours))) * 0.01f; var passup = generated * received; var generatedAmount = (uint)(amount * generated); diff --git a/Source/ACE.Server/WorldObjects/Player_Allegiance.cs b/Source/ACE.Server/WorldObjects/Player_Allegiance.cs index 27af2d9d04..6e873a7e68 100644 --- a/Source/ACE.Server/WorldObjects/Player_Allegiance.cs +++ b/Source/ACE.Server/WorldObjects/Player_Allegiance.cs @@ -1,6 +1,7 @@ using System; using System.Linq; +using ACE.Common; using ACE.Database.Models.Shard; using ACE.Entity; using ACE.Entity.Enum; @@ -60,6 +61,26 @@ public bool ExistedBeforeAllegianceXpChanges set { if (value) RemoveProperty(PropertyBool.ExistedBeforeAllegianceXpChanges); else SetProperty(PropertyBool.ExistedBeforeAllegianceXpChanges, value); } } + /// + /// The timestamp when the player swore allegiance + /// Used to calculate realtime sworn to patron + /// + public double? AllegianceSwearTimestamp + { + get => GetProperty(PropertyFloat.AllegianceSwearTimestamp); + set { if (!value.HasValue) RemoveProperty(PropertyFloat.AllegianceSwearTimestamp); else SetProperty(PropertyFloat.AllegianceSwearTimestamp, value.Value); } + } + + /// + /// The player age when they swore allegiance + /// Used to calculate gametime sworn to patron + /// + public int? AllegianceSwearAge + { + get => GetProperty(PropertyInt.AllegianceSwearTimestamp); + set { if (!value.HasValue) RemoveProperty(PropertyInt.AllegianceSwearTimestamp); else SetProperty(PropertyInt.AllegianceSwearTimestamp, value.Value); } + } + /// /// Called when a player tries to Swear Allegiance to a target /// @@ -125,6 +146,9 @@ public void SwearAllegiance(uint targetGuid, bool success, bool confirmed = fals AllegianceXPGenerated = 0; AllegianceOfficerRank = null; + AllegianceSwearTimestamp = Time.GetUnixTime(); + AllegianceSwearAge = Age; + // refresh ui panel Session.Network.EnqueueSend(new GameEventAllegianceUpdate(Session, Allegiance, AllegianceNode), new GameEventAllegianceAllegianceUpdateDone(Session));