diff --git a/.DS_Store b/.DS_Store
new file mode 100644
index 0000000..c8d8ac0
Binary files /dev/null and b/.DS_Store differ
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
new file mode 100644
index 0000000..89e20ed
--- /dev/null
+++ b/.vscode/extensions.json
@@ -0,0 +1,6 @@
+{
+ "recommendations": [
+ "openfl.lime-vscode-extension",
+ "redhat.vscode-xml"
+ ]
+}
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 0000000..5e9a7a1
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,21 @@
+{
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "name": "Build + Debug",
+ "type": "lime",
+ "request": "launch"
+ },
+ {
+ "name": "Debug",
+ "type": "lime",
+ "request": "launch",
+ "preLaunchTask": null
+ },
+ {
+ "name": "Macro",
+ "type": "haxe-eval",
+ "request": "launch"
+ }
+ ]
+}
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..d0535ae
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,14 @@
+{
+ "search.exclude": {
+ "export/**/*.*": true
+ },
+ "[haxe]": {
+ "editor.formatOnSave": true,
+ "editor.formatOnSaveMode":"modifications",
+ "editor.formatOnPaste": false,
+ "editor.codeActionsOnSave": {
+ "source.sortImports": "explicit"
+ }
+ },
+ "haxe.enableExtendedIndentation": true
+}
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
new file mode 100644
index 0000000..16a7764
--- /dev/null
+++ b/.vscode/tasks.json
@@ -0,0 +1,13 @@
+{
+ "version": "2.0.0",
+ "tasks": [
+ {
+ "type": "lime",
+ "command": "test",
+ "group": {
+ "kind": "build",
+ "isDefault": true
+ }
+ }
+ ]
+}
diff --git a/Project.xml b/Project.xml
new file mode 100644
index 0000000..15dc60b
--- /dev/null
+++ b/Project.xml
@@ -0,0 +1,90 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/assets/.DS_Store b/assets/.DS_Store
new file mode 100644
index 0000000..1fe6ead
Binary files /dev/null and b/assets/.DS_Store differ
diff --git a/assets/data/readme.txt b/assets/data/readme.txt
new file mode 100644
index 0000000..1f12468
--- /dev/null
+++ b/assets/data/readme.txt
@@ -0,0 +1 @@
+Add other file format(s) data assets
\ No newline at end of file
diff --git a/assets/fonts/readme.txt b/assets/fonts/readme.txt
new file mode 100644
index 0000000..2afcbfd
--- /dev/null
+++ b/assets/fonts/readme.txt
@@ -0,0 +1 @@
+Add ttf file(s) fonts assets
\ No newline at end of file
diff --git a/assets/images/Border.png b/assets/images/Border.png
new file mode 100644
index 0000000..f8ca112
Binary files /dev/null and b/assets/images/Border.png differ
diff --git a/assets/images/BorderSlice.png b/assets/images/BorderSlice.png
new file mode 100644
index 0000000..1ff3f83
Binary files /dev/null and b/assets/images/BorderSlice.png differ
diff --git a/assets/images/FloorTexture.png b/assets/images/FloorTexture.png
new file mode 100644
index 0000000..c5280cd
Binary files /dev/null and b/assets/images/FloorTexture.png differ
diff --git a/assets/images/Orb.png b/assets/images/Orb.png
new file mode 100644
index 0000000..efe5acd
Binary files /dev/null and b/assets/images/Orb.png differ
diff --git a/assets/images/OrbShadow.png b/assets/images/OrbShadow.png
new file mode 100644
index 0000000..385b071
Binary files /dev/null and b/assets/images/OrbShadow.png differ
diff --git a/assets/images/OtherOrb.png b/assets/images/OtherOrb.png
new file mode 100644
index 0000000..cedad28
Binary files /dev/null and b/assets/images/OtherOrb.png differ
diff --git a/assets/images/OtherOrbShadow.png b/assets/images/OtherOrbShadow.png
new file mode 100644
index 0000000..b8a611a
Binary files /dev/null and b/assets/images/OtherOrbShadow.png differ
diff --git a/assets/images/WallLeft.png b/assets/images/WallLeft.png
new file mode 100644
index 0000000..5c1e856
Binary files /dev/null and b/assets/images/WallLeft.png differ
diff --git a/assets/images/WallUp.png b/assets/images/WallUp.png
new file mode 100644
index 0000000..487308e
Binary files /dev/null and b/assets/images/WallUp.png differ
diff --git a/assets/images/readme.txt b/assets/images/readme.txt
new file mode 100644
index 0000000..c9aa0ab
--- /dev/null
+++ b/assets/images/readme.txt
@@ -0,0 +1 @@
+Add png file(s) images assets
\ No newline at end of file
diff --git a/assets/music/readme.txt b/assets/music/readme.txt
new file mode 100644
index 0000000..dcb6647
--- /dev/null
+++ b/assets/music/readme.txt
@@ -0,0 +1 @@
+add mp3/ogg file(s) music assets
\ No newline at end of file
diff --git a/assets/sounds/readme.txt b/assets/sounds/readme.txt
new file mode 100644
index 0000000..a25ee50
--- /dev/null
+++ b/assets/sounds/readme.txt
@@ -0,0 +1 @@
+Add mp3/ogg file(s) sounds assets
\ No newline at end of file
diff --git a/hxformat.json b/hxformat.json
new file mode 100644
index 0000000..66cb386
--- /dev/null
+++ b/hxformat.json
@@ -0,0 +1,15 @@
+{
+ "lineEnds": {
+ "leftCurly": "both",
+ "rightCurly": "both",
+ "objectLiteralCurly": {
+ "leftCurly": "after"
+ }
+ },
+ "sameLine": {
+ "ifElse": "next",
+ "doWhile": "next",
+ "tryBody": "next",
+ "tryCatch": "next"
+ }
+}
diff --git a/source/Main.hx b/source/Main.hx
new file mode 100644
index 0000000..443fac7
--- /dev/null
+++ b/source/Main.hx
@@ -0,0 +1,16 @@
+package;
+
+import flixel.FlxGame;
+import flixel.FlxSprite;
+import openfl.display.Sprite;
+
+class Main extends Sprite
+{
+ public function new()
+ {
+ super();
+
+ FlxSprite.defaultAntialiasing = true;
+ addChild(new FlxGame(640, 480, PlayState));
+ }
+}
diff --git a/source/PlayState.hx b/source/PlayState.hx
new file mode 100644
index 0000000..bf4182f
--- /dev/null
+++ b/source/PlayState.hx
@@ -0,0 +1,161 @@
+package;
+
+import flixel.FlxCamera;
+import flixel.FlxG;
+import flixel.FlxSprite;
+import flixel.FlxState;
+import flixel.addons.display.FlxBackdrop;
+import flixel.addons.nape.FlxNapeSpace;
+import flixel.math.FlxMath;
+import haxe.EnumTools;
+import nape.phys.Material;
+import props.BorderSlice;
+import props.Orb;
+import props.OtherOrb;
+import props.PlayerOrb;
+import ui.DeadzoneOverlay;
+import ui.HUD;
+
+using flixel.util.FlxSpriteUtil;
+
+/**
+ * @author TiagoLr ( ~~~ProG4mr~~~ )
+ */
+class PlayState extends FlxState
+{
+ static var followStyles = EnumTools.createAll(FlxCameraFollowStyle);
+
+ var player:PlayerOrb;
+ var hud:HUD;
+ var deadzoneOverlay:DeadzoneOverlay;
+
+ override public function create():Void
+ {
+ FlxNapeSpace.init();
+
+ // final levelMinX = -FlxG.stage.stageWidth;
+ // final levelMaxX = FlxG.stage.stageWidth;
+ // final levelMinY = -FlxG.stage.stageHeight;
+ // final levelMaxY = FlxG.stage.stageHeight;
+ final levelMinX = 0;
+ final levelMaxX = FlxG.stage.stageWidth * 2;
+ final levelMinY = 0;
+ final levelMaxY = FlxG.stage.stageHeight * 2;
+ final levelWidth = levelMaxX - levelMinX;
+ final levelHeight = levelMaxY - levelMinY;
+
+ super.create();
+
+ FlxNapeSpace.velocityIterations = 5;
+ FlxNapeSpace.positionIterations = 5;
+
+ // repeating backdrop
+ final backdrop = new FlxBackdrop("assets/images/FloorTexture.png");
+ #if debug
+ backdrop.ignoreDrawDebug = true;
+ #end
+ add(backdrop);
+
+ // create nape wall colliders
+ final border = 10;
+ FlxNapeSpace.createWalls(levelMinX + border, levelMinY + border, levelMaxX - border, levelMaxY - border, border, new Material(1.0, 0.0, 0.0, 1));
+
+ // Walls border sprite
+ final borderSprite = new BorderSlice(levelMinX, levelMinY, levelWidth, levelHeight);
+ add(borderSprite);
+
+ // Player orb
+ player = new PlayerOrb(levelMinX + levelWidth / 2, levelMinY + levelHeight / 2);
+ add(player);
+ // if the player is using a virtual pad, add it to the state
+ if (player.controls.virtualPad != null)
+ add(player.controls.virtualPad);
+
+ // Other orbs
+ for (i in 0...5)
+ {
+ final orb = new OtherOrb(0, 0, i);
+ add(orb);
+ orb.randomizeVelocity();
+
+ // randomize spawn position until it's far enough from the player, up to 20 times
+ var tries = 20;
+ do orb.randomizePosition(levelMinX, levelMaxX, levelMinY, levelMaxY)
+ while (Math.abs(orb.x - player.x) < 200 && Math.abs(orb.y - player.y) < 200 && tries-- > 0);
+ }
+
+ hud = new HUD();
+ add(hud);
+
+ // Camera Overlay
+ deadzoneOverlay = new DeadzoneOverlay();
+ add(deadzoneOverlay);
+
+ FlxG.camera.pixelPerfectRender = false;
+ FlxG.camera.setScrollBounds(levelMinX, levelMaxX, levelMinY, levelMaxY);
+ FlxG.worldBounds.set(levelMinX, levelMinY, levelWidth, levelHeight);
+ FlxG.camera.follow(player, followStyles[0], 1);
+ deadzoneOverlay.redraw(FlxG.camera); // now that deadzone is present
+ }
+
+ override public function update(elapsed:Float):Void
+ {
+ super.update(elapsed);
+
+ final justPressed = FlxG.keys.justPressed;
+
+ if (justPressed.Y) setStyle(1);
+ if (justPressed.H) setStyle(-1);
+
+ if (justPressed.U) setLerp(.1);
+ if (justPressed.J) setLerp(-.1);
+
+ if (justPressed.I) setLead(.5);
+ if (justPressed.K) setLead(-.5);
+
+ if (justPressed.O) setZoom(.1);
+ if (justPressed.L) setZoom(-.1);
+
+ if (justPressed.M) FlxG.camera.shake();
+ }
+
+ public function setZoom(delta:Float)
+ {
+ final newZoom = FlxG.camera.zoom + delta;
+ FlxG.camera.zoom = FlxMath.bound(Math.round(newZoom * 10) / 10, 0.5, 4);
+ hud.updateZoom(FlxG.camera.zoom);
+ }
+
+ function setLead(delta:Float)
+ {
+ var cam = FlxG.camera;
+ cam.followLead.x += delta;
+ cam.followLead.y += delta;
+
+ if (cam.followLead.x < 0)
+ {
+ cam.followLead.x = 0;
+ cam.followLead.y = 0;
+ }
+
+ hud.updateCamLead(cam.followLead.x);
+ }
+
+ function setLerp(delta:Float)
+ {
+ var cam = FlxG.camera;
+ cam.followLerp += delta;
+ cam.followLerp = Math.round(10 * cam.followLerp) / 10; // adding or subtracting .1 causes roundoff errors
+ hud.updateCamLerp(cam.followLerp);
+ }
+
+ function setStyle(delta:Int)
+ {
+ final nextStyleIndex = (followStyles.indexOf(FlxG.camera.style) + delta) % followStyles.length;
+ FlxG.camera.follow(player, followStyles[nextStyleIndex], FlxG.camera.followLerp);
+
+ deadzoneOverlay.redraw(FlxG.camera);
+
+ hud.updateStyle(FlxG.camera.style);
+ }
+}
diff --git a/source/input/PlayerControls.hx b/source/input/PlayerControls.hx
new file mode 100644
index 0000000..beb0696
--- /dev/null
+++ b/source/input/PlayerControls.hx
@@ -0,0 +1,123 @@
+package input;
+
+import flixel.FlxG;
+import flixel.input.gamepad.FlxGamepadInputID;
+import flixel.input.keyboard.FlxKey;
+import flixel.ui.FlxVirtualPad;
+
+class PlayerControls
+{
+ /**
+ * Maps input types to their corresponding keyboard button
+ */
+ static public var keyMap:Map> =
+ [
+ Input.LEFT => [FlxKey.A, FlxKey.LEFT ],
+ Input.DOWN => [FlxKey.S, FlxKey.DOWN ],
+ Input.RIGHT => [FlxKey.D, FlxKey.RIGHT],
+ Input.UP => [FlxKey.W, FlxKey.UP ]
+ ];
+
+ #if FLX_GAMEPAD
+ /**
+ * Maps input types to their corresponding gamepad dpad button
+ */
+ static public var buttonMap:Map =
+ [
+ Input.LEFT => { dpad:DPAD_LEFT , analog:LEFT_STICK_DIGITAL_LEFT },
+ Input.DOWN => { dpad:DPAD_DOWN , analog:LEFT_STICK_DIGITAL_DOWN },
+ Input.RIGHT => { dpad:DPAD_RIGHT, analog:LEFT_STICK_DIGITAL_RIGHT },
+ Input.UP => { dpad:DPAD_UP , analog:LEFT_STICK_DIGITAL_UP }
+ ];
+ #end
+
+ /**
+ * Reference to the gamepad controlling this orb
+ */
+ public var virtualPad:VirtualPad = null;
+
+ public function new()
+ {
+ // create a virtual pad to play on mobile devices
+ final useVirtualPad = #if html5 FlxG.html5.onMobile #elseif mobile true #else false #end;
+ if (useVirtualPad)
+ virtualPad = new VirtualPad();
+ }
+
+ public function isGamepadConnected()
+ {
+ #if FLX_GAMEPAD
+ return FlxG.gamepads.numActiveGamepads > 0;
+ #else
+ return false;
+ #end
+ }
+
+ /**
+ * Helper to detect keyboard or virtual pad presses
+ */
+ inline public function inputPressed(input:Input)
+ {
+ return keyPressed(input) || virtualPadPressed(input) || gamePadPressed(input);
+ }
+
+ /**
+ * Helper to detect keyboard presses
+ */
+ inline function keyPressed(input:Input)
+ {
+ return FlxG.keys.anyPressed(keyMap[input]);
+ }
+
+ /**
+ * Helper to detect virtual pad presses
+ */
+ inline function virtualPadPressed(input:Input)
+ {
+ return virtualPad != null && virtualPad.pressed(input);
+ }
+
+ /**
+ * Helper to detect gamepad presses
+ */
+ inline function gamePadPressed(input:Input)
+ {
+ #if FLX_GAMEPAD
+ final buttons = buttonMap[input];
+ return FlxG.gamepads.anyPressed(buttons.dpad) || FlxG.gamepads.anyPressed(buttons.analog);
+ #else
+ return false;
+ #end
+ }
+}
+
+/**
+ * Simplified virtual pad that takes an Input and returns whether the corresponding button is pressed
+ */
+abstract VirtualPad(FlxVirtualPad) from FlxVirtualPad to FlxVirtualPad
+{
+ inline public function new()
+ {
+ this = new FlxVirtualPad(FULL, NONE);
+ }
+
+ public function pressed(input:Input)
+ {
+ return switch(input)
+ {
+ case Input.LEFT : this.buttonLeft.pressed;
+ case Input.RIGHT: this.buttonRight.pressed;
+ case Input.UP : this.buttonUp.pressed;
+ case Input.DOWN : this.buttonDown.pressed;
+ default: false;
+ }
+ }
+}
+
+enum Input
+{
+ LEFT;
+ RIGHT;
+ UP;
+ DOWN;
+}
\ No newline at end of file
diff --git a/source/props/BorderSlice.hx b/source/props/BorderSlice.hx
new file mode 100644
index 0000000..ae89aa5
--- /dev/null
+++ b/source/props/BorderSlice.hx
@@ -0,0 +1,25 @@
+package props;
+
+import flixel.addons.display.FlxSliceSprite;
+import flixel.math.FlxRect;
+
+@:forward
+abstract BorderSlice(FlxSliceSprite) from FlxSliceSprite to FlxSliceSprite
+{
+ inline public function new (x = 0.0, y = 0.0, width:Float, height:Float)
+ {
+ this = new FlxSliceSprite("assets/images/BorderSlice.png", new FlxRect(15, 15, 20, 20), width, height);
+ this.x = x;
+ this.y = y;
+ // reduce vertice counts by stretching rather than tiling
+ this.fillCenter = false;
+ this.stretchBottom = true;
+ this.stretchTop = true;
+ this.stretchLeft = true;
+ this.stretchRight = true;
+ this.stretchCenter = true;
+ #if debug
+ this.ignoreDrawDebug = true;
+ #end
+ }
+}
\ No newline at end of file
diff --git a/source/props/Orb.hx b/source/props/Orb.hx
new file mode 100644
index 0000000..3f501c8
--- /dev/null
+++ b/source/props/Orb.hx
@@ -0,0 +1,77 @@
+package props;
+
+import flixel.FlxG;
+import flixel.FlxSprite;
+import flixel.addons.nape.FlxNapeSprite;
+import flixel.util.FlxDestroyUtil;
+
+/**
+ * @author TiagoLr ( ~~~ProG4mr~~~ )
+ */
+class Orb extends FlxNapeSprite
+{
+ var shadow:FlxSprite;
+
+ public function new(x = 0.0, y = 0.0, radius:Int, ?graphic, ?shadowGraphic)
+ {
+ super(x, y, null, false);
+ createCircularBody(radius);
+ body.allowRotation = false;
+ pixelPerfectPosition = false;
+ pixelPerfectRender = false;
+
+ // create a shadow that follows the sprite around
+ shadow = new FlxSprite(x, y, shadowGraphic);
+ shadow.blend = MULTIPLY;
+ shadow.pixelPerfectPosition = false;
+ shadow.pixelPerfectRender = false;
+ #if debug
+ shadow.ignoreDrawDebug = true;
+ #end
+
+ // call loadGraphic after the body is created so it adjusts the hitbox
+ if (graphic != null)
+ loadGraphic(graphic);
+ }
+
+ override function loadGraphic(graphic, animated = false, frameWidth = 0, frameHeight = 0, unique = false, ?key:String)
+ {
+ super.loadGraphic(graphic, animated, frameWidth, frameHeight, unique, key);
+
+ // adjust flixel hitbox to match the radius for tighter camera following
+ if (body != null && body.shapes != null)
+ {
+ width = body.bounds.width;
+ height = body.bounds.height;
+ centerOffsets(false);
+ origin.set(width / 2, height / 2);
+
+ // same offset for shadowP
+ shadow.offset.copyFrom(offset);
+ }
+
+ return this;
+ }
+
+ override public function update(elapsed:Float):Void
+ {
+ super.update(elapsed);
+
+ shadow.update(elapsed);
+ shadow.x = x;
+ shadow.y = y;
+ }
+
+ override function draw()
+ {
+ // draw shadow first, so it's underneath
+ shadow.draw();
+ super.draw();
+ }
+
+ override function destroy()
+ {
+ super.destroy();
+ shadow = FlxDestroyUtil.destroy(shadow);
+ }
+}
diff --git a/source/props/OtherOrb.hx b/source/props/OtherOrb.hx
new file mode 100644
index 0000000..a6b9218
--- /dev/null
+++ b/source/props/OtherOrb.hx
@@ -0,0 +1,40 @@
+package props;
+
+import flixel.FlxG;
+import flixel.FlxSprite;
+// import flixel.math.FlxMath;
+import flixel.util.FlxDestroyUtil;
+
+// import nape.geom.Vec2;
+
+class OtherOrb extends Orb
+{
+ public function new(x = 0.0, y = 0.0, colorIndex:Int)
+ {
+ super(x, y, 50, null, "assets/images/OtherOrbShadow.png");
+
+ loadGraphic("assets/images/OtherOrb.png", true, 140, 140);
+ animation.frameIndex = colorIndex;
+ setBodyMaterial(1, 0.0, 0.0, 0.5);
+ }
+
+ /**
+ * Randomizes the position of this orb within the given bounds
+ */
+ public function randomizePosition(minX = 0, maxX = 0, minY = 0, maxY = 0)
+ {
+ x = body.position.x = FlxG.random.int(minX, maxX - Math.ceil(width));
+ y = body.position.y = FlxG.random.int(minY, maxY - Math.ceil(height));
+ return this;
+ }
+
+ /**
+ * Randomizes the velocity of this orb with the given absolute speed range and a random angle
+ */
+ public function randomizeVelocity(min = 100, max = 200)
+ {
+ body.velocity.setxy(FlxG.random.float(min, max), 0);
+ body.velocity.angle = FlxG.random.float(0, 2 * Math.PI);
+ return this;
+ }
+}
\ No newline at end of file
diff --git a/source/props/PlayerOrb.hx b/source/props/PlayerOrb.hx
new file mode 100644
index 0000000..fc59458
--- /dev/null
+++ b/source/props/PlayerOrb.hx
@@ -0,0 +1,59 @@
+package props;
+
+import flixel.FlxG;
+import input.PlayerControls;
+import nape.geom.Vec2;
+
+/**
+ * User controlled orb
+ */
+class PlayerOrb extends Orb
+{
+ /**
+ * The impulse applied each frame when pressing the corresponding key
+ */
+ static inline var IMPULSE = 20;
+
+ /**
+ * Used Internally to avoid creating new instances each frame
+ */
+ static final impulseHelper = new Vec2();
+
+ public var controls:PlayerControls;
+
+ public function new(x = 0.0, y = 0.0)
+ {
+ super(x, y, 18, "assets/images/Orb.png", "assets/images/OrbShadow.png");
+ // small amount of drag
+ setDrag(0.98);
+
+ controls = new PlayerControls();
+ }
+
+ override function update(elapsed:Float)
+ {
+ super.update(elapsed);
+
+ // apply impusles to the body based on key presses
+
+ if (controls.inputPressed(LEFT))
+ applyImpulseXY(-IMPULSE, 0);
+
+ if (controls.inputPressed(DOWN))
+ applyImpulseXY(0, IMPULSE);
+
+ if (controls.inputPressed(RIGHT))
+ applyImpulseXY(IMPULSE, 0);
+
+ if (controls.inputPressed(UP))
+ applyImpulseXY(0, -IMPULSE);
+ }
+
+ /**
+ * Helper to apply impulse via x and y floats to avoid creating new Vec2 instances each frame
+ */
+ inline function applyImpulseXY(x:Float, y:Float)
+ {
+ body.applyImpulse(impulseHelper.setxy(x, y));
+ }
+}
\ No newline at end of file
diff --git a/source/ui/DeadzoneOverlay.hx b/source/ui/DeadzoneOverlay.hx
new file mode 100644
index 0000000..909738d
--- /dev/null
+++ b/source/ui/DeadzoneOverlay.hx
@@ -0,0 +1,81 @@
+package ui;
+
+import flixel.FlxCamera;
+import flixel.FlxG;
+import flixel.FlxSprite;
+import flixel.math.FlxRect;
+import flixel.util.FlxColor;
+
+using flixel.util.FlxSpriteUtil;
+
+/**
+ * Sprite used to draw a representation of the current follow styles.
+ * For most styles, it draws the deadzone.
+ */
+class DeadzoneOverlay extends FlxSprite
+{
+ public function new ()
+ {
+ super();
+
+ scrollFactor.set(0, 0);// move with the camera
+
+ #if debug
+ ignoreDrawDebug = true;
+ #end
+ }
+
+ public function redraw(targetCamera:FlxCamera)
+ {
+ if (targetCamera.style == SCREEN_BY_SCREEN)
+ {
+ // just hide it, otherwise we'd need to redraw it with zoom changes
+ visible = false;
+ return;
+ }
+ visible = true;
+
+ final lineLength = 12;
+ final padding = 2;
+ final thickness = 3 + padding;
+ final halfThickness = thickness / 2;
+ final lineStyle:LineStyle = {color: FlxColor.WHITE, thickness: thickness - padding};
+
+ if (targetCamera.style == NO_DEAD_ZONE)
+ {
+ // No deadzone, draw a simple crosshair in the center of the camera's view
+ final reticalSize = 20;
+ // pad the graphic a little, for thick lines
+ makeGraphic(reticalSize + thickness, reticalSize + thickness, FlxColor.TRANSPARENT, true);
+ x = (camera.width - frameWidth) / 2;
+ y = (camera.height - frameHeight) / 2;
+
+ final centerX = frameWidth / 2;
+ final centerY = frameHeight / 2;
+ final reticalHalfSize = reticalSize / 2;
+ this.drawLine(centerX, centerY - reticalHalfSize, centerX, centerY + reticalHalfSize, lineStyle);
+ this.drawLine(centerX - reticalHalfSize, centerY, centerX + reticalHalfSize, centerY, lineStyle);
+ return;
+ }
+
+ // draw the deadzone's corners
+ final dz:FlxRect = targetCamera.deadzone;
+ x = dz.x - halfThickness;
+ y = dz.y - halfThickness;
+ // pad the graphic a little, for thick lines
+ makeGraphic(Std.int(dz.width + thickness), Std.int(dz.height + thickness), FlxColor.TRANSPARENT, true);
+
+ // Top-Left
+ this.drawLine(dz.left - x, dz.top - y, dz.left - x + lineLength, dz.top - y, lineStyle);
+ this.drawLine(dz.left - x, dz.top - y, dz.left - x, dz.top + lineLength - y, lineStyle);
+ // Top-Right
+ this.drawLine(dz.right - x, dz.top - y, dz.right - x - lineLength, dz.top - y, lineStyle);
+ this.drawLine(dz.right - x, dz.top - y, dz.right - x, dz.top + lineLength - y, lineStyle);
+ // Bottom-Left
+ this.drawLine(dz.left - x, dz.bottom - y, dz.left - x + lineLength, dz.bottom - y, lineStyle);
+ this.drawLine(dz.left - x, dz.bottom - y, dz.left - x, dz.bottom - lineLength - y, lineStyle);
+ // Bottom-Right
+ this.drawLine(dz.right - x, dz.bottom - y, dz.right - x - lineLength, dz.bottom - y, lineStyle);
+ this.drawLine(dz.right - x, dz.bottom - y, dz.right - x, dz.bottom - lineLength - y, lineStyle);
+ }
+}
\ No newline at end of file
diff --git a/source/ui/HUD.hx b/source/ui/HUD.hx
new file mode 100644
index 0000000..6e9862c
--- /dev/null
+++ b/source/ui/HUD.hx
@@ -0,0 +1,97 @@
+package ui;
+
+import flixel.FlxCamera;
+import flixel.FlxG;
+import flixel.FlxSprite;
+import flixel.group.FlxGroup;
+import flixel.text.FlxText;
+
+inline var WIDTH = 200;
+inline var HEIGHT = 180;
+
+/**
+ * @author TiagoLr ( ~~~ProG4mr~~~ )
+ */
+class HUD extends FlxGroup
+{
+ var txtStyle:Text;
+ var txtLerp:Text;
+ var txtLead:Text;
+ var txtZoom:Text;
+
+ public function new()
+ {
+ super();
+
+ var left = 6;
+ var startY = 10;
+
+ add(new Text(left, startY, "[W,A,S,D] or arrows to control the orb."));
+
+ add(new Text(left, startY + 20, "[H] or [Y] to change follow style."));
+ add(txtStyle = new GreenText(left, startY + 33, "LOCKON"));
+
+ add(new Text(left, startY + 55, "[U] or [J] to change lerp."));
+ add(txtLerp = new GreenText(left, startY + 68, "Camera lerp: 1"));
+
+ add(new Text(left, startY + 95, "[I] or [K] to change lead."));
+ add(txtLead = new GreenText(left, startY + 108, "Camera lead: 0"));
+
+ add(new Text(left, startY + 135, "[O] or [L] to change zoom."));
+ add(txtZoom = new GreenText(left, startY + 148, "Camera zoom: 1"));
+
+ // create new camera in the top-right corner that only draws this
+ camera = new FlxCamera(440, 0, WIDTH, HEIGHT, 1.0);
+ camera.alpha = .5;
+ camera.bgColor = 0x80000000;
+ FlxG.cameras.add(camera, false);
+ }
+
+ public function updateStyle(style:FlxCameraFollowStyle)
+ {
+ txtStyle.text = Std.string(style);
+ }
+
+ public function updateCamLerp(lerp:Float)
+ {
+ txtLerp.text = "Camera lerp: " + lerp;
+ }
+
+ public function updateCamLead(lead:Float)
+ {
+ txtLead.text = "Camera lead: " + lead;
+ }
+
+ public function updateZoom(zoom:Float)
+ {
+ txtZoom.text = "Camera Zoom: " + Math.floor(zoom * 10) / 10;
+ }
+}
+
+/**
+ * A simplified, specialized FlxText instance, mainly used to omit the fieldWidth arg
+ */
+@:forward
+abstract Text(FlxText) from FlxText to FlxText
+{
+ inline public function new (x = 0.0, y = 0.0, ?text:String, size = 8)
+ {
+ this = new FlxText(x, y, WIDTH, text, size);
+ #if debug
+ this.ignoreDrawDebug = true;
+ #end
+ }
+}
+
+/**
+ * An even more specialized version of Text used to highlight the changing camera values
+ */
+@:forward
+abstract GreenText(Text) from Text to Text
+{
+ inline public function new (x = 0.0, y = 0.0, ?text:String)
+ {
+ this = new Text(x, y, text);
+ this.setFormat(null, 11, 0x55FF55);
+ }
+}