From c1cea068d472e2f6f81321e144d2fc5ea7cf19b9 Mon Sep 17 00:00:00 2001 From: Eric Myllyoja Date: Sat, 26 Feb 2022 23:26:30 -0500 Subject: [PATCH] Added boppers, started work on Week 4. --- source/Character.hx | 2 + source/LoadingState.hx | 9 +++- source/PlayState.hx | 84 ++-------------------------------- source/play/stage/Stage.hx | 69 +++++++++++++++++++++++++--- source/play/stage/StageData.hx | 59 ++++++++++++++++++++---- 5 files changed, 126 insertions(+), 97 deletions(-) diff --git a/source/Character.hx b/source/Character.hx index 0c1eb4118..999dcbe54 100644 --- a/source/Character.hx +++ b/source/Character.hx @@ -689,6 +689,8 @@ class Character extends FlxSprite */ public function dance() { + if (animation == null) + return; if (!debugMode) { switch (curCharacter) diff --git a/source/LoadingState.hx b/source/LoadingState.hx index 016377ecb..263aa3fcb 100644 --- a/source/LoadingState.hx +++ b/source/LoadingState.hx @@ -187,7 +187,14 @@ class LoadingState extends MusicBeatState static function getNextState(target:FlxState, stopMusic = false):FlxState { - Paths.setCurrentLevel("week" + PlayState.storyWeek); + if (PlayState.storyWeek == 0) + { + Paths.setCurrentLevel('tutorial'); + } + else + { + Paths.setCurrentLevel("week" + PlayState.storyWeek); + } #if NO_PRELOAD_ALL var loaded = isSoundLoaded(getSongPath()) && (!PlayState.SONG.needsVoices || isSoundLoaded(getVocalPath())) diff --git a/source/PlayState.hx b/source/PlayState.hx index 8c441c39c..b7007e345 100644 --- a/source/PlayState.hx +++ b/source/PlayState.hx @@ -393,53 +393,9 @@ class PlayState extends MusicBeatState loadStage(curStageId); case "milf" | 'satin-panties' | 'high': - curStageId = 'limo'; - defaultCamZoom *= 0.90; + curStageId = 'limoRide'; + loadStage(curStageId); - var skyBG:FlxSprite = new FlxSprite(-120, -50).loadGraphic(Paths.image('limo/limoSunset')); - - var overlayShader:OverlayBlend = new OverlayBlend(); - var sunOverlay:FlxSprite = new FlxSprite().loadGraphic(Paths.image('limo/limoOverlay')); - sunOverlay.setGraphicSize(Std.int(sunOverlay.width * 2)); - sunOverlay.updateHitbox(); - overlayShader.funnyShit.input = sunOverlay.pixels; - skyBG.shader = overlayShader; - - skyBG.scrollFactor.set(0.1, 0.1); - add(skyBG); - - var bgLimo:FlxSprite = new FlxSprite(-200, 480); - bgLimo.frames = Paths.getSparrowAtlas('limo/bgLimo'); - bgLimo.animation.addByPrefix('drive', "background limo pink", 24); - bgLimo.animation.play('drive'); - bgLimo.scrollFactor.set(0.4, 0.4); - add(bgLimo); - - grpLimoDancers = new FlxTypedGroup(); - add(grpLimoDancers); - - for (i in 0...5) - { - var dancer:BackgroundDancer = new BackgroundDancer((370 * i) + 130, bgLimo.y - 400); - dancer.scrollFactor.set(0.4, 0.4); - grpLimoDancers.add(dancer); - } - - var overlayShit:FlxSprite = new FlxSprite(-500, -600).loadGraphic(Paths.image('limo/limoOverlay')); - overlayShit.alpha = 0.5; - // add(overlayShit); - // var shaderBullshit = new BlendModeEffect(new OverlayShader(), FlxColor.RED); - // FlxG.camera.setFilters([new ShaderFilter(cast shaderBullshit.shader)]); - // overlayShit.shader = shaderBullshit; - - limo = new FlxSprite(-120, 550); - limo.frames = Paths.getSparrowAtlas('limo/limoDrive'); - limo.animation.addByPrefix('drive', "Limo stage", 24); - limo.animation.play('drive'); - limo.antialiasing = true; - - fastCar = new FlxSprite(-300, 160).loadGraphic(Paths.image('limo/fastCarLol')); - // add(limo); case "cocoa" | 'eggnog': curStageId = 'mall'; @@ -734,12 +690,6 @@ class PlayState extends MusicBeatState // REPOSITIONING PER STAGE switch (curStageId) { - case 'limo': - boyfriend.y -= 220; - boyfriend.x += 260; - - resetFastCar(); - add(fastCar); case 'mall': boyfriend.x += 200; case 'mallEvil': @@ -776,7 +726,7 @@ class PlayState extends MusicBeatState gf.x -= 170; gf.y -= 75; } - case 'stage' | 'phillyStreets': + case 'phillyStreets': dad.y = 870 - dad.height; } @@ -801,10 +751,6 @@ class PlayState extends MusicBeatState bfTankCutsceneLayer = new FlxGroup(); add(bfTankCutsceneLayer); - // Shitty layering but whatev it works LOL - if (curStageId == 'limo') - add(limo); - add(dad); add(boyfriend); } @@ -2802,28 +2748,6 @@ class PlayState extends MusicBeatState FlxG.camera.focusOn(camFollow.getPosition()); } - var fastCarCanDrive:Bool = true; - - function resetFastCar():Void - { - fastCar.x = -12600; - fastCar.y = FlxG.random.int(140, 250); - fastCar.velocity.x = 0; - fastCarCanDrive = true; - } - - function fastCarDrive() - { - FlxG.sound.play(Paths.soundRandom('carPass', 0, 1), 0.7); - - fastCar.velocity.x = (FlxG.random.int(170, 220) / FlxG.elapsed) * 3; - fastCarCanDrive = false; - new FlxTimer().start(2, function(tmr:FlxTimer) - { - resetFastCar(); - }); - } - function moveTank():Void { if (!inCutscene) @@ -2972,8 +2896,6 @@ class PlayState extends MusicBeatState dancer.dance(); }); - if (FlxG.random.bool(10) && fastCarCanDrive) - fastCarDrive(); case 'tank': tankWatchtower.dance(); } diff --git a/source/play/stage/Stage.hx b/source/play/stage/Stage.hx index 125fa7c7d..0e6737811 100644 --- a/source/play/stage/Stage.hx +++ b/source/play/stage/Stage.hx @@ -25,6 +25,7 @@ class Stage extends FlxSpriteGroup implements IHook var namedProps:Map = new Map(); var characters:Map = new Map(); + var boppers:Array = new Array(); /** * The Stage elements get initialized at the beginning of the game. @@ -58,7 +59,16 @@ class Stage extends FlxSpriteGroup implements IHook trace(' Placing prop: ${dataProp.name} (${dataProp.assetPath})'); var isAnimated = dataProp.animations.length > 0; - var propSprite = new FlxSprite(); + + var propSprite:FlxSprite; + if (dataProp.danceEvery != 0) + { + propSprite = new Bopper(dataProp.danceEvery); + } + else + { + propSprite = new FlxSprite(); + } if (isAnimated) { @@ -91,7 +101,15 @@ class Stage extends FlxSpriteGroup implements IHook for (propAnim in dataProp.animations) { - propSprite.animation.addByPrefix(propAnim.name, propAnim.prefix, propAnim.frameRate, propAnim.loop); + if (propAnim.frameIndices.length == 0) + { + propSprite.animation.addByPrefix(propAnim.name, propAnim.prefix, propAnim.frameRate, propAnim.loop, propAnim.flipX, propAnim.flipY); + } + else + { + propSprite.animation.addByIndices(propAnim.name, propAnim.prefix, propAnim.frameIndices, "", propAnim.frameRate, propAnim.loop, + propAnim.flipX, propAnim.flipY); + } } if (dataProp.startingAnimation != null) @@ -99,18 +117,44 @@ class Stage extends FlxSpriteGroup implements IHook propSprite.animation.play(dataProp.startingAnimation); } - if (dataProp.name != null) + if (Std.isOfType(propSprite, Bopper)) { - namedProps.set(dataProp.name, propSprite); + addBopper(cast propSprite, dataProp.name); + } + else + { + addProp(propSprite, dataProp.name); } - trace(' Prop placed.'); - this.add(propSprite); } this.refresh(); } + /** + * Add a sprite to the stage. + * @param prop The sprite to add. + * @param name (Optional) A unique name for the sprite. + * You can call `getNamedProp(name)` to retrieve it later. + */ + public function addProp(prop:FlxSprite, ?name:String = null) + { + if (name != null) + { + namedProps.set(name, prop); + } + this.add(prop); + } + + /** + * Add a sprite to the stage which animates to the beat of the song. + */ + public function addBopper(bopper:Bopper, ?name:String = null) + { + boppers.push(bopper); + this.addProp(bopper, name); + } + /** * Refreshes the stage, by redoing the render order of all props. * It does this based on the `zIndex` of each prop. @@ -167,7 +211,12 @@ class Stage extends FlxSpriteGroup implements IHook public function onBeatHit(curBeat:Int):Void { // Override me in your scripted stage to perform custom behavior! - // trace('Stage.onBeatHit(${curBeat})'); + // Make sure to call super.onBeatHit(curBeat) if you want to keep the boppers dancing. + + for (bopper in boppers) + { + bopper.onBeatHit(curBeat); + } } /** @@ -271,6 +320,12 @@ class Stage extends FlxSpriteGroup implements IHook } characters.clear(); + for (bopper in boppers) + { + bopper.destroy(); + } + boppers = []; + for (sprite in this.group) { sprite.destroy(); diff --git a/source/play/stage/StageData.hx b/source/play/stage/StageData.hx index 5912ad824..d5278f26c 100644 --- a/source/play/stage/StageData.hx +++ b/source/play/stage/StageData.hx @@ -32,7 +32,7 @@ class StageDataParser { // Clear any stages that are cached if there were any. clearStageCache(); - trace("Loading stage cache..."); + trace("[STAGEDATA] Loading stage cache..."); #if polymod // @@ -91,14 +91,14 @@ class StageDataParser { if (stageCache.exists(stageId)) { - trace('Successfully fetch stage: ${stageId}'); + trace('[STAGEDATA] Successfully fetch stage: ${stageId}'); var stage:Stage = stageCache.get(stageId); stage.revive(); return stage; } else { - trace('Failed to fetch stage, not found in cache: ${stageId}'); + trace('[STAGEDATA] Failed to fetch stage, not found in cache: ${stageId}'); return null; } } @@ -125,7 +125,7 @@ class StageDataParser { var rawJson:String = loadStageFile(stageId); - var stageData:StageData = migrateStageData(rawJson); + var stageData:StageData = migrateStageData(rawJson, stageId); return validateStageData(stageId, stageData); } @@ -143,22 +143,32 @@ class StageDataParser return rawJson; } - static function migrateStageData(rawJson:String) + static function migrateStageData(rawJson:String, stageId:String) { // If you update the stage data format in a breaking way, // handle migration here by checking the `version` value. - var stageData:StageData = cast Json.parse(rawJson); - - return stageData; + try + { + var stageData:StageData = cast Json.parse(rawJson); + return stageData; + } + catch (e) + { + trace(' Error parsing data for stage: ${stageId}'); + trace(' ${e}'); + return null; + } } static final DEFAULT_NAME:String = "Untitled Stage"; static final DEFAULT_CAMERAZOOM:Float = 1.0; static final DEFAULT_ZINDEX:Int = 0; + static final DEFAULT_DANCEEVERY:Int = 0; static final DEFAULT_SCALE:Float = 1.0; static final DEFAULT_POSITION:Array = [0, 0]; static final DEFAULT_SCROLL:Array = [0, 0]; + static final DEFAULT_FRAMEINDICES:Array = []; static final DEFAULT_CHARACTER_DATA:StageDataCharacter = { zIndex: DEFAULT_ZINDEX, @@ -174,6 +184,12 @@ class StageDataParser */ static function validateStageData(id:String, input:StageData):Null { + if (input == null) + { + trace('[STAGEDATA] ERROR: Could not parse stage data for "${id}".'); + return null; + } + if (input.version != STAGE_DATA_VERSION) { trace('[STAGEDATA] ERROR: Could not load stage data for "$id": missing version'); @@ -217,6 +233,11 @@ class StageDataParser inputProp.zIndex = DEFAULT_ZINDEX; } + if (inputProp.danceEvery == null) + { + inputProp.danceEvery = DEFAULT_DANCEEVERY; + } + if (inputProp.scale == null) { inputProp.scale = DEFAULT_SCALE; @@ -261,6 +282,11 @@ class StageDataParser inputAnimation.frameRate = 24; } + if (inputAnimation.frameIndices == null) + { + inputAnimation.frameIndices = DEFAULT_FRAMEINDICES; + } + if (inputAnimation.loop == null) { inputAnimation.loop = true; @@ -361,6 +387,15 @@ typedef StageDataProp = */ var scale:OneOfTwo>; + /** + * If not zero, this prop will play an animation every X beats of the song. + * This requires animations to be defined. If `danceLeft` and `danceRight` are defined, + * they will alternated between, otherwise the `idle` animation will be used. + * + * @default 0 + */ + var danceEvery:Null; + /** * How much the prop scrolls relative to the camera. Used to create a parallax effect. * Represented as a float or as an [x, y] array of two floats. @@ -397,6 +432,14 @@ typedef StageDataPropAnimation = */ var prefix:String; + /** + * If you want this animation to use only certain frames of an animation with a given prefix, + * select them here. + * @example [0, 1, 2, 3] (use only the first four frames) + * @default [] (all frames) + */ + var frameIndices:Array; + /** * The speed of the animation in frames per second. * @default 24