// 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;
/// Scale applied to the model for collision detection and rendering.
public float ModelScale = 1;
public byte ID;
public int TextureId = -1, MobTextureId = -1;
public short Health = 20;
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;
public SkinType SkinType;
public AnimatedComponent anim;
internal float uScale = 1, vScale = 1;
protected DateTime lastModelChange = new DateTime(1, 1, 1);
/// 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;
void UpdateModel() {
BlockModel model = Model as BlockModel;
if (model != null)
model.CalcState(Utils.FastByte(ModelName));
}
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, 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));
}
internal Matrix4 TransformMatrix(float scale, Vector3 pos) {
return
Matrix4.RotateZ(-RotZ * Utils.Deg2Rad) *
Matrix4.RotateX(-RotX * Utils.Deg2Rad) *
Matrix4.RotateY(-RotY * Utils.Deg2Rad) *
Matrix4.Scale(scale) *
Matrix4.Translate(pos.X, pos.Y, pos.Z);
}
/// Sets the model associated with this entity.
/// Can be either 'name' or 'name'|'scale'.
public void SetModel(string model) {
ModelScale = 1;
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;
}
Model = game.ModelCache.Get(ModelName);
ParseScale(scale);
lastModelChange = DateTime.UtcNow;
MobTextureId = -1;
UpdateModel();
Size = Model.CollisionSize * ModelScale;
modelAABB = Model.PickingBounds.Scale(ModelScale);
}
void ParseScale(string scale) {
if (scale == null) return;
float value;
if (!Utils.TryParseDecimal(scale, out value)) return;
Utils.Clamp(ref value, 0.25f, Model.MaxScale);
ModelScale = value;
}
}
}