Overhaul hollywoo & hollywoo-flixel. Close #178

This commit is contained in:
2023-03-02 07:37:55 -07:00
parent 28892ab188
commit 6efd3b1973
5 changed files with 202 additions and 61 deletions

View File

@@ -2,6 +2,7 @@ package hollywoo;
import hollywoo.Scene; import hollywoo.Scene;
import hollywoo.Movie; import hollywoo.Movie;
import haxe.ds.Option;
enum Appearance { enum Appearance {
FirstAppearance; FirstAppearance;
@@ -10,13 +11,31 @@ enum Appearance {
typedef Continuation = Void -> Void; typedef Continuation = Void -> Void;
interface Director<Set, StagePosition, StageFacing, ScreenPosition, Actor, Sound, Song, Prop, VoiceTrack> { typedef StagePosition = {
var movie(default, default):Movie<Set, StagePosition, StageFacing, ScreenPosition, Actor, Sound, Song, Prop, VoiceTrack>; x:Float,
function showScene(scene:Scene<Set, StagePosition, StageFacing, ScreenPosition, Actor, Prop>, appearance:Appearance, cc:Continuation):Void; y:Float,
function showCharacter(character:Character<StagePosition, StageFacing, Actor>, appearance:Appearance, cc:Continuation):Void; z:Float,
function hideCharacter(character:Character<StagePosition, StageFacing, Actor>, cc:Continuation):Void; };
function moveCharacter(character:Character<StagePosition, StageFacing, Actor>, toPos:StagePosition, toFacing:StageFacing, cc:Continuation):Void;
function swapCharacters(a:Character<StagePosition, StageFacing, Actor>, b:Character<StagePosition, StageFacing, Actor>, cc:Continuation):Void; enum StageFacing {
TowardsCharacter(name:String);
AwayFromCharacter(name:String);
TowardsPosition(name:String);
AwayFromPosition(name:String);
}
typedef AutoZConfig = {
zPerLayer:Float,
frontLayer:Int
};
interface Director<Set:Cloneable<Set>, ScreenPosition, Actor, Sound, Song, Prop, VoiceTrack> {
var movie(default, default):Movie<Set, ScreenPosition, Actor, Sound, Song, Prop, VoiceTrack>;
var autoZConfig(default,null):Option<AutoZConfig>;
function showSet(set:Set, time:SceneTime, perspective:ScenePerspective, appearance:Appearance, cc:Continuation):Void;
function hideSet(set:Set, cc:Continuation):Void;
function showCharacter(character:Character<Actor>, appearance:Appearance, cc:Continuation):Void;
function hideCharacter(character:Character<Actor>, cc:Continuation):Void;
function playSound(sound:Sound, volumeMod:Float, waitForEnd:Bool, cc:Continuation):Void; function playSound(sound:Sound, volumeMod:Float, waitForEnd:Bool, cc:Continuation):Void;
function stopSound(sound:Sound):Void; function stopSound(sound:Sound):Void;
function playSong(song:Song, volumeMod:Float, loop:Bool, waitForEnd:Bool, cc:Continuation):Void; function playSong(song:Song, volumeMod:Float, loop:Bool, waitForEnd:Bool, cc:Continuation):Void;
@@ -25,7 +44,7 @@ interface Director<Set, StagePosition, StageFacing, ScreenPosition, Actor, Sound
function stopVoiceTrack(track:VoiceTrack):Void; function stopVoiceTrack(track:VoiceTrack):Void;
function startWaitForInput(cc:Continuation):Void; function startWaitForInput(cc:Continuation):Void;
function stopWaitForInput():Void; function stopWaitForInput():Void;
function showDialog(speakerName:String, type:SpeechType<StagePosition, StageFacing, Actor>, wryly:String, dialog:String, cc:Continuation):Void; function showDialog(speakerName:String, type:SpeechType<Actor>, wryly:String, dialog:String, cc:Continuation):Void;
function hideDialog():Void; function hideDialog():Void;
function showTitleCard(text:Array<String>, cc:Continuation):Void; function showTitleCard(text:Array<String>, cc:Continuation):Void;
function hideTitleCard():Void; function hideTitleCard():Void;

View File

@@ -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 // 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). // 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. // This allows assets to be declared in Hollywoo files where they first appear, but still loaded before execution starts.

View File

@@ -13,6 +13,10 @@ import uuid.Uuid;
using kiss.FuzzyMapTools; using kiss.FuzzyMapTools;
typedef Cloneable<T> = {
function clone():T;
}
enum DelayHandling { enum DelayHandling {
Auto; Auto;
AutoWithSkip; AutoWithSkip;
@@ -37,8 +41,4 @@ enum CreditsLine {
* Model/controller of a Hollywoo film, and main execution script * Model/controller of a Hollywoo film, and main execution script
*/ */
@:build(kiss.Kiss.build()) @:build(kiss.Kiss.build())
class Movie<Set, StagePosition, StageFacing, ScreenPosition, Actor, Sound, Song, Prop, VoiceTrack> extends AsyncEmbeddedScript { class Movie<Set:Cloneable<Set>, ScreenPosition, Actor, Sound, Song, Prop, VoiceTrack> extends AsyncEmbeddedScript {}
// TODO for some reason this wasn't working when declared in Movie.kiss:
// Mutable representation of frames in time:
var scenes:FuzzyMap<Scene<Set, StagePosition, StageFacing, ScreenPosition, Actor, Prop>> = new FuzzyMap<Scene<Set, StagePosition, StageFacing, ScreenPosition, Actor, Prop>>();
}

View File

@@ -1,3 +1,6 @@
(defMacro makeCC [&body b]
`->:Void {,@b})
// This file is designed to be loaded again by subclasses, with macroVar "subclass" set // This file is designed to be loaded again by subclasses, with macroVar "subclass" set
(#unless subclass (#unless subclass
@@ -14,9 +17,8 @@
(prop &mut :DelayHandling delayHandling AutoWithSkip) (prop &mut :DelayHandling delayHandling AutoWithSkip)
// TODO for some reason this won't work when declared in Kiss syntax:
// Mutable representation of frames in time: // Mutable representation of frames in time:
// var scenes:Map<String, Scene<Set, StagePosition, StageFacing, ScreenPosition, Actor>> = []; (prop :FuzzyMap<Scene<Set,ScreenPosition,Actor,Prop>> scenes (new FuzzyMap<Scene<Set,ScreenPosition,Actor,Prop>>))
(prop :FuzzyMap<Bool> shownScenes (new FuzzyMap<Bool>)) (prop :FuzzyMap<Bool> shownScenes (new FuzzyMap<Bool>))
(prop :FuzzyMap<Bool> shownCharacters (new FuzzyMap<Bool>)) (prop :FuzzyMap<Bool> shownCharacters (new FuzzyMap<Bool>))
@@ -40,7 +42,8 @@
(method :Void showDialog [actorName dialogType wryly text cc] (method :Void showDialog [actorName dialogType wryly text cc]
(when intercutMap (when intercutMap
(whenLet [sceneForActor (try (dictGet intercutMap actorName) (catch [e] null))] (whenLet [sceneForActor (try (dictGet intercutMap actorName) (catch [e] null))]
(setScene sceneForActor ->{}))) (unless (= sceneForActor sceneKey)
(setScene sceneForActor ->{}))))
(let [cc ->:Void {(director.hideDialog) (cc)} (let [cc ->:Void {(director.hideDialog) (cc)}
&mut skipCC cc] &mut skipCC cc]
@@ -83,11 +86,66 @@
[])] [])]
(dictSet voiceLines "$actorName $key" (objectWith [start line.start end line.end] trackKey alts)))))) (dictSet voiceLines "$actorName $key" (objectWith [start line.start end line.end] trackKey alts))))))
(method _ccForEach <>[T] [:Iterable<T> collection :(T,Continuation)->Void do_ :Continuation finalCC]
(let [:Iterator<T> 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<Actor> c :Continuation cc]
(director.hideCharacter c cc)
(makeCC
// hide current scene props, etc.
(_ccForEach
currentScene.propsOnScreen
->[:ScreenProp<ScreenPosition,Prop> p :Continuation cc]
(director.hideProp p.prop cc)
cc))))))
(cc)))
(method _showScene [:Scene<Set,ScreenPosition,Actor,Prop> 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<ScreenPosition,Prop> p :Continuation cc]
(director.showPropOnScreen p.prop p.screenPosition cc)
cc))))))
(defNew (defNew
[ [
// "View" in the Model-View-Controller architecture: // "View" in the Model-View-Controller architecture:
&prop :Director<Set,StagePosition,StageFacing,ScreenPosition,Actor,Sound,Song,Prop,VoiceTrack> director &prop :Director<Set,ScreenPosition,Actor,Sound,Song,Prop,VoiceTrack> director
&opt :String voiceLinesJson &opt :String voiceLinesJson
&opt :String positionsJson
]
[
:Map<String,StagePosition> stagePositions
(if positionsJson
(haxe.Json.parse (sys.io.File.getContent positionsJson))
(new Map))
] ]
(set director.movie this) (set director.movie this)
@@ -99,7 +157,7 @@
// Some real magic happens here. This macro defines a method, AND a reader macro // 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. // 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] (defMacro hollywooMethod [nameAndType canSkip argList &builder b &body body]
(let [args (expList argList) (let [args (expList argList)
numArgs args.length numArgs args.length
@@ -162,25 +220,24 @@
(dictSet scenes name (objectWith (dictSet scenes name (objectWith
[ [
set set
(dictGet sets setKey) (.clone (dictGet sets setKey))
characters characters
(new FuzzyMap<Character<StagePosition,StageFacing,Actor>>) (new FuzzyMap<Character<Actor>>)
propsOnScreen propsOnScreen
(new FuzzyMap<Prop>) (new FuzzyMap<ScreenProp<ScreenPosition,Prop>>)
] ]
time time
perspective))) perspective)))
(hollywooMethod newScene true [name :Scene<Set,StagePosition,StageFacing,ScreenPosition,Actor,Prop> scene]
(assert isLoading)
(dictSet scenes name scene))
(hollywooMethod setScene false [name :Continuation cc] (hollywooMethod setScene false [name :Continuation cc]
(set sceneKey name) (_hideCurrentScene
(director.showScene (makeCC
(dictGet scenes name) (set sceneKey name)
(appearanceFlag shownScenes name) (_showScene
cc)) (dictGet scenes name)
(appearanceFlag shownScenes name)
cc))))
(hollywooMethod newSound true [name :Sound s] (hollywooMethod newSound true [name :Sound s]
(assert isLoading) (assert isLoading)
@@ -221,38 +278,62 @@
(assert isLoading) (assert isLoading)
(dictSet actors name actor)) (dictSet actors name actor))
(hollywooMethod addCharacter false [actorName :StagePosition position :StageFacing facing :Continuation cc] (hollywooMethod autoZProcess false [:StagePosition position :Continuation cc]
(let [character (object stagePosition position stageFacing facing actor (dictGet actors actorName))] // handle auto z recursively
(dictSet .characters (_currentScene) actorName character) (ifLet [(Some (objectWith zPerLayer frontLayer)) director.autoZConfig]
(director.showCharacter {
character (doFor =>name otherCharacter .characters (_currentScene)
(appearanceFlag shownCharacters actorName) (when (and (= position.x otherCharacter.stagePosition.x) (= position.y otherCharacter.stagePosition.y) (= position.z otherCharacter.stagePosition.z))
cc))) (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] (hollywooMethod removeCharacter false [actorName :Continuation cc]
(let [c (dictGet .characters (_currentScene) actorName)] (let [c (dictGet .characters (_currentScene) actorName)]
(.remove .characters (_currentScene) actorName) (.remove .characters (_currentScene) actorName)
(director.hideCharacter c cc))) (director.hideCharacter c cc)))
(hollywooMethod moveCharacter false [actorName :StagePosition newPosition :StageFacing newFacing :Continuation cc] // INSTANTLY move a character:
(let [c (dictGet .characters (_currentScene) actorName)] (hollywooMethod moveCharacter false [actorName :Dynamic newPosition :StageFacing newFacing :Continuation cc]
(director.moveCharacter c newPosition newFacing ->:Void { (let [newPosition (typeCase [newPosition]
(set c.stagePosition newPosition) ([:String pKey] (dictGet stagePositions pKey))
(set c.stageFacing newFacing) (otherwise newPosition))]
(cc) (removeCharacter actorName
}))) (makeCC
(addCharacter actorName newPosition newFacing cc)))))
// INSTANTLY swap characters
(hollywooMethod swapCharacters false [actorNameA actorNameB :Continuation cc] (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) (let [a (dictGet .characters (_currentScene) actorNameA)
b (dictGet .characters (_currentScene) actorNameB)] asp a.stagePosition
(director.swapCharacters a b ->:Void asf a.stageFacing
(let [tempStagePos a.stagePosition b (dictGet .characters (_currentScene) actorNameB)
tempStageFacing a.stageFacing] bsp b.stagePosition
(set a.stagePosition b.stagePosition) bsf b.stageFacing]
(set a.stageFacing b.stageFacing) (removeCharacter actorNameA
(set b.stagePosition tempStagePos) (makeCC
(set b.stageFacing tempStageFacing) (removeCharacter actorNameB
(cc))))) (makeCC
(addCharacter actorNameA bsp bsf
(makeCC
(addCharacter actorNameB asp asf cc)))))))))
// TODO moveCharacter remove them, add them to another scene // TODO moveCharacter remove them, add them to another scene
// TODO moveCharacterAndFollow remove them, add them to another scene, set that the scene // TODO moveCharacterAndFollow remove them, add them to another scene, set that the scene
@@ -262,10 +343,13 @@
(dictSet props name prop)) (dictSet props name prop))
(hollywooMethod addPropToScreen false [name :ScreenPosition position :Continuation cc] (hollywooMethod addPropToScreen false [name :ScreenPosition position :Continuation cc]
(dictSet .propsOnScreen (_currentScene) name (dictGet props name)) (let [prop (dictGet props name)]
(director.showPropOnScreen (dictGet props name) position cc)) (dictSet .propsOnScreen (_currentScene) name (object screenPosition position prop prop))
(director.showPropOnScreen prop position cc)))
(hollywooMethod removeProp false [name :Continuation cc] (hollywooMethod removeProp false [name :Continuation cc]
(.remove .propsOnScreen (_currentScene) name)
// TODO (propsOnStage.remove name)
(director.hideProp (dictGet props name) cc)) (director.hideProp (dictGet props name) cc))
// Dialogue: // Dialogue:

View File

@@ -1,6 +1,8 @@
package hollywoo; package hollywoo;
import kiss.FuzzyMap; import kiss.FuzzyMap;
import hollywoo.Director;
import hollywoo.Movie;
enum SceneTime { enum SceneTime {
Morning; Morning;
@@ -15,26 +17,31 @@ enum ScenePerspective {
Mixed; Mixed;
} }
typedef Character<StagePosition, StageFacing, Actor> = { typedef Character<Actor> = {
stagePosition:StagePosition, stagePosition:StagePosition,
stageFacing:StageFacing, stageFacing:StageFacing,
actor:Actor actor:Actor
}; };
enum SpeechType<StagePosition, StageFacing, Actor> { enum SpeechType<Actor> {
Super; Super;
OffScreen(actor:Actor); OffScreen(actor:Actor);
VoiceOver(actor:Actor); VoiceOver(actor:Actor);
TextMessage(actor:Actor); TextMessage(actor:Actor);
FromPhone(actor:Actor); FromPhone(actor:Actor);
OnScreen(character:Character<StagePosition, StageFacing, Actor>); OnScreen(character:Character<Actor>);
Custom(type:String, actor:Actor, args:Dynamic); Custom(type:String, actor:Actor, args:Dynamic);
} }
typedef Scene<Set, StagePosition, StageFacing, ScreenPosition, Actor, Prop> = { typedef ScreenProp<ScreenPosition,Prop> = {
screenPosition:ScreenPosition,
prop:Prop
};
typedef Scene<Set:Cloneable<Set>, ScreenPosition, Actor, Prop> = {
set:Set, set:Set,
characters:FuzzyMap<Character<StagePosition, StageFacing, Actor>>, characters:FuzzyMap<Character<Actor>>,
propsOnScreen:FuzzyMap<Prop>, propsOnScreen:FuzzyMap<ScreenProp<ScreenPosition,Prop>>,
// TODO props on stage // TODO props on stage
time:SceneTime, time:SceneTime,
perspective:ScenePerspective perspective:ScenePerspective