From 994ffaf9cd3d59b77227e9418f34fd528570baaf Mon Sep 17 00:00:00 2001 From: Nat Quayle Nelson Date: Thu, 2 Mar 2023 07:37:55 -0700 Subject: [PATCH] Overhaul hollywoo & hollywoo-flixel. Close #178 --- .../src/hollywoo_flixel/ActorFlxSprite.hx | 1 + .../src/hollywoo_flixel/ActorFlxSprite.kiss | 10 +- .../src/hollywoo_flixel/Aliases.kiss | 6 + .../src/hollywoo_flixel/FlxDirector.hx | 4 +- .../src/hollywoo_flixel/FlxDirector.kiss | 222 +++++++----------- .../src/hollywoo_flixel/FlxMovie.hx | 33 ++- .../hollywoo_flixel/HollywooFlixelDSL.kiss | 46 +--- .../src/hollywoo_flixel/SceneFlxState.hx | 15 -- .../src/hollywoo_flixel/SceneFlxState.kiss | 36 --- projects/hollywoo/src/hollywoo/Director.hx | 35 ++- .../hollywoo/src/hollywoo/HollywooDSL.kiss | 31 +++ projects/hollywoo/src/hollywoo/Movie.hx | 10 +- projects/hollywoo/src/hollywoo/Movie.kiss | 168 +++++++++---- projects/hollywoo/src/hollywoo/Scene.hx | 19 +- 14 files changed, 334 insertions(+), 302 deletions(-) create mode 100644 projects/hollywoo-flixel/src/hollywoo_flixel/Aliases.kiss delete mode 100644 projects/hollywoo-flixel/src/hollywoo_flixel/SceneFlxState.hx delete mode 100644 projects/hollywoo-flixel/src/hollywoo_flixel/SceneFlxState.kiss diff --git a/projects/hollywoo-flixel/src/hollywoo_flixel/ActorFlxSprite.hx b/projects/hollywoo-flixel/src/hollywoo_flixel/ActorFlxSprite.hx index bfbb37c6..fa5c889c 100644 --- a/projects/hollywoo-flixel/src/hollywoo_flixel/ActorFlxSprite.hx +++ b/projects/hollywoo-flixel/src/hollywoo_flixel/ActorFlxSprite.hx @@ -5,6 +5,7 @@ import kiss.List; import kiss.FuzzyMap; import flixel.FlxSprite; import hollywoo_flixel.FlxMovie; +import hollywoo.Director; typedef AnimationArgs = { frames:Array, diff --git a/projects/hollywoo-flixel/src/hollywoo_flixel/ActorFlxSprite.kiss b/projects/hollywoo-flixel/src/hollywoo_flixel/ActorFlxSprite.kiss index 4eed7acb..df746a99 100644 --- a/projects/hollywoo-flixel/src/hollywoo_flixel/ActorFlxSprite.kiss +++ b/projects/hollywoo-flixel/src/hollywoo_flixel/ActorFlxSprite.kiss @@ -1,11 +1,15 @@ -(prop :FlxStageFacing defaultFacing) +(loadFrom "hollywoo-flixel" "src/hollywoo_flixel/Aliases.kiss") + +(prop :StageFacing defaultFacing) (prop :FuzzyMap animationNames (new FuzzyMap)) -(defNew [&prop :String assetPath &opt frameWidth frameHeight :FlxStageFacing defaultFacing :Map animations] +(prop :Float z 0) + +(defNew [&prop :String assetPath &opt frameWidth frameHeight :StageFacing defaultFacing :Map animations] (super) - (set this.defaultFacing (or defaultFacing Right)) + (set this.defaultFacing (or defaultFacing FacingRight)) (if (and frameWidth frameHeight) (loadGraphic assetPath true frameWidth frameHeight) diff --git a/projects/hollywoo-flixel/src/hollywoo_flixel/Aliases.kiss b/projects/hollywoo-flixel/src/hollywoo_flixel/Aliases.kiss new file mode 100644 index 00000000..2393dc9b --- /dev/null +++ b/projects/hollywoo-flixel/src/hollywoo_flixel/Aliases.kiss @@ -0,0 +1,6 @@ +(defAlias &ident Left "Left") +(defAlias &ident LeftBehind "Left2") +(defAlias &ident Right "Right") +(defAlias &ident RightBehind "Right2") +(defAlias &ident FacingLeft (TowardsPosition "OffScreenLeft")) +(defAlias &ident FacingRight (TowardsPosition "OffScreenRight")) \ No newline at end of file diff --git a/projects/hollywoo-flixel/src/hollywoo_flixel/FlxDirector.hx b/projects/hollywoo-flixel/src/hollywoo_flixel/FlxDirector.hx index 77c4dced..c4e7b17e 100644 --- a/projects/hollywoo-flixel/src/hollywoo_flixel/FlxDirector.hx +++ b/projects/hollywoo-flixel/src/hollywoo_flixel/FlxDirector.hx @@ -7,6 +7,7 @@ import flixel.FlxSprite; import flixel.input.actions.FlxAction; import flixel.input.actions.FlxActionManager; import flixel.input.mouse.FlxMouseButton; +import flixel.group.FlxGroup; import flixel.tweens.FlxTween; import hollywoo.Movie; import hollywoo.Scene; @@ -18,6 +19,7 @@ import flixel.system.FlxSound; import flixel.util.FlxTimer; import haxe.Constraints; import kiss_flixel.SpriteTools; +import haxe.ds.Option; @:build(kiss.Kiss.build()) -class FlxDirector implements Director {} +class FlxDirector implements Director {} diff --git a/projects/hollywoo-flixel/src/hollywoo_flixel/FlxDirector.kiss b/projects/hollywoo-flixel/src/hollywoo_flixel/FlxDirector.kiss index e1d2e50c..496aa4c5 100644 --- a/projects/hollywoo-flixel/src/hollywoo_flixel/FlxDirector.kiss +++ b/projects/hollywoo-flixel/src/hollywoo_flixel/FlxDirector.kiss @@ -1,6 +1,10 @@ (prop :FlxActionDigital continueAction) (prop actionManager (new FlxActionManager)) -(prop &mut :Movie movie) +(prop &mut :Movie movie) +(loadFrom "hollywoo-flixel" "src/hollywoo_flixel/Aliases.kiss") + +(prop :Array> spriteLayers []) +(var LAYER_MAX 5) (defNew [] (set continueAction (new FlxActionDigital "Continue" onContinue)) @@ -9,111 +13,62 @@ (continueAction.addMouse LEFT JUST_PRESSED) (actionManager.addAction continueAction) (FlxG.inputs.add actionManager) - (set actionManager.resetOnStateSwitch NONE)) + (set actionManager.resetOnStateSwitch NONE) + + // TODO characters will be in front of every prop layer -- characters need their own group layer + (doFor i (range LAYER_MAX) + (let [g (new FlxTypedGroup)] + (spriteLayers.push g) + (FlxG.state.add g)))) -(prop &mut :SceneFlxState currentState) -(method :Void showScene [:Scene scene :Appearance appearance :Continuation cc] - // Close the last scene state - (when currentState - (currentState.close)) - // TODO on the first appearance, give a super (for some scenes but probably not others... hm....) - (set currentState (cast scene SceneFlxState)) - (set currentState.parent FlxG.state) - (FlxG.state.openSubState currentState) - // Re-show characters in case their actor sprites were moved in different scenes: - (doFor =>name character currentState.characters - (showCharacter character ReAppearance ->:Void {})) +(method :Void showSet [:FlxSprite setSprite :SceneTime time :ScenePerspective perspective :Appearance appearance :Continuation cc] + (setSprite.setGraphicSize FlxG.width) + (when (> setSprite.height FlxG.height) + (setSprite.setGraphicSize 0 FlxG.height)) + (setSprite.updateHitbox) + (setSprite.screenCenter) + (FlxG.state.add setSprite) (cc)) -(method :Void cleanup [] - (FlxG.state.openSubState null)) +(method :Void hideSet [:FlxSprite set :Continuation cc] + (FlxG.state.remove set) + (cc)) -(var STAGE_LEFT_X 150) -(var STAGE_RIGHT_X (- 1280 150)) -(var STAGE_BEHIND_DY -250) -(var ACTOR_Y 500) +(method :Void cleanup [] 0) + +(var STAGE_LEFT_X 150.0) +(var STAGE_RIGHT_X (- FlxG.width STAGE_LEFT_X)) +(var STAGE_BEHIND_DY 250.0) + +(prop :Option autoZConfig (Some (object zPerLayer STAGE_BEHIND_DY frontLayer 0))) + +(var ACTOR_Y 500.0) (var ACTOR_WIDTH 300) -(method :Void showCharacter [:Character character :Appearance appearance :Continuation cc] +(method :Void showCharacter [:Character character :Appearance appearance :Continuation cc] // TODO on the first appearance, show name and description (maybe? also probably not for all?) // TODO also allow for manually defined flipped frames so text on clothing doesn't mirror (set character.actor.flipX ?!(= character.stageFacing character.actor.defaultFacing)) + // All actors same width, display centered on x (character.actor.setGraphicSize ACTOR_WIDTH) (character.actor.updateHitbox) - (ifLet [(FullControl layer rpos) character.stagePosition] - { - (assert (<= 0 layer SceneFlxState.LAYER_MAX) "Layer $layer is out of range 0-$SceneFlxState.LAYER_MAX") - (SpriteTools.scaleStampOn character.actor (canvas) rpos) - (let [[x y] (SpriteTools.positionOn character.actor (canvas) rpos)] - (set character.actor.x x) - (set character.actor.y y) - (if (= layer SceneFlxState.LAYER_MAX) - // In front of everything: - (currentState.add character.actor) - (.add (nth currentState.spriteLayers layer) character.actor))) - } - { - (set character.actor.x - (- (case character.stagePosition - (Left - (set currentState.actorOnLeft character.actor) - (set currentState.characterOnLeft character) - STAGE_LEFT_X) - (LeftBehind - STAGE_LEFT_X) - (Right - (set currentState.actorOnRight character.actor) - (set currentState.characterOnRight character) - STAGE_RIGHT_X) - (RightBehind - STAGE_RIGHT_X) - (otherwise (throw "unsupported stage position"))) - (/ character.actor.width 2))) - (set character.actor.y ACTOR_Y) - (let [bottom (+ character.actor.y character.actor.height)] - (when (> bottom 720) - (-= character.actor.y (- bottom 720)))) - (let [&mut reAddFront false] - (case character.stagePosition - ((or LeftBehind RightBehind) - (set reAddFront true) - (+= character.actor.y STAGE_BEHIND_DY)) - (otherwise)) - (when reAddFront - (when currentState.actorOnLeft (currentState.remove currentState.actorOnLeft)) - (when currentState.actorOnRight (currentState.remove currentState.actorOnRight))) - (currentState.add character.actor) - (when reAddFront - (when currentState.actorOnLeft (currentState.add currentState.actorOnLeft)) - (when currentState.actorOnRight (currentState.add currentState.actorOnRight)))) - }) + (set character.actor.x + (- character.stagePosition.x + (/ character.actor.width 2))) + (set character.actor.y character.stagePosition.y) + // Bump sprites up from the bottom if they're too tall + (let [bottom (+ character.actor.y character.actor.height)] + (when (> bottom FlxG.height) + (-= character.actor.y (- bottom FlxG.height)))) + // Display with y adjusted by z: + (-= character.actor.y character.stagePosition.z) + (FlxG.state.add character.actor) (cc)) -(method :Void hideCharacter [:Character character :Continuation cc] - (when (= currentState.actorOnLeft character.actor) - (set currentState.actorOnLeft null) - (set currentState.characterOnLeft null)) - (when (= currentState.actorOnRight character.actor) - (set currentState.actorOnRight null) - (set currentState.characterOnRight null)) - (currentState.remove character.actor) +(method :Void hideCharacter [:Character character :Continuation cc] + (FlxG.state.remove character.actor) (cc)) -(method :Void moveCharacter [:Character character toPos toFacing :Continuation cc] - // Directors don't have to change the character, but FlxDirector does because that state determines - // where actors are drawn in showCharacter: - (set character.stagePosition toPos) - (set character.stageFacing toFacing) - (hideCharacter character - ->:Void (showCharacter character ReAppearance cc))) - -(method :Void swapCharacters [:Character a :Character b :Continuation cc] - (let [noCC ->:Void {} - tempStagePos a.stagePosition - tempStageFacing a.stageFacing] - (moveCharacter a b.stagePosition b.stageFacing noCC) - (moveCharacter b tempStagePos tempStageFacing cc))) - (prop &mut :Null nextCC) (method onContinue [:FlxActionDigital continueAction] (whenLet [cc nextCC] @@ -134,23 +89,24 @@ (prop &mut :FlxSprite titleCard null) (method :Void showTitleCard [:Array text :Continuation cc] (set titleCard (new FlxSprite)) - (titleCard.makeGraphic 1280 720 FlxColor.BLACK true) + (titleCard.makeGraphic FlxG.width FlxG.height FlxColor.BLACK true) (SpriteTools.writeOnSprite (text.shift) TITLE_SIZE titleCard (object x (Percent 0.5) y (Pixels TITLE_Y))) (localVar &mut subtitleY (+ TITLE_Y TITLE_SIZE TITLE_MARGIN)) (doFor subtitle text (SpriteTools.writeOnSprite subtitle SUBTITLES_SIZE titleCard (object x (Percent 0.5) y (Pixels subtitleY))) (+= subtitleY SUBTITLES_SIZE SUBTITLES_MARGIN)) - (currentState.add titleCard) + (FlxG.state.add titleCard) // Allow skipping (startWaitForInput cc)) (method :Void hideTitleCard [] - (currentState.remove titleCard)) + (FlxG.state.remove titleCard)) (var DIALOG_X 300) -(var DIALOG_WIDTH (- 1280 ACTOR_WIDTH ACTOR_WIDTH)) (var DIALOG_Y 500) -(var DIALOG_HEIGHT (- 720 DIALOG_Y)) +// Make these aliases so they get FlxG.width's current value when queried (not 0 at start time) +(defAlias &ident DIALOG_WIDTH (- FlxG.width ACTOR_WIDTH ACTOR_WIDTH)) +(defAlias &ident DIALOG_HEIGHT (- FlxG.height DIALOG_Y)) // TODO these could be customizable to the Actor, wrylies, etc. (var DIALOG_BOX_COLOR FlxColor.BLACK) (var DIALOG_COLOR FlxColor.WHITE) @@ -160,29 +116,17 @@ (var &mut :FlxText dialogText) (var &mut :FlxText speakerNameText) -(method showDialog [:String speakerName :SpeechType type :String wryly :String text :Continuation cc] +(method showDialog [:String speakerName :SpeechType type :String wryly :String text :Continuation cc] // TODO handle text messages, wrylies, off-screen, from-phone, etc. via (case type) // TODO attribute on-screen dialogue to the character's stageposition - // When an actor is associated with the line, check for an animation matching the wryly // TODO allow sounds for wrylies, like the dispatch click (localVar &mut nameOnRight false) (case type ((OnScreen character) - (case character.stagePosition - ((or Right RightBehind) (set nameOnRight true)) - (otherwise)) - // If the character is behind another character, swap them so the speaker is front - (case character.stagePosition - (LeftBehind - (if currentState.characterOnLeft - (swapCharacters character currentState.characterOnLeft ->:Void {})) - (moveCharacter character Left character.stageFacing ->:Void {})) - (RightBehind - (if currentState.characterOnRight - (swapCharacters character currentState.characterOnRight ->:Void {})) - (moveCharacter character Right character.stageFacing ->:Void {})) - (otherwise)) + (when (> character.stagePosition.x (/ FlxG.width 2)) + (set nameOnRight true)) + // When an actor is associated with the line, check for an animation matching the wryly (let [actor (the ActorFlxSprite character.actor)] (if wryly (actor.playAnimation wryly) @@ -198,23 +142,23 @@ (set dialogBox (new FlxSprite DIALOG_X DIALOG_Y)) (dialogBox.makeGraphic DIALOG_WIDTH DIALOG_HEIGHT DIALOG_BOX_COLOR)) - (currentState.add dialogBox) + (FlxG.state.add dialogBox) (dialogBox.revive) // show the dialog (unless dialogText (set dialogText (new FlxText DIALOG_X DIALOG_Y DIALOG_WIDTH "" DIALOG_SIZE))) - (currentState.add dialogText) + (FlxG.state.add dialogText) (set dialogText.text text) // TODO actually page through the dialog instead of sizing it down? // ^ though that doesn't work automatically with VO unless individual word times are kept in the json // (which would be really clunky) (set dialogText.size DIALOG_SIZE) - (while (< 720 (+ dialogText.y dialogText.height)) + (while (< FlxG.height (+ dialogText.y dialogText.height)) (-= dialogText.size 6)) // show the speaker name (unless speakerNameText (set speakerNameText (new FlxText DIALOG_X DIALOG_Y 0 "" DIALOG_SIZE))) - (currentState.add speakerNameText) + (FlxG.state.add speakerNameText) (if speakerName { (set speakerNameText.text "${speakerName}:") @@ -277,12 +221,12 @@ (var PROP_MAX_WIDTH 500) (method :Void quickShowPropOnScreen [:FlxSprite prop :FlxScreenPosition position :Continuation cc] - (let [left (/ 1280 6) - right (- 1280 left) - upper (/ 720 6) - lower (- 720 upper) - centerX (/ 1280 2) - centerY (/ 720 2) + (let [left (/ FlxG.width 6) + right (- FlxG.width left) + upper (/ FlxG.height 6) + lower (- FlxG.height upper) + centerX (/ FlxG.width 2) + centerY (/ FlxG.height 2) [x y] (case position (Center [centerX centerY]) @@ -317,7 +261,7 @@ (-= prop.y (- propBottom FlxG.height)))) (otherwise))) - (currentState.add prop))) + (FlxG.state.add prop))) (cc)) (prop &mut :FlxSprite _canvas null) @@ -328,15 +272,15 @@ _canvas) (method :Void smartShowPropOnScreen [:FlxSprite prop :Int layer :RelativePosition rpos :Continuation cc] - (assert (<= 0 layer SceneFlxState.LAYER_MAX) "Layer $layer is out of range 0-$SceneFlxState.LAYER_MAX") + (assert (<= 0 layer LAYER_MAX) "Layer $layer is out of range 0-$LAYER_MAX") (SpriteTools.scaleStampOn prop (canvas) rpos) (let [[x y] (SpriteTools.positionOn prop (canvas) rpos)] (set prop.x x) (set prop.y y) - (if (= layer SceneFlxState.LAYER_MAX) + (if (= layer LAYER_MAX) // In front of everything: - (currentState.add prop) - (.add (nth currentState.spriteLayers layer) prop))) + (FlxG.state.add prop) + (.add (nth spriteLayers layer) prop))) (cc)) (method :Void showPropOnScreen [:FlxSprite prop :FlxScreenPosition position :Continuation cc] @@ -346,8 +290,8 @@ (quickShowPropOnScreen prop position cc))) (method :Void hideProp [:FlxSprite prop cc] - (currentState.remove prop) - (doFor layer currentState.spriteLayers + (FlxG.state.remove prop) + (doFor layer spriteLayers (layer.remove prop)) (cc)) @@ -360,19 +304,19 @@ (prop &mut :FlxSprite blackBG null) (method :Void showBlackScreen [] (set blackBG (new FlxSprite)) - (blackBG.makeGraphic 1280 720 FlxColor.BLACK true) - (currentState.add blackBG)) + (blackBG.makeGraphic FlxG.width FlxG.height FlxColor.BLACK true) + (FlxG.state.add blackBG)) (method :Void hideBlackScreen [] - (currentState.remove blackBG)) + (FlxG.state.remove blackBG)) // TODO maybe credits need their own substate so an after-credits scene could be done. // currently the bg will cover whatever the final scene was. (method :Void rollCredits [:Array credits cc] (localVar bg (new FlxSprite)) - (bg.makeGraphic 1280 720 FlxColor.BLACK true) - (currentState.add bg) - (localVar &mut textY 720) + (bg.makeGraphic FlxG.width FlxG.height FlxColor.BLACK true) + (FlxG.state.add bg) + (localVar &mut textY FlxG.height) (var oneColSize 64) (var twoColSize 48) (var threeColSize 32) @@ -392,9 +336,9 @@ ((TwoColumn col1 col2) (let [t1 (_ctext col1 twoColSize) t2 (_ctext col2 twoColSize)] - (set t1.x (- (* 1280 0.3) t1.width (/ twoColumnGap 2))) + (set t1.x (- (* FlxG.width 0.3) t1.width (/ twoColumnGap 2))) (set t1.y textY) - (set t2.x (+ (* 1280 0.3) (/ twoColumnGap 2))) + (set t2.x (+ (* FlxG.width 0.3) (/ twoColumnGap 2))) (set t2.y textY)) (+= textY twoColSize creditMargin)) // Left-justified, centered, right-justified three column lines @@ -406,11 +350,11 @@ (set t1.y textY) (t2.screenCenter) (set t2.y textY) - (set t3.x (- 1280 creditMargin t3.width)) + (set t3.x (- FlxG.width creditMargin t3.width)) (set t3.y textY)) (+= textY threeColSize creditMargin)) (otherwise))) (doFor text creditsText - (currentState.add text) + (FlxG.state.add text) (FlxTween.linearMotion text text.x text.y text.x (- text.y textY) 200 false (object onComplete ->:Void _ (cc))))) \ No newline at end of file diff --git a/projects/hollywoo-flixel/src/hollywoo_flixel/FlxMovie.hx b/projects/hollywoo-flixel/src/hollywoo_flixel/FlxMovie.hx index 0c93c79f..31593701 100644 --- a/projects/hollywoo-flixel/src/hollywoo_flixel/FlxMovie.hx +++ b/projects/hollywoo-flixel/src/hollywoo_flixel/FlxMovie.hx @@ -3,12 +3,13 @@ package hollywoo_flixel; import flixel.FlxState; import flixel.FlxSprite; import flixel.system.FlxSound; +import hollywoo.Director; import hollywoo.Movie; import hollywoo_flixel.ActorFlxSprite; -import hollywoo_flixel.SceneFlxState; import kiss_flixel.SpriteTools; import openfl.Assets; +/* enum FlxStagePosition { Left; Right; @@ -19,11 +20,7 @@ enum FlxStagePosition { // AND don't move the object automatically for any reason FullControl(layer:Int, pos:RelativePosition); } - -enum FlxStageFacing { - Left; - Right; -} +*/ enum FlxScreenPosition { // Shortcuts @@ -42,7 +39,7 @@ enum FlxScreenPosition { /** * Model/controller of a Hollywoo-Flixel film, and main execution script */ -class FlxMovie extends Movie { +class FlxMovie extends Movie { // Think of HollywooFlixelDSL.kiss as the corresponding Kiss file for this class! public function new(director:FlxDirector, ?voiceLinesAssetPath:String) { @@ -50,6 +47,28 @@ class FlxMovie extends Moviename scene scenes - (.destroy (cast scene SceneFlxState)))) + (setSprite.loadGraphic assetPath false 0 0 true) // Load uniquely so we can draw on sets for specific scenes + (newSet name setSprite))) (method newFlxSound [name path &opt :Float volume] (set volume (or volume 1)) @@ -56,41 +52,11 @@ (localVar buttonsPerColumn 25) (doFor [num label] (enumerate (sort (collect (runners.keys)))) (let [runner (dictGet runners label) - b (new flixel.ui.FlxButton 0 buttonY label ->{(.remove (cast (_currentScene) SceneFlxState) buttons)(runner)})] + b (new flixel.ui.FlxButton 0 buttonY label ->{(FlxG.state.remove buttons)(runner)})] (let [column (Std.int (/ num buttonsPerColumn))] (set b.x (* b.width column))) (buttons.add b)) (+= buttonY 20) (when (= (- buttonsPerColumn 1) (% num buttonsPerColumn)) (set buttonY 0))) - (.add (cast (_currentScene) SceneFlxState) buttons))) - -(defMacro withProp [propKey name &body body] - `(let [,name (dictGet props ,propKey)] - ,@body - (cc))) - -// like withProp, but you promise to call CC yourself in the body: -(defMacro withPropCC [propKey name &body body] - `(let [,name (dictGet props ,propKey)] - ,@body)) - -(defMacro withActor [actorKey name &body body] - `(let [,name (dictGet actors ,actorKey)] - ,@body - (cc))) - -// like withActor, but you promise to call CC yourself in the body: -(defMacro withActorCC [actorKey name &body body] - `(let [,name (dictGet actors ,actorKey)] - ,@body)) - -(defMacro withSet [sceneKey name &body body] - `(let [,name .setSprite (cast (dictGet scenes ,sceneKey) SceneFlxState)] - ,@body - (cc))) - -// like withSet, but you promise to call CC yourself in the body: -(defMacro withSetCC [sceneKey name &body body] - `(let [,name .setSprite (cast (dictGet scenes ,sceneKey) SceneFlxState)] - ,@body)) \ No newline at end of file + (FlxG.state.add buttons))) \ No newline at end of file diff --git a/projects/hollywoo-flixel/src/hollywoo_flixel/SceneFlxState.hx b/projects/hollywoo-flixel/src/hollywoo_flixel/SceneFlxState.hx deleted file mode 100644 index d25f99a9..00000000 --- a/projects/hollywoo-flixel/src/hollywoo_flixel/SceneFlxState.hx +++ /dev/null @@ -1,15 +0,0 @@ -package hollywoo_flixel; - -import kiss.Prelude; -import kiss.List; -import kiss.FuzzyMap; -import flixel.FlxSubState; -import flixel.FlxState; -import flixel.FlxSprite; -import flixel.group.FlxGroup; -import flixel.FlxG; -import hollywoo.Scene; -import hollywoo_flixel.FlxMovie; - -@:build(kiss.Kiss.build()) -class SceneFlxState extends FlxSubState {} diff --git a/projects/hollywoo-flixel/src/hollywoo_flixel/SceneFlxState.kiss b/projects/hollywoo-flixel/src/hollywoo_flixel/SceneFlxState.kiss deleted file mode 100644 index 53f02efb..00000000 --- a/projects/hollywoo-flixel/src/hollywoo_flixel/SceneFlxState.kiss +++ /dev/null @@ -1,36 +0,0 @@ -// Track which actors are in FRONT of the stage so actors can appear behind them -(prop &mut :ActorFlxSprite actorOnLeft null) -(prop &mut :Character characterOnLeft null) -(prop &mut :ActorFlxSprite actorOnRight null) -(prop &mut :Character characterOnRight null) - -// Track props in an arbitrary number of layers -(prop :Array> spriteLayers []) -(var LAYER_MAX 5) - -(defNew [&prop :FlxSprite setSprite &prop :SceneTime time &prop :ScenePerspective perspective] - [ - &mut :FlxState parent null - :FuzzyMap> characters (new FuzzyMap>) - :Map propsOnScreen (new Map) - ] - (super) - (add setSprite) - // TODO characters will be in front of every prop layer -- characters need their own group layer - (doFor i (range LAYER_MAX) - (let [g (new FlxTypedGroup)] - (spriteLayers.push g) - (add g)))) - -(method &override :Void create [] - (super.create) - (setSprite.setGraphicSize FlxG.width) - (when (> setSprite.height FlxG.height) - (setSprite.setGraphicSize 0 FlxG.height)) - (setSprite.updateHitbox) - (setSprite.screenCenter)) - -(method &override :Void update [:Float elapsed] - (when parent - (parent.update elapsed)) - (super.update elapsed)) \ No newline at end of file diff --git a/projects/hollywoo/src/hollywoo/Director.hx b/projects/hollywoo/src/hollywoo/Director.hx index 1ad58e5c..bff15bf3 100644 --- a/projects/hollywoo/src/hollywoo/Director.hx +++ b/projects/hollywoo/src/hollywoo/Director.hx @@ -2,6 +2,7 @@ package hollywoo; import hollywoo.Scene; import hollywoo.Movie; +import haxe.ds.Option; enum Appearance { FirstAppearance; @@ -10,13 +11,31 @@ enum Appearance { typedef Continuation = Void -> Void; -interface Director { - var movie(default, default):Movie; - function showScene(scene:Scene, appearance:Appearance, cc:Continuation):Void; - function showCharacter(character:Character, appearance:Appearance, cc:Continuation):Void; - function hideCharacter(character:Character, cc:Continuation):Void; - function moveCharacter(character:Character, toPos:StagePosition, toFacing:StageFacing, cc:Continuation):Void; - function swapCharacters(a:Character, b:Character, cc:Continuation):Void; +typedef StagePosition = { + x:Float, + y:Float, + z:Float, +}; + +enum StageFacing { + TowardsCharacter(name:String); + AwayFromCharacter(name:String); + TowardsPosition(name:String); + AwayFromPosition(name:String); +} + +typedef AutoZConfig = { + zPerLayer:Float, + frontLayer:Int +}; + +interface Director, ScreenPosition, Actor, Sound, Song, Prop, VoiceTrack> { + var movie(default, default):Movie; + var autoZConfig(default,null):Option; + function showSet(set:Set, time:SceneTime, perspective:ScenePerspective, appearance:Appearance, cc:Continuation):Void; + function hideSet(set:Set, cc:Continuation):Void; + function showCharacter(character:Character, appearance:Appearance, cc:Continuation):Void; + function hideCharacter(character:Character, cc:Continuation):Void; function playSound(sound:Sound, volumeMod:Float, waitForEnd:Bool, cc:Continuation):Void; function stopSound(sound:Sound):Void; function playSong(song:Song, volumeMod:Float, loop:Bool, waitForEnd:Bool, cc:Continuation):Void; @@ -25,7 +44,7 @@ interface Director, wryly:String, dialog:String, cc:Continuation):Void; + function showDialog(speakerName:String, type:SpeechType, wryly:String, dialog:String, cc:Continuation):Void; function hideDialog():Void; function showTitleCard(text:Array, cc:Continuation):Void; function hideTitleCard():Void; diff --git a/projects/hollywoo/src/hollywoo/HollywooDSL.kiss b/projects/hollywoo/src/hollywoo/HollywooDSL.kiss index b4e820b7..7203b991 100644 --- a/projects/hollywoo/src/hollywoo/HollywooDSL.kiss +++ b/projects/hollywoo/src/hollywoo/HollywooDSL.kiss @@ -1,3 +1,34 @@ +(defMacro withProp [propKey name &body body] + `(let [,name (dictGet props ,propKey)] + ,@body + (cc))) + +// like withProp, but you promise to call CC yourself in the body: +(defMacro withPropCC [propKey name &body body] + `(let [,name (dictGet props ,propKey)] + ,@body)) + +(defMacro withActor [actorKey name &body body] + `(let [,name (dictGet actors ,actorKey)] + ,@body + (cc))) + +// like withActor, but you promise to call CC yourself in the body: +(defMacro withActorCC [actorKey name &body body] + `(let [,name (dictGet actors ,actorKey)] + ,@body)) + +// Do something with the given scene's instance of its set +(defMacro withSceneSet [sceneKey name &body body] + `(let [,name .set (dictGet scenes ,sceneKey)] + ,@body + (cc))) + +// like withSceneSet, but you promise to call CC yourself in the body: +(defMacro withSceneSetCC [sceneKey name &body body] + `(let [,name .set (dictGet scenes ,sceneKey)] + ,@body)) + // When this file is loaded, all expressions in (preload <...>) will be collected. When (end) is called, they will // be injected into a method called (doPreload). // This allows assets to be declared in Hollywoo files where they first appear, but still loaded before execution starts. diff --git a/projects/hollywoo/src/hollywoo/Movie.hx b/projects/hollywoo/src/hollywoo/Movie.hx index b5c793a0..3a0871e9 100644 --- a/projects/hollywoo/src/hollywoo/Movie.hx +++ b/projects/hollywoo/src/hollywoo/Movie.hx @@ -13,6 +13,10 @@ import uuid.Uuid; using kiss.FuzzyMapTools; +typedef Cloneable = { + function clone():T; +} + enum DelayHandling { Auto; AutoWithSkip; @@ -37,8 +41,4 @@ enum CreditsLine { * Model/controller of a Hollywoo film, and main execution script */ @:build(kiss.Kiss.build()) -class Movie extends AsyncEmbeddedScript { - // TODO for some reason this wasn't working when declared in Movie.kiss: - // Mutable representation of frames in time: - var scenes:FuzzyMap> = new FuzzyMap>(); -} +class Movie, ScreenPosition, Actor, Sound, Song, Prop, VoiceTrack> extends AsyncEmbeddedScript {} diff --git a/projects/hollywoo/src/hollywoo/Movie.kiss b/projects/hollywoo/src/hollywoo/Movie.kiss index 37791397..7f3a1b21 100644 --- a/projects/hollywoo/src/hollywoo/Movie.kiss +++ b/projects/hollywoo/src/hollywoo/Movie.kiss @@ -1,3 +1,6 @@ +(defMacro makeCC [&body b] + `->:Void {,@b}) + // This file is designed to be loaded again by subclasses, with macroVar "subclass" set (#unless subclass @@ -14,9 +17,8 @@ (prop &mut :DelayHandling delayHandling AutoWithSkip) - // TODO for some reason this won't work when declared in Kiss syntax: // Mutable representation of frames in time: - // var scenes:Map> = []; + (prop :FuzzyMap> scenes (new FuzzyMap>)) (prop :FuzzyMap shownScenes (new FuzzyMap)) (prop :FuzzyMap shownCharacters (new FuzzyMap)) @@ -40,7 +42,8 @@ (method :Void showDialog [actorName dialogType wryly text cc] (when intercutMap (whenLet [sceneForActor (try (dictGet intercutMap actorName) (catch [e] null))] - (setScene sceneForActor ->{}))) + (unless (= sceneForActor sceneKey) + (setScene sceneForActor ->{})))) (let [cc ->:Void {(director.hideDialog) (cc)} &mut skipCC cc] @@ -83,11 +86,66 @@ [])] (dictSet voiceLines "$actorName $key" (objectWith [start line.start end line.end] trackKey alts)))))) + (method _ccForEach <>[T] [:Iterable collection :(T,Continuation)->Void do_ :Continuation finalCC] + (let [:Iterator iter (collection.iterator)] + (withFunctions + [ + (:Void doNext [] + (if (iter.hasNext) + (do_ (iter.next) doNext) + (finalCC))) + ] + (doNext)))) + + (method _hideCurrentScene [:Continuation cc] + (if sceneKey + // hide current scene background + (let [currentScene (dictGet scenes sceneKey)] + (director.hideSet currentScene.set + (makeCC + // hide current scene characters + (_ccForEach + currentScene.characters + ->[:Character c :Continuation cc] + (director.hideCharacter c cc) + (makeCC + // hide current scene props, etc. + (_ccForEach + currentScene.propsOnScreen + ->[:ScreenProp p :Continuation cc] + (director.hideProp p.prop cc) + cc)))))) + (cc))) + + (method _showScene [:Scene scene :Appearance appearance :Continuation cc] + // Show current scene background + (director.showSet scene.set scene.time scene.perspective appearance + (makeCC + // Show current scene characters + (_ccForEach + (object iterator ->(scene.characters.keys)) + ->[:String key :Continuation cc] + (director.showCharacter (dictGet scene.characters key) (appearanceFlag shownCharacters key) cc) + (makeCC + // hide current scene props, etc. + (_ccForEach + scene.propsOnScreen + ->[:ScreenProp p :Continuation cc] + (director.showPropOnScreen p.prop p.screenPosition cc) + cc)))))) + (defNew [ // "View" in the Model-View-Controller architecture: - &prop :Director director + &prop :Director director &opt :String voiceLinesJson + &opt :String positionsJson + ] + [ + :Map stagePositions + (if positionsJson + (haxe.Json.parse (sys.io.File.getContent positionsJson)) + (new Map)) ] (set director.movie this) @@ -99,7 +157,7 @@ // Some real magic happens here. This macro defines a method, AND a reader macro // for calling it with cc passed automatically if cc is an argument. - +// GOTCHA: DO NOT use (method) directly in this file!! (defMacro hollywooMethod [nameAndType canSkip argList &builder b &body body] (let [args (expList argList) numArgs args.length @@ -162,25 +220,24 @@ (dictSet scenes name (objectWith [ set - (dictGet sets setKey) + (.clone (dictGet sets setKey)) characters - (new FuzzyMap>) + (new FuzzyMap>) propsOnScreen - (new FuzzyMap) + (new FuzzyMap>) ] time perspective))) -(hollywooMethod newScene true [name :Scene scene] - (assert isLoading) - (dictSet scenes name scene)) - (hollywooMethod setScene false [name :Continuation cc] - (set sceneKey name) - (director.showScene - (dictGet scenes name) - (appearanceFlag shownScenes name) - cc)) + (_hideCurrentScene + (makeCC + (set sceneKey name) + (_showScene + (dictGet scenes name) + (appearanceFlag shownScenes name) + cc)))) + (hollywooMethod newSound true [name :Sound s] (assert isLoading) @@ -221,38 +278,62 @@ (assert isLoading) (dictSet actors name actor)) -(hollywooMethod addCharacter false [actorName :StagePosition position :StageFacing facing :Continuation cc] - (let [character (object stagePosition position stageFacing facing actor (dictGet actors actorName))] - (dictSet .characters (_currentScene) actorName character) - (director.showCharacter - character - (appearanceFlag shownCharacters actorName) - cc))) +(hollywooMethod autoZProcess false [:StagePosition position :Continuation cc] + // handle auto z recursively + (ifLet [(Some (objectWith zPerLayer frontLayer)) director.autoZConfig] + { + (doFor =>name otherCharacter .characters (_currentScene) + (when (and (= position.x otherCharacter.stagePosition.x) (= position.y otherCharacter.stagePosition.y) (= position.z otherCharacter.stagePosition.z)) + (moveCharacter name (object x position.x y position.y z (+ otherCharacter.stagePosition.z zPerLayer)) otherCharacter.stageFacing cc) + (return))) + (cc) + } + (cc))) + +(hollywooMethod addCharacter false [actorName :Dynamic position :StageFacing facing :Continuation cc] + (let [position (typeCase [position] + ([:String pKey] (dictGet stagePositions pKey)) + (otherwise position)) + character (object stagePosition position stageFacing facing actor (dictGet actors actorName))] + (autoZProcess position + (makeCC + (dictSet .characters (_currentScene) actorName character) + (director.showCharacter + character + (appearanceFlag shownCharacters actorName) + cc))))) (hollywooMethod removeCharacter false [actorName :Continuation cc] (let [c (dictGet .characters (_currentScene) actorName)] (.remove .characters (_currentScene) actorName) (director.hideCharacter c cc))) -(hollywooMethod moveCharacter false [actorName :StagePosition newPosition :StageFacing newFacing :Continuation cc] - (let [c (dictGet .characters (_currentScene) actorName)] - (director.moveCharacter c newPosition newFacing ->:Void { - (set c.stagePosition newPosition) - (set c.stageFacing newFacing) - (cc) - }))) +// INSTANTLY move a character: +(hollywooMethod moveCharacter false [actorName :Dynamic newPosition :StageFacing newFacing :Continuation cc] + (let [newPosition (typeCase [newPosition] + ([:String pKey] (dictGet stagePositions pKey)) + (otherwise newPosition))] + (removeCharacter actorName + (makeCC + (addCharacter actorName newPosition newFacing cc))))) +// INSTANTLY swap characters (hollywooMethod swapCharacters false [actorNameA actorNameB :Continuation cc] + // remove both, then re-add both, so they don't trigger + // cascading auto z adjustments on top of each other: (let [a (dictGet .characters (_currentScene) actorNameA) - b (dictGet .characters (_currentScene) actorNameB)] - (director.swapCharacters a b ->:Void - (let [tempStagePos a.stagePosition - tempStageFacing a.stageFacing] - (set a.stagePosition b.stagePosition) - (set a.stageFacing b.stageFacing) - (set b.stagePosition tempStagePos) - (set b.stageFacing tempStageFacing) - (cc))))) + asp a.stagePosition + asf a.stageFacing + b (dictGet .characters (_currentScene) actorNameB) + bsp b.stagePosition + bsf b.stageFacing] + (removeCharacter actorNameA + (makeCC + (removeCharacter actorNameB + (makeCC + (addCharacter actorNameA bsp bsf + (makeCC + (addCharacter actorNameB asp asf cc))))))))) // TODO moveCharacter remove them, add them to another scene // TODO moveCharacterAndFollow remove them, add them to another scene, set that the scene @@ -262,10 +343,13 @@ (dictSet props name prop)) (hollywooMethod addPropToScreen false [name :ScreenPosition position :Continuation cc] - (dictSet .propsOnScreen (_currentScene) name (dictGet props name)) - (director.showPropOnScreen (dictGet props name) position cc)) + (let [prop (dictGet props name)] + (dictSet .propsOnScreen (_currentScene) name (object screenPosition position prop prop)) + (director.showPropOnScreen prop position cc))) (hollywooMethod removeProp false [name :Continuation cc] + (.remove .propsOnScreen (_currentScene) name) + // TODO (propsOnStage.remove name) (director.hideProp (dictGet props name) cc)) // Dialogue: diff --git a/projects/hollywoo/src/hollywoo/Scene.hx b/projects/hollywoo/src/hollywoo/Scene.hx index 58864492..89ab499f 100644 --- a/projects/hollywoo/src/hollywoo/Scene.hx +++ b/projects/hollywoo/src/hollywoo/Scene.hx @@ -1,6 +1,8 @@ package hollywoo; import kiss.FuzzyMap; +import hollywoo.Director; +import hollywoo.Movie; enum SceneTime { Morning; @@ -15,26 +17,31 @@ enum ScenePerspective { Mixed; } -typedef Character = { +typedef Character = { stagePosition:StagePosition, stageFacing:StageFacing, actor:Actor }; -enum SpeechType { +enum SpeechType { Super; OffScreen(actor:Actor); VoiceOver(actor:Actor); TextMessage(actor:Actor); FromPhone(actor:Actor); - OnScreen(character:Character); + OnScreen(character:Character); Custom(type:String, actor:Actor, args:Dynamic); } -typedef Scene = { +typedef ScreenProp = { + screenPosition:ScreenPosition, + prop:Prop +}; + +typedef Scene, ScreenPosition, Actor, Prop> = { set:Set, - characters:FuzzyMap>, - propsOnScreen:FuzzyMap, + characters:FuzzyMap>, + propsOnScreen:FuzzyMap>, // TODO props on stage time:SceneTime, perspective:ScenePerspective