(loadFrom "nat-archive-tool" "src/nat/Lib.kiss") (var TEXT_SIZE 16) (prop &mut :EntrySpriteSystem spriteSystem) (method :PlaygroundSystem playgroundSystem [] (set spriteSystem (new EntrySpriteSystem this controller))) (prop :KeyShortcutHandler shortcutHandler (new FlxKeyShortcutHandler)) (method &override :Void create [] (super.create) (FlxG.plugins.add (new FlxMouseControl)) (set FlxG.sound.muteKeys null) (set FlxG.sound.volumeDownKeys null) (set FlxG.sound.volumeUpKeys null) // 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 screenCamera (new FlxCamera 0 0 FlxG.width FlxG.height)) (prop &mut :SimpleWindow uiWindow (new SimpleWindow 0.8 0.8 true "escape")) (set uiWindow.cameras [screenCamera]) (set uiWindow.keyboardEnabled false) (prop :FlxTypedGroup entryGroup (new FlxTypedGroup)) (add entryGroup) (prop debugLayer (new DebugLayer)) (set debugLayer.cameras [FlxG.camera]) (add debugLayer) (FlxG.camera.calculateScrollBounds entryGroup SCROLL_BOUND_MARGIN)) (method :Void showPrefixMap [:Map map] (clearUI) (doFor =>key thing map (displayMessage "$key - $thing"))) (method :Void hidePrefixMap [] (clearUI)) (prop &mut :Bool confirmQuit false) (defAlias &ident sh (cast shortcutHandler FlxKeyShortcutHandler)) (method &override :Void update [:Float elapsed] (super.update elapsed) (when sh.currentMap (sh.update)) (when (and FlxG.keys.justPressed.V FlxG.keys.pressed.CONTROL) (when (and textInput textInput.hasFocus) (whenLet [text (Clipboard.generalClipboard.getData ClipboardFormats.TEXT_FORMAT)] (when (textInput.text.endsWith "v") (set textInput.text (substr textInput.text 0 -1))) (+= textInput.text text) (set textInput.caretIndex textInput.text.length)))) (when FlxG.keys.justPressed.ESCAPE (when sh.currentMap (sh.cancel))) (when FlxG.keys.justPressed.DELETE (Sys.exit 0)) // Press ENTER to type a command to run (when (and !textInput FlxG.keys.justPressed.ENTER) (set confirmQuit false) (typeCommand)) (when (and textInput !textInput.alive) (set textInput null)) // Control the UI camera with WASD, and the playground camera with arrow keys: (var KEYBOARD_SCROLL_SPEED 800) (FlxG.camera.updateKeyControl elapsed KEYBOARD_SCROLL_SPEED ->{FlxG.keys.pressed.LEFT} ->{FlxG.keys.pressed.RIGHT} ->{FlxG.keys.pressed.UP} ->{FlxG.keys.pressed.DOWN}) (FlxG.camera.updateMouseBorderControl elapsed KEYBOARD_SCROLL_SPEED 0.01 screenCamera) (FlxG.camera.updateScrollWheelZoom elapsed 1) // Don't check keys that can be used in shortcuts outside this block: (unless (or sh.currentMap (and textInput textInput.hasFocus)) (when FlxG.keys.justPressed.SEMICOLON (set confirmQuit false) (sh.start) (return)) // +/- keys to change an entry's z (doFor e (controller.getSelectedEntries) (when FlxG.keys.justPressed.MINUS (withWritableComponents archive e [positions Positions] (-= .z (dictGet positions (spriteSystem.getPlaygroundKey)) 1))) (when FlxG.keys.justPressed.PLUS (withWritableComponents archive e [positions Positions] (+= .z (dictGet positions (spriteSystem.getPlaygroundKey)) 1)))))) (method :Void typeCommand [] (enterText "command to run:" ->commandName (controller.tryRunCommand commandName) Math.POSITIVE_INFINITY)) (prop &mut :ArchiveController controller) (prop &mut :Archive archive) (prop &mut :FlxText textInputLabel null) (prop &mut :FlxInputText textInput null) (method :Void enterText [prompt resolve maxLength] (displayMessage prompt) (set textInput (new FlxInputText 0 0 FlxG.width "" TEXT_SIZE)) (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] (clearUI) (set textInput.callback null) (set textInput null) (resolve text)) //([_ FlxInputText.]) (otherwise {})))) (uiWindow.addControl 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] (entryGroup.forEach ->s (set s.mousePressedCallback ->:Void [s _ _] { (entryGroup.forEach ->s (set s.mousePressedCallback ->:Void [_ _ _] null)) (resolve .e (cast s EntrySprite)) }))) (method :Void chooseEntries [prompt archive resolve min max] (_chooseEntries prompt archive resolve min max [])) // TODO is it possible to resolve with less than max? // TODO this version that just delegates to (chooseEntry) should be reusable, which is tricky because ArchiveUI is an interface // It also needs a way to resolve with less than the maximum, which may be infinity (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 (= (#if debug 1 max) collectedEntries.length) (resolve collectedEntries) // Otherwise, recurse (chooseNextEntry))})}] (set chooseNextEntry _chooseNextEntry) (_chooseNextEntry))) (var SCROLL_BOUND_MARGIN 2000) (method handleChanges [:Archive archive :ChangeSet changeSet] (when changeSet // process the WikipediaImageSystem and run spriteSystem process on newly created entries that get one (archive.processSystems this) // Do a second loop through the systems, so Playground systems that trigger Core systems have their effects processed (archive.processSystems this)) (doFor e changeSet // Entries whose data changed to remove them from the sprite pool will already have been removed // by refreshEntry() (when (spriteSystem.entries.exists e.id) // refresh the sprites for entries that changed data but still should have sprites (when (spriteSystem.sprites.exists e.id) (spriteSystem.onRemoveEntry archive e)) (spriteSystem.processEntry archive e))) (FlxG.camera.calculateScrollBounds entryGroup SCROLL_BOUND_MARGIN)) (method :Void displayMessage [:String message] (print message) (uiWindow.makeText message) (uiWindow.show)) (method :Void clearUI [] (uiWindow.clearControls) (uiWindow.hide)) (method :Void reportError [:String error] (print error) (uiWindow.makeText error FlxColor.RED) (uiWindow.show)) (method :Void onSelectionChanged [:Array selectedEntries :Array lastSelectedEntries] (doFor e (selectedEntries.concat lastSelectedEntries) (whenLet [sprite (dictGet spriteSystem.sprites e.id)] (sprite.updateColor)))) (method :Option cursorPosition [] (let [pos (FlxG.mouse.getWorldPosition FlxG.camera)] (Some (object x pos.x y pos.y z 0.0))))