ModernUO/Projects/Scripts/Accounting/Account.cs

1028 lines
30 KiB
C#

using System;
using System.Collections.Generic;
using System.Net;
using System.Xml;
using Server.Misc;
using Server.Mobiles;
using Server.Multis;
using Server.Network;
namespace Server.Accounting
{
public class Account : IAccount, IComparable<Account>
{
public static readonly TimeSpan YoungDuration = TimeSpan.FromHours(40.0);
public static readonly TimeSpan InactiveDuration = TimeSpan.FromDays(180.0);
public static readonly TimeSpan EmptyInactiveDuration = TimeSpan.FromDays(30.0);
private AccessLevel m_AccessLevel;
private TimeSpan m_TotalGameTime;
private List<AccountComment> m_Comments;
private List<AccountTag> m_Tags;
private readonly Mobile[] m_Mobiles;
private PasswordProtectionAlgorithm m_PasswordAlgorithm;
/// <summary>
/// Deletes the account, all characters of the account, and all houses of those characters
/// </summary>
public void Delete()
{
for (int i = 0; i < Length; ++i)
{
Mobile m = this[i];
if (m == null)
continue;
List<BaseHouse> list = BaseHouse.GetHouses(m);
for (int j = 0; j < list.Count; ++j)
list[j].Delete();
m.Delete();
m.Account = null;
m_Mobiles[i] = null;
}
if (LoginIPs.Length != 0 && AccountHandler.IPTable.ContainsKey(LoginIPs[0]))
--AccountHandler.IPTable[LoginIPs[0]];
Accounts.Remove(Username);
}
/// <summary>
/// Object detailing information about the hardware of the last person to log into this account
/// </summary>
public HardwareInfo HardwareInfo { get; set; }
/// <summary>
/// List of IP addresses for restricted access. '*' wildcard supported. If the array contains zero entries, all IP addresses are allowed.
/// </summary>
public string[] IPRestrictions { get; set; }
/// <summary>
/// List of IP addresses which have successfully logged into this account.
/// </summary>
public IPAddress[] LoginIPs { get; set; }
/// <summary>
/// List of account comments. Type of contained objects is AccountComment.
/// </summary>
public List<AccountComment> Comments => m_Comments ?? (m_Comments = new List<AccountComment>());
/// <summary>
/// List of account tags. Type of contained objects is AccountTag.
/// </summary>
public List<AccountTag> Tags => m_Tags ?? (m_Tags = new List<AccountTag>());
/// <summary>
/// Account username. Case insensitive validation.
/// </summary>
public string Username { get; set; }
/// <summary>
/// Account email address.
/// </summary>
public string Email { get; set; }
/// <summary>
/// Account username and password. May be null.
/// </summary>
public string Password { get; set; }
/// <summary>
/// Initial AccessLevel for new characters created on this account.
/// </summary>
public AccessLevel AccessLevel
{
get => m_AccessLevel;
set => m_AccessLevel = value;
}
/// <summary>
/// Internal bitfield of account flags. Consider using direct access properties (Banned, Young), or GetFlag/SetFlag methods
/// </summary>
public int Flags { get; set; }
/// <summary>
/// Gets or sets a flag indicating if this account is banned.
/// </summary>
public bool Banned
{
get
{
bool isBanned = GetFlag(0);
if (!isBanned)
return false;
if (GetBanTags(out DateTime banTime, out TimeSpan banDuration))
if (banDuration != TimeSpan.MaxValue && DateTime.UtcNow >= banTime + banDuration)
{
SetUnspecifiedBan(null); // clear
Banned = false;
return false;
}
return true;
}
set => SetFlag(0, value);
}
/// <summary>
/// Gets or sets a flag indicating if the characters created on this account will have the young status.
/// </summary>
public bool Young
{
get => !GetFlag(1);
set
{
SetFlag(1, !value);
m_YoungTimer?.Stop();
m_YoungTimer = null;
}
}
/// <summary>
/// The date and time of when this account was created.
/// </summary>
public DateTime Created { get; }
/// <summary>
/// Gets or sets the date and time when this account was last accessed.
/// </summary>
public DateTime LastLogin { get; set; }
/// <summary>
/// An account is considered inactive based upon LastLogin and InactiveDuration. If the account is empty, it is based upon EmptyInactiveDuration
/// </summary>
public bool Inactive
{
get
{
if (AccessLevel != AccessLevel.Player)
return false;
TimeSpan inactiveLength = DateTime.UtcNow - LastLogin;
return inactiveLength > (Count == 0 ? EmptyInactiveDuration : InactiveDuration);
}
}
/// <summary>
/// Gets the total game time of this account, also considering the game time of characters
/// that have been deleted.
/// </summary>
public TimeSpan TotalGameTime
{
get
{
for (int i = 0; i < m_Mobiles.Length; i++)
if (m_Mobiles[i] is PlayerMobile m && m.NetState != null)
return m_TotalGameTime + (DateTime.UtcNow - m.SessionStart);
return m_TotalGameTime;
}
}
/// <summary>
/// Gets the value of a specific flag in the Flags bitfield.
/// </summary>
/// <param name="index">The zero-based flag index.</param>
public bool GetFlag(int index) => (Flags & 1 << index) != 0;
/// <summary>
/// Sets the value of a specific flag in the Flags bitfield.
/// </summary>
/// <param name="index">The zero-based flag index.</param>
/// <param name="value">The value to set.</param>
public void SetFlag(int index, bool value)
{
if (value)
Flags |= 1 << index;
else
Flags &= ~(1 << index);
}
/// <summary>
/// Adds a new tag to this account. This method does not check for duplicate names.
/// </summary>
/// <param name="name">New tag name.</param>
/// <param name="value">New tag value.</param>
public void AddTag(string name, string value)
{
Tags.Add(new AccountTag(name, value));
}
/// <summary>
/// Removes all tags with the specified name from this account.
/// </summary>
/// <param name="name">Tag name to remove.</param>
public void RemoveTag(string name)
{
for (int i = Tags.Count - 1; i >= 0; --i)
{
if (i >= Tags.Count)
continue;
AccountTag tag = Tags[i];
if (tag.Name == name)
Tags.RemoveAt(i);
}
}
/// <summary>
/// Modifies an existing tag or adds a new tag if no tag exists.
/// </summary>
/// <param name="name">Tag name.</param>
/// <param name="value">Tag value.</param>
public void SetTag(string name, string value)
{
for (int i = 0; i < Tags.Count; ++i)
{
AccountTag tag = Tags[i];
if (tag.Name == name)
{
tag.Value = value;
return;
}
}
AddTag(name, value);
}
/// <summary>
/// Gets the value of a tag -or- null if there are no tags with the specified name.
/// </summary>
/// <param name="name">Name of the desired tag value.</param>
public string GetTag(string name)
{
for (int i = 0; i < Tags.Count; ++i)
{
AccountTag tag = Tags[i];
if (tag.Name == name)
return tag.Value;
}
return null;
}
public void SetUnspecifiedBan(Mobile from)
{
SetBanTags(from, DateTime.MinValue, TimeSpan.Zero);
}
public void SetBanTags(Mobile from, DateTime banTime, TimeSpan banDuration)
{
if (from == null)
RemoveTag("BanDealer");
else
SetTag("BanDealer", from.ToString());
if (banTime == DateTime.MinValue)
RemoveTag("BanTime");
else
SetTag("BanTime", XmlConvert.ToString(banTime, XmlDateTimeSerializationMode.Utc));
if (banDuration == TimeSpan.Zero)
RemoveTag("BanDuration");
else
SetTag("BanDuration", banDuration.ToString());
}
public bool GetBanTags(out DateTime banTime, out TimeSpan banDuration)
{
string tagTime = GetTag("BanTime");
string tagDuration = GetTag("BanDuration");
if (tagTime != null)
banTime = Utility.GetXMLDateTime(tagTime, DateTime.MinValue);
else
banTime = DateTime.MinValue;
if (tagDuration == "Infinite")
banDuration = TimeSpan.MaxValue;
else if (tagDuration != null)
banDuration = Utility.ToTimeSpan(tagDuration);
else
banDuration = TimeSpan.Zero;
return banTime != DateTime.MinValue && banDuration != TimeSpan.Zero;
}
public void SetPassword(string plainPassword)
{
Password = AccountSecurity.CurrentPasswordProtection.EncryptPassword(plainPassword);
m_PasswordAlgorithm = AccountSecurity.AlgorithmName;
}
public bool CheckPassword(string plainPassword)
{
bool ok = AccountSecurity.GetPasswordProtection(m_PasswordAlgorithm).ValidatePassword(Password, plainPassword);
if (!ok)
return false;
// Upgrade the password protection in case we change the algorithm
if (m_PasswordAlgorithm != AccountSecurity.AlgorithmName)
SetPassword(plainPassword);
return true;
}
private Timer m_YoungTimer;
public static void Initialize()
{
EventSink.Connected += EventSink_Connected;
EventSink.Disconnected += EventSink_Disconnected;
EventSink.Login += EventSink_Login;
}
private static void EventSink_Connected(Mobile m)
{
if (!(m.Account is Account acc))
return;
if (acc.Young && acc.m_YoungTimer == null)
{
acc.m_YoungTimer = new YoungTimer(acc);
acc.m_YoungTimer.Start();
}
}
private static void EventSink_Disconnected(Mobile m)
{
if (!(m.Account is Account acc))
return;
if (acc.m_YoungTimer != null)
{
acc.m_YoungTimer.Stop();
acc.m_YoungTimer = null;
}
if (!(m is PlayerMobile pm))
return;
acc.m_TotalGameTime += DateTime.UtcNow - pm.SessionStart;
}
private static void EventSink_Login(Mobile m)
{
if (!(m is PlayerMobile pm))
return;
if (!(m.Account is Account acc))
return;
if (pm.Young && acc.Young)
{
TimeSpan ts = YoungDuration - acc.TotalGameTime;
int hours = Math.Max((int)ts.TotalHours, 0);
m.SendAsciiMessage("You will enjoy the benefits and relatively safe status of a young player for {0} more hour{1}.", hours, hours != 1 ? "s" : "");
}
}
public void RemoveYoungStatus(int message)
{
Young = false;
for (int i = 0; i < m_Mobiles.Length; i++)
if (m_Mobiles[i] is PlayerMobile m && m.Young)
{
m.Young = false;
if (m.NetState != null)
{
if (message > 0)
m.SendLocalizedMessage(message);
m.SendLocalizedMessage(1019039); // You are no longer considered a young player of Ultima Online, and are no longer subject to the limitations and benefits of being in that caste.
}
}
}
public void CheckYoung()
{
if (TotalGameTime >= YoungDuration)
RemoveYoungStatus(1019038); // You are old enough to be considered an adult, and have outgrown your status as a young player!
}
private class YoungTimer : Timer
{
private readonly Account m_Account;
public YoungTimer(Account account)
: base(TimeSpan.FromMinutes(1.0), TimeSpan.FromMinutes(1.0))
{
m_Account = account;
Priority = TimerPriority.FiveSeconds;
}
protected override void OnTick()
{
m_Account.CheckYoung();
}
}
public Account(string username, string password)
{
Username = username;
SetPassword(password);
m_AccessLevel = AccessLevel.Player;
Created = LastLogin = DateTime.UtcNow;
m_TotalGameTime = TimeSpan.Zero;
m_Mobiles = new Mobile[7];
IPRestrictions = Array.Empty<string>();
LoginIPs = Array.Empty<IPAddress>();
Accounts.Add(this);
}
public Account(XmlElement node)
{
Username = Utility.GetText(node["username"], "empty");
// Note: ModernUO doesn't support plain passwords, MD5, or SHA1.
// TODO: Offload passwords to its own module so it can be easily written/upgraded
Password = Utility.GetText(node["password"], null);
Enum.TryParse(Utility.GetText(node["passwordAlgorithm"], null), true, out m_PasswordAlgorithm);
Enum.TryParse(Utility.GetText(node["accessLevel"], "Player"), true, out m_AccessLevel);
Flags = Utility.GetXMLInt32(Utility.GetText(node["flags"], "0"), 0);
Created = Utility.GetXMLDateTime(Utility.GetText(node["created"], null), DateTime.UtcNow);
LastLogin = Utility.GetXMLDateTime(Utility.GetText(node["lastLogin"], null), DateTime.UtcNow);
TotalGold = Utility.GetXMLInt32(Utility.GetText(node["totalGold"], "0"), 0);
TotalPlat = Utility.GetXMLInt32(Utility.GetText(node["totalPlat"], "0"), 0);
m_Mobiles = LoadMobiles(node);
m_Comments = LoadComments(node);
m_Tags = LoadTags(node);
LoginIPs = LoadAddressList(node);
IPRestrictions = LoadAccessCheck(node);
for (int i = 0; i < m_Mobiles.Length; ++i)
if (m_Mobiles[i] != null)
m_Mobiles[i].Account = this;
TimeSpan totalGameTime = Utility.GetXMLTimeSpan(Utility.GetText(node["totalGameTime"], null), TimeSpan.Zero);
if (totalGameTime == TimeSpan.Zero)
for (int i = 0; i < m_Mobiles.Length; i++)
if (m_Mobiles[i] is PlayerMobile m)
totalGameTime += m.GameTime;
m_TotalGameTime = totalGameTime;
if (Young)
CheckYoung();
Accounts.Add(this);
}
/// <summary>
/// Deserializes a list of string values from an xml element. Null values are not added to the list.
/// </summary>
/// <param name="node">The XmlElement from which to deserialize.</param>
/// <returns>String list. Value will never be null.</returns>
public static string[] LoadAccessCheck(XmlElement node)
{
string[] stringList;
XmlElement accessCheck = node["accessCheck"];
if (accessCheck != null)
{
List<string> list = new List<string>();
foreach (XmlElement ip in accessCheck.GetElementsByTagName("ip"))
{
string text = Utility.GetText(ip, null);
if (text != null)
list.Add(text);
}
stringList = list.ToArray();
}
else
{
stringList = Array.Empty<string>();
}
return stringList;
}
/// <summary>
/// Deserializes a list of IPAddress values from an xml element.
/// </summary>
/// <param name="node">The XmlElement from which to deserialize.</param>
/// <returns>Address list. Value will never be null.</returns>
public static IPAddress[] LoadAddressList(XmlElement node)
{
IPAddress[] list;
XmlElement addressList = node["addressList"];
if (addressList != null)
{
int count = Utility.GetXMLInt32(Utility.GetAttribute(addressList, "count", "0"), 0);
list = new IPAddress[count];
count = 0;
foreach (XmlElement ip in addressList.GetElementsByTagName("ip"))
if (count < list.Length)
if (IPAddress.TryParse(Utility.GetText(ip, null), out IPAddress address))
{
list[count] = Utility.Intern(address);
count++;
}
if (count != list.Length)
{
IPAddress[] old = list;
list = new IPAddress[count];
for (int i = 0; i < count && i < old.Length; ++i)
list[i] = old[i];
}
}
else
{
list = Array.Empty<IPAddress>();
}
return list;
}
/// <summary>
/// Deserializes a list of Mobile instances from an xml element.
/// </summary>
/// <param name="node">The XmlElement instance from which to deserialize.</param>
/// <returns>Mobile list. Value will never be null.</returns>
public static Mobile[] LoadMobiles(XmlElement node)
{
Mobile[] list = new Mobile[7];
XmlElement chars = node["chars"];
// int length = Accounts.GetInt32( Accounts.GetAttribute( chars, "length", "6" ), 6 );
// list = new Mobile[length];
// Above is legacy, no longer used
if (chars != null)
foreach (XmlElement ele in chars.GetElementsByTagName("char"))
try
{
int index = Utility.GetXMLInt32(Utility.GetAttribute(ele, "index", "0"), 0);
uint serial = Utility.GetXMLUInt32(Utility.GetText(ele, "0"), 0);
if (index >= 0 && index < list.Length)
list[index] = World.FindMobile(serial);
}
catch
{
// ignored
}
return list;
}
/// <summary>
/// Deserializes a list of AccountComment instances from an xml element.
/// </summary>
/// <param name="node">The XmlElement from which to deserialize.</param>
/// <returns>Comment list. Value will never be null.</returns>
public static List<AccountComment> LoadComments(XmlElement node)
{
List<AccountComment> list = null;
XmlElement comments = node["comments"];
if (comments != null)
{
list = new List<AccountComment>();
foreach (XmlElement comment in comments.GetElementsByTagName("comment"))
try { list.Add(new AccountComment(comment)); }
catch
{
// ignored
}
}
return list;
}
/// <summary>
/// Deserializes a list of AccountTag instances from an xml element.
/// </summary>
/// <param name="node">The XmlElement from which to deserialize.</param>
/// <returns>Tag list. Value will never be null.</returns>
public static List<AccountTag> LoadTags(XmlElement node)
{
List<AccountTag> list = null;
XmlElement tags = node["tags"];
if (tags != null)
{
list = new List<AccountTag>();
foreach (XmlElement tag in tags.GetElementsByTagName("tag"))
try { list.Add(new AccountTag(tag)); }
catch
{
// ignored
}
}
return list;
}
/// <summary>
/// Checks if a specific NetState is allowed access to this account.
/// </summary>
/// <param name="ns">NetState instance to check.</param>
/// <returns>True if allowed, false if not.</returns>
public bool HasAccess(NetState ns) => ns != null && HasAccess(ns.Address);
public bool HasAccess(IPAddress ipAddress)
{
AccessLevel level = AccountHandler.LockdownLevel;
if (level > AccessLevel.Player)
{
bool hasAccess = false;
if (m_AccessLevel >= level)
hasAccess = true;
else
for (int i = 0; !hasAccess && i < Length; ++i)
{
Mobile m = this[i];
if (m?.AccessLevel >= level)
hasAccess = true;
}
Console.WriteLine("{0} {1}", hasAccess ? "yes" : "no", m_AccessLevel);
if (!hasAccess)
return false;
}
bool accessAllowed = IPRestrictions.Length == 0 || IPLimiter.IsExempt(ipAddress);
for (int i = 0; !accessAllowed && i < IPRestrictions.Length; ++i)
accessAllowed = Utility.IPMatch(IPRestrictions[i], ipAddress);
return accessAllowed;
}
/// <summary>
/// Records the IP address of 'ns' in its 'LoginIPs' list.
/// </summary>
/// <param name="ns">NetState instance to record.</param>
public void LogAccess(NetState ns)
{
if (ns != null) LogAccess(ns.Address);
}
public void LogAccess(IPAddress ipAddress)
{
if (IPLimiter.IsExempt(ipAddress))
return;
if (LoginIPs.Length == 0)
{
if (AccountHandler.IPTable.ContainsKey(ipAddress))
AccountHandler.IPTable[ipAddress]++;
else
AccountHandler.IPTable[ipAddress] = 1;
}
bool contains = false;
for (int i = 0; !contains && i < LoginIPs.Length; ++i)
contains = LoginIPs[i].Equals(ipAddress);
if (contains)
return;
IPAddress[] old = LoginIPs;
LoginIPs = new IPAddress[old.Length + 1];
for (int i = 0; i < old.Length; ++i)
LoginIPs[i] = old[i];
LoginIPs[old.Length] = ipAddress;
}
/// <summary>
/// Checks if a specific NetState is allowed access to this account. If true, the NetState IPAddress is added to the address list.
/// </summary>
/// <param name="ns">NetState instance to check.</param>
/// <returns>True if allowed, false if not.</returns>
public bool CheckAccess(NetState ns) => ns != null && CheckAccess(ns.Address);
public bool CheckAccess(IPAddress ipAddress)
{
bool hasAccess = HasAccess(ipAddress);
if (hasAccess)
LogAccess(ipAddress);
return hasAccess;
}
/// <summary>
/// Serializes this Account instance to an XmlTextWriter.
/// </summary>
/// <param name="xml">The XmlTextWriter instance from which to serialize.</param>
public void Save(XmlTextWriter xml)
{
xml.WriteStartElement("account");
xml.WriteStartElement("username");
xml.WriteString(Username);
xml.WriteEndElement();
xml.WriteStartElement("passwordAlgorithm");
xml.WriteString(m_PasswordAlgorithm.ToString());
xml.WriteEndElement();
xml.WriteStartElement("password");
xml.WriteString(Password);
xml.WriteEndElement();
if (m_AccessLevel != AccessLevel.Player)
{
xml.WriteStartElement("accessLevel");
xml.WriteString(m_AccessLevel.ToString());
xml.WriteEndElement();
}
if (Flags != 0)
{
xml.WriteStartElement("flags");
xml.WriteString(XmlConvert.ToString(Flags));
xml.WriteEndElement();
}
xml.WriteStartElement("created");
xml.WriteString(XmlConvert.ToString(Created, XmlDateTimeSerializationMode.Utc));
xml.WriteEndElement();
xml.WriteStartElement("lastLogin");
xml.WriteString(XmlConvert.ToString(LastLogin, XmlDateTimeSerializationMode.Utc));
xml.WriteEndElement();
xml.WriteStartElement("totalGameTime");
xml.WriteString(XmlConvert.ToString(TotalGameTime));
xml.WriteEndElement();
xml.WriteStartElement("chars");
for (int i = 0; i < m_Mobiles.Length; ++i)
{
Mobile m = m_Mobiles[i];
if (m?.Deleted == false)
{
xml.WriteStartElement("char");
xml.WriteAttributeString("index", i.ToString());
xml.WriteString(m.Serial.Value.ToString());
xml.WriteEndElement();
}
}
xml.WriteEndElement();
if (m_Comments?.Count > 0)
{
xml.WriteStartElement("comments");
for (int i = 0; i < m_Comments.Count; ++i)
m_Comments[i].Save(xml);
xml.WriteEndElement();
}
if (m_Tags?.Count > 0)
{
xml.WriteStartElement("tags");
for (int i = 0; i < m_Tags.Count; ++i)
m_Tags[i].Save(xml);
xml.WriteEndElement();
}
if (LoginIPs.Length > 0)
{
xml.WriteStartElement("addressList");
xml.WriteAttributeString("count", LoginIPs.Length.ToString());
for (int i = 0; i < LoginIPs.Length; ++i)
{
xml.WriteStartElement("ip");
xml.WriteString(LoginIPs[i].ToString());
xml.WriteEndElement();
}
xml.WriteEndElement();
}
if (IPRestrictions.Length > 0)
{
xml.WriteStartElement("accessCheck");
for (int i = 0; i < IPRestrictions.Length; ++i)
{
xml.WriteStartElement("ip");
xml.WriteString(IPRestrictions[i]);
xml.WriteEndElement();
}
xml.WriteEndElement();
}
xml.WriteStartElement("totalGold");
xml.WriteString(XmlConvert.ToString(TotalGold));
xml.WriteEndElement();
xml.WriteStartElement("totalPlat");
xml.WriteString(XmlConvert.ToString(TotalPlat));
xml.WriteEndElement();
xml.WriteEndElement();
}
/// <summary>
/// Gets the current number of characters on this account.
/// </summary>
public int Count
{
get
{
int count = 0;
for (int i = 0; i < Length; ++i)
if (this[i] != null)
++count;
return count;
}
}
/// <summary>
/// Gets the maximum amount of characters allowed to be created on this account. Values other than 1, 5, 6, or 7 are not supported by the client.
/// </summary>
public int Limit => Core.SA ? 7 : Core.AOS ? 6 : 5;
/// <summary>
/// Gets the maximum amount of characters that this account can hold.
/// </summary>
public int Length => m_Mobiles.Length;
/// <summary>
/// Gets or sets the character at a specified index for this account. Out of bound index values are handled; null returned for get, ignored for set.
/// </summary>
public Mobile this[int index]
{
get
{
if (index >= 0 && index < m_Mobiles.Length)
{
Mobile m = m_Mobiles[index];
if (m?.Deleted == true)
{
m.Account = null;
m_Mobiles[index] = m = null;
}
return m;
}
return null;
}
set
{
if (index >= 0 && index < m_Mobiles.Length)
{
if (m_Mobiles[index] != null)
m_Mobiles[index].Account = null;
m_Mobiles[index] = value;
if (m_Mobiles[index] != null)
m_Mobiles[index].Account = this;
}
}
}
public override string ToString() => Username;
public int CompareTo(Account other) => other == null ? 1 : Username.CompareTo(other.Username);
public int CompareTo(IAccount other) => other == null ? 1 : Username.CompareTo(other.Username);
/// <summary>
/// This amount represents the current amount of Gold owned by the player.
/// The value does not include the value of Platinum and ranges from
/// 0 to 999,999,999 by default.
/// </summary>
[CommandProperty(AccessLevel.Administrator)]
public int TotalGold { get; private set; }
/// <summary>
/// This amount represents the current amount of Platinum owned by the player.
/// The value does not include the value of Gold and ranges from
/// 0 to 2,147,483,647 by default.
/// One Platinum represents the value of CurrencyThreshold in Gold.
/// </summary>
[CommandProperty(AccessLevel.Administrator)]
public int TotalPlat { get; private set; }
/// <summary>
/// Attempts to deposit the given amount of Gold into this account.
/// If the given amount is greater than the CurrencyThreshold,
/// Platinum will be deposited to offset the difference.
/// </summary>
/// <param name="amount">Amount to deposit.</param>
/// <returns>True if successful, false if amount given is less than or equal to zero.</returns>
public bool DepositGold(int amount)
{
if (amount <= 0) return false;
int plat = Math.DivRem(amount, AccountGold.CurrencyThreshold, out int gold);
TotalPlat += plat;
TotalGold += gold;
return true;
}
/// <summary>
/// Attempts to deposit the given amount of Platinum into this account.
/// </summary>
/// <param name="amount">Amount to deposit.</param>
/// <returns>True if successful, false if amount given is less than or equal to zero.</returns>
public bool DepositPlat(int amount)
{
if (amount <= 0) return false;
TotalPlat += amount;
return true;
}
/// <summary>
/// Attempts to withdraw the given amount of Gold from this account.
/// If the given amount is greater than the CurrencyThreshold,
/// Platinum will be withdrawn to offset the difference.
/// </summary>
/// <param name="amount">Amount to withdraw.</param>
/// <returns>True if successful, false if balance was too low.</returns>
public bool WithdrawGold(int amount)
{
if (amount <= 0) return true;
if (amount > TotalGold) return false;
TotalGold -= amount;
return true;
}
/// <summary>
/// Attempts to withdraw the given amount of Platinum from this account.
/// </summary>
/// <param name="amount">Amount to withdraw.</param>
/// <returns>True if successful, false if balance was too low.</returns>
public bool WithdrawPlat(int amount)
{
if (amount <= 0) return true;
if (amount > TotalPlat) return false;
TotalPlat -= amount;
return true;
}
/// <summary>
/// Returns total gold inclusive of platinum.
/// This is strictly for backwards compatibility
/// </summary>
/// <returns>Total gold, capped at Int32.MaxValue</returns>
public long GetTotalGold() => TotalGold + TotalPlat * AccountGold.CurrencyThreshold;
}
}