(loadFrom "nat-archive-tool" "src/nat/Lib.kiss") // TODO store a map of Entry IDs -> EntrySprites. // TODO handleChanges() will need to kill every changed Entry's sprite and make a new one // TODO make the EntrySprite constructor assign the entry a serialized position component // maybe by writing a Map positions component so there can be multiple? (method &override :Void create [] (super.create) (FlxG.plugins.add (new FlxMouseControl)) // TODO find a better way to pass the archiveDir to a HaxeFlixel game (let [archiveDir (or (Sys.getEnv "NAT_DIR") (throw "NAT_DIR environment variable must be set")) archive (new Archive archiveDir)] (set this.archive archive) (set controller (new ArchiveController archive this))) (prop &mut :FlxGroup uiGroup (new FlxGroup)) (add uiGroup) (prop :FlxGroup entryGroup (new FlxGroup)) (add entryGroup) (prop uiCamera (new FlxCamera 0 0 FlxG.width FlxG.height)) (set uiCamera.bgColor FlxColor.TRANSPARENT) (FlxG.cameras.add uiCamera false) (set uiGroup.cameras [uiCamera]) // TODO make a button that can be clicked to run typeCommand() // make interactible sprites for entries that have images // TODO allow configuring the tags at runtime and erasing/re-creating sprites later // TODO allow using other position keys and erasing/re-creating sprites later (prop &mut :EntrySpriteSystem spriteSystem) (set spriteSystem (new EntrySpriteSystem "!done" "Playground-MAIN" this controller)) (archive.addSystem spriteSystem) (spriteSystem.process archive)) (method &override :Void update [:Float elapsed] (super.update elapsed) // Press ENTER to type a command to run (when (and !textInput FlxG.keys.justPressed.ENTER) (typeCommand)) (when (and textInput !textInput.alive) (set textInput null)) // Press ESCAPE to clear the UI and cancel any input (when FlxG.keys.justPressed.ESCAPE (clearUI)) // Scroll the UI with the mouse: (var UI_SCROLL_FACTOR 2) (+= uiCamera.y (* FlxG.mouse.wheel UI_SCROLL_FACTOR)) // TODO allow changing the a scroll factor // Control the UI camera with the arrow keys, and the playground camera with wasd: (var KEYBOARD_SCROLL_SPEED 200) (method cameraKeyControl [:FlxCamera camera :Void->Bool leftKey :Void->Bool rightKey :Void->Bool upKey :Void->Bool downKey] // but not when textInput is focused (unless (and textInput textInput.hasFocus) (let [scrollPerSec (/ KEYBOARD_SCROLL_SPEED 60) &mut :FlxVector movement (new FlxPoint)] (when (leftKey) (-= movement.x 1)) (when (rightKey) (+= movement.x 1)) (when (upKey) (-= movement.y 1)) (when (downKey) (+= movement.y 1)) (when (< 0 movement.length) (set movement (movement.normalize))) (movement.scale scrollPerSec) (+= camera.scroll.x movement.x) (+= camera.scroll.y movement.y)))) // don't move the ui camera before ui has been placed -- new UI elements could appear offscreen (when (> uiGroup.length 0) (cameraKeyControl uiCamera ->{FlxG.keys.pressed.LEFT} ->{FlxG.keys.pressed.RIGHT} ->{FlxG.keys.pressed.UP} ->{FlxG.keys.pressed.DOWN})) (cameraKeyControl FlxG.camera ->{FlxG.keys.pressed.A} ->{FlxG.keys.pressed.D} ->{FlxG.keys.pressed.W} ->{FlxG.keys.pressed.S})) (method :Void typeCommand [] (enterText "command to run:" ->commandName (controller.tryRunCommand commandName) Math.POSITIVE_INFINITY)) (prop &mut :ArchiveController controller) (prop &mut :Archive archive) (method :Void setController [controller] (set this.controller controller)) (prop &mut :FlxText textInputLabel null) (prop &mut :FlxInputText textInput null) (method :Void enterText [prompt resolve maxLength] (set textInputLabel (new FlxText 0 0 300 prompt)) (showUI textInputLabel) (set textInput (new FlxInputText 0 0 300 "")) (set textInput.hasFocus true) (set textInput.callback ->:Void [text action] // Super weird that this check is necessary (when textInput (case [text action] ([text FlxInputText.ENTER_ACTION] (set textInput.callback null) (hideUI textInput) // This part is hacky... (set lastUI textInputLabel) (hideUI textInputLabel) (resolve text)) //([_ FlxInputText.]) (otherwise {})))) (showUI textInput)) (method :Void enterNumber [prompt resolve min max &opt inStepsOf] (enterText prompt ->:Void [numberStr] (let [number (try (Std.parseFloat numberStr) (catch [e] (reportError "Not a number: $numberStr") (return)))] (resolve number)) Math.POSITIVE_INFINITY)) (method :Void chooseEntry [prompt :Archive archive resolve] (resolve null)) (method :Void chooseEntries [prompt archive resolve min max] (_chooseEntries prompt archive resolve min max [])) // TODO is it possible to resolve with less than max? (method :Void _chooseEntries [prompt archive resolve min max :Array collectedEntries] (let [&mut :Void->Void chooseNextEntry null _chooseNextEntry ->:Void {(chooseEntry prompt archive ->:Void e {(collectedEntries.push e) // If the maximum is reached, return it (if (= max collectedEntries.length) (resolve collectedEntries) // Otherwise, recurse (chooseNextEntry))})}] (set chooseNextEntry _chooseNextEntry) (_chooseNextEntry))) (method handleChanges [:Archive archive :ChangeSet changeSet] (doFor e changeSet // refresh the sprites for entries that changed data. // Entries whose data changed to remove them from the sprite pool will already have been removed // by refreshEntry() (when (spriteSystem.entries.exists e.id) (when (spriteSystem.sprites.exists e.id) (spriteSystem.onRemoveEntry archive e)) (spriteSystem.processEntry archive e)))) (prop &mut :Int uiY 0) (prop &mut :FlxSprite lastUI null) (method :Void showUI [:FlxSprite ui] (set ui.y uiY) (uiGroup.add ui) (set lastUI ui) (+= uiY ui.height) (when (> uiY FlxG.height) (+= uiCamera.scroll.y ui.height))) (method :Void hideUI [:FlxSprite ui] (uiGroup.remove ui) (ui.kill) (when (= lastUI ui) (-= uiY ui.height))) (method :Void displayMessage [:String message] (let [messageText (new FlxText 0 0 0 (message.replace "\n" "|"))] (showUI messageText))) (method :Void clearUI [] (uiGroup.kill) (set uiGroup (new FlxGroup)) (set uiGroup.cameras [uiCamera]) (add uiGroup) (set uiY 0) (set uiCamera.scroll.x 0) (set uiCamera.scroll.y 0)) (method :Void reportError [:String error] (let [text (new FlxText 0 0 0 (error.replace "\n" "|"))] (text.setFormat null 8 FlxColor.RED) (showUI text)))