Files
kiss-vscode/projects/hollywoo-flixel/src/hollywoo_flixel/FlxDirector.kiss

313 lines
13 KiB
Plaintext

(prop :FlxActionDigital continueAction)
(prop actionManager (new FlxActionManager))
(prop &mut :Movie<String,FlxStagePosition,FlxStageFacing,FlxScreenPosition,ActorFlxSprite,FlxSound,String,FlxSprite,FlxSound> movie)
(defNew []
(set continueAction (new FlxActionDigital "Continue" onContinue))
// TODO allow configuring continue keys -- any key, specifically mapped keys, etc.
(continueAction.addKey SPACE JUST_PRESSED)
(continueAction.addMouse LEFT JUST_PRESSED)
(actionManager.addAction continueAction)
(FlxG.inputs.add actionManager)
(set actionManager.resetOnStateSwitch NONE))
(prop &mut :SceneFlxState currentState)
(method :Void showScene [:Scene<String,FlxStagePosition,FlxStageFacing,FlxScreenPosition,ActorFlxSprite,FlxSprite> 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 {}))
(cc))
(method :Void cleanup []
(FlxG.state.openSubState null))
(var STAGE_LEFT_X 150)
(var STAGE_RIGHT_X (- 1280 150))
(var STAGE_BEHIND_DY -250)
(var ACTOR_Y 500)
(var ACTOR_WIDTH 300)
(method :Void showCharacter [:Character<FlxStagePosition,FlxStageFacing,ActorFlxSprite> 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))
(character.actor.setGraphicSize ACTOR_WIDTH)
(character.actor.updateHitbox)
(set character.actor.x
(- (case character.stagePosition
(Left
(set currentState.actorOnLeft character.actor)
STAGE_LEFT_X)
(LeftBehind
STAGE_LEFT_X)
(Right
(set currentState.actorOnRight character.actor)
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))))
(cc))
(method :Void hideCharacter [:Character<FlxStagePosition,FlxStageFacing,ActorFlxSprite> character :Continuation cc]
(when (= currentState.actorOnLeft character.actor)
(set currentState.actorOnLeft null))
(when (= currentState.actorOnRight character.actor)
(set currentState.actorOnRight null))
(currentState.remove character.actor)
(cc))
(method :Void moveCharacter [:Character<FlxStagePosition,FlxStageFacing,ActorFlxSprite> character fromPos fromFacing toPos toFacing :Continuation cc]
(hideCharacter character
->:Void (showCharacter character ReAppearance cc)))
(prop &mut :Null<Continuation> nextCC)
(method onContinue [:FlxActionDigital continueAction]
(whenLet [cc nextCC]
(set nextCC null)
(cc)))
(method :Void startWaitForInput [:Continuation cc]
(set nextCC cc))
(method :Void stopWaitForInput []
(set nextCC null))
(var TITLE_Y 240)
(var TITLE_SIZE 72)
(var TITLE_MARGIN 100)
(var SUBTITLES_MARGIN 30)
(var SUBTITLES_SIZE 48)
(prop &mut :FlxSprite titleCard null)
(method :Void showTitleCard [:Array<String> text :Continuation cc]
(set titleCard (new FlxSprite))
(titleCard.makeGraphic 1280 720 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)
// Allow skipping
(startWaitForInput cc))
(method :Void hideTitleCard []
(currentState.remove titleCard))
(var DIALOG_X 300)
(var DIALOG_WIDTH (- 1280 ACTOR_WIDTH ACTOR_WIDTH))
(var DIALOG_Y 500)
(var DIALOG_HEIGHT (- 720 DIALOG_Y))
// TODO these could be customizable to the Actor, wrylies, etc.
(var DIALOG_BOX_COLOR FlxColor.BLACK)
(var DIALOG_COLOR FlxColor.WHITE)
(var DIALOG_SIZE 24)
(var &mut :FlxSprite dialogBox)
(var &mut :FlxText dialogText)
(var &mut :FlxText speakerNameText)
(method showDialog [:String speakerName :SpeechType<FlxStagePosition,FlxStageFacing,ActorFlxSprite> 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 the actor is in the scene, check for an animation matching the wryly
// TODO allow sounds for wrylies, like the dispatch click
(localVar &mut nameOnRight false)
(doFor =>actorName character currentState.characters
(when (= actorName speakerName)
(case character.stagePosition
((or Right RightBehind) (set nameOnRight true))
(otherwise))
(let [actor (the ActorFlxSprite character.actor)]
(if wryly
(actor.playAnimation wryly)
(actor.playAnimation "neutral")))))
// Make a dialog box
(unless dialogBox
(set dialogBox (new FlxSprite DIALOG_X DIALOG_Y))
(dialogBox.makeGraphic DIALOG_WIDTH DIALOG_HEIGHT DIALOG_BOX_COLOR))
(currentState.add dialogBox)
(dialogBox.revive)
// show the dialog
(unless dialogText
(set dialogText (new FlxText DIALOG_X DIALOG_Y DIALOG_WIDTH "" DIALOG_SIZE)))
(currentState.add dialogText)
(set dialogText.text text)
// TODO actually page through the dialog instead of sizing it down?
(set dialogText.size DIALOG_SIZE)
(while (< 720 (+ 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)
(if speakerName
{
(set speakerNameText.text "${speakerName}:")
(set speakerNameText.x (if nameOnRight (- (+ DIALOG_X DIALOG_WIDTH) speakerNameText.fieldWidth) DIALOG_X))
(speakerNameText.revive)
(set dialogText.y (+ DIALOG_Y speakerNameText.height))
}
(set dialogText.y DIALOG_Y))
(dialogText.revive)
(startWaitForInput cc))
(method :Void hideDialog []
(dialogText.kill)
(speakerNameText.kill)
(dialogBox.kill))
(method :Void playSound [:FlxSound sound :Float volumeMod :Bool waitForEnd :Continuation cc]
(let [originalVolume sound.volume
restoreOriginalVolume ->(set sound.volume originalVolume)]
(*= sound.volume volumeMod)
(set sound.onComplete
(if waitForEnd
->{(restoreOriginalVolume) (cc)}
restoreOriginalVolume)))
(sound.play)
(unless waitForEnd (cc)))
(var DELAY_BETWEEN_VOICE_TRACKS 0.1)
(prop :Map<FlxSound,Function> restoreOriginalVolumes (new Map))
(method :Void playVoiceTrack [:FlxSound track :Float volumeMod :Float start :Float end :Continuation cc]
(let [originalVolume track.volume
restoreOriginalVolume ->(set track.volume originalVolume)]
(dictSet restoreOriginalVolumes track restoreOriginalVolume)
(*= track.volume volumeMod)
(set track.onComplete ->{(restoreOriginalVolume) (movie.delay DELAY_BETWEEN_VOICE_TRACKS cc)}))
(track.play true (* 1000 start) (* 1000 end)))
(method :Void stopVoiceTrack [:FlxSound track]
(track.stop)
((dictGet restoreOriginalVolumes track)))
(prop &mut :FlxSound music)
(prop MUSIC_FADE_SEC 1)
(prop MUSIC_FADE_STEPS 10)
(method :Void playSong [:String song :Float volumeMod :Bool loop :Bool waitForEnd :Continuation cc]
(set music (FlxG.sound.play song 0 loop null true (if waitForEnd cc ->{})))
(.start (new FlxTimer)
(/ MUSIC_FADE_SEC MUSIC_FADE_STEPS)
->:Void _ (+= music.volume (/ volumeMod MUSIC_FADE_STEPS))
MUSIC_FADE_STEPS)
(set music.persist true)
(unless waitForEnd (cc)))
(method :Void stopSong [] (when music (music.stop)))
(var PROP_MIN_WIDTH 200)
(var PROP_MAX_WIDTH 500)
(method :Void showPropOnScreen [:FlxSprite prop :FlxScreenPosition position :Continuation cc]
// TODO assign the other possible positions
// TODO allow absolute position and relative position by screen percentages
(let [[x y]
(case position
(Center [(/ 1280 2) (/ 720 2)])
(otherwise (throw "not implemented")))]
(let [width (min (max prop.width PROP_MIN_WIDTH) PROP_MAX_WIDTH)]
(prop.setGraphicSize width)
(prop.updateHitbox)
// if the prop is too tall, shrink it heightwise
(when (> prop.height DIALOG_Y)
(prop.setGraphicSize 0 DIALOG_Y)
(prop.updateHitbox))
(set prop.x (- x (/ prop.width 2)))
(set prop.y (- y (/ prop.height 2)))
// if the prop would block the dialogue box, bump it up
(let [propBottom (+ prop.y prop.height)]
(when (> propBottom DIALOG_Y)
(-= prop.y (- propBottom DIALOG_Y))))
(currentState.add prop)))
// TODO give the prop reveal some time to land
(cc))
(method :Void hideProp [prop cc]
(currentState.remove prop)
(cc))
(prop :Array<FlxText> creditsText [])
(method _ctext [:String text :Int size]
(let [t (new FlxText 0 0 0 text size)]
(creditsText.push t)
t))
// 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<CreditsLine> credits cc]
(localVar bg (new FlxSprite))
(bg.makeGraphic 1280 720 FlxColor.BLACK true)
(currentState.add bg)
(localVar &mut textY 720)
(var oneColSize 64)
(var twoColSize 48)
(var threeColSize 32)
(var creditMargin 10)
(var twoColumnGap 50)
(doFor line credits
(case line
(Break
(+= textY oneColSize))
// Centered, big one column lines
((OneColumn col1)
(let [t (_ctext col1 oneColSize)]
(t.screenCenter)
(set t.y textY))
(+= textY oneColSize creditMargin))
// Centered left/right two column lines
((TwoColumn col1 col2)
(let [t1 (_ctext col1 twoColSize)
t2 (_ctext col2 twoColSize)]
(set t1.x (- (/ 1280 2) t1.width (/ twoColumnGap 2)))
(set t1.y textY)
(set t2.x (+ (/ 1280 2) (/ twoColumnGap 2)))
(set t2.y textY))
(+= textY twoColSize creditMargin))
// Left-justified, centered, right-justified three column lines
((ThreeColumn col1 col2 col3)
(let [t1 (_ctext col1 threeColSize)
t2 (_ctext col2 threeColSize)
t3 (_ctext col3 threeColSize)]
(set t1.x creditMargin)
(set t1.y textY)
(t2.screenCenter)
(set t2.y textY)
(set t3.x (- 1280 creditMargin t3.width))
(set t3.y textY))
(+= textY threeColSize creditMargin))
(otherwise)))
(doFor text creditsText
(currentState.add text)
(FlxTween.linearMotion text text.x text.y text.x (- text.y textY) 200 false (object onComplete ->:Void _ (cc)))))