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); + } +}