300 lines
12 KiB
Plaintext
300 lines
12 KiB
Plaintext
(loadFrom "nat-archive-tool" "src/nat/Lib.kiss")
|
|
|
|
// TODO handleChanges() will need to kill every changed Entry's sprite and make a new one
|
|
|
|
// make interactible sprites for entries that have images
|
|
|
|
(var TEXT_SIZE 16)
|
|
|
|
(prop &mut :EntrySpriteSystem spriteSystem)
|
|
|
|
(method :PlaygroundSystem playgroundSystem []
|
|
(set spriteSystem (new EntrySpriteSystem this controller)))
|
|
|
|
(prop :KeyShortcutHandler<Entry> shortcutHandler (new FlxKeyShortcutHandler<Entry>))
|
|
(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 &mut :FlxGroup uiGroup (new FlxGroup))
|
|
(add uiGroup)
|
|
|
|
(prop :FlxTypedGroup<EntrySprite> entryGroup (new FlxTypedGroup<EntrySprite>))
|
|
(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])
|
|
|
|
(prop mouseDragCamera (new FlxCamera 0 0 FlxG.width FlxG.height))
|
|
(set mouseDragCamera.bgColor FlxColor.TRANSPARENT)
|
|
(FlxG.cameras.add mouseDragCamera false)
|
|
(prop mouseDragSprite (new FlxSprite 0 0))
|
|
(prop &mut :FlxPoint mouseDown null)
|
|
(mouseDragSprite.makeGraphic FlxG.width FlxG.height FlxColor.TRANSPARENT)
|
|
(set mouseDragSprite.cameras [mouseDragCamera])
|
|
(add mouseDragSprite)
|
|
(FlxMouseEventManager.add mouseDragSprite
|
|
// mouseDown
|
|
->s {
|
|
(set mouseDown (FlxG.mouse.getScreenPosition))
|
|
(let [&mut clickedOnSomething false]
|
|
(entryGroup.forEach
|
|
->entrySprite
|
|
(when (.containsPoint (entrySprite.getScreenBounds) (FlxG.mouse.getScreenPosition))
|
|
(set clickedOnSomething true)
|
|
(unless (controller.isSelected entrySprite.e)
|
|
(controller.SelectEntry entrySprite.e))))
|
|
(unless clickedOnSomething (controller.SelectEntries [])))
|
|
}
|
|
// mouseUp
|
|
->s {
|
|
(set mouseDown null)
|
|
(mouseDragSprite.makeGraphic FlxG.width FlxG.height FlxColor.TRANSPARENT true)
|
|
}
|
|
// mouseOver
|
|
->s {}
|
|
// mouseOut
|
|
->s {}
|
|
// also send events to sprites that overlap the dragging background
|
|
true
|
|
true
|
|
false)
|
|
(FlxMouseEventManager.setMouseMoveCallback mouseDragSprite
|
|
->s (when mouseDown (unless FlxMouseControl.isDragging
|
|
// draw the selection rectangle
|
|
(mouseDragSprite.makeGraphic FlxG.width FlxG.height FlxColor.TRANSPARENT true)
|
|
(let [curPos (FlxG.mouse.getScreenPosition)
|
|
x1 (min curPos.x mouseDown.x)
|
|
y1 (min curPos.y mouseDown.y)
|
|
x2 (max curPos.x mouseDown.x)
|
|
y2 (max curPos.y mouseDown.y)
|
|
selectWidth (- x2 x1)
|
|
selectHeight (- y2 y1)
|
|
rectangle (new FlxRect x1 y1 selectWidth selectHeight)]
|
|
(mouseDragSprite.drawRect x1 y1 selectWidth selectHeight FlxColor.TRANSPARENT (object color FlxColor.LIME))
|
|
// Handle entry selection
|
|
(entryGroup.forEach ->entrySprite
|
|
(let [overlaps (rectangle.overlaps (entrySprite.getScreenBounds))]
|
|
(when !(= overlaps (controller.isSelected entrySprite.e))
|
|
(controller.ToggleSelectEntry entrySprite.e))))))))
|
|
|
|
(FlxG.camera.calculateScrollBounds entryGroup SCROLL_BOUND_MARGIN))
|
|
|
|
(defAlias &ident sh (cast shortcutHandler FlxKeyShortcutHandler<Dynamic>))
|
|
(method &override :Void update [:Float elapsed]
|
|
(super.update elapsed)
|
|
|
|
(when sh.currentMap
|
|
(sh.update))
|
|
|
|
(when FlxG.keys.justPressed.ESCAPE
|
|
(if (and textInput textInput.hasFocus)
|
|
{
|
|
(set textInput.callback null)
|
|
(hideUI textInput)
|
|
// This part is hacky...
|
|
(set lastUI textInputLabel)
|
|
(hideUI textInputLabel)
|
|
}
|
|
(Sys.exit 0)))
|
|
|
|
// 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 WASD, and the playground camera with arrow keys:
|
|
(var KEYBOARD_SCROLL_SPEED 200)
|
|
|
|
(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.15)
|
|
|
|
(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
|
|
(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))))
|
|
|
|
// don't move the ui camera before ui has been placed -- new UI elements could appear offscreen
|
|
(when (> uiGroup.length 0)
|
|
(unless (and textInput textInput.hasFocus)
|
|
(uiCamera.updateKeyControl
|
|
elapsed
|
|
KEYBOARD_SCROLL_SPEED
|
|
// TODO support dvorak
|
|
->{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)
|
|
|
|
(prop &mut :FlxText textInputLabel null)
|
|
(prop &mut :FlxInputText textInput null)
|
|
|
|
(method :Void enterText [prompt resolve maxLength]
|
|
(set textInputLabel (new FlxText 0 0 300 prompt TEXT_SIZE))
|
|
(showUI textInputLabel)
|
|
(set textInput (new FlxInputText 0 0 300 "" 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]
|
|
(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<Entry> 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)))
|
|
|
|
(var SCROLL_BOUND_MARGIN 200)
|
|
(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)
|
|
|
|
(FlxG.camera.calculateScrollBounds entryGroup SCROLL_BOUND_MARGIN))
|
|
|
|
(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))))
|
|
|
|
(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" "|") TEXT_SIZE)]
|
|
(showUI messageText)))
|
|
|
|
(method :Void clearUI []
|
|
(when textInput
|
|
(set textInput.callback null))
|
|
(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)))
|
|
|
|
(method :Void onSelectionChanged [:Array<Entry> selectedEntries :Array<Entry> lastSelectedEntries]
|
|
(doFor e (selectedEntries.concat lastSelectedEntries)
|
|
(whenLet [sprite (dictGet spriteSystem.sprites e.id)]
|
|
(sprite.updateColor)))) |