// Copyright 2014-2017 ClassicalSharp | Licensed under BSD-3 using System; using ClassicalSharp.Model; using OpenTK; #if USE16_BIT using BlockID = System.UInt16; #else using BlockID = System.Byte; #endif namespace ClassicalSharp.Entities { /// Contains a model, along with position, velocity, and rotation. /// May also contain other fields and properties. public abstract partial class Entity { public Entity(Game game) { this.game = game; SkinType = game.DefaultPlayerSkinType; anim = new AnimatedComponent(game, this); } /// The model of this entity. (used for collision detection and rendering) public IModel Model; /// The name of the model of this entity. public string ModelName; /// BlockID if model name is a vaid block id. /// This avoids needing to repeatedly parse ModelName as a byte. public BlockID ModelBlock; /// Scale applied to the model for collision detection and rendering. public Vector3 ModelScale = new Vector3(1.0f); public byte ID; public int TextureId = -1, MobTextureId = -1; public short Health = 10; public Vector3 Position; public Vector3 Velocity; public Vector3 OldVelocity; public float HeadX, HeadY, RotX, RotY, RotZ; protected Game game; protected internal bool onGround; internal float StepSize; internal int tickCount; internal Matrix4 transform; public SkinType SkinType; public AnimatedComponent anim; public float uScale = 1, vScale = 1; protected DateTime lastModelChange = new DateTime(1, 1, 1); public bool NoShade = false; /// Rotation of the entity's head horizontally. (i.e. looking north or east) public float HeadYRadians { get { return HeadY * Utils.Deg2Rad; } set { HeadY = value * Utils.Rad2Deg; } } /// Rotation of the entity's head vertically. (i.e. looking up or down) public float HeadXRadians { get { return HeadX * Utils.Deg2Rad; } set { HeadX = value * Utils.Rad2Deg; } } /// Returns the size of the model that is used for collision detection. public Vector3 Size; protected void UpdateModel() { BlockModel model = Model as BlockModel; if (model != null) model.CalcState(ModelBlock); } public abstract void Tick(double delta); public abstract void SetLocation(LocationUpdate update, bool interpolate); public abstract void Despawn(); /// Renders the entity's model, interpolating between the previous and next state. public abstract void RenderModel(double deltaTime, float t); /// Renders the entity's name over the top of its model. /// Assumes that RenderModel was previously called this frame. public abstract void RenderName(); public virtual void ContextLost() { } public virtual void ContextRecreated() { } /// Gets the position of the player's eye in the world. public Vector3 EyePosition { get { return new Vector3(Position.X, Position.Y + Model.GetEyeY(this) * ModelScale.Y, Position.Z); } } /// Gets the block just underneath the player's feet position. public BlockID BlockUnderFeet { get { return GetBlock(new Vector3(Position.X, Position.Y - 0.01f, Position.Z)); } } /// Gets the block at player's eye position. public BlockID BlockAtHead { get { return GetBlock(EyePosition); } } protected BlockID GetBlock(Vector3 coords) { return game.World.SafeGetBlock(Vector3I.Floor(coords)); } public Matrix4 TransformMatrix(Vector3 scale, Vector3 pos) { Matrix4 rotZ, rotX, rotY, translate, scaleM; Matrix4.RotateX(out rotX, -RotX * Utils.Deg2Rad); Matrix4.RotateY(out rotY, -RotY * Utils.Deg2Rad); Matrix4.RotateZ(out rotZ, -RotZ * Utils.Deg2Rad); Matrix4.Scale(out scaleM, scale.X, scale.Y, scale.Z); Matrix4.Translate(out translate, pos.X, pos.Y, pos.Z); return rotZ * rotX * rotY * scaleM * translate; } /// Gets the brightness colour of this entity. public virtual int Colour() { Vector3I P = Vector3I.Floor(EyePosition); return game.World.IsValidPos(P) ? game.Lighting.LightCol(P.X, P.Y, P.Z) : game.Lighting.Outside; } /// Sets the model associated with this entity. /// Can be either 'name' or 'name'|'scale'. public void SetModel(string model) { ModelScale = new Vector3(1.0f); int sep = model.IndexOf('|'); string scale = sep == -1 ? null : model.Substring(sep + 1); ModelName = sep == -1 ? model : model.Substring(0, sep); if (Utils.CaselessEquals(model, "giant")) { ModelName = "humanoid"; ModelScale *= 2; } ModelBlock = Block.Air; if (Byte.TryParse(ModelName, out ModelBlock)) { ModelName = "block"; } Model = game.ModelCache.Get(ModelName); ParseScale(scale); lastModelChange = DateTime.UtcNow; MobTextureId = -1; UpdateModel(); UpdateModelBounds(); } public void UpdateModelBounds() { Size = Model.CollisionSize * ModelScale; modelAABB = Model.PickingBounds; modelAABB.Min *= ModelScale; modelAABB.Max *= ModelScale; } void ParseScale(string scale) { if (scale == null) return; float value; if (!Utils.TryParseDecimal(scale, out value)) return; Utils.Clamp(ref value, 0.01f, Model.MaxScale); ModelScale = new Vector3(value); } } }