using System; using System.Text; using System.Globalization; using System.Collections.Generic; using Woodpecker.Game.Rooms.Pathfinding; namespace Woodpecker.Game.Rooms.Units { /// /// A non-inheritable class that is the base for room users, bots and virtual pets. /// public abstract class roomUnit { #region Fields #region General /// /// The room user ID (not the database ID) of this room unit. /// public int ID; #endregion #region Pathfinding /// /// The X position of this room unit in the room. /// public byte X; /// /// The Y position of this room unit in the room. /// public byte Y; /// /// The height of this room unit in the room as a float. /// public float Z; /// /// True if this room unit is currently moving in the room. /// public bool Moves; /// /// The next X position of this room unit in the room. /// public byte nextX; /// /// The next Y position of this room unit in the room. /// public byte nextY; /// /// The next height of this room unit in the room as a float. /// public float nextZ; /// /// True if this user has set a tile to move to and the moving hasn't been processed yet. /// public bool goalTileSet; /// /// The X coordinate of the tile that this user wants to move to. /// public byte goalX; /// /// The Y coordinate of the tile that this user wants to move to. /// public byte goalY; /// /// True if this room unit is allowed to move to the next tile without being affected by the map. /// public bool allowOverideNextTile; public List Path = new List(); #endregion #region Appearance /// /// The current head rotation of this room user a byte. /// public byte rotationHead; /// /// The current body rotation of this room user as a byte. /// public byte rotationBody; #endregion #region Statuses /// /// A System.Collections.Generic.Dictionary with a string as key and a roomUnitStatus as value. /// private Dictionary Statuses = new Dictionary(); #endregion #endregion #region Methods /// /// Adds a given unique status to this room unit and requests a refresh in the room. /// /// The key the status should have in the status collection. The key should be unique. /// The actual name of the status. /// Optional. The data/variable this status will hold. /// The amount of seconds this status lasts. If the status expires, it is removed and the room unit is refreshed in room. /// Optional. The status name can be replaced with another status name (the 'Action') after a certain amount of seconds. /// The delay between the original status and the action in seconds. /// The amount of seconds the action lasts once enabled. Upon expiring of the action, the normal status is restored. public void addStatus(string Key, string Name, string Data, int secsLifetime, string Action, int secsActionSwitch, int secsActionLength) { if (this.Statuses.ContainsKey(Key)) this.Statuses.Remove(Key); this.Statuses.Add(Key, new roomUnitStatus(Name, Data, secsLifetime, Action, secsActionSwitch, secsActionLength)); } /// /// Tries to remove a status with a given key. A request in the room for this room unit is requested. /// /// The key of the status to remove. public void removeStatus(string Key) { this.Statuses.Remove(Key); this.requiresUpdate = true; } /// /// Returns a boolean indicating if this room unit currently has a status with a given key. /// /// The key of the status to check for. public bool hasStatus(string Key) { return this.Statuses.ContainsKey(Key); } /// /// Private boolean holding the current update status of this room unit. /// private bool _requiresUpdate; /// /// Holds true if this room unit currently requires an update in the room for whatever reason. /// public bool requiresUpdate { get { if (_requiresUpdate) return true; lock (this.Statuses) { foreach (KeyValuePair lStatus in this.Statuses) { if (lStatus.Value.Updated) return true; } } return false; } set { _requiresUpdate = value; } } /// /// Converts the current status of this room unit to a string and returns it. /// public string ToStatusString() { StringBuilder sb = new StringBuilder(); sb.Append(this.ID + " "); sb.Append(this.X + ","); sb.Append(this.Y + ","); sb.Append(this.Z.ToString("0.00", CultureInfo.InvariantCulture) + ","); sb.Append(this.rotationHead + ","); sb.Append(this.rotationBody + "/"); if (this.Moves) sb.Append("mv " + this.nextX + "," + this.nextY + "," + this.nextZ.ToString("0.00", CultureInfo.InvariantCulture) + "/"); lock (this.Statuses) { List statusesToRemove = new List(); foreach (KeyValuePair statusCollector in this.Statuses) { if (statusCollector.Value.Valid) { sb.Append(statusCollector.Value.Name); if (statusCollector.Value.Data != null) { sb.Append(' '); sb.Append(statusCollector.Value.Data); } sb.Append('/'); } else statusesToRemove.Add(statusCollector.Key); } foreach (string sStatus in statusesToRemove) this.Statuses.Remove(sStatus); // Remove invalid statuses } sb.Append(Convert.ToChar(13)); return sb.ToString(); } #endregion } /// /// Represents a unique status of a room unit. Thanks to Joe Hegarty (Thor) for the 'is still active, actiontimer' etc stuff. /// public class roomUnitStatus { #region Fields /// /// The key/name of this status. Will be replaced by 'Action' when this is a flipping status. /// public string Name; /// /// The value of this status. This field can be omitted. /// public string Data; /// /// A string that will hold the action to be switched with the key when this is a flipping status. /// private string Action; /// /// The amount of seconds that this status last before it is switched with the action. /// private int mActionSwitch; /// /// The amount of seconds that the action of this status lasts before it turns into the normal status again. /// private int mActionLength; /// /// A double that keeps the amount of seconds the action of this status requires updating. /// private double mActionEndTime; /// /// A double that keeps the amount of seconds for stuff. /// private double mEndTime; /// /// True if this status is currently using it's Action value. /// private bool mActionActive; private bool mLastCheckResult; public bool Updated { get { bool ret = false; bool Check = this.Valid; if (Check != mLastCheckResult) ret = true; mLastCheckResult = Check; return ret; } } /// /// True if this status is still active. /// public bool Valid { get { if (this.Static) return true; else { if (mEndTime < DateTime.Now.TimeOfDay.TotalSeconds) // Non-persistent status expired return false; } if (this.Action != null) // Status changes (eg, carry item) { if (mActionEndTime < DateTime.Now.TimeOfDay.TotalSeconds) // Status requires update { string s = this.Name; this.Name = this.Action; this.Action = s; // Calculate new action length int switchSeconds = 0; if (mActionActive) switchSeconds = mActionSwitch; else switchSeconds = mActionLength; mActionActive = !mActionActive; mActionEndTime = DateTime.Now.TimeOfDay.TotalSeconds + switchSeconds; mLastCheckResult = !mLastCheckResult; } } return true; // Still valid! } } /// /// True if this status does not expire/change. /// public bool Static { get { return mEndTime == 0.0d; } } #endregion #region Constructors public roomUnitStatus(string Name, string Data, int secsLifetime, string Action, int secsActionSwitch, int secsActionLength) { this.Name = Name; this.Data = Data; if (secsLifetime > 0) mEndTime = DateTime.Now.TimeOfDay.TotalSeconds + secsLifetime; if (Action != null) { this.Action = Action; mActionSwitch = secsActionSwitch; mActionLength = secsActionLength; mActionEndTime = DateTime.Now.TimeOfDay.TotalSeconds + secsActionSwitch; } } #endregion } }