using System; using System.Collections.Generic; using System.Text; using Substrate.Core; using Substrate.Nbt; namespace Substrate { /// /// Encompases data to specify player abilities, especially mode-dependent abilities. /// /// Whether or not any of these values are respected by the game client is dependent upon the active game mode. public class PlayerAbilities : ICopyable { private bool _flying = false; private bool _instabuild = false; private bool _mayfly = false; private bool _invulnerable = false; private bool _maybuild = true; private float _walkSpeed = 0.1f; private float _flySpeed = 0.05f; /// /// Gets or sets whether the player is currently flying. /// public bool Flying { get { return _flying; } set { _flying = value; } } /// /// Gets or sets whether the player can instantly build or mine. /// public bool InstantBuild { get { return _instabuild; } set { _instabuild = value; } } /// /// Gets or sets whether the player is allowed to fly. /// public bool MayFly { get { return _mayfly; } set { _mayfly = value; } } /// /// Gets or sets whether the player can take damage. /// public bool Invulnerable { get { return _invulnerable; } set { _invulnerable = value; } } /// /// Gets or sets whether the player can create or destroy blocks. /// public bool MayBuild { get { return _maybuild; } set { _maybuild = value; } } /// /// Gets or sets the player's walking speed. Always 0.1. /// public float FlySpeed { get { return _flySpeed; } set { _flySpeed = value; } } /// /// Gets or sets the player's flying speed. Always 0.05. /// public float WalkSpeed { get { return _walkSpeed; } set { _walkSpeed = value; } } #region ICopyable Members /// public PlayerAbilities Copy () { PlayerAbilities pa = new PlayerAbilities(); pa._flying = _flying; pa._instabuild = _instabuild; pa._mayfly = _mayfly; pa._invulnerable = _invulnerable; pa._maybuild = _maybuild; pa._walkSpeed = _walkSpeed; pa._flySpeed = _flySpeed; return pa; } #endregion } public enum PlayerGameType { Survival = 0, Creative = 1, Adventure = 2, } /// /// Represents a Player from either single- or multi-player Minecraft. /// /// Unlike objects, objects do not need to be added to chunks. They /// are stored individually or within level data. public class Player : Entity, INbtObject, ICopyable, IItemContainer { private static readonly SchemaNodeCompound _schema = Entity.Schema.MergeInto(new SchemaNodeCompound("") { new SchemaNodeScaler("AttackTime", TagType.TAG_SHORT), new SchemaNodeScaler("DeathTime", TagType.TAG_SHORT), new SchemaNodeScaler("Health", TagType.TAG_SHORT), new SchemaNodeScaler("HurtTime", TagType.TAG_SHORT), new SchemaNodeScaler("Dimension", TagType.TAG_INT), new SchemaNodeList("Inventory", TagType.TAG_COMPOUND, ItemCollection.Schema), //new SchemaNodeList("EnderItems", TagType.TAG_COMPOUND, ItemCollection.Schema, SchemaOptions.OPTIONAL), new SchemaNodeScaler("World", TagType.TAG_STRING, SchemaOptions.OPTIONAL), new SchemaNodeScaler("Sleeping", TagType.TAG_BYTE, SchemaOptions.CREATE_ON_MISSING), new SchemaNodeScaler("SleepTimer", TagType.TAG_SHORT, SchemaOptions.CREATE_ON_MISSING), new SchemaNodeScaler("SpawnX", TagType.TAG_INT, SchemaOptions.OPTIONAL), new SchemaNodeScaler("SpawnY", TagType.TAG_INT, SchemaOptions.OPTIONAL), new SchemaNodeScaler("SpawnZ", TagType.TAG_INT, SchemaOptions.OPTIONAL), new SchemaNodeScaler("foodLevel", TagType.TAG_INT, SchemaOptions.OPTIONAL), new SchemaNodeScaler("foodTickTimer", TagType.TAG_INT, SchemaOptions.OPTIONAL), new SchemaNodeScaler("foodExhaustionLevel", TagType.TAG_FLOAT, SchemaOptions.OPTIONAL), new SchemaNodeScaler("foodSaturationLevel", TagType.TAG_FLOAT, SchemaOptions.OPTIONAL), new SchemaNodeScaler("XpP", TagType.TAG_FLOAT, SchemaOptions.OPTIONAL), new SchemaNodeScaler("XpLevel", TagType.TAG_INT, SchemaOptions.OPTIONAL), new SchemaNodeScaler("XpTotal", TagType.TAG_INT, SchemaOptions.OPTIONAL), new SchemaNodeScaler("Score", TagType.TAG_INT, SchemaOptions.OPTIONAL), new SchemaNodeScaler("playerGameType", TagType.TAG_INT, SchemaOptions.OPTIONAL), new SchemaNodeCompound("abilities", new SchemaNodeCompound("") { new SchemaNodeScaler("flying", TagType.TAG_BYTE), new SchemaNodeScaler("instabuild", TagType.TAG_BYTE), new SchemaNodeScaler("mayfly", TagType.TAG_BYTE), new SchemaNodeScaler("invulnerable", TagType.TAG_BYTE), new SchemaNodeScaler("mayBuild", TagType.TAG_BYTE, SchemaOptions.OPTIONAL), new SchemaNodeScaler("walkSpeed", TagType.TAG_FLOAT, SchemaOptions.OPTIONAL), new SchemaNodeScaler("flySpeed", TagType.TAG_FLOAT, SchemaOptions.OPTIONAL), }, SchemaOptions.OPTIONAL), }); private const int _CAPACITY = 105; private const int _ENDER_CAPACITY = 27; private short _attackTime; private short _deathTime; private short _health; private short _hurtTime; private int _dimension; private byte _sleeping; private short _sleepTimer; private int? _spawnX; private int? _spawnY; private int? _spawnZ; private int? _foodLevel; private int? _foodTickTimer; private float? _foodExhaustion; private float? _foodSaturation; private float? _xpP; private int? _xpLevel; private int? _xpTotal; private int? _score; private string _world; private string _name; private PlayerAbilities _abilities; private PlayerGameType? _gameType; private ItemCollection _inventory; private ItemCollection _enderItems; /// /// Gets or sets the number of ticks left in the player's "invincibility shield" after last struck. /// public int AttackTime { get { return _attackTime; } set { _attackTime = (short)value; } } /// /// Gets or sets the number of ticks that the player has been dead for. /// public int DeathTime { get { return _deathTime; } set { _deathTime = (short)value; } } /// /// Gets or sets the amount of the player's health. /// public int Health { get { return _health; } set { _health = (short)value; } } /// /// Gets or sets the player's Hurt Time value. /// public int HurtTime { get { return _hurtTime; } set { _hurtTime = (short)value; } } /// /// Gets or sets the dimension that the player is currently in. /// public int Dimension { get { return _dimension; } set { _dimension = value; } } public PlayerGameType GameType { get { return _gameType ?? PlayerGameType.Survival; } set { _gameType = value; } } /// /// Gets or sets a value indicating whether the player is sleeping in a bed. /// public bool IsSleeping { get { return _sleeping == 1; } set { _sleeping = (byte)(value ? 1 : 0); } } /// /// Gets or sets the player's Sleep Timer value. /// public int SleepTimer { get { return _sleepTimer; } set { _sleepTimer = (short)value; } } /// /// Gets or sets the player's personal spawn point, set by sleeping in beds. /// public SpawnPoint Spawn { get { return new SpawnPoint(_spawnX ?? 0, _spawnY ?? 0, _spawnZ ?? 0); } set { _spawnX = value.X; _spawnY = value.Y; _spawnZ = value.Z; } } /// /// Tests if the player currently has a personal spawn point. /// public bool HasSpawn { get { return _spawnX != null && _spawnY != null && _spawnZ != null; } } /// /// Gets or sets the name of the world that the player is currently within. /// public string World { get { return _world; } set { _world = value; } } /// /// Gets or sets the name that is used when the player is read or written from a . /// public string Name { get { return _name; } set { _name = value; } } /// /// Gets or sets the player's score. /// public int Score { get { return _score ?? 0; } set { _score = value; } } /// /// Gets or sets the player's XP Level. /// public int XPLevel { get { return _xpLevel ?? 0; } set { _xpLevel = value; } } /// /// Gets or sets the amount of the player's XP points. /// public int XPTotal { get { return _xpTotal ?? 0; } set { _xpTotal = value; } } /// /// Gets or sets the hunger level of the player. Valid values range 0 - 20. /// public int HungerLevel { get { return _foodLevel ?? 0; } set { _foodLevel = value; } } /// /// Gets or sets the player's hunger saturation level, which is reserve food capacity above . /// public float HungerSaturationLevel { get { return _foodSaturation ?? 0; } set { _foodSaturation = value; } } /// /// Gets or sets the counter towards the next hunger point decrement. Valid values range 0.0 - 4.0. /// public float HungerExhaustionLevel { get { return _foodExhaustion ?? 0; } set { _foodExhaustion = value; } } /// /// Gets or sets the timer used to periodically heal or damage the player based on . Valid values range 0 - 80. /// public int HungerTimer { get { return _foodTickTimer ?? 0; } set { _foodTickTimer = value; } } /// /// Gets the state of the player's abilities. /// public PlayerAbilities Abilities { get { return _abilities; } } /// /// Creates a new object with reasonable default values. /// public Player () : base() { _inventory = new ItemCollection(_CAPACITY); _enderItems = new ItemCollection(_ENDER_CAPACITY); _abilities = new PlayerAbilities(); // Sane defaults _dimension = 0; _sleeping = 0; _sleepTimer = 0; Air = 300; Health = 20; Fire = -20; } /// /// Creates a copy of a object. /// /// The to copy fields from. protected Player (Player p) : base(p) { _attackTime = p._attackTime; _deathTime = p._deathTime; _health = p._health; _hurtTime = p._hurtTime; _dimension = p._dimension; _gameType = p._gameType; _sleeping = p._sleeping; _sleepTimer = p._sleepTimer; _spawnX = p._spawnX; _spawnY = p._spawnY; _spawnZ = p._spawnZ; _world = p._world; _inventory = p._inventory.Copy(); _enderItems = p._inventory.Copy(); _foodLevel = p._foodLevel; _foodTickTimer = p._foodTickTimer; _foodSaturation = p._foodSaturation; _foodExhaustion = p._foodExhaustion; _xpP = p._xpP; _xpLevel = p._xpLevel; _xpTotal = p._xpTotal; _abilities = p._abilities.Copy(); } /// /// Clears the player's personal spawn point. /// public void ClearSpawn () { _spawnX = null; _spawnY = null; _spawnZ = null; } private bool AbilitiesSet () { return _abilities.Flying || _abilities.InstantBuild || _abilities.MayFly || _abilities.Invulnerable; } #region INBTObject Members /// /// Gets a representing the schema of a Player. /// public static new SchemaNodeCompound Schema { get { return _schema; } } /// /// Attempt to load a Player subtree into the without validation. /// /// The root node of a Player subtree. /// The returns itself on success, or null if the tree was unparsable. public virtual new Player LoadTree (TagNode tree) { TagNodeCompound ctree = tree as TagNodeCompound; if (ctree == null || base.LoadTree(tree) == null) { return null; } _attackTime = ctree["AttackTime"].ToTagShort(); _deathTime = ctree["DeathTime"].ToTagShort(); _health = ctree["Health"].ToTagShort(); _hurtTime = ctree["HurtTime"].ToTagShort(); _dimension = ctree["Dimension"].ToTagInt(); _sleeping = ctree["Sleeping"].ToTagByte(); _sleepTimer = ctree["SleepTimer"].ToTagShort(); if (ctree.ContainsKey("SpawnX")) { _spawnX = ctree["SpawnX"].ToTagInt(); } if (ctree.ContainsKey("SpawnY")) { _spawnY = ctree["SpawnY"].ToTagInt(); } if (ctree.ContainsKey("SpawnZ")) { _spawnZ = ctree["SpawnZ"].ToTagInt(); } if (ctree.ContainsKey("World")) { _world = ctree["World"].ToTagString(); } if (ctree.ContainsKey("foodLevel")) { _foodLevel = ctree["foodLevel"].ToTagInt(); } if (ctree.ContainsKey("foodTickTimer")) { _foodTickTimer = ctree["foodTickTimer"].ToTagInt(); } if (ctree.ContainsKey("foodExhaustionLevel")) { _foodExhaustion = ctree["foodExhaustionLevel"].ToTagFloat(); } if (ctree.ContainsKey("foodSaturationLevel")) { _foodSaturation = ctree["foodSaturationLevel"].ToTagFloat(); } if (ctree.ContainsKey("XpP")) { _xpP = ctree["XpP"].ToTagFloat(); } if (ctree.ContainsKey("XpLevel")) { _xpLevel = ctree["XpLevel"].ToTagInt(); } if (ctree.ContainsKey("XpTotal")) { _xpTotal = ctree["XpTotal"].ToTagInt(); } if (ctree.ContainsKey("Score")) { _score = ctree["Score"].ToTagInt(); } if (ctree.ContainsKey("abilities")) { TagNodeCompound pb = ctree["abilities"].ToTagCompound(); _abilities = new PlayerAbilities(); _abilities.Flying = pb["flying"].ToTagByte().Data == 1; _abilities.InstantBuild = pb["instabuild"].ToTagByte().Data == 1; _abilities.MayFly = pb["mayfly"].ToTagByte().Data == 1; _abilities.Invulnerable = pb["invulnerable"].ToTagByte().Data == 1; if (pb.ContainsKey("mayBuild")) _abilities.MayBuild = pb["mayBuild"].ToTagByte().Data == 1; if (pb.ContainsKey("walkSpeed")) _abilities.WalkSpeed = pb["walkSpeed"].ToTagFloat(); if (pb.ContainsKey("flySpeed")) _abilities.FlySpeed = pb["flySpeed"].ToTagFloat(); } if (ctree.ContainsKey("PlayerGameType")) { _gameType = (PlayerGameType)ctree["PlayerGameType"].ToTagInt().Data; } _inventory.LoadTree(ctree["Inventory"].ToTagList()); if (ctree.ContainsKey("EnderItems")) { if (ctree["EnderItems"].ToTagList().Count > 0) _enderItems.LoadTree(ctree["EnderItems"].ToTagList()); } return this; } /// /// Attempt to load a Player subtree into the with validation. /// /// The root node of a Player subtree. /// The returns itself on success, or null if the tree failed validation. public virtual new Player LoadTreeSafe (TagNode tree) { if (!ValidateTree(tree)) { return null; } return LoadTree(tree); } /// /// Builds a Player subtree from the current data. /// /// The root node of a Player subtree representing the current data. public virtual new TagNode BuildTree () { TagNodeCompound tree = base.BuildTree() as TagNodeCompound; tree["AttackTime"] = new TagNodeShort(_attackTime); tree["DeathTime"] = new TagNodeShort(_deathTime); tree["Health"] = new TagNodeShort(_health); tree["HurtTime"] = new TagNodeShort(_hurtTime); tree["Dimension"] = new TagNodeInt(_dimension); tree["Sleeping"] = new TagNodeByte(_sleeping); tree["SleepTimer"] = new TagNodeShort(_sleepTimer); if (_spawnX != null && _spawnY != null && _spawnZ != null) { tree["SpawnX"] = new TagNodeInt(_spawnX ?? 0); tree["SpawnY"] = new TagNodeInt(_spawnY ?? 0); tree["SpawnZ"] = new TagNodeInt(_spawnZ ?? 0); } else { tree.Remove("SpawnX"); tree.Remove("SpawnY"); tree.Remove("SpawnZ"); } if (_world != null) { tree["World"] = new TagNodeString(_world); } if (_foodLevel != null) tree["foodLevel"] = new TagNodeInt(_foodLevel ?? 0); if (_foodTickTimer != null) tree["foodTickTimer"] = new TagNodeInt(_foodTickTimer ?? 0); if (_foodExhaustion != null) tree["foodExhaustionLevel"] = new TagNodeFloat(_foodExhaustion ?? 0); if (_foodSaturation != null) tree["foodSaturation"] = new TagNodeFloat(_foodSaturation ?? 0); if (_xpP != null) tree["XpP"] = new TagNodeFloat(_xpP ?? 0); if (_xpLevel != null) tree["XpLevel"] = new TagNodeInt(_xpLevel ?? 0); if (_xpTotal != null) tree["XpTotal"] = new TagNodeInt(_xpTotal ?? 0); if (_score != null) tree["Score"] = new TagNodeInt(_score ?? 0); if (_gameType != null) tree["playerGameType"] = new TagNodeInt((int)(_gameType ?? PlayerGameType.Survival)); if (AbilitiesSet()) { TagNodeCompound pb = new TagNodeCompound(); pb["flying"] = new TagNodeByte(_abilities.Flying ? (byte)1 : (byte)0); pb["instabuild"] = new TagNodeByte(_abilities.InstantBuild ? (byte)1 : (byte)0); pb["mayfly"] = new TagNodeByte(_abilities.MayFly ? (byte)1 : (byte)0); pb["invulnerable"] = new TagNodeByte(_abilities.Invulnerable ? (byte)1 : (byte)0); pb["mayBuild"] = new TagNodeByte(_abilities.MayBuild ? (byte)1 : (byte)0); pb["walkSpeed"] = new TagNodeFloat(_abilities.WalkSpeed); pb["flySpeed"] = new TagNodeFloat(_abilities.FlySpeed); tree["abilities"] = pb; } tree["Inventory"] = _inventory.BuildTree(); tree["EnderItems"] = _enderItems.BuildTree(); return tree; } /// /// Validate a Player subtree against a schema defintion. /// /// The root node of a Player subtree. /// Status indicating whether the tree was valid against the internal schema. public virtual new bool ValidateTree (TagNode tree) { return new NbtVerifier(tree, _schema).Verify(); } #endregion #region ICopyable Members /// /// Creates a deep-copy of the . /// /// A deep-copy of the . public virtual new Player Copy () { return new Player(this); } #endregion #region IItemContainer Members /// /// Gets access to an representing the player's equipment and inventory. /// public ItemCollection Items { get { return _inventory; } } #endregion public ItemCollection EnderItems { get { return _enderItems; } } } }