using System; using System.Collections.Generic; namespace Otter { /// /// Class used for a game object. The bread and butter of your game. Entities are added to Scenes which are controlled by the Game. /// public class Entity { #region Public Fields /// /// The X position of the Entity. /// public float X; /// /// The Y position of the Entity. /// public float Y; /// /// How long the Entity has been active. /// public float Timer; /// /// Determines if the Entity will render. /// public bool Visible = true; /// /// Determines if the Entity will collide with other entities. The entity can still check for /// collisions, but will not register as a collision with other entities. /// public bool Collidable = true; /// /// Deteremines if the Entity's update functions will run automatically from the Scene. /// public bool AutoUpdate = true; /// /// Determines if the Entity's render functions will run automatically from the Scene. /// public bool AutoRender = true; /// /// The tween manager that controls all tweens on this entity. /// public Tweener Tweener = new Tweener(); /// /// An action that fires when the entity is added to a Scene. /// public Action OnAdded = delegate { }; /// /// An action that fires when the entity is updated. /// public Action OnUpdate = delegate { }; /// /// An action that fires in the entity's UpdateFirst(). /// public Action OnUpdateFirst = delegate { }; /// /// An action that is fired in the entity's UpdateLast(). /// public Action OnUpdateLast = delegate { }; /// /// An action that fires when the entity is removed from a Scene. /// public Action OnRemoved = delegate { }; /// /// An action that fires when the entity is rendered. /// public Action OnRender = delegate { }; /// /// The name of this entity. Default's to the Type name. /// public string Name; /// /// The order in which to render this entity. Higher numbers draw later. /// public int Layer; /// /// The order in which to update this entity. Higher numbers update later. /// public int Order; /// /// The pause group this entity is a part of. /// public int Group; /// /// How long the entity should live in the scene before removing itself. If this is set the /// entity will be automatically removed when the Timer exceeds this value. /// public float LifeSpan; /// /// Is true if the Entity has been updated by the Scene at least once. /// public bool UpdatedOnce { get; private set; } #endregion Public Fields #region Public Indexers public Component this[int id] { get { if (componentsById.ContainsKey(id)) return componentsById[id]; return null; } } #endregion #region Internal Fields internal bool MarkedForRemoval = false; internal bool MarkedForAdd = false; internal int oldLayer, oldOrder; #endregion Internal Fields #region Private Fields private List componentsToRemove = new List(); private List componentsToAdd = new List(); Dictionary componentsById = new Dictionary(); int nextComponentId; #endregion Private Fields #region Public Constructors /// /// Create an entity. /// /// The x position to place the entity. /// The y position to place the entity. /// The graphic to assign to the entity. Defaults to null. /// The collider to assign to the entity. Defaults to null. /// The name of the entity. Defaults to the type name. public Entity(float x = 0, float y = 0, Graphic graphic = null, Collider collider = null, string name = "") { X = x; Y = y; InstanceId = -1; Graphics = new List(); Components = new List(); Colliders = new List(); Surfaces = new List(); if (graphic != null) { Graphic = graphic; } if (collider != null) { Collider = collider; } if (name == "") { Name = this.GetType().Name; } else { Name = name; } } #endregion Public Constructors #region Public Properties /// /// The list of graphics to render. /// public List Graphics { get; private set; } /// /// The list of components to update and render. /// public List Components { get; private set; } /// /// The list of colliders to use for collision checks. /// public List Colliders { get; private set; } /// /// The list of surfaces the entity should draw to. /// public List Surfaces { get; private set; } /// /// The Scene that controls and updates this entity. /// public Scene Scene { get; internal set; } /// /// The int id of the Entity for the Scene its currently in. /// public int InstanceId { get; internal set; } /// /// Returns true if the entity is currently in a Scene, or is queued to be added to a Scene next update. /// public bool IsInScene { get { return Scene != null; } } /// /// The default Surface that the entity should render to. /// public Surface Surface { get { if (Surfaces == null) return null; if (Surfaces.Count == 0) return null; return Surfaces[Surfaces.Count - 1]; } set { Surfaces.Clear(); Surfaces.Add(value); } } /// /// The currently overlapped entity. This only works when using an Overlap collision check, and there is a result. /// public Entity Overlapped { get; private set; } /// /// Set to a collider by using the SetHitbox method. Shortcut reference. /// public BoxCollider Hitbox { get; private set; } /// /// Returns the first available collider, or set the Collider. /// public Collider Collider { get { if (Colliders.Count == 0) return null; return Colliders[0]; } set { SetCollider(value); } } /// /// A reference to the Input object in the Game that controls the Scene. /// public Input Input { get { return Scene.Game.Input; } } /// /// A reference to the Game that controls the Scene. /// public Game Game { get { return Scene.Game; } } /// /// If the entity is currently paused by the scene. /// public bool IsPaused { get { if (Scene != null) { return Scene.IsGroupPaused(Group); } return false; } } /// /// The x position in screen space of the entity. /// public float ScreenX { get { if (Scene != null) { return X - Scene.CameraX; } return X; } } /// /// The y position in screen space of the entity. /// public float ScreenY { get { if (Scene != null) { return Y - Scene.CameraY; } return Y; } } /// /// Returns the first available graphic, or set the graphic. /// public Graphic Graphic { get { if (Graphics.Count == 0) return null; return Graphics[0]; } set { SetGraphic(value); } } /// /// The position of the Entity represented as a Vector2 /// public Vector2 Position { get { return new Vector2(X, Y); } set { X = value.X; Y = value.Y; } } #endregion Public Properties #region Public Methods /// /// Sets the graphic to a new graphic, removing all previous graphics. /// /// public void SetGraphic(Graphic g) { Graphics.Clear(); Graphics.Add(g); } /// /// Set the X and Y position to a value. /// /// The value of the X and Y position. public void SetPosition(float xy) { X = Y = xy; } /// /// Add to the X and Y positions of the Entity. /// /// The amount to add to the x position. /// The amount to add to the y position. public void AddPosition(float x, float y) { X += x; Y += y; } /// /// Add to the X and Y position of the Entity. /// /// The axis to add from. /// The amount to muliply the axis values by before adding. public void AddPosition(Axis axis, float multiplier = 1) { AddPosition(axis.X * multiplier, axis.Y * multiplier); } /// /// Add to the X and Y position of the Entity. /// /// The Vector2 to add to the position. public void AddPosition(Vector2 vector) { AddPosition(vector.X, vector.Y); } /// /// Set the position of the Entity. /// /// The new x position. /// The new y position. public void SetPosition(float x, float y) { X = x; Y = y; } /// /// Set the position of the Entity to another Entity's position. /// /// The Entity to match positions with. public void SetPosition(Entity e, float offsetX = 0, float offsetY = 0) { SetPosition(e.X + offsetX, e.Y + offsetY); } /// /// Set the position of the Entity. /// /// The vector of the new position. public void SetPosition(Vector2 v) { SetPosition(v.X, v.Y); } /// /// Adds a Graphic to the Entity. /// /// The Graphic to add. /// The added Graphic. public T AddGraphic(T g) where T : Graphic { Graphics.Add(g); return g; } /// /// Adds a Graphic to the Entity. /// /// /// The Graphic to add. /// The X position to place the Graphic relative to the Entity. /// The Y position to place the Graphic relative to the Entity. /// The added Graphic. public T AddGraphic(T g, float x, float y) where T : Graphic { Graphics.Add(g); g.X = x; g.Y = y; return g; } /// /// Adds the graphics to the Entity. /// /// The Graphics to add. public List AddGraphics(params Graphic[] graphics) { var r = new List(); foreach (var g in graphics) { r.Add(AddGraphic(g)); } return r; } /// /// Adds a graphic to the Entity and sets its Scroll value to 0. /// /// /// The Graphic to add. /// The added Graphic. public T AddGraphicGUI(T g) where T : Graphic { g.Scroll = 0; return AddGraphic(g); } /// /// Adds a graphic to the Entity and sets its Scroll value to 0. /// /// /// The Graphic to add. /// The X position to place the Graphic relative to the Entity. /// The Y position to place the Graphic relative to the Entity. /// The added Graphic. public T AddGraphicGUI(T g, float x, float y) where T : Graphic { g.Scroll = 0; return AddGraphic(g, x, y); } /// /// Adds Graphics to the Entity and sets their Scroll values to 0. /// /// /// The Graphics to add. /// The added Graphics. public List AddGraphicsGUI(params Graphic[] graphics) { var r = new List(); foreach (var g in graphics) { r.Add(AddGraphicGUI(g)); } return r; } /// /// Removes a Graphic from the Entity. /// /// The Graphic to remove. public T RemoveGraphic(T g) where T : Graphic { Graphics.Remove(g); return g; } /// /// Removes Graphics from the Entity. /// /// The Graphics to remove. public void RemoveGraphics(params Graphic[] g) { g.Each(gr => RemoveGraphic(gr)); } /// /// Remove all the graphics from the entity. /// public void ClearGraphics() { Graphics.Clear(); } /// /// Adds a component to the entity. /// /// public T AddComponent(T c) where T : Component { if (c.Entity != null) return c; c.Entity = this; componentsToAdd.Add(c); return c; } /// /// Creates and adds a Component to the entity. /// /// The type of Component to create. /// The arguments for the Component's constructor. /// The newly created Component. public T AddComponent(params object[] constructorArgs) where T : Component { var c = (T)Activator.CreateInstance(typeof(T), constructorArgs); return AddComponent(c); } /// /// Add multiple components to the entity. /// /// The components to add. /// A list of the added components. public List AddComponents(params Component[] c) { var r = new List(); foreach (var com in c) { r.Add(AddComponent(com)); } return r; } /// /// Removes a component from the Entity. /// /// public T RemoveComponent(T c) where T : Component { if (componentsToAdd.Contains(c)) { componentsToAdd.Remove(c); return c; } componentsToRemove.Add(c); return c; } /// /// Removes the first component of type T from the Entity. /// /// The type of component to remove. /// The removed component. public T RemoveComponent() where T : Component { return RemoveComponent(GetComponent()); } /// /// Remove all components from the Entity. /// public void ClearComponents() { foreach (var c in Components) { RemoveComponent(c); } } /// /// Add a surface that the entity should render to. /// /// public void AddSurface(Surface target) { Surfaces.Add(target); } /// /// Remove a surface from the list of surfaces that the entity should render to. /// /// public void RemoveSurface(Surface target) { Surfaces.Remove(target); } /// /// Remove all Surfaces from the list of Surfaces that the Entity should render to. /// public void ClearSurfaces() { Surfaces.Clear(); } /// /// Shortcut to set the Collider of the entity to a BoxCollider. Using this will set the "Hitbox" field to this Collider. /// /// The width of the box collider. /// The height of the box collider. /// The tags assigned to the box collider. /// The created box collider. public BoxCollider SetHitbox(int width, int height, params int[] tags) { var hitbox = new BoxCollider(width, height, tags); SetCollider(hitbox); Hitbox = hitbox; return hitbox; } /// /// Shortcut to set the Collider of the entity to a BoxCollider. Using this will set the "Hitbox" field to this Collider. /// /// The width of the box collider. /// The height of the box collider. /// The first tag to add. /// The rest of the tags to add. /// public BoxCollider SetHitbox(int width, int height, Enum tag, params Enum[] tags) { var hitbox = new BoxCollider(width, height, tag, tags); SetCollider(hitbox); Hitbox = hitbox; return hitbox; } /// /// Get the first instance of an Entity of type T in this Entity's Scene. /// /// The entity type to search for. /// The first entity of that type in the scene. public T GetEntity() where T : Entity { return Scene.GetEntity(); } /// /// Get a list of entities of type T from this Entity's Scene. /// /// The type of entity to collect. /// A list of entities of type T. public List GetEntities() where T : Entity { return Scene.GetEntities(); } /// /// Get the first Component of type T. /// /// The type of Component to look for. /// The Component. public T GetComponent() where T : Component { foreach (var c in Components) { if (c is T) return (T)c; } foreach (var c in componentsToAdd) { if (c is T) return (T)c; } return null; } /// /// Get the first Component of Type type. /// /// The Type of Component to look for. /// The first Component of that Type. public Component GetComponent(Type type) { foreach (var c in Components) { if (c.GetType() == type) return c; } return null; } /// /// Get all Components of type T. /// /// The type of Component to look for. /// A list of Components of type T. public List GetComponents() where T : Component { var list = new List(); foreach (var c in Components) { if (c is T) list.Add((T)c); } foreach (var c in componentsToAdd) { if (c is T) list.Add((T)c); } return list; } /// /// Get the first graphic of type T. /// /// The type of graphic to look for. /// The graphic. public T GetGraphic() where T : Graphic { foreach (var g in Graphics) { if (g is T) return (T)g; } return null; } /// /// Get the first collider of type T. /// /// The type of collider to look for. /// The collider. public T GetCollider() where T : Collider { foreach (var c in Colliders) { if (c is T) return (T)c; } return null; } /// /// Add a collider to the entity. /// /// public T AddCollider(T c) where T : Collider { Colliders.Add(c); c.Entity = this; c.Added(); if (Scene != null) { Scene.AddColliderInternal(c); } return c; } /// /// Remove the collider from the entity. /// /// public T RemoveCollider(T c) where T : Collider { if (Colliders.Contains(c)) { Colliders.Remove(c); c.Removed(); c.Entity = null; if (Scene != null) { Scene.RemoveColliderInternal(c); } } return c; } /// /// Remove all colliders from the entity. /// public void ClearColliders() { var colliders = new List(Colliders); foreach (var c in colliders) { RemoveCollider(c); } Hitbox = null; } /// /// Remove all current colliders and set collider to a new one. /// /// public T SetCollider(T c) where T : Collider { ClearColliders(); return AddCollider(c); } /// /// Adds colliders to the entity. /// /// public List AddColliders(params Collider[] colliders) { var r = new List(); foreach (var c in colliders) { r.Add(AddCollider(c)); } return r; } /// /// Removes colliders from the entity. /// /// public void RemoveColliders(params Collider[] colliders) { foreach (var c in colliders) { RemoveCollider(c); } } /// /// Checks for a collision using the first available Collider. /// /// The X position to check for a collision at. /// The Y position to check for a collision at. /// The int tags to check for. /// public Collider Collide(float x, float y, params int[] tags) { return Collider.Collide(x, y, tags); } /// /// Checks for a collision using the first available Collider. /// /// The X position to check for a collision at. /// The Y position to check for a collision at. /// The enum tags to check for. /// public Collider Collide(float x, float y, params Enum[] tags) { return Collider.Collide(x, y, tags); } /// /// Checks for a collision with the first available Collider. /// /// The X position to check for a collision at. /// The Y position to check for a collision at. /// The int tags to check for. /// A list of all colliders touched. public List CollideList(float x, float y, params int[] tags) { return Collider.CollideList(x, y, tags); } /// /// Checks for a collision with the first available Collider. /// /// The X position to check for a collision at. /// The Y position to check for a collision at. /// The enum tags to check for. /// A list of all colliders touched. public List CollideList(float x, float y, params Enum[] tags) { return Collider.CollideList(x, y, tags); } /// /// Checks for a collision with the first available Collider. /// /// The X position to check for a collision at. /// The Y position to check for a collision at. /// The int tags to check for. /// A list of all entities touched. public List CollideEntities(float x, float y, params int[] tags) { return Collider.CollideEntities(x, y, tags); } /// /// Checks for a collision with the first available Collider. /// /// The X position to check for a collision at. /// The Y position to check for a collision at. /// The enum tags to check for. /// A list of all Entities touched. public List CollideEntities(float x, float y, params Enum[] tags) { return Collider.CollideEntities(x, y, tags); } public List CollideEntities(float x, float y, List entities) where T : Entity { return Collider.CollideEntities(x, y, entities); } public List CollideEntities(float x, float y, params int[] tags) where T : Entity { return Collider.CollideEntities(x, y, tags); } public List CollideEntities(float x, float y, params Enum[] tags) where T : Entity { return Collider.CollideEntities(x, y, tags); } /// /// Checks for an overlap using the first available collider. /// /// The X position to check for a collision at. /// The Y position to check for a collision at. /// The int tags to check for. /// True if there is a collision. public bool Overlap(float x, float y, params int[] tags) { Overlapped = null; if (Collider == null) return false; // If no collider, cant possibly overlap. var result = Collider.Overlap(x, y, tags); if (result) { Overlapped = Collider.Collide(x, y, tags).Entity; } return result; } /// /// Checks for an overlap using the first available collider. /// /// The X position to check for a collision at. /// The Y position to check for a collision at. /// The enum tags to check for. /// True if there is a collision. public bool Overlap(float x, float y, params Enum[] tags) { return Overlap(x, y, Util.EnumToIntArray(tags)); } /// /// Checks for an overlap using the first available collider. /// /// The X position to check for a collision at. /// The Y position to check for a collision at. /// The Entity to check for a collision with. /// True if there is a collision. public bool Overlap(float x, float y, Entity e) { Overlapped = null; var result = Collider.Overlap(x, y, e); if (result) { Overlapped = Collider.Collide(x, y, e).Entity; } return result; } /// /// Called when the entity is added to a scene. The reference to the Scene is available here. /// public virtual void Added() { } /// /// Called when the entity is removed from a scene. The reference to Scene is now null. /// public virtual void Removed() { } /// /// Called when the Scene begins. /// public virtual void SceneBegin() { } /// /// Called when the Scene ends. /// public virtual void SceneEnd() { } /// /// Called when the Scene is paused. /// public virtual void ScenePause() { } /// /// Called when the Scene is resumed. /// public virtual void SceneResume() { } /// /// Called when the entity is paused by the Scene. /// public virtual void Paused() { } /// /// Called when the entity is resumed by the Scene. /// public virtual void Resumed() { } /// /// Tweens a set of numeric properties on an object. /// /// The object to tween. /// The values to tween to, in an anonymous type ( new { prop1 = 100, prop2 = 0} ). /// Duration of the tween in seconds. /// Delay before the tween starts, in seconds. /// The tween created, for setting properties on. public Tween Tween(object target, object values, float duration, float delay = 0) { return Tweener.Tween(target, values, duration, delay); } /// /// Called first during the update. Happens before Update. /// public virtual void UpdateFirst() { } /// /// Called last during the update. Happens after Update. /// public virtual void UpdateLast() { } /// /// Called during the update of the game. /// public virtual void Update() { } /// /// Called when the entity is rendering to the screen. /// public virtual void Render() { } /// /// Called before an entity is rendered. Things rendered here will appear below the Entity's Graphics. /// public virtual void Prerender() { } /// /// Remove this entity from the Scene it's in. /// public void RemoveSelf() { if (Scene != null && !MarkedForRemoval) { Scene.Remove(this); } } #endregion Public Methods #region Internal Methods internal void UpdateComponentLists() { while (componentsToRemove.Count > 0) { var removing = new List(componentsToRemove); componentsToRemove.Clear(); foreach (var c in removing) { Components.Remove(c); componentsById.Remove(c.InstanceId); c.InstanceId = -1; } foreach (var c in removing) { c.Removed(); c.Entity = null; } } while (componentsToAdd.Count > 0) { var adding = new List(componentsToAdd); componentsToAdd.Clear(); foreach (var c in adding) { Components.Add(c); var id = GetNextComponentId(); componentsById.Add(id, c); c.InstanceId = id; } foreach (var c in adding) { c.Added(); } } } internal void UpdateFirstInternal() { Game.UpdateCount++; UpdatedOnce = true; UpdateComponentLists(); foreach (var c in Components) { c.UpdateFirst(); } if (OnUpdateFirst != null) { OnUpdateFirst(); } UpdateFirst(); } internal void UpdateLastInternal() { UpdateComponentLists(); foreach (var c in Components) { c.UpdateLast(); c.Timer += Game.DeltaTime; } UpdateLast(); if (OnUpdateLast != null) { OnUpdateLast(); } foreach (var g in Graphics) { g.Update(); } Timer += Game.DeltaTime; if (LifeSpan > 0) { if (Timer >= LifeSpan) { RemoveSelf(); } } } internal void UpdateInternal() { UpdateComponentLists(); foreach (var c in Components) { c.Update(); } if (OnUpdate != null) { OnUpdate(); } Tweener.Update(Game.DeltaTime); Update(); } internal void RenderInternal() { if (!UpdatedOnce) return; //prevent rendering before update if (!Visible) return; if (Scene == null) return; if (Surface == null) { RenderEntity(); } else { foreach (var surface in Surfaces) { Surface temp = Draw.Target; Draw.SetTarget(surface); RenderEntity(); Draw.SetTarget(temp); } } } #endregion Internal Methods #region Private Methods int GetNextComponentId() { var id = nextComponentId; nextComponentId++; return id; } private void RenderEntity() { Prerender(); foreach (var c in Components) { if (!c.RenderAfterEntity) { if (c.Visible) c.Render(); } } foreach (var g in Graphics) { if (g.Relative) { g.Render(X, Y); } else { g.Render(0, 0); } } Render(); if (OnRender != null) { OnRender(); } foreach (var c in Components) { if (c.RenderAfterEntity) { if (c.Visible) c.Render(); } } } #endregion Private Methods } }