/// <summary> /// Returns the amount to scale the XP for a fellow /// based on distance from the earner /// </summary> public double GetDistanceScalar(Player earner, Player fellow, XpType xpType) { if (earner == null || fellow == null) return 0.0f; if (xpType == XpType.Quest) return 1.0f; // https://asheron.fandom.com/wiki/Announcements_-_2004/01_-_Mirror,_Mirror#Rollout_Article // If they are indoors while you are outdoors, or vice-versa. if (earner.Location.Indoors != fellow.Location.Indoors) return 0.0f; // If you are both indoors but in different landblocks. if (earner.Location.Indoors && fellow.Location.Indoors && earner.Location.Landblock != fellow.Location.Landblock) return 0.0f; var dist = earner.Location.Distance2D(fellow.Location); if (dist >= MaxDistance * 2.0f) return 0.0f; if (dist <= MaxDistance) return 1.0f; var scalar = 1.0f - (dist - MaxDistance) / MaxDistance; return Math.Max(0.0f, scalar); }
/// <summary> /// Adds XP to a player's total XP, handles triggers (vitae, level up) /// </summary> private void UpdateXpAndLevel(long amount, XpType xpType) { // until we are max level we must make sure that we send var xpTable = DatManager.PortalDat.XpTable; var maxLevel = GetMaxLevel(); var maxLevelXp = xpTable.CharacterLevelXPList.Last(); if (Level != maxLevel) { var addAmount = amount; var amountLeftToEnd = (long)maxLevelXp - TotalExperience ?? 0; if (amount > amountLeftToEnd) { addAmount = amountLeftToEnd; } AvailableExperience += addAmount; TotalExperience += addAmount; var xpTotalUpdate = new GameMessagePrivateUpdatePropertyInt64(this, PropertyInt64.TotalExperience, TotalExperience ?? 0); var xpAvailUpdate = new GameMessagePrivateUpdatePropertyInt64(this, PropertyInt64.AvailableExperience, AvailableExperience ?? 0); Session.Network.EnqueueSend(xpTotalUpdate, xpAvailUpdate); CheckForLevelup(); } if (HasVitae && xpType != XpType.Allegiance) { UpdateXpVitae(amount); } }
/// <summary> /// Directly grants XP to the player, without the XP modifier /// </summary> /// <param name="amount">The amount of XP to grant to the player</param> /// <param name="xpType">The source of the XP being granted</param> /// <param name="shareable">If TRUE, this XP can be shared with fellowship members</param> public void GrantXP(long amount, XpType xpType, ShareType shareType = ShareType.All) { if (Fellowship != null && Fellowship.ShareXP && shareType.HasFlag(ShareType.Fellowship)) { // this will divy up the XP, and re-call this function // with ShareType.Fellowship removed Fellowship.SplitXp((ulong)amount, xpType, shareType, this); return; } UpdateXpAndLevel(amount, xpType); // for passing XP up the allegiance chain, // this function is only called at the very beginning, to start the process. if (shareType.HasFlag(ShareType.Allegiance)) { UpdateXpAllegiance(amount); } // only certain types of XP are granted to items if (xpType == XpType.Kill || xpType == XpType.Quest) { GrantItemXP(amount); } }
/// <summary> /// Directly grants XP to the player, without the XP modifier /// </summary> /// <param name="amount">The amount of XP to grant to the player</param> /// <param name="passup">The source of the XP being granted</param> /// <param name="shareable">If TRUE, this XP can be shared with fellowship members</param> public void GrantXP(long amount, XpType xpType, bool shareable = true) { if (shareable && Fellowship != null && Fellowship.ShareXP && Fellowship.ShareableMembers.ContainsKey(Guid.Full)) { // this will divy up the XP, and re-call this function // with shareable = false Fellowship.SplitXp((ulong)amount, xpType, this); return; } UpdateXpAndLevel(amount); // for passing XP up the allegiance chain, // this function is only called at the very beginning, to start the process. if (xpType != XpType.Allegiance) { UpdateXpAllegiance(amount); } // only certain types of XP are granted to items if (xpType == XpType.Kill || xpType == XpType.Quest) { GrantItemXP(amount); } }
private void AddLuminance(long amount, XpType xpType) { var available = AvailableLuminance ?? 0; var maximum = MaximumLuminance ?? 0; if (available == maximum) { return; } // this is similar to Player_Xp.UpdateXpAndLevel() var remaining = maximum - available; var addAmount = Math.Min(amount, remaining); AvailableLuminance = available + addAmount; if (xpType == XpType.Quest) { Session.Network.EnqueueSend(new GameMessageSystemChat($"You've earned {amount:N0} Luminance.", ChatMessageType.Broadcast)); } UpdateLuminance(); }
/// <summary> /// Returns the amount to scale the XP for a fellow /// based on distance from the leader /// </summary> private double GetDistanceScalar(Player earner, Player fellow, XpType xpType) { if (earner == null || fellow == null) { return(0.0f); } if (xpType == XpType.Quest) { return(1.0f); } var earnerPosition = earner.Location; var fellowPosition = fellow.Location; var dist = fellowPosition.Distance2D(earnerPosition); if (dist >= MaxDistance * 2.0f) { return(0.0f); } if (dist <= MaxDistance) { return(1.0f); } var scalar = 1 - (dist - MaxDistance) / MaxDistance; return(Math.Max(0.0f, scalar)); }
/// <summary> /// Splits luminance amongst fellowship members, depending on XP type and fellow settings /// </summary> /// <param name="amount">The input amount of luminance</param> /// <param name="xpType">The type of lumaniance (quest luminance is handled differently)</param> /// <param name="player">The fellowship member who originated the luminance</param> public void SplitLuminance(ulong amount, XpType xpType, ShareType shareType, Player player) { // https://asheron.fandom.com/wiki/Announcements_-_2002/02_-_Fever_Dreams#Letter_to_the_Players_1 shareType &= ~ShareType.Fellowship; if (xpType == XpType.Quest) { // quest luminance is not shared player.GrantLuminance((long)amount, XpType.Quest, shareType); } else { // pre-filter: evenly divide between luminance-eligible fellows var shareableMembers = GetFellowshipMembers().Values.Where(f => f.MaximumLuminance != null).ToList(); if (shareableMembers.Count == 0) { return; } var perAmount = (long)Math.Round((double)(amount / (ulong)shareableMembers.Count)); // further filter to fellows in radar range var inRange = shareableMembers.Intersect(WithinRange(player, true)).ToList(); foreach (var member in inRange) { var fellowXpType = player == member ? xpType : XpType.Fellowship; member.GrantLuminance(perAmount, fellowXpType, shareType); } } }
/// <summary> /// Splits XP amongst fellowship members, depending on XP type and fellow settings /// </summary> /// <param name="amount">The input amount of XP</param> /// <param name="xpType">The type of XP (quest XP is handled differently)</param> /// <param name="player">The fellowship member who originated the XP</param> public void SplitXp(ulong amount, XpType xpType, ShareType shareType, Player player) { // https://asheron.fandom.com/wiki/Announcements_-_2002/02_-_Fever_Dreams#Letter_to_the_Players_1 var fellowshipMembers = GetFellowshipMembers(); shareType &= ~ShareType.Fellowship; // quest turn-ins: flat share (retail default) if (xpType == XpType.Quest && !PropertyManager.GetBool("fellow_quest_bonus").Item) { var perAmount = (long)amount / fellowshipMembers.Count; foreach (var member in fellowshipMembers.Values) { var fellowXpType = player == member ? XpType.Quest : XpType.Fellowship; member.GrantXP(perAmount, fellowXpType, shareType); } } // divides XP evenly to all the sharable fellows within level range, // but with a significant boost to the amount of xp, based on # of fellowship members else if (EvenShare) { var totalAmount = (ulong)Math.Round(amount * GetMemberSharePercent()); foreach (var member in fellowshipMembers.Values) { var shareAmount = (ulong)Math.Round(totalAmount * GetDistanceScalar(player, member, xpType)); var fellowXpType = player == member ? xpType : XpType.Fellowship; member.GrantXP((long)shareAmount, fellowXpType, shareType); } return; } // divides XP to all sharable fellows within level range // based on each fellowship member's level else { var levelXPSum = fellowshipMembers.Values.Select(p => p.GetXPToNextLevel(p.Level.Value)).Sum(); foreach (var member in fellowshipMembers.Values) { var levelXPScale = (double)member.GetXPToNextLevel(member.Level.Value) / levelXPSum; var playerTotal = (ulong)Math.Round(amount * levelXPScale * GetDistanceScalar(player, member, xpType)); var fellowXpType = player == member ? xpType : XpType.Fellowship; member.GrantXP((long)playerTotal, fellowXpType, shareType); } } }
/// <summary> /// Applies luminance modifiers before adding luminance /// </summary> public void EarnLuminance(long amount, XpType xpType, ShareType shareType = ShareType.All) { // following the same model as Player_Xp var modifier = PropertyManager.GetDouble("luminance_modifier").Item; var m_amount = (long)Math.Round(amount * modifier); GrantLuminance(m_amount, xpType, shareType); }
/// <summary> /// Applies luminance modifiers before adding luminance /// </summary> public void EarnLuminance(long amount, XpType xpType, ShareType shareType = ShareType.All) { // following the same model as Player_Xp var modifier = PropertyManager.GetDouble("luminance_modifier").Item; // should this be passed upstream to fellowship? var enchantment = GetXPAndLuminanceModifier(xpType); var m_amount = (long)Math.Round(amount * enchantment * modifier); GrantLuminance(m_amount, xpType, shareType); }
/// <summary> /// Directly grants luminance to the player, without any additional luminance modifiers /// </summary> public void GrantLuminance(long amount, XpType xpType, ShareType shareType = ShareType.All) { if (Fellowship != null && Fellowship.ShareXP && shareType.HasFlag(ShareType.Fellowship)) { // this will divy up the luminance, and re-call this function // with ShareType.Fellowship removed Fellowship.SplitLuminance((ulong)amount, xpType, shareType, this); } else { AddLuminance(amount, xpType); } }
/// <summary> /// Splits XP amongst fellowship members, depending on XP type and fellow settings /// </summary> /// <param name="amount">The input amount of XP</param> /// <param name="xpType">The type of XP (quest XP is handled differently)</param> /// <param name="player">The fellowship member who originated the XP</param> public void SplitXp(ulong amount, XpType xpType, Player player) { var shareableMembers = GetShareableMembers(); // handle sharing quest XP with fellows if (xpType == XpType.Quest) { foreach (var member in shareableMembers.Values) { var fellowXpType = player == member ? XpType.Quest : XpType.Fellowship; member.GrantXP((long)amount, fellowXpType, false); } } // divides XP evenly to all the sharable fellows within level range, // but with a significant boost to the amount of xp, based on # of fellowship members else if (EvenShare) { var totalAmount = (ulong)Math.Round(amount * GetMemberSharePercent()); foreach (var member in shareableMembers.Values) { var shareAmount = (ulong)Math.Round(totalAmount * GetDistanceScalar(player, member)); var fellowXpType = player == member ? xpType : XpType.Fellowship; member.GrantXP((long)shareAmount, fellowXpType, false); } return; } // divides XP to all sharable fellows within level range // based on each fellowship member's level else { var levelSum = shareableMembers.Values.Select(p => p.Level ?? 1).Sum(); foreach (var member in shareableMembers.Values) { var levelScale = (float)(member.Level ?? 1) / levelSum; var playerTotal = (ulong)Math.Round(amount * levelScale * GetDistanceScalar(player, member)); var fellowXpType = player == member ? xpType : XpType.Fellowship; member.GrantXP((long)playerTotal, fellowXpType, false); } } }
/// <summary> /// Returns the multiplier to XP and Luminance from Trinkets and Augmentations /// </summary> public float GetXPAndLuminanceModifier(XpType xpType) { var enchantmentBonus = EnchantmentManager.GetXPBonus(); var augBonus = 0.0f; if (xpType == XpType.Kill && AugmentationBonusXp > 0) { augBonus = AugmentationBonusXp * 0.05f; } var modifier = 1.0f + enchantmentBonus + augBonus; //Console.WriteLine($"XPAndLuminanceModifier: {modifier}"); return(modifier); }
private void AddLuminance(long amount, XpType xpType) { if (AvailableLuminance == MaximumLuminance) { return; } // this is similar to Player_Xp.UpdateXpAndLevel() var remaining = (MaximumLuminance - AvailableLuminance) ?? 0; var addAmount = Math.Min(amount, remaining); AvailableLuminance += addAmount; UpdateLuminance(); }
/// <summary> /// A player earns XP through natural progression, ie. kills and quests completed /// </summary> /// <param name="amount">The amount of XP being added</param> /// <param name="xpType">The source of XP being added</param> /// <param name="shareable">True if this XP can be shared with Fellowship</param> public void EarnXP(long amount, XpType xpType, ShareType shareType = ShareType.All) { //Console.WriteLine($"{Name}.EarnXP({amount}, {sharable}, {fixedAmount})"); // apply xp modifier var modifier = PropertyManager.GetDouble("xp_modifier").Item; var enchantment = EnchantmentManager.GetXPMod(); var m_amount = (long)Math.Round(amount * enchantment * modifier); if (m_amount < 0) { log.Warn($"{Name}.EarnXP({amount}, {shareType})"); log.Warn($"modifier: {modifier}, enchantment: {enchantment}, m_amount: {m_amount}"); return; } GrantXP(m_amount, xpType, shareType); }
private void AddLuminance(long amount, XpType xpType) { var available = AvailableLuminance ?? 0; var maximum = MaximumLuminance ?? 0; if (available == maximum) { return; } // this is similar to Player_Xp.UpdateXpAndLevel() var remaining = maximum - available; var addAmount = Math.Min(amount, remaining); AvailableLuminance = available + addAmount; UpdateLuminance(); }
/// <summary> /// A player earns XP through natural progression, ie. kills and quests completed /// </summary> /// <param name="amount">The amount of XP being added</param> /// <param name="xpType">The source of XP being added</param> /// <param name="shareable">True if this XP can be shared with Fellowship</param> public void EarnXP(long amount, XpType xpType, ShareType shareType = ShareType.All) { //Console.WriteLine($"{Name}.EarnXP({amount}, {sharable}, {fixedAmount})"); // apply xp modifier var modifier = PropertyManager.GetDouble("xp_modifier").Item; // should this be passed upstream to fellowship / allegiance? var enchantment = GetXPAndLuminanceModifier(xpType); var m_amount = (long)Math.Round(amount * enchantment * modifier); if (m_amount < 0) { log.Warn($"{Name}.EarnXP({amount}, {shareType})"); log.Warn($"modifier: {modifier}, enchantment: {enchantment}, m_amount: {m_amount}"); return; } GrantXP(m_amount, xpType, shareType); }
/// <summary> /// Adds XP to a player's total XP, handles triggers (vitae, level up) /// </summary> private void UpdateXpAndLevel(long amount, XpType xpType) { // until we are max level we must make sure that we send var xpTable = DatManager.PortalDat.XpTable; var maxLevel = GetMaxLevel(); var maxLevelXp = 9223372036854775807UL; // max int64 number if (Level != maxLevel) { var addAmount = amount; var amountLeftToEnd = (long)maxLevelXp - TotalExperience ?? 0; if (amount > amountLeftToEnd) { addAmount = amountLeftToEnd; } AvailableExperience += addAmount; TotalExperience += addAmount; var xpTotalUpdate = new GameMessagePrivateUpdatePropertyInt64(this, PropertyInt64.TotalExperience, TotalExperience ?? 0); var xpAvailUpdate = new GameMessagePrivateUpdatePropertyInt64(this, PropertyInt64.AvailableExperience, AvailableExperience ?? 0); Session.Network.EnqueueSend(xpTotalUpdate, xpAvailUpdate); CheckForLevelup(); } if (xpType == XpType.Quest) { Session.Network.EnqueueSend(new GameMessageSystemChat($"You've earned {amount:N0} experience.", ChatMessageType.Broadcast)); } if (HasVitae && xpType != XpType.Allegiance) { UpdateXpVitae(amount); } }
/// <summary> /// Directly grants XP to the player, without the XP modifier /// </summary> /// <param name="amount">The amount of XP to grant to the player</param> /// <param name="xpType">The source of the XP being granted</param> /// <param name="shareable">If TRUE, this XP can be shared with fellowship members</param> public void GrantXP(long amount, XpType xpType, ShareType shareType = ShareType.All) { if (IsOlthoiPlayer) { if (HasVitae) { UpdateXpVitae(amount); } return; } if (Fellowship != null && Fellowship.ShareXP && shareType.HasFlag(ShareType.Fellowship)) { // this will divy up the XP, and re-call this function // with ShareType.Fellowship removed Fellowship.SplitXp((ulong)amount, xpType, shareType, this); return; } // Make sure UpdateXpAndLevel is done on this players thread EnqueueAction(new ActionEventDelegate(() => UpdateXpAndLevel(amount, xpType))); // for passing XP up the allegiance chain, // this function is only called at the very beginning, to start the process. if (shareType.HasFlag(ShareType.Allegiance)) { UpdateXpAllegiance(amount); } // only certain types of XP are granted to items if (xpType == XpType.Kill || xpType == XpType.Quest) { GrantItemXP(amount); } }