commit 4a44eb0f26942fe84ecc21f369be4cff31ab884a Author: Matt Thorson Date: Mon May 4 20:50:38 2020 -0700 Initial commit diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..dfe0770 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/BeefProj.toml b/BeefProj.toml new file mode 100644 index 0000000..90cd527 --- /dev/null +++ b/BeefProj.toml @@ -0,0 +1,8 @@ +FileVersion = 1 +Dependencies = {corlib = "*", SDL2 = "*"} + +[Project] +Name = "Strawberry" +TargetType = "BeefLib" +StartupObject = "Program" +DefaultNamespace = "Strawberry" diff --git a/src/Draw.bf b/src/Draw.bf new file mode 100644 index 0000000..c069159 --- /dev/null +++ b/src/Draw.bf @@ -0,0 +1,16 @@ +namespace Strawberry +{ + static public class Draw + { + static public void Rect(int x, int y, int w, int h, SDL2.SDL.Color color) + { + SDL2.SDL.SetRenderDrawColor(Game.mRenderer, color.r, color.g, color.b, color.a); + SDL2.SDL.RenderFillRect(Game.mRenderer, &SDL2.SDL.Rect((int32)x, (int32)y, (int32)w, (int32)h)); + } + + static public void Rect(Rect rect, SDL2.SDL.Color color) + { + Rect(rect.X, rect.Y, rect.Width, rect.Height, color); + } + } +} diff --git a/src/Entity.bf b/src/Entity.bf new file mode 100644 index 0000000..d48c01c --- /dev/null +++ b/src/Entity.bf @@ -0,0 +1,347 @@ +using System; +using System.Collections; + +namespace BeefMonocle +{ + public class Entity + { + public Scene Scene { get; private set; } + public int Depth; + public bool Active = true; + public bool Visible = true; + public bool Collidable = true; + public bool DeleteOnRemove = true; + + public this(int x, int y) + { + Positionf = .(x, y); + } + + public void Added(Scene scene) + { + Scene = scene; + } + + public void Removed() + { + Scene = null; + } + + public virtual void Started() + { + + } + + public virtual void Update() + { + + } + + public virtual void Draw() + { + + } + + // ===== Position ===== + + public Vector Positionf; + + public float Xf + { + [Inline] + get + { + return Positionf.X; + } + + [Inline] + set + { + Positionf.X = value; + } + } + + public float Yf + { + [Inline] + get + { + return Positionf.Y; + } + + [Inline] + set + { + Positionf.Y = value; + } + } + + public Point Position + { + [Inline] + get + { + return Positionf.Round(); + } + + [Inline] + set + { + Positionf = value; + } + } + + public int X + { + [Inline] + get + { + return (int)Math.Round(Positionf.X); + } + + [Inline] + set + { + Positionf.X = value; + } + } + + public int Y + { + [Inline] + get + { + return (int)Math.Round(Positionf.Y); + } + + [Inline] + set + { + Positionf.Y = value; + } + } + + // ===== Hitbox ===== + + public Rect Hitbox; + + public int Width + { + [Inline] + get + { + return Hitbox.Width; + } + + [Inline] + set + { + Hitbox.Width = value; + } + } + + public int Height + { + [Inline] + get + { + return Hitbox.Height; + } + + [Inline] + set + { + Hitbox.Height = value; + } + } + + public Rect SceneHitbox + { + [Inline] + get + { + return Hitbox + Position; + } + } + + public int Left + { + [Inline] + get + { + return Position.X + Hitbox.Left; + } + + [Inline] + set + { + X = value - Hitbox.Left; + } + } + + public int Right + { + [Inline] + get + { + return Position.X + Hitbox.Right; + } + + [Inline] + set + { + Y = value - Hitbox.Right; + } + } + + public int Top + { + [Inline] + get + { + return Position.Y + Hitbox.Top; + } + + [Inline] + set + { + Y = value - Hitbox.Top; + } + } + + public int Bottom + { + [Inline] + get + { + return Position.Y + Hitbox.Bottom; + } + + [Inline] + set + { + Y = value - Hitbox.Bottom; + } + } + + // ===== Collisions ===== + + public bool Check(Point point) + { + return SceneHitbox.Contains(point); + } + + public bool Check(Rect rect) + { + return SceneHitbox.Intersects(rect); + } + + public bool Check(Entity other) + { + return other.Collidable && SceneHitbox.Intersects(other.SceneHitbox); + } + + public bool Check(Entity other, Point offset) + { + return other.Collidable && (SceneHitbox + offset).Intersects(other.SceneHitbox); + } + + public bool CheckOutside(Entity other, Point offset) + { + return other.Collidable && !SceneHitbox.Intersects(other.SceneHitbox) && (SceneHitbox + offset).Intersects(other.SceneHitbox); + } + + public bool Check() where T : Entity + { + for (var e in Scene.All(scope List)) + if (Check(e)) + return true; + + return false; + } + + public bool Check(Point offset) where T : Entity + { + for (var e in Scene.All(scope List)) + if (Check(e, offset)) + return true; + + return false; + } + + public bool CheckOutside(Point offset) where T : Entity + { + for (var e in Scene.All(scope List)) + if (CheckOutside(e, offset)) + return true; + + return false; + } + + public T First() where T : Entity + { + for (var e in Scene.All(scope List)) + if (Check(e)) + return e; + + return null; + } + + public T First(Point offset) where T : Entity + { + for (var e in Scene.All(scope List)) + if (Check(e, offset)) + return e; + + return null; + } + + public T FirstOutside(Point offset) where T : Entity + { + for (var e in Scene.All(scope List)) + if (CheckOutside(e, offset)) + return e; + + return null; + } + + public List All(List into) where T : Entity + { + for (var e in Scene.All(scope List)) + if (Check(e)) + into.Add(e); + + return into; + } + + public List All(Point offset, List into) where T : Entity + { + for (var e in Scene.All(scope List)) + if (Check(e, offset)) + into.Add(e); + + return into; + } + + public List AllOutside(Point offset, List into) where T : Entity + { + for (var e in Scene.All(scope List)) + if (CheckOutside(e, offset)) + into.Add(e); + + return into; + } + + // ===== Misc ===== + + public void DrawHitbox(SDL2.SDL.Color color) + { + Draw.Rect(SceneHitbox, color); + } + + static public int operator<=>(Entity a, Entity b) + { + return a.Depth <=> b.Depth; + } + } +} diff --git a/src/Game.bf b/src/Game.bf new file mode 100644 index 0000000..04e95c2 --- /dev/null +++ b/src/Game.bf @@ -0,0 +1,105 @@ +using SDL2; +using System; +using System.Collections; + +namespace BeefMonocle +{ + static + { + static public Game Game; + } + + public class Game : SDLApp + { + public List VirtualInputs; + public float DeltaTime { get; private set; } + public float Time { get; private set; } + public float PreviousTime { get; private set; } + + private Scene scene; + private Scene switchToScene; + private bool updating; + private SDL.Rect screenRect; + + public this(String windowTitle, int32 width, int32 height) + : base() + { + Game = this; + VirtualInputs = new List(); + DeltaTime = 1 / 60f; + + mTitle.Set(windowTitle); + mWidth = width; + mHeight = height; + + screenRect = SDL.Rect(0, 0, width, height); + } + + public ~this() + { + if (scene != null) + delete scene; + + if (switchToScene != scene && switchToScene != null) + delete switchToScene; + + delete VirtualInputs; + + Game = null; + } + + public new virtual void Init() + { + base.Init(); + } + + public override void Update() + { + base.Update(); + + //Switch scenes + if (switchToScene != scene) + { + if (scene != null) + delete scene; + scene = switchToScene; + scene.Started(); + } + + for (var i in VirtualInputs) + i.Update(); + + if (scene != null) + scene.Update(); + + PreviousTime = Time; + Time += DeltaTime; + } + + public override void Draw() + { + base.Draw(); + + SDL2.SDL.SetRenderDrawColor(mRenderer, 0, 0, 0, 255); + SDL2.SDL.RenderFillRect(mRenderer, &screenRect); + + if (Scene != null) + Scene.Draw(); + } + + public Scene Scene + { + get + { + return scene; + } + + set + { + if (switchToScene != scene && switchToScene != null) + delete switchToScene; + switchToScene = value; + } + } + } +} diff --git a/src/Input/VirtualAxis.bf b/src/Input/VirtualAxis.bf new file mode 100644 index 0000000..c49cb3f --- /dev/null +++ b/src/Input/VirtualAxis.bf @@ -0,0 +1,194 @@ +using System.Collections; +using System; + +namespace Strawberry +{ + public class VirtualAxis : VirtualInput + { + public enum OverlapBehaviors { TakeNewer, TakeOlder, CancelOut } + + public float Value { get; private set; } + public int IntValue { get; private set; } + public bool Pressed { get; private set; } + public bool Released { get; private set; } + public bool Repeating { get; private set; } + + private List nodes; + private float pressBuffer; + private float releaseBuffer; + private float repeatStart; + private float repeatInterval; + + private float lastPress; + private float lastRelease; + private float lastPressClear; + private float lastReleaseClear; + + public this() + { + nodes = new List(); + } + + public ~this() + { + for (var n in nodes) + delete n; + delete nodes; + } + + public override void Update() + { + for (var n in nodes) + n.Update(); + + //Value + let last = IntValue; + Value = 0; + for (var n in nodes) + { + if (n.Value != 0) + { + Value = n.Value; + break; + } + } + IntValue = Math.Sign(Value); + + //Press + if (last != IntValue && IntValue != 0) + lastPress = Game.Time; + Pressed = IntValue != 0 && lastPress > lastPressClear && Game.Time - lastPress <= pressBuffer; + + //Repeat + if (IntValue != 0 && repeatStart > 0 && Game.Time - lastPress >= repeatStart) + { + Repeating = true; + + int a = (int)((Game.PreviousTime - lastPress) / repeatInterval); + int b = (int)((Game.Time - lastPress) / repeatInterval); + if (a != b) + Pressed = true; + } + else + Repeating = false; + + //Release + if (last != 0 && IntValue == 0) + lastRelease = Game.Time; + Released = IntValue == 0 && lastRelease > lastReleaseClear && Game.Time - lastRelease <= releaseBuffer; + } + + public void ClearPressBuffer() + { + lastPressClear = Game.Time; + } + + public void ClearReleaseBuffer() + { + lastReleaseClear = Game.Time; + } + + // Setup Calls + + public VirtualAxis Keys(SDL2.SDL.Scancode negativeKey, SDL2.SDL.Scancode positiveKey, OverlapBehaviors overlapBehavior = .TakeNewer) + { + nodes.Add(new KeyboardKeys(negativeKey, positiveKey, overlapBehavior)); + return this; + } + + public VirtualAxis PressBuffer(float time) + { + pressBuffer = time; + return this; + } + + public VirtualAxis ReleaseBuffer(float time) + { + releaseBuffer = time; + return this; + } + + public VirtualAxis Repeat(float start, float interval) + { + repeatStart = start; + repeatInterval = interval; + return this; + } + + // Nodes + + private abstract class Node + { + public abstract float Value { get; } + public virtual void Update() { } + } + + private class KeyboardKeys : Node + { + public OverlapBehaviors OverlapBehavior; + public SDL2.SDL.Scancode NegativeKeycode; + public SDL2.SDL.Scancode PositiveKeycode; + + private float value; + private bool turned; + + public this(SDL2.SDL.Scancode negativeKey, SDL2.SDL.Scancode positiveKey, OverlapBehaviors overlapBehavior = .TakeNewer) + { + NegativeKeycode = negativeKey; + PositiveKeycode = positiveKey; + OverlapBehavior = overlapBehavior; + } + + public override void Update() + { + if (Game.IsKeyDown(PositiveKeycode)) + { + if (Game.IsKeyDown(NegativeKeycode)) + { + switch (OverlapBehavior) + { + case OverlapBehaviors.CancelOut: + value = 0; + break; + + case OverlapBehaviors.TakeNewer: + if (!turned) + { + value *= -1; + turned = true; + } + break; + + case OverlapBehaviors.TakeOlder: + //value stays the same + break; + } + } + else + { + turned = false; + value = 1; + } + } + else if (Game.IsKeyDown(NegativeKeycode)) + { + turned = false; + value = -1; + } + else + { + turned = false; + value = 0; + } + } + + public override float Value + { + get + { + return value; + } + } + } + } +} diff --git a/src/Input/VirtualButton.bf b/src/Input/VirtualButton.bf new file mode 100644 index 0000000..5f860bf --- /dev/null +++ b/src/Input/VirtualButton.bf @@ -0,0 +1,137 @@ +using System.Collections; +using System; +using System.Diagnostics; + +namespace Strawberry +{ + public class VirtualButton : VirtualInput + { + public bool Check { get; private set; } + public bool Pressed { get; private set; } + public bool Released { get; private set; } + public bool Repeating { get; private set; } + + private List nodes; + private float pressBuffer; + private float releaseBuffer; + private float repeatStart; + private float repeatInterval; + + private float lastPress; + private float lastRelease; + private float lastPressClear; + private float lastReleaseClear; + + public this() + { + nodes = new List(); + } + + public ~this() + { + for (var n in nodes) + delete n; + delete nodes; + } + + override public void Update() + { + //Check + let last = Check; + Check = false; + for (var n in nodes) + { + if (n.Check) + { + Check = true; + break; + } + } + + //Press + if (!last && Check) + lastPress = Game.Time; + Pressed = Check && lastPress > lastPressClear && Game.Time - lastPress <= pressBuffer; + + //Repeat + if (Check && repeatStart > 0 && Game.Time - lastPress >= repeatStart) + { + Repeating = true; + + int a = (int)((Game.PreviousTime - lastPress) / repeatInterval); + int b = (int)((Game.Time - lastPress) / repeatInterval); + if (a != b) + Pressed = true; + } + else + Repeating = false; + + //Release + if (last && !Check) + lastRelease = Game.Time; + Released = !Check && lastRelease > lastReleaseClear && Game.Time - lastRelease <= releaseBuffer; + } + + public void ClearPressBuffer() + { + lastPressClear = Game.Time; + } + + public void ClearReleaseBuffer() + { + lastReleaseClear = Game.Time; + } + + // Setup Calls + + public VirtualButton Key(SDL2.SDL.Scancode keycode) + { + nodes.Add(new KeyboardKey(keycode)); + return this; + } + + public VirtualButton PressBuffer(float time) + { + pressBuffer = time; + return this; + } + + public VirtualButton ReleaseBuffer(float time) + { + releaseBuffer = time; + return this; + } + + public VirtualButton Repeat(float start, float interval) + { + repeatStart = start; + repeatInterval = interval; + return this; + } + + // Nodes + + private abstract class Node + { + public abstract bool Check { get; } + } + + private class KeyboardKey : Node + { + public SDL2.SDL.Scancode Keycode; + + public this(SDL2.SDL.Scancode keycode) + { + Keycode = keycode; + } + + override public bool Check + { + get + { + return Game.IsKeyDown(Keycode); + } + } + } + } +} diff --git a/src/Input/VirtualInput.bf b/src/Input/VirtualInput.bf new file mode 100644 index 0000000..c00f89f --- /dev/null +++ b/src/Input/VirtualInput.bf @@ -0,0 +1,20 @@ +namespace Strawberry +{ + public abstract class VirtualInput + { + public this() + { + Game.VirtualInputs.Add(this); + } + + public ~this() + { + Game.VirtualInputs.Remove(this); + } + + public virtual void Update() + { + + } + } +} diff --git a/src/Numerics/Point.bf b/src/Numerics/Point.bf new file mode 100644 index 0000000..ab26a3d --- /dev/null +++ b/src/Numerics/Point.bf @@ -0,0 +1,58 @@ +using System; + +namespace Strawberry +{ + public struct Point + { + static public readonly Point Right = Point(1, 0); + static public readonly Point Left = Point(-1, 0); + static public readonly Point Up = Point(0, -1); + static public readonly Point Down = Point(0, 1); + static public readonly Point Zero = Point(0, 0); + static public readonly Point One = Point(1, 1); + + public int X; + public int Y; + + public this() + { + this = default; + } + + public this(int x, int y) + { + X = x; + Y = y; + } + + static public explicit operator Point(Vector a) + { + return Point((int)a.X, (int)a.Y); + } + + static public bool operator==(Point a, Point b) + { + return a.X == b.X && a.Y == b.Y; + } + + static public Point operator+(Point a, Point b) + { + return Point(a.X + b.X, a.Y + b.Y); + } + + static public Point operator-(Point a, Point b) + { + return Point(a.X - b.X, a.Y - b.Y); + } + + static public Point operator*(Point a, int b) + { + return Point(a.X * b, a.Y * b); + } + + static public Point operator/(Point a, int b) + { + return Point(a.X / b, a.Y / b); + } + } +} diff --git a/src/Numerics/Rect.bf b/src/Numerics/Rect.bf new file mode 100644 index 0000000..6c78458 --- /dev/null +++ b/src/Numerics/Rect.bf @@ -0,0 +1,129 @@ +using System; + +namespace Strawberry +{ + public struct Rect + { + public int X; + public int Y; + public int Width; + public int Height; + + public this() + { + this = default; + } + + public this(int x, int y, int width, int height) + { + X = x; + Y = y; + Width = width; + Height = height; + } + + public int Left + { + [Inline] + get + { + return X; + } + + [Inline] + set mut + { + X = value; + } + } + + public int Right + { + [Inline] + get + { + return X + Width; + } + + [Inline] + set mut + { + X = value - Width; + } + } + + public int Top + { + [Inline] + get + { + return Y; + } + + [Inline] + set mut + { + Y = value; + } + } + + public int Bottom + { + [Inline] + get + { + return Y + Height; + } + + [Inline] + set mut + { + Y = value - Height; + } + } + + public Rect MirrorX(int axis = 0) + { + var rect = this; + rect.X = axis - X - Width; + return rect; + } + + public Rect MirrorY(int axis = 0) + { + var rect = this; + rect.Y = axis - Y - Height; + return rect; + } + + public Rect Inflate(int amount) + { + return Rect(X - amount, Y - amount, Width + amount * 2, Height + amount * 2); + } + + public bool Intersects(Rect rect) + { + return (X + Width) > rect.X && (rect.X + rect.Width) > X && (Y + Height) > rect.Y && (rect.Y + rect.Height) > Y; + } + + public bool Contains(Point point) + { + return point.X >= X && point.X < X + Width && point.Y >= Y && point.Y < Y + Height; + } + + static public bool operator==(Rect a, Rect b) + { + return a.X == b.X && a.Y == b.Y && a.Width == b.Width && a.Height == b.Height; + } + + static public Rect operator+(Rect a, Point b) + { + return Rect(a.X + b.X, a.Y + b.Y, a.Width, a.Height); + } + + static public Rect operator-(Rect a, Point b) + { + return Rect(a.X - b.X, a.Y - b.Y, a.Width, a.Height); + } + } +} diff --git a/src/Numerics/Vector.bf b/src/Numerics/Vector.bf new file mode 100644 index 0000000..e4a5e0e --- /dev/null +++ b/src/Numerics/Vector.bf @@ -0,0 +1,82 @@ +using System; + +namespace Strawberry +{ + public struct Vector + { + static public readonly Vector Right = Vector(1, 0); + static public readonly Vector Left = Vector(-1, 0); + static public readonly Vector Up = Vector(0, -1); + static public readonly Vector Down = Vector(0, 1); + static public readonly Vector Zero = Vector(0, 0); + static public readonly Vector One = Vector(1, 1); + + public float X; + public float Y; + + public this() + { + this = default; + } + + public this(float x, float y) + { + X = x; + Y = y; + } + + public float Length + { + [Inline] + get + { + return Math.Sqrt(LengthSquared); + } + } + + public float LengthSquared + { + [Inline] + get + { + return X * X + Y * Y; + } + } + + [Inline] + public Point Round() + { + return Point((int)Math.Round(X), (int)Math.Round(Y)); + } + + static public operator Vector(Point a) + { + return Vector(a.X, a.Y); + } + + static public bool operator==(Vector a, Vector b) + { + return a.X == b.X && a.Y == b.Y; + } + + static public Vector operator+(Vector a, Vector b) + { + return Vector(a.X + b.X, a.Y + b.Y); + } + + static public Vector operator-(Vector a, Vector b) + { + return Vector(a.X - b.X, a.Y - b.Y); + } + + static public Vector operator*(Vector a, float b) + { + return Vector(a.X * b, a.Y * b); + } + + static public Vector operator/(Vector a, float b) + { + return Vector(a.X / b, a.Y / b); + } + } +} diff --git a/src/Physics/Actor.bf b/src/Physics/Actor.bf new file mode 100644 index 0000000..7bf51b7 --- /dev/null +++ b/src/Physics/Actor.bf @@ -0,0 +1,112 @@ +using System; + +namespace Strawberry +{ + [Reflect] + public class Actor : Entity + { + private Vector remainder; + + public this(int x, int y) + : base(x, y) + { + + } + + public bool GroundCheck(int distance = 1) + { + return Check(.(0, distance)); + } + + public virtual bool IsRiding(Solid solid) + { + return Check(solid, .(0, 1)); + } + + public virtual void Squish() + { + + } + + public bool MoveX(float amount, Action onCollide = null) + { + remainder.X += amount; + let move = (int)Math.Round(remainder.X); + if (move != 0) + { + remainder.X -= move; + return MoveExactX(move, onCollide); + } + else + return false; + } + + public bool MoveY(float amount, Action onCollide = null) + { + remainder.Y += amount; + let move = (int)Math.Round(remainder.Y); + if (move != 0) + { + remainder.Y -= move; + return MoveExactY(move, onCollide); + } + else + return false; + } + + public bool MoveExactX(int amount, Action onCollide = null) + { + int move = amount; + int sign = Math.Sign(amount); + while (move != 0) + { + if (Check(.(sign, 0))) + { + ZeroRemainderX(); + onCollide?.Invoke(); + return true; + } + + X += sign; + move -= sign; + } + + return false; + } + + public bool MoveExactY(int amount, Action onCollide = null) + { + int move = amount; + int sign = Math.Sign(amount); + while (move != 0) + { + if (Check(.(0, sign))) + { + ZeroRemainderY(); + onCollide?.Invoke(); + return true; + } + + Y += sign; + move -= sign; + } + + return false; + } + + public void ZeroRemainderX() + { + remainder.X = 0; + } + + public void ZeroRemainderY() + { + remainder.Y = 0; + } + + public void ZeroRemainder() + { + remainder = Vector.Zero; + } + } +} diff --git a/src/Physics/Platform.bf b/src/Physics/Platform.bf new file mode 100644 index 0000000..facfb68 --- /dev/null +++ b/src/Physics/Platform.bf @@ -0,0 +1,57 @@ +using System; +using System.Collections; + +namespace Strawberry +{ + public abstract class Platform : Entity + { + private Vector remainder; + + public this(int x, int y) + : base(x, y) + { + + } + + public void MoveX(float amount, Action onCollide = null) + { + remainder.X += amount; + let move = (int)Math.Round(remainder.X); + if (move != 0) + { + remainder.X -= move; + MoveExactX(move, onCollide); + } + } + + public void MoveY(float amount, Action onCollide = null) + { + remainder.Y += amount; + let move = (int)Math.Round(remainder.Y); + if (move != 0) + { + remainder.Y -= move; + MoveExactY(move, onCollide); + } + } + + public abstract void MoveExactX(int amount, Action onCollide = null); + public abstract void MoveExactY(int amount, Action onCollide = null); + public abstract List GetRiders(List into); + + public void ZeroRemainderX() + { + remainder.X = 0; + } + + public void ZeroRemainderY() + { + remainder.Y = 0; + } + + public void ZeroRemainder() + { + remainder = Vector.Zero; + } + } +} diff --git a/src/Physics/Solid.bf b/src/Physics/Solid.bf new file mode 100644 index 0000000..f67ceb6 --- /dev/null +++ b/src/Physics/Solid.bf @@ -0,0 +1,42 @@ +using System.Collections; + +namespace Strawberry +{ + public class Solid : Platform + { + public this(int x, int y, Rect hitbox) + : base(x, y) + { + Hitbox = hitbox; + } + + public override void Draw() + { + DrawHitbox(.(255, 255, 255, 255)); + } + + public override List GetRiders(List into) + { + for (var a in Scene.All(scope List)) + if (a.IsRiding(this)) + into.Add(a); + return into; + } + + public override void MoveExactX(int amount, System.Action onCollide = null) + { + if (amount != 0) + { + + } + } + + public override void MoveExactY(int amount, System.Action onCollide = null) + { + if (amount != 0) + { + + } + } + } +} diff --git a/src/Scene.bf b/src/Scene.bf new file mode 100644 index 0000000..576fb9e --- /dev/null +++ b/src/Scene.bf @@ -0,0 +1,148 @@ +using System.Collections; +using System; + +namespace Strawberry +{ + public class Scene + { + private List entities; + private HashSet toRemove; + private HashSet toAdd; + + public this() + { + entities = new List(); + toAdd = new HashSet(); + toRemove = new HashSet(); + } + + public ~this() + { + for (var e in entities) + if (e.DeleteOnRemove) + delete e; + delete entities; + + for (var e in toAdd) + if (e.DeleteOnRemove) + delete e; + delete toAdd; + + delete toRemove; + } + + public virtual void Started() + { + + } + + public virtual void Update() + { + UpdateLists(); + for (var e in entities) + if (e.Active) + e.Update(); + } + + public virtual void Draw() + { + for (var e in entities) + if (e.Visible) + e.Draw(); + } + + public T Add(T e) where T : Entity + { + if (e.Scene == null) + toAdd.Add(e); + return e; + } + + public T Remove(T e) where T : Entity + { + if (e.Scene == this) + toRemove.Add(e); + return e; + } + + private void UpdateLists() + { + if (toRemove.Count > 0) + { + for (var e in toRemove) + { + entities.Remove(e); + e.Removed(); + if (e.DeleteOnRemove) + delete e; + } + + toRemove.Clear(); + } + + if (toAdd.Count > 0) + { + for (var e in toAdd) + { + entities.Add(e); + e.Added(this); + } + + for (var e in toAdd) + e.Started(); + + toAdd.Clear(); + } + } + + // Finding Entities + + public T First() where T : Entity + { + for (var e in entities) + if (e is T) + return e as T; + return null; + } + + public T First(Point point) where T : Entity + { + for (var e in entities) + if (e is T && e.Check(point)) + return e as T; + return null; + } + + public T First(Rect rect) where T : Entity + { + for (var e in entities) + if (e is T && e.Check(rect)) + return e as T; + return null; + } + + public List All(List into) where T : Entity + { + for (var e in entities) + if (e is T) + into.Add(e as T); + return into; + } + + public List All(Point point, List into) where T : Entity + { + for (var e in entities) + if (e is T && e.Check(point)) + into.Add(e as T); + return into; + } + + public List All(Rect rect, List into) where T : Entity + { + for (var e in entities) + if (e is T && e.Check(rect)) + into.Add(e as T); + return into; + } + } +}