From 32df13aedfe41c04c699646d09567b0a3b08e3b3 Mon Sep 17 00:00:00 2001 From: Brandon Date: Mon, 26 Oct 2020 19:06:42 -0400 Subject: [PATCH 01/16] switch setup start [REAL] --- Project.xml | 6 +++++- source/FreeplayState.hx | 26 ++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/Project.xml b/Project.xml index dcde8dc16..5d0c0ee71 100644 --- a/Project.xml +++ b/Project.xml @@ -25,6 +25,9 @@ + + + @@ -91,5 +94,6 @@ - + + diff --git a/source/FreeplayState.hx b/source/FreeplayState.hx index f745d5da6..7474830a8 100644 --- a/source/FreeplayState.hx +++ b/source/FreeplayState.hx @@ -5,6 +5,13 @@ import flixel.FlxSprite; import flixel.addons.display.FlxGridOverlay; import flixel.text.FlxText; +#if switch + import openfl.events.GameInputEvent; + import openfl.ui.GameInput; + import openfl.ui.GameInputDevice; + import openfl.ui.GameInputControl; +#end + class FreeplayState extends MusicBeatState { var songs:Array = ["Bopeebo", "Dadbattle", "Fresh", "Tutorial\nlol"]; @@ -48,6 +55,17 @@ class FreeplayState extends MusicBeatState curSelected += 1; } + #if switch + if (gamepad.anyJustPressed(["UP", "DPAD_UP", "LEFT_STICK_DIGITAL_UP"])) + { + curSelected -= 1; + } + if (gamepad.anyJustPressed(["DOWN", "DPAD_DOWN", "LEFT_STICK_DIGITAL_DOWN"])) + { + curSelected += 1; + } + #end + if (curSelected < 0) curSelected = songs.length - 1; if (curSelected >= songs.length) @@ -61,6 +79,14 @@ class FreeplayState extends MusicBeatState FlxG.switchState(new PlayState()); } + #if switch + if (gamepad.anyJustPressed(["B"])) //"B" is swapped with "A" on Switch + { + PlayState.SONG = Song.loadFromJson(songs[curSelected].toLowerCase()); + FlxG.switchState(new PlayState()); + } + #end + super.update(elapsed); } } From f388fa0b21c1bd292bf45cca84fb99f490619268 Mon Sep 17 00:00:00 2001 From: Brandon Date: Fri, 30 Oct 2020 19:39:13 -0400 Subject: [PATCH 02/16] transparent --- source/FreeplayState.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/FreeplayState.hx b/source/FreeplayState.hx index 7474830a8..626e40256 100644 --- a/source/FreeplayState.hx +++ b/source/FreeplayState.hx @@ -80,7 +80,7 @@ class FreeplayState extends MusicBeatState } #if switch - if (gamepad.anyJustPressed(["B"])) //"B" is swapped with "A" on Switch + if (gamepad.anyJustPressed(["B"])) //"B" is swapped with "A" on Switch { PlayState.SONG = Song.loadFromJson(songs[curSelected].toLowerCase()); FlxG.switchState(new PlayState()); From 61f74e74273091628f882b22304ec20daac4380e Mon Sep 17 00:00:00 2001 From: Brandon Date: Fri, 30 Oct 2020 19:47:19 -0400 Subject: [PATCH 03/16] caught up 10/30/ --- Project.xml | 3 + source/Alphabet.hx | 103 +++++++++++----- source/AnimationDebug.hx | 3 + source/Boyfriend.hx | 13 ++ source/ButtonRemapSubstate.hx | 11 ++ source/Character.hx | 36 +++++- source/ChartingState.hx | 89 +++++++++++--- source/FreeplayState.hx | 25 ++-- source/GameOverState.hx | 48 +++++--- source/GameOverSubstate.hx | 89 ++++++++++++++ source/Main.hx | 2 +- source/MenuItem.hx | 38 ++++++ source/MusicBeatState.hx | 4 + source/MusicBeatSubstate.hx | 78 ++++++++++++ source/Note.hx | 3 +- source/PauseSubState.hx | 8 +- source/PlayState.hx | 225 +++++++++++++--------------------- source/Section.hx | 2 + source/StoryMenuState.hx | 172 ++++++++++++++++++++++++++ source/TitleState.hx | 105 ++++++++++++---- 20 files changed, 818 insertions(+), 239 deletions(-) create mode 100644 source/ButtonRemapSubstate.hx create mode 100644 source/GameOverSubstate.hx create mode 100644 source/MenuItem.hx create mode 100644 source/MusicBeatSubstate.hx create mode 100644 source/StoryMenuState.hx diff --git a/Project.xml b/Project.xml index 5d0c0ee71..37d7467f2 100644 --- a/Project.xml +++ b/Project.xml @@ -44,6 +44,8 @@ + + @@ -96,4 +98,5 @@ + diff --git a/source/Alphabet.hx b/source/Alphabet.hx index bb8cce924..20556e359 100644 --- a/source/Alphabet.hx +++ b/source/Alphabet.hx @@ -17,6 +17,8 @@ class Alphabet extends FlxSpriteGroup public var delay:Float = 0.05; public var paused:Bool = false; + public var text:String = ""; + var _finalText:String = ""; var _curText:String = ""; @@ -28,15 +30,79 @@ class Alphabet extends FlxSpriteGroup // amp, backslash, question mark, apostrophy, comma, angry faic, period var lastSprite:AlphaCharacter; var xPosResetted:Bool = false; + var lastWasSpace:Bool = false; - public function new(x:Float, y:Float, text:String = "", ?bold:Bool = false) + var splitWords:Array = []; + + public function new(x:Float, y:Float, text:String = "", ?bold:Bool = false, typed:Bool = false) { super(x, y); _finalText = text; + this.text = text; - var arrayShit:Array = text.split(""); - trace(arrayShit); + if (typed) + { + startTypedText(); + } + else + { + addText(); + } + } + + public function addText() + { + doSplitWords(); + + for (character in splitWords) + { + // if (character.fastCodeAt() == " ") + // { + // } + + if (character == " ") + { + lastWasSpace = true; + } + + if (AlphaCharacter.alphabet.contains(character.toLowerCase())) + { + var xPos:Float = 0; + if (lastSprite != null) + { + xPos = lastSprite.x + lastSprite.frameWidth; + } + + if (lastWasSpace) + { + xPos += 40; + lastWasSpace = false; + } + + // var letter:AlphaCharacter = new AlphaCharacter(30 * loopNum, 0); + var letter:AlphaCharacter = new AlphaCharacter(xPos, 0); + letter.createBold(character); + add(letter); + + lastSprite = letter; + } + + // loopNum += 1; + } + } + + function doSplitWords():Void + { + splitWords = _finalText.split(""); + } + + public function startTypedText():Void + { + _finalText = text; + doSplitWords(); + + // trace(arrayShit); var loopNum:Int = 0; @@ -52,7 +118,7 @@ class Alphabet extends FlxSpriteGroup // xPos = 0; } - if (AlphaCharacter.alphabet.contains(arrayShit[loopNum].toLowerCase())) + if (AlphaCharacter.alphabet.contains(splitWords[loopNum].toLowerCase())) { if (lastSprite != null && !xPosResetted) { @@ -67,7 +133,7 @@ class Alphabet extends FlxSpriteGroup // var letter:AlphaCharacter = new AlphaCharacter(30 * loopNum, 0); var letter:AlphaCharacter = new AlphaCharacter(xPos, 55 * yMulti); - letter.createBold(arrayShit[loopNum]); + letter.createBold(splitWords[loopNum]); add(letter); lastSprite = letter; @@ -76,32 +142,7 @@ class Alphabet extends FlxSpriteGroup loopNum += 1; tmr.time = FlxG.random.float(0.03, 0.09); - }, arrayShit.length); - - for (character in arrayShit) - { - // if (character.fastCodeAt() == " ") - // { - // } - - if (AlphaCharacter.alphabet.contains(character.toLowerCase())) - { - /* var xPos:Float = 0; - if (lastSprite != null) - { - xPos = lastSprite.x + lastSprite.frameWidth - 40; - } - - // var letter:AlphaCharacter = new AlphaCharacter(30 * loopNum, 0); - var letter:AlphaCharacter = new AlphaCharacter(xPos, 0); - letter.createBold(character); - add(letter); - - lastSprite = letter; */ - } - - // loopNum += 1; - } + }, splitWords.length); } override function update(elapsed:Float) diff --git a/source/AnimationDebug.hx b/source/AnimationDebug.hx index ae3ca510e..e74b45c56 100644 --- a/source/AnimationDebug.hx +++ b/source/AnimationDebug.hx @@ -39,6 +39,9 @@ class AnimationDebug extends FlxState gridBG.scrollFactor.set(0.5, 0.5); add(gridBG); + if (daAnim == 'bf') + isDad = false; + if (isDad) { dad = new Character(0, 0, daAnim); diff --git a/source/Boyfriend.hx b/source/Boyfriend.hx index 4796b8549..483f4d0e6 100644 --- a/source/Boyfriend.hx +++ b/source/Boyfriend.hx @@ -26,6 +26,10 @@ class Boyfriend extends Character animation.addByPrefix('singRIGHTmiss', 'BF NOTE RIGHT MISS', 24, false); animation.addByPrefix('singDOWNmiss', 'BF NOTE DOWN MISS', 24, false); animation.addByPrefix('hey', 'BF HEY', 24, false); + + animation.addByPrefix('firstDeath', "BF dies", 24, false); + animation.addByPrefix('deathLoop', "BF Dead Loop", 24, true); + animation.addByPrefix('deathConfirm', "BF Dead confirm", 24, false); playAnim('idle'); antialiasing = true; @@ -40,6 +44,9 @@ class Boyfriend extends Character addOffset("singLEFTmiss", 12, 24); addOffset("singDOWNmiss", -11, -19); addOffset("hey", 7, 4); + addOffset('firstDeath', 37, 11); + addOffset('deathLoop', 37, 5); + addOffset('deathConfirm', 37, 69); } override function update(elapsed:Float) @@ -48,6 +55,12 @@ class Boyfriend extends Character { playAnim('idle', true, false, 10); } + + if (animation.curAnim.name == 'firstDeath' && animation.curAnim.finished) + { + playAnim('deathLoop'); + } + super.update(elapsed); } } diff --git a/source/ButtonRemapSubstate.hx b/source/ButtonRemapSubstate.hx new file mode 100644 index 000000000..64ff07948 --- /dev/null +++ b/source/ButtonRemapSubstate.hx @@ -0,0 +1,11 @@ +package; + +import flixel.FlxSubState; + +class ButtonRemapSubstate extends FlxSubState +{ + public function new() + { + super(); + } +} diff --git a/source/Character.hx b/source/Character.hx index 85df3268f..af441f023 100644 --- a/source/Character.hx +++ b/source/Character.hx @@ -73,7 +73,7 @@ class Character extends FlxSprite animation.addByPrefix('singDOWN', 'spooky DOWN note', 24, false); animation.addByPrefix('singLEFT', 'note sing left', 24, false); animation.addByPrefix('singRIGHT', 'spooky sing right', 24, false); - animation.addByIndices('danceLeft', 'spooky dance idle', [16, 0, 2, 6], "", 12, false); + animation.addByIndices('danceLeft', 'spooky dance idle', [0, 2, 6], "", 12, false); animation.addByIndices('danceRight', 'spooky dance idle', [8, 10, 12, 14], "", 12, false); addOffset('danceLeft'); @@ -85,6 +85,21 @@ class Character extends FlxSprite addOffset("singDOWN", -50, -130); playAnim('danceRight'); + case 'monster': + tex = FlxAtlasFrames.fromSparrow(AssetPaths.Monster_Assets__png, AssetPaths.Monster_Assets__xml); + frames = tex; + animation.addByPrefix('idle', 'monster idle', 24); + animation.addByPrefix('singUP', 'monster up note', 24, false); + animation.addByPrefix('singDOWN', 'monster down', 24, false); + animation.addByPrefix('singLEFT', 'Monster left note', 24, false); + animation.addByPrefix('singRIGHT', 'Monster Right note', 24, false); + + addOffset('idle'); + addOffset("singUP", -20, 50); + addOffset("singRIGHT", -51); + addOffset("singLEFT", -30); + addOffset("singDOWN", -30, -40); + playAnim('idle'); } } @@ -113,6 +128,8 @@ class Character extends FlxSprite playAnim('danceLeft'); case 'dad': playAnim('idle'); + case 'monster': + playAnim('idle'); } } @@ -125,6 +142,23 @@ class Character extends FlxSprite { offset.set(daOffset[0], daOffset[1]); } + + if (curCharacter == 'gf') + { + if (AnimName == 'singLEFT') + { + danced = true; + } + else if (AnimName == 'singRIGHT') + { + danced = false; + } + + if (AnimName == 'singUP' || AnimName == 'singDOWN') + { + danced = !danced; + } + } } public function addOffset(name:String, x:Float = 0, y:Float = 0) diff --git a/source/ChartingState.hx b/source/ChartingState.hx index 87d0409f4..a4cf96c92 100644 --- a/source/ChartingState.hx +++ b/source/ChartingState.hx @@ -1,5 +1,6 @@ package; +import Section.SwagSection; import Song.SwagSong; import flixel.FlxG; import flixel.FlxSprite; @@ -68,6 +69,8 @@ class ChartingState extends MusicBeatState **/ var curSelectedNote:Array; + var tempBpm:Int = 0; + override function create() { gridBG = FlxGridOverlay.create(GRID_SIZE, GRID_SIZE, GRID_SIZE * 8, GRID_SIZE * 16); @@ -81,9 +84,9 @@ class ChartingState extends MusicBeatState else { _song = { - song: 'tutorial', + song: 'Monster', notes: [], - bpm: 100, + bpm: 95, sections: 0, needsVoices: false, player1: 'bf', @@ -93,6 +96,8 @@ class ChartingState extends MusicBeatState }; } + tempBpm = _song.bpm; + addSection(); // sections = _song.notes; @@ -172,7 +177,7 @@ class ChartingState extends MusicBeatState stepperBPM.value = Conductor.bpm; stepperBPM.name = 'song_bpm'; - var characters:Array = ["bf", 'dad', 'gf', 'spooky']; + var characters:Array = ["bf", 'dad', 'gf', 'spooky', 'monster']; var player1DropDown = new FlxUIDropDownMenu(10, 100, FlxUIDropDownMenu.makeStrIdLabelArray(characters, true), function(character:String) { @@ -208,6 +213,8 @@ class ChartingState extends MusicBeatState var stepperLength:FlxUINumericStepper; var check_mustHitSection:FlxUICheckBox; + var check_changeBPM:FlxUICheckBox; + var stepperSectionBPM:FlxUINumericStepper; function addSectionUI():Void { @@ -218,7 +225,11 @@ class ChartingState extends MusicBeatState stepperLength.value = _song.notes[curSection].lengthInSteps; stepperLength.name = "section_length"; - var stepperCopy:FlxUINumericStepper = new FlxUINumericStepper(110, 30, 1, 1, 1, 999, 0); + stepperSectionBPM = new FlxUINumericStepper(10, 80, 1, Conductor.bpm, 0, 999, 0); + stepperSectionBPM.value = Conductor.bpm; + stepperSectionBPM.name = 'section_bpm'; + + var stepperCopy:FlxUINumericStepper = new FlxUINumericStepper(110, 30, 1, 1, -999, 999, 0); var copyButton:FlxButton = new FlxButton(110, 8, "Copy last section", function() { @@ -229,15 +240,15 @@ class ChartingState extends MusicBeatState check_mustHitSection.name = 'check_mustHit'; check_mustHitSection.checked = true; // _song.needsVoices = check_mustHit.checked; - check_mustHitSection.callback = function() - { - // _song.needsVoices = check_mustHit.checked; - trace('CHECKED!'); - }; + + check_changeBPM = new FlxUICheckBox(10, 60, null, null, 'Change BPM', 100); + check_changeBPM.name = 'check_changeBPM'; tab_group_section.add(stepperLength); + tab_group_section.add(stepperSectionBPM); tab_group_section.add(stepperCopy); tab_group_section.add(check_mustHitSection); + tab_group_section.add(check_changeBPM); tab_group_section.add(copyButton); UI_box.addGroup(tab_group_section); @@ -267,7 +278,7 @@ class ChartingState extends MusicBeatState if (FlxG.sound.music != null) FlxG.sound.music.stop(); - FlxG.sound.playMusic('assets/music/' + daSong + '.mp3', 0.6); + FlxG.sound.playMusic('assets/music/' + daSong + TitleState.soundExt, 0.6); FlxG.sound.music.pause(); FlxG.sound.music.onComplete = function() { @@ -305,6 +316,9 @@ class ChartingState extends MusicBeatState { case 'Must hit section': _song.notes[curSection].mustHitSection = check.checked; + case 'Change BPM': + _song.notes[curSection].changeBPM = check.checked; + FlxG.log.add('changed bpm shit'); } } else if (id == FlxUINumericStepper.CHANGE_EVENT && (sender is FlxUINumericStepper)) @@ -323,6 +337,7 @@ class ChartingState extends MusicBeatState } else if (wname == 'song_bpm') { + tempBpm = Std.int(nums.value); Conductor.changeBPM(Std.int(nums.value)); } else if (wname == 'note_susLength') @@ -330,6 +345,11 @@ class ChartingState extends MusicBeatState curSelectedNote[2] = nums.value; updateGrid(); } + else if (wname == 'section_bpm') + { + _song.notes[curSection].bpm = Std.int(nums.value); + updateGrid(); + } } // FlxG.log.add(id + " WEED " + sender + " WEED " + data + " WEED " + params); @@ -448,12 +468,12 @@ class ChartingState extends MusicBeatState } } - _song.bpm = Conductor.bpm; + _song.bpm = tempBpm; - if (FlxG.keys.justPressed.UP) - Conductor.changeBPM(Conductor.bpm + 1); - if (FlxG.keys.justPressed.DOWN) - Conductor.changeBPM(Conductor.bpm - 1); + /* if (FlxG.keys.justPressed.UP) + Conductor.changeBPM(Conductor.bpm + 1); + if (FlxG.keys.justPressed.DOWN) + Conductor.changeBPM(Conductor.bpm - 1); */ if (FlxG.keys.justPressed.RIGHT) changeSection(curSection + 1); @@ -471,6 +491,7 @@ class ChartingState extends MusicBeatState if (_song.notes[sec] != null) { curSection = sec; + updateGrid(); if (updateMusic) @@ -489,6 +510,7 @@ class ChartingState extends MusicBeatState updateCurStep(); } + updateGrid(); updateSectionUI(); } } @@ -514,6 +536,8 @@ class ChartingState extends MusicBeatState stepperLength.value = sec.lengthInSteps; check_mustHitSection.checked = sec.mustHitSection; + check_changeBPM.checked = sec.changeBPM; + stepperSectionBPM.value = sec.bpm; } function updateNoteUI():Void @@ -535,6 +559,15 @@ class ChartingState extends MusicBeatState var sectionInfo:Array = _song.notes[curSection].sectionNotes; + if (_song.notes[curSection].changeBPM && _song.notes[curSection].bpm > 0) + { + Conductor.changeBPM(_song.notes[curSection].bpm); + } + else + { + Conductor.changeBPM(tempBpm); + } + /* // PORT BULLSHIT, INCASE THERE'S NO SUSTAIN DATA FOR A NOTE for (sec in 0..._song.notes.length) { @@ -567,8 +600,7 @@ class ChartingState extends MusicBeatState if (daSus > 0) { var sustainVis:FlxSprite = new FlxSprite(note.x + (GRID_SIZE / 2), - note.y + getYfromStrum(note.strumTime + Conductor.stepCrochet)).makeGraphic(8, - Math.floor(FlxMath.remapToRange(daSus, 0, Conductor.stepCrochet * 16, 0, gridBG.height))); + note.y + GRID_SIZE).makeGraphic(8, Math.floor(FlxMath.remapToRange(daSus, 0, Conductor.stepCrochet * 16, 0, gridBG.height))); curRenderedSustains.add(sustainVis); } } @@ -576,7 +608,16 @@ class ChartingState extends MusicBeatState private function addSection(lengthInSteps:Int = 16):Void { - _song.notes.push(new Section(lengthInSteps)); + var sec:SwagSection = { + lengthInSteps: lengthInSteps, + bpm: _song.bpm, + changeBPM: false, + mustHitSection: true, + sectionNotes: [], + typeOfSection: 0 + }; + + _song.notes.push(sec); } function selectNote(note:Note):Void @@ -611,6 +652,16 @@ class ChartingState extends MusicBeatState updateGrid(); } + function clearSong():Void + { + for (daSection in 0..._song.notes.length) + { + _song.notes[daSection].sectionNotes = []; + } + + updateGrid(); + } + private function addNote():Void { var noteStrum = Math.round(getStrumTime(dummyArrow.y) + (curSection * (Conductor.stepCrochet * 16))); @@ -638,7 +689,7 @@ class ChartingState extends MusicBeatState return FlxMath.remapToRange(strumTime, 0, 16 * Conductor.stepCrochet, gridBG.y, gridBG.y + gridBG.height); } - function calculateSectionLengths(?sec:Section):Int + function calculateSectionLengths(?sec:SwagSection):Int { var daLength:Int = 0; diff --git a/source/FreeplayState.hx b/source/FreeplayState.hx index 626e40256..f2a717fec 100644 --- a/source/FreeplayState.hx +++ b/source/FreeplayState.hx @@ -14,7 +14,7 @@ import flixel.text.FlxText; class FreeplayState extends MusicBeatState { - var songs:Array = ["Bopeebo", "Dadbattle", "Fresh", "Tutorial\nlol"]; + var songs:Array = ["Bopeebo", "Dadbattle", "Fresh", "Tutorial", "Spookeez", "South", "Monster"]; var selector:FlxText; var curSelected:Int = 0; @@ -30,11 +30,17 @@ class FreeplayState extends MusicBeatState for (i in 0...songs.length) { - var songText:Alphabet = new Alphabet(40, (70 * i) + 30, songs[i]); + var songText:Alphabet = new Alphabet(0, (70 * i) + 30, songs[i], true, false); add(songText); + songText.x += 40; + // DONT PUT X IN THE FIRST PARAMETER OF new ALPHABET() !! + // songText.screenCenter(X); } + FlxG.sound.playMusic('assets/music/title' + TitleState.soundExt, 0); + FlxG.sound.music.fadeIn(2, 0, 0.8); selector = new FlxText(); + selector.size = 40; selector.text = ">"; add(selector); @@ -46,11 +52,16 @@ class FreeplayState extends MusicBeatState override function update(elapsed:Float) { - if (FlxG.keys.justPressed.UP) + super.update(elapsed); + var upP = controls.UP_P; + var downP = controls.DOWN_P; + var accepted = controls.ACCEPT; + + if (upP) { curSelected -= 1; } - if (FlxG.keys.justPressed.DOWN) + if (downP) { curSelected += 1; } @@ -73,10 +84,11 @@ class FreeplayState extends MusicBeatState selector.y = (70 * curSelected) + 30; - if (FlxG.keys.justPressed.ENTER) + if (accepted) { PlayState.SONG = Song.loadFromJson(songs[curSelected].toLowerCase()); FlxG.switchState(new PlayState()); + FlxG.sound.music.stop(); } #if switch @@ -84,9 +96,8 @@ class FreeplayState extends MusicBeatState { PlayState.SONG = Song.loadFromJson(songs[curSelected].toLowerCase()); FlxG.switchState(new PlayState()); + FlxG.sound.music.stop(); } #end - - super.update(elapsed); } } diff --git a/source/GameOverState.hx b/source/GameOverState.hx index d3de48311..064325478 100644 --- a/source/GameOverState.hx +++ b/source/GameOverState.hx @@ -10,26 +10,44 @@ import flixel.tweens.FlxTween; class GameOverState extends FlxTransitionableState { + var bfX:Float = 0; + var bfY:Float = 0; + + public function new(x:Float, y:Float) + { + super(); + + bfX = x; + bfY = y; + } + override function create() { - var loser:FlxSprite = new FlxSprite(100, 100); - var loseTex = FlxAtlasFrames.fromSparrow(AssetPaths.lose__png, AssetPaths.lose__xml); - loser.frames = loseTex; - loser.animation.addByPrefix('lose', 'lose', 24, false); - loser.animation.play('lose'); - add(loser); + /* var loser:FlxSprite = new FlxSprite(100, 100); + var loseTex = FlxAtlasFrames.fromSparrow(AssetPaths.lose__png, AssetPaths.lose__xml); + loser.frames = loseTex; + loser.animation.addByPrefix('lose', 'lose', 24, false); + loser.animation.play('lose'); + // add(loser); */ - var restart:FlxSprite = new FlxSprite(500, 50).loadGraphic(AssetPaths.restart__png); - restart.setGraphicSize(Std.int(restart.width * 0.6)); - restart.updateHitbox(); - restart.alpha = 0; - restart.antialiasing = true; - add(restart); + var bf:Boyfriend = new Boyfriend(bfX, bfY); + // bf.scrollFactor.set(); + add(bf); + bf.playAnim('firstDeath'); + + FlxG.camera.follow(bf, LOCKON, 0.001); + /* + var restart:FlxSprite = new FlxSprite(500, 50).loadGraphic(AssetPaths.restart__png); + restart.setGraphicSize(Std.int(restart.width * 0.6)); + restart.updateHitbox(); + restart.alpha = 0; + restart.antialiasing = true; + // add(restart); */ FlxG.sound.music.fadeOut(2, FlxG.sound.music.volume * 0.6); - FlxTween.tween(restart, {alpha: 1}, 1, {ease: FlxEase.quartInOut}); - FlxTween.tween(restart, {y: restart.y + 40}, 7, {ease: FlxEase.quartInOut, type: PINGPONG}); + // FlxTween.tween(restart, {alpha: 1}, 1, {ease: FlxEase.quartInOut}); + // FlxTween.tween(restart, {y: restart.y + 40}, 7, {ease: FlxEase.quartInOut, type: PINGPONG}); super.create(); } @@ -48,6 +66,8 @@ class GameOverState extends FlxTransitionableState pressed = true; } + pressed = false; + if (pressed && !fading) { fading = true; diff --git a/source/GameOverSubstate.hx b/source/GameOverSubstate.hx new file mode 100644 index 000000000..1a7d89d58 --- /dev/null +++ b/source/GameOverSubstate.hx @@ -0,0 +1,89 @@ +package; + +import flixel.FlxG; +import flixel.FlxObject; +import flixel.FlxSubState; +import flixel.math.FlxPoint; +import flixel.util.FlxColor; +import flixel.util.FlxTimer; + +class GameOverSubstate extends MusicBeatSubstate +{ + var bf:Boyfriend; + var camFollow:FlxObject; + + public function new(x:Float, y:Float) + { + super(); + + Conductor.songPosition = 0; + + bf = new Boyfriend(x, y); + add(bf); + + camFollow = new FlxObject(bf.getGraphicMidpoint().x, bf.getGraphicMidpoint().y, 1, 1); + add(camFollow); + + FlxG.sound.play('assets/sounds/fnf_loss_sfx' + TitleState.soundExt); + Conductor.changeBPM(100); + + // FlxG.camera.followLerp = 1; + // FlxG.camera.focusOn(FlxPoint.get(FlxG.width / 2, FlxG.height / 2)); + FlxG.camera.scroll.set(); + FlxG.camera.target = null; + + bf.playAnim('firstDeath'); + } + + override function update(elapsed:Float) + { + super.update(elapsed); + + if (FlxG.keys.justPressed.ENTER) + { + endBullshit(); + } + + if (bf.animation.curAnim.name == 'firstDeath' && bf.animation.curAnim.curFrame == 12) + { + FlxG.camera.follow(camFollow, LOCKON, 0.01); + } + + if (bf.animation.curAnim.name == 'firstDeath' && bf.animation.curAnim.finished) + { + FlxG.sound.playMusic('assets/music/gameOver' + TitleState.soundExt); + } + + if (FlxG.sound.music.playing) + { + Conductor.songPosition = FlxG.sound.music.time; + } + } + + override function beatHit() + { + super.beatHit(); + + FlxG.log.add('beat'); + } + + var isEnding:Bool = false; + + function endBullshit():Void + { + if (!isEnding) + { + isEnding = true; + bf.playAnim('deathConfirm', true); + FlxG.sound.music.stop(); + FlxG.sound.play('assets/music/gameOverEnd' + TitleState.soundExt); + new FlxTimer().start(0.7, function(tmr:FlxTimer) + { + FlxG.camera.fade(FlxColor.BLACK, 2, false, function() + { + FlxG.switchState(new PlayState()); + }); + }); + } + } +} diff --git a/source/Main.hx b/source/Main.hx index e2e0f4dee..13376db44 100644 --- a/source/Main.hx +++ b/source/Main.hx @@ -9,7 +9,7 @@ class Main extends Sprite public function new() { super(); - addChild(new FlxGame(0, 0, FreeplayState)); + addChild(new FlxGame(0, 0, TitleState)); #if !mobile addChild(new FPS(10, 3, 0xFFFFFF)); diff --git a/source/MenuItem.hx b/source/MenuItem.hx new file mode 100644 index 000000000..5e7ddc763 --- /dev/null +++ b/source/MenuItem.hx @@ -0,0 +1,38 @@ +package; + +import flixel.FlxSprite; +import flixel.graphics.frames.FlxAtlasFrames; +import flixel.group.FlxSpriteGroup; +import flixel.math.FlxMath; + +class MenuItem extends FlxSpriteGroup +{ + public var targetY:Float = 0; + + public function new(x:Float, y:Float, weekNum:Int = 0, unlocked:Bool = false) + { + super(x, y); + + var tex = FlxAtlasFrames.fromSparrow(AssetPaths.campaign_menu_UI_assets__png, AssetPaths.campaign_menu_UI_assets__xml); + + var week:FlxSprite = new FlxSprite(); + week.frames = tex; + week.animation.addByPrefix('week0', "WEEK1 select", 24); + week.animation.addByPrefix('week1', "week2 select", 24); + add(week); + + week.animation.play('week' + weekNum); + week.updateHitbox(); + + if (!unlocked) + { + week.alpha = 0.6; + } + } + + override function update(elapsed:Float) + { + super.update(elapsed); + y = FlxMath.lerp(y, (targetY * 120) + 480, 0.17); + } +} diff --git a/source/MusicBeatState.hx b/source/MusicBeatState.hx index 321dc29c7..791cd95c7 100644 --- a/source/MusicBeatState.hx +++ b/source/MusicBeatState.hx @@ -15,6 +15,10 @@ class MusicBeatState extends FlxUIState private var curStep:Int = 0; private var curBeat:Int = 0; + private var controls(get, never):Controls; + + inline function get_controls():Controls + return PlayerSettings.player1.controls; override function create() { diff --git a/source/MusicBeatSubstate.hx b/source/MusicBeatSubstate.hx new file mode 100644 index 000000000..0e452e9cd --- /dev/null +++ b/source/MusicBeatSubstate.hx @@ -0,0 +1,78 @@ +package; + +import flixel.FlxSubState; + +class MusicBeatSubstate extends FlxSubState +{ + public function new() + { + super(); + } + + private var lastBeat:Float = 0; + private var lastStep:Float = 0; + + private var totalBeats:Int = 0; + private var totalSteps:Int = 0; + + private var curStep:Int = 0; + private var curBeat:Int = 0; + private var controls(get, never):Controls; + + inline function get_controls():Controls + return PlayerSettings.player1.controls; + + override function create() + { + #if (!web) + TitleState.soundExt = '.ogg'; + #end + + super.create(); + } + + override function update(elapsed:Float) + { + everyStep(); + + updateCurStep(); + curBeat = Math.round(curStep / 4); + + super.update(elapsed); + } + + /** + * CHECKS EVERY FRAME + */ + private function everyStep():Void + { + if (Conductor.songPosition > lastStep + Conductor.stepCrochet - Conductor.safeZoneOffset + || Conductor.songPosition < lastStep + Conductor.safeZoneOffset) + { + if (Conductor.songPosition > lastStep + Conductor.stepCrochet) + { + stepHit(); + } + } + } + + private function updateCurStep():Void + { + curStep = Math.floor(Conductor.songPosition / Conductor.stepCrochet); + } + + public function stepHit():Void + { + totalSteps += 1; + lastStep += Conductor.stepCrochet; + + if (totalSteps % 4 == 0) + beatHit(); + } + + public function beatHit():Void + { + lastBeat += Conductor.crochet; + totalBeats += 1; + } +} diff --git a/source/Note.hx b/source/Note.hx index 521dc22b8..748cbe185 100644 --- a/source/Note.hx +++ b/source/Note.hx @@ -130,8 +130,9 @@ class Note extends FlxSprite if (mustPress) { + // The * 0.5 us so that its easier to hit them too late, instead of too early if (strumTime > Conductor.songPosition - Conductor.safeZoneOffset - && strumTime < Conductor.songPosition + Conductor.safeZoneOffset) + && strumTime < Conductor.songPosition + (Conductor.safeZoneOffset * 0.5)) { canBeHit = true; } diff --git a/source/PauseSubState.hx b/source/PauseSubState.hx index a4a1278dc..dbf232b6d 100644 --- a/source/PauseSubState.hx +++ b/source/PauseSubState.hx @@ -9,7 +9,7 @@ import flixel.util.FlxColor; class PauseSubState extends FlxSubState { - public function new() + public function new(x:Float, y:Float) { super(); var bg:FlxSprite = new FlxSprite().makeGraphic(FlxG.width, FlxG.height, FlxColor.BLACK); @@ -17,6 +17,12 @@ class PauseSubState extends FlxSubState bg.scrollFactor.set(); add(bg); + var bf:Boyfriend = new Boyfriend(x, y); + bf.scrollFactor.set(); + // add(bf); + + bf.playAnim('firstDeath'); + bg.cameras = [FlxG.cameras.list[1]]; } diff --git a/source/PlayState.hx b/source/PlayState.hx index ef165d435..9f4784dfb 100644 --- a/source/PlayState.hx +++ b/source/PlayState.hx @@ -48,9 +48,6 @@ class PlayState extends MusicBeatState private var strumLine:FlxSprite; private var curSection:Int = 0; - private var sectionScores:Array = [[], []]; - private var sectionLengths:Array = []; - private var camFollow:FlxObject; private var strumLineNotes:FlxTypedGroup; private var playerStrums:FlxTypedGroup; @@ -72,11 +69,6 @@ class PlayState extends MusicBeatState private var camHUD:FlxCamera; private var camGame:FlxCamera; - var controls(get, never):Controls; - - inline function get_controls():Controls - return PlayerSettings.player1.controls; - override public function create() { // var gameCam:FlxCamera = FlxG.camera; @@ -89,14 +81,14 @@ class PlayState extends MusicBeatState FlxCamera.defaultCameras = [camGame]; - PlayerSettings.init(); - persistentUpdate = true; persistentDraw = true; if (SONG == null) SONG = Song.loadFromJson(curLevel); + Conductor.changeBPM(SONG.bpm); + var bg:FlxSprite = new FlxSprite(-600, -200).loadGraphic(AssetPaths.stageback__png); // bg.setGraphicSize(Std.int(bg.width * 2.5)); // bg.updateHitbox(); @@ -135,6 +127,8 @@ class PlayState extends MusicBeatState gf.visible = false; case "spooky": dad.y += 200; + case "monster": + dad.y += 100; } boyfriend = new Boyfriend(770, 450); @@ -194,6 +188,12 @@ class PlayState extends MusicBeatState strumLineNotes.cameras = [camHUD]; notes.cameras = [camHUD]; + healthBar.cameras = [camHUD]; + healthBarBG.cameras = [camHUD]; + healthHeads.cameras = [camHUD]; + + // if (SONG.song == 'South') + // FlxG.camera.alpha = 0.7; // UI_camera.zoom = 1; // cameras = [FlxG.cameras.list[1]]; @@ -213,6 +213,10 @@ class PlayState extends MusicBeatState startTimer = new FlxTimer().start(Conductor.crochet / 1000, function(tmr:FlxTimer) { + dad.dance(); + gf.dance(); + boyfriend.playAnim('idle'); + switch (swagCounter) { case 0: @@ -307,11 +311,6 @@ class PlayState extends MusicBeatState // NEW SHIT noteData = songData.notes; - for (i in 1...songData.sections + 1) - { - // noteData.push(ChartParser.parse(songData.song.toLowerCase(), i)); - } - var playerCounter:Int = 0; var daBeats:Int = 0; // Not exactly representative of 'daBeats' lol, just how much it has looped @@ -321,15 +320,12 @@ class PlayState extends MusicBeatState for (songNotes in section.sectionNotes) { - sectionScores[0].push(0); - sectionScores[1].push(0); - var daStrumTime:Float = songNotes[0]; var daNoteData:Int = Std.int(songNotes[1] % 4); var gottaHitNote:Bool = section.mustHitSection; - if (songNotes.noteData > 3) + if (songNotes[1] > 3) { gottaHitNote = !section.mustHitSection; } @@ -374,26 +370,7 @@ class PlayState extends MusicBeatState else { } - - // WILL HAVE TO REDO SCORE SYSTEM - /* if (section.mustHitSection) - { - if (playerCounter == 1) // is the player - { - swagNote.mustPress = true; - } - else - { - //sectionScores[0][daBeats] += swagNote.noteScore; - } - } - */ } - - /* // only need to do it once - if (section.mustHitSection) - sectionLengths.push(Math.round(coolSection / 4)); - */ daBeats += 1; } @@ -471,8 +448,6 @@ class PlayState extends MusicBeatState } } - var sectionScored:Bool = false; - override function openSubState(SubState:FlxSubState) { if (paused) @@ -526,7 +501,7 @@ class PlayState extends MusicBeatState persistentDraw = true; paused = true; - openSubState(new PauseSubState()); + openSubState(new PauseSubState(boyfriend.getScreenPosition().x, boyfriend.getScreenPosition().y)); } if (FlxG.keys.justPressed.ESCAPE) @@ -581,16 +556,6 @@ class PlayState extends MusicBeatState // Conductor.lastSongPos = FlxG.sound.music.time; } - var playerTurn:Int = 0; - if (sectionLengths.length > curSection) - playerTurn = totalBeats % (sectionLengths[curSection] * 8); - - if (playerTurn == (sectionLengths[curSection] * 8) - 1 && !sectionScored) - { - // popUpScore(); - sectionScored = true; - } - if (generatedMusic && PlayState.SONG.notes[Std.int(curStep / 16)] != null) { if (curBeat % 4 == 0) @@ -625,11 +590,6 @@ class PlayState extends MusicBeatState FlxG.camera.zoom = FlxMath.lerp(1.05, FlxG.camera.zoom, 0.96); } - if (playerTurn < 4) - { - sectionScored = false; - } - FlxG.watch.addQuick("beatShit", totalBeats); if (curSong == 'Fresh') @@ -667,7 +627,17 @@ class PlayState extends MusicBeatState if (health <= 0) { boyfriend.stunned = true; - FlxG.switchState(new GameOverState()); + + persistentUpdate = false; + persistentDraw = false; + paused = true; + + vocals.stop(); + FlxG.sound.music.stop(); + + openSubState(new GameOverSubstate(boyfriend.getScreenPosition().x, boyfriend.getScreenPosition().y)); + + // FlxG.switchState(new GameOverState(boyfriend.getScreenPosition().x, boyfriend.getScreenPosition().y)); } if (unspawnNotes[0] != null) @@ -702,15 +672,18 @@ class PlayState extends MusicBeatState switch (Math.abs(daNote.noteData)) { case 2: - dad.playAnim('singUP'); + dad.playAnim('singUP', true); case 3: - dad.playAnim('singRIGHT'); + dad.playAnim('singRIGHT', true); case 1: - dad.playAnim('singDOWN'); + dad.playAnim('singDOWN', true); case 0: - dad.playAnim('singLEFT'); + dad.playAnim('singLEFT', true); } + if (SONG.needsVoices) + vocals.volume = 1; + daNote.kill(); notes.remove(daNote, true); daNote.destroy(); @@ -724,7 +697,7 @@ class PlayState extends MusicBeatState { if (daNote.tooLate) { - health -= 0.05; + health -= 0.03; vocals.volume = 0; } @@ -744,12 +717,10 @@ class PlayState extends MusicBeatState private function popUpScore(strumtime:Float):Void { var noteDiff:Float = Math.abs(strumtime - Conductor.songPosition); - // boyfriend.playAnim('hey'); - // vocals.volume = 1; + vocals.volume = 1; var placement:String = Std.string(combo); - // var placement:String = sectionScores[1][curSection] + '/' + sectionScores[0][curSection]; var coolText:FlxText = new FlxText(0, 0, 0, placement, 32); coolText.screenCenter(); @@ -878,72 +849,6 @@ class PlayState extends MusicBeatState var downR = controls.DOWN_R; var leftR = controls.LEFT_R; - /* - var gamepad = FlxG.gamepads.lastActive; - if (gamepad != null) - { - if (gamepad.anyPressed(["DPAD_LEFT", "LEFT_STICK_DIGITAL_LEFT", X])) - { - left = true; - } - - if (gamepad.anyPressed(["DPAD_RIGHT", "LEFT_STICK_DIGITAL_RIGHT", B])) - { - right = true; - } - - if (gamepad.anyPressed(['DPAD_UP', "LEFT_STICK_DIGITAL_UP", Y])) - { - up = true; - } - - if (gamepad.anyPressed(["DPAD_DOWN", "LEFT_STICK_DIGITAL_DOWN", A])) - { - down = true; - } - - if (gamepad.anyJustPressed(["DPAD_LEFT", "LEFT_STICK_DIGITAL_LEFT", X])) - { - leftP = true; - } - - if (gamepad.anyJustPressed(["DPAD_RIGHT", "LEFT_STICK_DIGITAL_RIGHT", B])) - { - rightP = true; - } - - if (gamepad.anyJustPressed(['DPAD_UP', "LEFT_STICK_DIGITAL_UP", Y])) - { - upP = true; - } - - if (gamepad.anyJustPressed(["DPAD_DOWN", "LEFT_STICK_DIGITAL_DOWN", A])) - { - downP = true; - } - - if (gamepad.anyJustReleased(["DPAD_LEFT", "LEFT_STICK_DIGITAL_LEFT", X])) - { - leftR = true; - } - - if (gamepad.anyJustReleased(["DPAD_RIGHT", "LEFT_STICK_DIGITAL_RIGHT", B])) - { - rightR = true; - } - - if (gamepad.anyJustReleased(['DPAD_UP', "LEFT_STICK_DIGITAL_UP", Y])) - { - upR = true; - } - - if (gamepad.anyJustReleased(["DPAD_DOWN", "LEFT_STICK_DIGITAL_DOWN", A])) - { - downR = true; - } - } - */ - // FlxG.watch.addQuick('asdfa', upP); if ((upP || rightP || downP || leftP) && !boyfriend.stunned && generatedMusic) { @@ -1067,14 +972,16 @@ class PlayState extends MusicBeatState { if (!boyfriend.stunned) { - health -= 0.08; + health -= 0.055; if (combo > 5) { gf.playAnim('sad'); } combo = 0; - FlxG.sound.play('assets/sounds/missnote' + FlxG.random.int(1, 3) + TitleState.soundExt, FlxG.random.float(0.05, 0.2)); + FlxG.sound.play('assets/sounds/missnote' + FlxG.random.int(1, 3) + TitleState.soundExt, FlxG.random.float(0.1, 0.2)); + // FlxG.sound.play('assets/sounds/missnote1' + TitleState.soundExt, 1, false); + // FlxG.log.add('played imss note'); boyfriend.stunned = true; @@ -1154,8 +1061,11 @@ class PlayState extends MusicBeatState { if (!note.wasGoodHit) { - popUpScore(note.strumTime); - combo += 1; + if (!note.isSustainNote) + { + popUpScore(note.strumTime); + combo += 1; + } if (note.noteData >= 0) health += 0.03; @@ -1182,7 +1092,6 @@ class PlayState extends MusicBeatState } }); - sectionScores[1][curSection] += note.noteScore; note.wasGoodHit = true; vocals.volume = 1; @@ -1194,12 +1103,22 @@ class PlayState extends MusicBeatState override function stepHit() { - if (vocals.time > Conductor.songPosition + Conductor.stepCrochet || vocals.time < Conductor.songPosition - Conductor.stepCrochet) + if (SONG.needsVoices) { - vocals.pause(); - vocals.time = Conductor.songPosition; - vocals.play(); + if (vocals.time > Conductor.songPosition + Conductor.stepCrochet + || vocals.time < Conductor.songPosition - Conductor.stepCrochet) + { + vocals.pause(); + vocals.time = Conductor.songPosition; + vocals.play(); + } } + + if (dad.curCharacter == 'spooky' && totalSteps % 4 == 2) + { + // dad.dance(); + } + super.stepHit(); } @@ -1212,16 +1131,40 @@ class PlayState extends MusicBeatState notes.sort(FlxSort.byY, FlxSort.DESCENDING); } + FlxG.log.add('change bpm' + SONG.notes[Std.int(curStep / 16)].changeBPM); + if (SONG.notes[Std.int(curStep / 16)].changeBPM) + { + Conductor.changeBPM(SONG.notes[Std.int(curStep / 16)].bpm); + FlxG.log.add('CHANGED BPM!'); + } + else + Conductor.changeBPM(SONG.bpm); + if (camZooming && FlxG.camera.zoom < 1.35 && totalBeats % 4 == 0) FlxG.camera.zoom += 0.025; - dad.dance(); + // Dad doesnt interupt his own notes + if (SONG.notes[Std.int(curStep / 16)].mustHitSection) + dad.dance(); + healthHeads.setGraphicSize(Std.int(healthHeads.width + 20)); if (totalBeats % gfSpeed == 0) + { gf.dance(); + } if (!boyfriend.animation.curAnim.name.startsWith("sing")) boyfriend.playAnim('idle'); + + if (totalBeats % 8 == 6) + { + boyfriend.playAnim('hey', true); + + if (SONG.song == 'Tutorial' && dad.curCharacter == 'gf') + { + dad.playAnim('cheer', true); + } + } } } diff --git a/source/Section.hx b/source/Section.hx index c811e306d..19770b6ea 100644 --- a/source/Section.hx +++ b/source/Section.hx @@ -6,6 +6,8 @@ typedef SwagSection = var lengthInSteps:Int; var typeOfSection:Int; var mustHitSection:Bool; + var bpm:Int; + var changeBPM:Bool; } class Section diff --git a/source/StoryMenuState.hx b/source/StoryMenuState.hx new file mode 100644 index 000000000..bae5703c0 --- /dev/null +++ b/source/StoryMenuState.hx @@ -0,0 +1,172 @@ +package; + +import flixel.FlxG; +import flixel.FlxSprite; +import flixel.graphics.frames.FlxAtlasFrames; +import flixel.group.FlxGroup.FlxTypedGroup; +import flixel.group.FlxGroup; +import flixel.text.FlxText; + +using StringTools; + +class StoryMenuState extends MusicBeatState +{ + var scoreText:FlxText; + + var weekData:Array = [['Tutorial', 'Bopeebo', 'Fresh', 'Dad Battle'], ['Spookeez', 'South', 'Monster']]; + var weekUnlocked:Array = [true, false]; + + var curWeek:Int = 0; + + var txtTracklist:FlxText; + + var grpWeekText:FlxTypedGroup; + + var grpLocks:FlxTypedGroup; + + var difficultySelectors:FlxGroup; + + override function create() + { + scoreText = new FlxText(10, 10, 0, "SCORE: 49324858", 36); + scoreText.setFormat("VCR OSD Mono", 32); + add(scoreText); + + var rankText:FlxText = new FlxText(0, 10); + rankText.text = 'RANK: GREAT'; + rankText.setFormat("assets/fonts/vcr.ttf", 32); + rankText.size = scoreText.size; + rankText.screenCenter(X); + add(rankText); + + var ui_tex = FlxAtlasFrames.fromSparrow(AssetPaths.campaign_menu_UI_assets__png, AssetPaths.campaign_menu_UI_assets__xml); + var yellowBG:FlxSprite = new FlxSprite(0, 56).makeGraphic(FlxG.width, 400, 0xFFF9CF51); + + grpWeekText = new FlxTypedGroup(); + add(grpWeekText); + + grpLocks = new FlxTypedGroup(); + add(grpLocks); + + for (i in 0...weekData.length) + { + var unlocked:Bool = true; + + if (i == 1) + unlocked = false; + + var weekThing:MenuItem = new MenuItem(0, yellowBG.y + yellowBG.height + 10, i, unlocked); + weekThing.y += ((weekThing.height + 20) * i); + weekThing.targetY = i; + grpWeekText.add(weekThing); + + weekThing.screenCenter(X); + weekThing.antialiasing = true; + // weekThing.updateHitbox(); + + if (!weekUnlocked[i]) + { + var lock:FlxSprite = new FlxSprite(weekThing.width + 10 + weekThing.x); + lock.frames = ui_tex; + lock.animation.addByPrefix('lock', 'lock'); + lock.animation.play('lock'); + lock.ID = i; + lock.antialiasing = true; + grpLocks.add(lock); + } + } + + difficultySelectors = new FlxGroup(); + add(difficultySelectors); + + var leftArrow:FlxSprite = new FlxSprite(grpWeekText.members[0].x + 400, grpWeekText.members[0].y + 10); + leftArrow.frames = ui_tex; + leftArrow.animation.addByPrefix('idle', "arrow left"); + leftArrow.animation.play('idle'); + difficultySelectors.add(leftArrow); + + var sprDifficulty:FlxSprite = new FlxSprite(leftArrow.x + 70, leftArrow.y); + sprDifficulty.frames = ui_tex; + sprDifficulty.animation.addByPrefix('easy', 'EASY'); + sprDifficulty.animation.addByPrefix('normal', 'NORMAL'); + sprDifficulty.animation.addByPrefix('hard', 'HARD'); + sprDifficulty.animation.play('easy'); + difficultySelectors.add(sprDifficulty); + + var rightArrow:FlxSprite = new FlxSprite(sprDifficulty.x + sprDifficulty.width + 20, sprDifficulty.y); + rightArrow.frames = ui_tex; + rightArrow.animation.addByPrefix('idle', 'arrow right'); + rightArrow.animation.play('idle'); + difficultySelectors.add(rightArrow); + + add(yellowBG); + + txtTracklist = new FlxText(FlxG.width * 0.05, yellowBG.x + yellowBG.height + 100, 0, "Tracks", 32); + txtTracklist.alignment = CENTER; + txtTracklist.font = rankText.font; + txtTracklist.color = 0xFFe55777; + add(txtTracklist); + + updateText(); + + super.create(); + } + + override function update(elapsed:Float) + { + // scoreText.setFormat('VCR OSD Mono', 32); + // scoreText.text = "Score SHIT"; + // FlxG.watch.addQuick('font', scoreText.font); + + difficultySelectors.visible = weekUnlocked[curWeek]; + + grpLocks.forEach(function(lock:FlxSprite) + { + lock.y = grpWeekText.members[lock.ID].y; + }); + + if (controls.UP_P) + changeWeek(-1); + if (controls.DOWN_P) + changeWeek(1); + + super.update(elapsed); + } + + function changeWeek(change:Int = 0):Void + { + curWeek += change; + + if (curWeek >= weekData.length) + curWeek = 0; + if (curWeek < 0) + curWeek = weekData.length - 1; + + var bullShit:Int = 0; + + for (item in grpWeekText.members) + { + item.targetY = bullShit - curWeek; + bullShit++; + } + + updateText(); + } + + function updateText() + { + txtTracklist.text = "Tracks\n"; + + var stringThing:Array = weekData[curWeek]; + + for (i in stringThing) + { + txtTracklist.text += "\n" + i; + } + + txtTracklist.text = txtTracklist.text.toUpperCase(); + + txtTracklist.screenCenter(X); + txtTracklist.x -= FlxG.width * 0.35; + } +} diff --git a/source/TitleState.hx b/source/TitleState.hx index b575df457..beb3c9d64 100644 --- a/source/TitleState.hx +++ b/source/TitleState.hx @@ -25,7 +25,8 @@ class TitleState extends MusicBeatState var blackScreen:FlxSprite; var credGroup:FlxGroup; - var credTextShit:FlxText; + var credTextShit:Alphabet; + var textGroup:FlxGroup; override public function create():Void { @@ -33,8 +34,21 @@ class TitleState extends MusicBeatState TitleState.soundExt = '.ogg'; #end + PlayerSettings.init(); + + // DEBUG BULLSHIT + super.create(); + #if SKIP_TO_PLAYSTATE + FlxG.switchState(new StoryMenuState()); + #else + startIntro(); + #end + } + + function startIntro() + { if (!initialized) { var diamond:FlxGraphic = FlxGraphic.fromClass(GraphicTransTileDiamond); @@ -78,19 +92,21 @@ class TitleState extends MusicBeatState credGroup = new FlxGroup(); add(credGroup); + textGroup = new FlxGroup(); blackScreen = new FlxSprite().makeGraphic(FlxG.width, FlxG.height, FlxColor.BLACK); credGroup.add(blackScreen); - credTextShit = new FlxText(0, 0, 0, "ninjamuffin99\nPhantomArcade\nEvilsk8er\nAnd Kawaisprite", 24); + credTextShit = new Alphabet(0, 0, "ninjamuffin99\nPhantomArcade\nkawaisprite\nevilsk8er", true); credTextShit.screenCenter(); - credTextShit.alignment = CENTER; + + // credTextShit.alignment = CENTER; credTextShit.visible = false; FlxTween.tween(credTextShit, {y: credTextShit.y + 20}, 2.9, {ease: FlxEase.quadInOut, type: PINGPONG}); - credGroup.add(credTextShit); + // credGroup.add(credTextShit); FlxG.sound.playMusic('assets/music/freakyMenu' + TitleState.soundExt, 0, false); @@ -127,7 +143,7 @@ class TitleState extends MusicBeatState new FlxTimer().start(2, function(tmr:FlxTimer) { - FlxG.switchState(new PlayState()); + FlxG.switchState(new FreeplayState()); }); FlxG.sound.play('assets/music/titleShoot' + TitleState.soundExt, 0.7); } @@ -135,6 +151,36 @@ class TitleState extends MusicBeatState super.update(elapsed); } + function createCoolText(textArray:Array) + { + for (i in 0...textArray.length) + { + var money:Alphabet = new Alphabet(0, 0, textArray[i], true, false); + money.screenCenter(X); + money.y += (i * 60) + 200; + credGroup.add(money); + textGroup.add(money); + } + } + + function addMoreText(text:String) + { + var coolText:Alphabet = new Alphabet(0, 0, text, true, false); + coolText.screenCenter(X); + coolText.y += (textGroup.length * 60) + 200; + credGroup.add(coolText); + textGroup.add(coolText); + } + + function deleteCoolText() + { + while (textGroup.members.length > 0) + { + credGroup.remove(textGroup.members[0], true); + textGroup.remove(textGroup.members[0], true); + } + } + override function beatHit() { super.beatHit(); @@ -144,35 +190,47 @@ class TitleState extends MusicBeatState switch (curBeat) { case 1: - credTextShit.visible = true; + createCoolText(['ninjamuffin99', 'phantomArcade', 'kawaisprite', 'evilsk8er']); + // credTextShit.visible = true; case 3: - credTextShit.text += '\npresent...'; + addMoreText('present'); + // credTextShit.text += '\npresent...'; + // credTextShit.addText(); case 4: - credTextShit.visible = false; - credTextShit.text = 'In association \nwith'; - credTextShit.screenCenter(); + deleteCoolText(); + // credTextShit.visible = false; + // credTextShit.text = 'In association \nwith'; + // credTextShit.screenCenter(); case 5: - credTextShit.visible = true; + createCoolText(['In association', 'with']); case 7: - credTextShit.text += '\nNewgrounds'; + addMoreText('newgrounds'); + // credTextShit.text += '\nNewgrounds'; case 8: - credTextShit.visible = false; - credTextShit.text = 'Shoutouts Tom Fulp'; - credTextShit.screenCenter(); + deleteCoolText(); + // credTextShit.visible = false; + + // credTextShit.text = 'Shoutouts Tom Fulp'; + // credTextShit.screenCenter(); case 9: - credTextShit.visible = true; + createCoolText(['Shoutouts Tom Fulp']); + // credTextShit.visible = true; case 11: - credTextShit.text += '\nlmao'; + addMoreText('lmao'); + // credTextShit.text += '\nlmao'; case 12: - credTextShit.visible = false; - credTextShit.text = "Friday"; - credTextShit.screenCenter(); + deleteCoolText(); + // credTextShit.visible = false; + // credTextShit.text = "Friday"; + // credTextShit.screenCenter(); case 13: - credTextShit.visible = true; + addMoreText('Friday'); + // credTextShit.visible = true; case 14: - credTextShit.text += '\nNight'; + addMoreText('Night'); + // credTextShit.text += '\nNight'; case 15: - credTextShit.text += '\nFunkin'; + addMoreText('Funkin'); // credTextShit.text += '\nFunkin'; case 16: skipIntro(); @@ -187,6 +245,7 @@ class TitleState extends MusicBeatState { FlxG.camera.flash(FlxColor.WHITE, 4); remove(credGroup); + skippedIntro = true; } } } From 7dea03c9344605577fdf15baa77a748814674bcb Mon Sep 17 00:00:00 2001 From: Brandon Date: Fri, 30 Oct 2020 21:05:10 -0400 Subject: [PATCH 04/16] bmp files cant have transparency :( --- Project.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.xml b/Project.xml index 37d7467f2..4464c9678 100644 --- a/Project.xml +++ b/Project.xml @@ -2,7 +2,7 @@ - + From be3adae29c3442301bf2d57dd85d3e3a9c836928 Mon Sep 17 00:00:00 2001 From: Brandon Date: Fri, 30 Oct 2020 22:06:21 -0400 Subject: [PATCH 05/16] Update Project.xml --- Project.xml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Project.xml b/Project.xml index 4464c9678..06de8f4ac 100644 --- a/Project.xml +++ b/Project.xml @@ -2,7 +2,7 @@ - + @@ -96,7 +96,6 @@ - - + From 4d811189dd50297360200a4dcee86be47161c541 Mon Sep 17 00:00:00 2001 From: Brandon Date: Sat, 31 Oct 2020 21:05:59 -0400 Subject: [PATCH 06/16] enum syntax --- source/Controls.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/Controls.hx b/source/Controls.hx index 697082bd6..c90b85054 100644 --- a/source/Controls.hx +++ b/source/Controls.hx @@ -11,7 +11,7 @@ import flixel.input.gamepad.FlxGamepadButton; import flixel.input.gamepad.FlxGamepadInputID; import flixel.input.keyboard.FlxKey; -enum abstract Action(String) to String from String +@:enum abstract Action(String) to String from String { var UP = "up"; var LEFT = "left"; From 1866069af20541158478e9e5d4ca58eccb90c56a Mon Sep 17 00:00:00 2001 From: Brandon Date: Sat, 31 Oct 2020 21:11:14 -0400 Subject: [PATCH 07/16] latest halloween --- source/Alphabet.hx | 136 +++++++++++++++++++---- source/ChartingState.hx | 27 ++++- source/Controls.hx | 31 +++++- source/DialogueBox.hx | 86 +++++++++++++++ source/FreeplayState.hx | 2 + source/MainMenuState.hx | 168 +++++++++++++++++++++++++++++ source/MenuCharacter.hx | 28 +++++ source/MenuItem.hx | 4 +- source/PlayState.hx | 226 ++++++++++++++++++++++++++++++--------- source/Song.hx | 4 +- source/StoryMenuState.hx | 173 ++++++++++++++++++++++++++++-- source/TitleState.hx | 65 +++++++---- 12 files changed, 845 insertions(+), 105 deletions(-) create mode 100644 source/DialogueBox.hx create mode 100644 source/MainMenuState.hx create mode 100644 source/MenuCharacter.hx diff --git a/source/Alphabet.hx b/source/Alphabet.hx index 20556e359..4e04631ef 100644 --- a/source/Alphabet.hx +++ b/source/Alphabet.hx @@ -34,20 +34,26 @@ class Alphabet extends FlxSpriteGroup var splitWords:Array = []; + var isBold:Bool = false; + public function new(x:Float, y:Float, text:String = "", ?bold:Bool = false, typed:Bool = false) { super(x, y); _finalText = text; this.text = text; + isBold = bold; - if (typed) + if (text != "") { - startTypedText(); - } - else - { - addText(); + if (typed) + { + startTypedText(); + } + else + { + addText(); + } } } @@ -55,6 +61,7 @@ class Alphabet extends FlxSpriteGroup { doSplitWords(); + var xPos:Float = 0; for (character in splitWords) { // if (character.fastCodeAt() == " ") @@ -68,10 +75,9 @@ class Alphabet extends FlxSpriteGroup if (AlphaCharacter.alphabet.contains(character.toLowerCase())) { - var xPos:Float = 0; if (lastSprite != null) { - xPos = lastSprite.x + lastSprite.frameWidth; + xPos = lastSprite.x + lastSprite.width; } if (lastWasSpace) @@ -82,7 +88,14 @@ class Alphabet extends FlxSpriteGroup // var letter:AlphaCharacter = new AlphaCharacter(30 * loopNum, 0); var letter:AlphaCharacter = new AlphaCharacter(xPos, 0); - letter.createBold(character); + + if (isBold) + letter.createBold(character); + else + { + letter.createLetter(character); + } + add(letter); lastSprite = letter; @@ -97,6 +110,8 @@ class Alphabet extends FlxSpriteGroup splitWords = _finalText.split(""); } + public var personTalking:String = 'gf'; + public function startTypedText():Void { _finalText = text; @@ -106,34 +121,79 @@ class Alphabet extends FlxSpriteGroup var loopNum:Int = 0; + var xPos:Float = 0; + var curRow:Int = 0; + new FlxTimer().start(0.05, function(tmr:FlxTimer) { - var xPos:Float = 0; - // trace(_finalText.fastCodeAt(loopNum) + " " + _finalText.charAt(loopNum)); if (_finalText.fastCodeAt(loopNum) == "\n".code) { yMulti += 1; xPosResetted = true; - // xPos = 0; + xPos = 0; + curRow += 1; } - if (AlphaCharacter.alphabet.contains(splitWords[loopNum].toLowerCase())) + if (splitWords[loopNum] == " ") + { + lastWasSpace = true; + } + + var isNumber:Bool = AlphaCharacter.numbers.contains(splitWords[loopNum]); + var isSymbol:Bool = AlphaCharacter.symbols.contains(splitWords[loopNum]); + if (AlphaCharacter.alphabet.contains(splitWords[loopNum].toLowerCase()) || isNumber || isSymbol) { if (lastSprite != null && !xPosResetted) { - xPos = lastSprite.x + lastSprite.frameWidth - 40; + lastSprite.updateHitbox(); + xPos += lastSprite.width + 3; + // if (isBold) + // xPos -= 80; } else { xPosResetted = false; } + if (lastWasSpace) + { + xPos += 20; + lastWasSpace = false; + } // trace(_finalText.fastCodeAt(loopNum) + " " + _finalText.charAt(loopNum)); // var letter:AlphaCharacter = new AlphaCharacter(30 * loopNum, 0); var letter:AlphaCharacter = new AlphaCharacter(xPos, 55 * yMulti); - letter.createBold(splitWords[loopNum]); + letter.row = curRow; + if (isBold) + { + letter.createBold(splitWords[loopNum]); + } + else + { + if (isNumber) + { + letter.createNumber(splitWords[loopNum]); + } + else if (isSymbol) + { + letter.createSymbol(splitWords[loopNum]); + } + else + { + letter.createLetter(splitWords[loopNum]); + } + + letter.x += 90; + } + + if (FlxG.random.bool(40)) + { + var daSound:String = "GF_"; + FlxG.sound.play('assets/sounds/' + daSound + FlxG.random.int(1, 4) + TitleState.soundExt, 0.4); + } + add(letter); lastSprite = letter; @@ -141,7 +201,7 @@ class Alphabet extends FlxSpriteGroup loopNum += 1; - tmr.time = FlxG.random.float(0.03, 0.09); + tmr.time = FlxG.random.float(0.04, 0.09); }, splitWords.length); } @@ -155,8 +215,11 @@ class AlphaCharacter extends FlxSprite { public static var alphabet:String = "abcdefghijklmnopqrstuvwxyz"; - var numbers:String = "1234567890"; - var symbols:String = "|~#$%()*+-:;<=>@[]^_"; + public static var numbers:String = "1234567890"; + + public static var symbols:String = "|~#$%()*+-:;<=>@[]^_.,'!?"; + + public var row:Int = 0; public function new(x:Float, y:Float) { @@ -184,5 +247,42 @@ class AlphaCharacter extends FlxSprite animation.addByPrefix(letter, letter + " " + letterCase, 24); animation.play(letter); + updateHitbox(); + + FlxG.log.add('the row' + row); + + y = (110 - height); + y += row * 60; + } + + public function createNumber(letter:String):Void + { + animation.addByPrefix(letter, letter, 24); + animation.play(letter); + + updateHitbox(); + } + + public function createSymbol(letter:String) + { + switch (letter) + { + case '.': + animation.addByPrefix(letter, 'period', 24); + animation.play(letter); + y += 50; + case "'": + animation.addByPrefix(letter, 'apostraphie', 24); + animation.play(letter); + y -= 0; + case "?": + animation.addByPrefix(letter, 'question mark', 24); + animation.play(letter); + case "!": + animation.addByPrefix(letter, 'exclamation point', 24); + animation.play(letter); + } + + updateHitbox(); } } diff --git a/source/ChartingState.hx b/source/ChartingState.hx index a4cf96c92..3ebeb10f9 100644 --- a/source/ChartingState.hx +++ b/source/ChartingState.hx @@ -18,6 +18,7 @@ import flixel.group.FlxGroup.FlxTypedGroup; import flixel.group.FlxGroup; import flixel.math.FlxMath; import flixel.math.FlxPoint; +import flixel.system.FlxSound; import flixel.text.FlxText; import flixel.ui.FlxButton; import flixel.ui.FlxSpriteButton; @@ -71,6 +72,8 @@ class ChartingState extends MusicBeatState var tempBpm:Int = 0; + var vocals:FlxSound; + override function create() { gridBG = FlxGridOverlay.create(GRID_SIZE, GRID_SIZE, GRID_SIZE * 8, GRID_SIZE * 16); @@ -276,12 +279,24 @@ class ChartingState extends MusicBeatState function loadSong(daSong:String):Void { if (FlxG.sound.music != null) + { FlxG.sound.music.stop(); + // vocals.stop(); + } + + FlxG.sound.playMusic('assets/music/' + daSong + "_Inst" + TitleState.soundExt, 0.6); + + // WONT WORK FOR TUTORIAL! REDO LATER + vocals = new FlxSound().loadEmbedded("assets/music/" + daSong + "_Voices" + TitleState.soundExt); + FlxG.sound.list.add(vocals); - FlxG.sound.playMusic('assets/music/' + daSong + TitleState.soundExt, 0.6); FlxG.sound.music.pause(); + vocals.pause(); + FlxG.sound.music.onComplete = function() { + vocals.pause(); + vocals.time = 0; FlxG.sound.music.pause(); FlxG.sound.music.time = 0; }; @@ -430,6 +445,7 @@ class ChartingState extends MusicBeatState { PlayState.SONG = _song; FlxG.sound.music.stop(); + vocals.stop(); FlxG.switchState(new PlayState()); } @@ -440,9 +456,13 @@ class ChartingState extends MusicBeatState if (FlxG.sound.music.playing) { FlxG.sound.music.pause(); + vocals.pause(); } else + { + vocals.play(); FlxG.sound.music.play(); + } } if (FlxG.keys.justPressed.R) @@ -456,6 +476,7 @@ class ChartingState extends MusicBeatState if (FlxG.keys.pressed.W || FlxG.keys.pressed.S) { FlxG.sound.music.pause(); + vocals.pause(); var daTime:Float = 700 * FlxG.elapsed; @@ -465,6 +486,8 @@ class ChartingState extends MusicBeatState } else FlxG.sound.music.time += daTime; + + vocals.time = FlxG.sound.music.time; } } @@ -497,6 +520,7 @@ class ChartingState extends MusicBeatState if (updateMusic) { FlxG.sound.music.pause(); + vocals.pause(); var daNum:Int = 0; var daLength:Int = 0; @@ -507,6 +531,7 @@ class ChartingState extends MusicBeatState } FlxG.sound.music.time = (daLength - (_song.notes[sec].lengthInSteps)) * Conductor.stepCrochet; + vocals.time = FlxG.sound.music.time; updateCurStep(); } diff --git a/source/Controls.hx b/source/Controls.hx index c90b85054..b5715a614 100644 --- a/source/Controls.hx +++ b/source/Controls.hx @@ -11,7 +11,8 @@ import flixel.input.gamepad.FlxGamepadButton; import flixel.input.gamepad.FlxGamepadInputID; import flixel.input.keyboard.FlxKey; -@:enum abstract Action(String) to String from String +#if (haxe >= "4.0.0") +enum abstract Action(String) to String from String { var UP = "up"; var LEFT = "left"; @@ -30,6 +31,28 @@ import flixel.input.keyboard.FlxKey; var PAUSE = "pause"; var RESET = "reset"; } +#else +@:enum +abstract Action(String) to String from String +{ + var UP = "up"; + var LEFT = "left"; + var RIGHT = "right"; + var DOWN = "down"; + var UP_P = "up-press"; + var LEFT_P = "left-press"; + var RIGHT_P = "right-press"; + var DOWN_P = "down-press"; + var UP_R = "up-release"; + var LEFT_R = "left-release"; + var RIGHT_R = "right-release"; + var DOWN_R = "down-release"; + var ACCEPT = "accept"; + var BACK = "back"; + var PAUSE = "pause"; + var RESET = "reset"; +} +#end enum Device { @@ -402,9 +425,9 @@ class Controls extends FlxActionSet inline bindKeys(Control.DOWN, [S, FlxKey.DOWN]); inline bindKeys(Control.LEFT, [A, FlxKey.LEFT]); inline bindKeys(Control.RIGHT, [D, FlxKey.RIGHT]); - inline bindKeys(Control.ACCEPT, [Z, SPACE]); - inline bindKeys(Control.BACK, [X]); - inline bindKeys(Control.PAUSE, [P, ENTER]); + inline bindKeys(Control.ACCEPT, [Z, SPACE, ENTER]); + inline bindKeys(Control.BACK, [BACKSPACE, ESCAPE]); + inline bindKeys(Control.PAUSE, [P, ENTER, ESCAPE]); inline bindKeys(Control.RESET, [R]); case Duo(true): inline bindKeys(Control.UP, [W]); diff --git a/source/DialogueBox.hx b/source/DialogueBox.hx new file mode 100644 index 000000000..488fe35fd --- /dev/null +++ b/source/DialogueBox.hx @@ -0,0 +1,86 @@ +package; + +import flixel.FlxG; +import flixel.FlxSprite; +import flixel.graphics.frames.FlxAtlasFrames; +import flixel.group.FlxSpriteGroup; +import flixel.input.FlxKeyManager; + +class DialogueBox extends FlxSpriteGroup +{ + var box:FlxSprite; + + var dialogue:Alphabet; + var dialogueList:Array = []; + + public var finishThing:Void->Void; + + public function new(talkingRight:Bool = true, ?dialogueList:Array) + { + super(); + + box = new FlxSprite(40); + box.frames = FlxAtlasFrames.fromSparrow(AssetPaths.speech_bubble_talking__png, AssetPaths.speech_bubble_talking__xml); + box.animation.addByPrefix('normalOpen', 'Speech Bubble Normal Open', 24, false); + box.animation.addByPrefix('normal', 'speech bubble normal', 24); + box.animation.play('normalOpen'); + add(box); + + if (!talkingRight) + { + box.flipX = true; + } + + dialogue = new Alphabet(0, 80, "", false, true); + // dialogue.x = 90; + add(dialogue); + + this.dialogueList = dialogueList; + } + + var dialogueOpened:Bool = false; + var dialogueStarted:Bool = false; + + override function update(elapsed:Float) + { + if (box.animation.curAnim != null) + { + if (box.animation.curAnim.name == 'normalOpen' && box.animation.curAnim.finished) + { + box.animation.play('normal'); + dialogueOpened = true; + } + } + + if (dialogueOpened && !dialogueStarted) + { + startDialogue(); + dialogueStarted = true; + } + + if (FlxG.keys.justPressed.SPACE) + { + remove(dialogue); + + if (dialogueList[1] == null) + { + finishThing(); + kill(); + } + else + { + dialogueList.remove(dialogueList[0]); + startDialogue(); + } + } + + super.update(elapsed); + } + + function startDialogue():Void + { + var theDialog:Alphabet = new Alphabet(0, 70, dialogueList[0], false, true); + dialogue = theDialog; + add(theDialog); + } +} diff --git a/source/FreeplayState.hx b/source/FreeplayState.hx index f2a717fec..0927dd7e6 100644 --- a/source/FreeplayState.hx +++ b/source/FreeplayState.hx @@ -87,6 +87,7 @@ class FreeplayState extends MusicBeatState if (accepted) { PlayState.SONG = Song.loadFromJson(songs[curSelected].toLowerCase()); + PlayState.isStoryMode = false; FlxG.switchState(new PlayState()); FlxG.sound.music.stop(); } @@ -95,6 +96,7 @@ class FreeplayState extends MusicBeatState if (gamepad.anyJustPressed(["B"])) //"B" is swapped with "A" on Switch { PlayState.SONG = Song.loadFromJson(songs[curSelected].toLowerCase()); + PlayState.isStoryMode = false; FlxG.switchState(new PlayState()); FlxG.sound.music.stop(); } diff --git a/source/MainMenuState.hx b/source/MainMenuState.hx new file mode 100644 index 000000000..687f6191b --- /dev/null +++ b/source/MainMenuState.hx @@ -0,0 +1,168 @@ +package; + +import flixel.FlxG; +import flixel.FlxObject; +import flixel.FlxSprite; +import flixel.effects.FlxFlicker; +import flixel.graphics.frames.FlxAtlasFrames; +import flixel.group.FlxGroup.FlxTypedGroup; +import flixel.tweens.FlxEase; +import flixel.tweens.FlxTween; + +class MainMenuState extends MusicBeatState +{ + var curSelected:Int = 0; + + var menuItems:FlxTypedGroup; + + var optionShit:Array = ['story mode', 'freeplay', 'donate']; + + var magenta:FlxSprite; + var camFollow:FlxObject; + + override function create() + { + persistentUpdate = persistentDraw = true; + + var bg:FlxSprite = new FlxSprite(-80).loadGraphic(AssetPaths.menuBG__png); + bg.scrollFactor.x = 0; + bg.scrollFactor.y = 0.18; + bg.setGraphicSize(Std.int(bg.width * 1.1)); + bg.updateHitbox(); + bg.screenCenter(); + bg.antialiasing = true; + add(bg); + + camFollow = new FlxObject(0, 0, 1, 1); + add(camFollow); + + magenta = new FlxSprite(-80).loadGraphic(AssetPaths.menuBGMagenta__png); + magenta.scrollFactor.x = 0; + magenta.scrollFactor.y = 0.18; + magenta.setGraphicSize(Std.int(magenta.width * 1.1)); + magenta.updateHitbox(); + magenta.screenCenter(); + magenta.visible = false; + magenta.antialiasing = true; + add(magenta); + // magenta.scrollFactor.set(); + + menuItems = new FlxTypedGroup(); + add(menuItems); + + var tex = FlxAtlasFrames.fromSparrow(AssetPaths.FNF_main_menu_assets__png, AssetPaths.FNF_main_menu_assets__xml); + + for (i in 0...optionShit.length) + { + var menuItem:FlxSprite = new FlxSprite(0, 60 + (i * 160)); + menuItem.frames = tex; + menuItem.animation.addByPrefix('idle', optionShit[i] + " basic", 24); + menuItem.animation.addByPrefix('selected', optionShit[i] + " white", 24); + menuItem.animation.play('idle'); + menuItem.ID = i; + menuItem.screenCenter(X); + menuItems.add(menuItem); + menuItem.scrollFactor.set(); + menuItem.antialiasing = true; + } + + FlxG.camera.follow(camFollow, null, 0.06); + + changeItem(); + + super.create(); + } + + override function update(elapsed:Float) + { + if (controls.UP_P) + { + FlxG.sound.play('assets/sounds/scrollMenu' + TitleState.soundExt); + changeItem(-1); + } + + if (controls.DOWN_P) + { + FlxG.sound.play('assets/sounds/scrollMenu' + TitleState.soundExt); + changeItem(1); + } + + if (controls.BACK) + { + FlxG.switchState(new TitleState()); + } + + super.update(elapsed); + + if (controls.ACCEPT) + { + if (optionShit[curSelected] == 'donate') + { + FlxG.openURL('https://ninja-muffin24.itch.io/funkin'); + } + else + { + FlxG.sound.play('assets/sounds/confirmMenu' + TitleState.soundExt); + + FlxFlicker.flicker(magenta, 1.1, 0.15, false); + + menuItems.forEach(function(spr:FlxSprite) + { + if (curSelected != spr.ID) + { + FlxTween.tween(spr, {alpha: 0}, 0.4, { + ease: FlxEase.quadOut, + onComplete: function(twn:FlxTween) + { + spr.kill(); + } + }); + } + else + { + FlxFlicker.flicker(spr, 1, 0.06, false, false, function(flick:FlxFlicker) + { + var daChoice:String = optionShit[curSelected]; + + switch (daChoice) + { + case 'story mode': + FlxG.switchState(new StoryMenuState()); + case 'freeplay': + FlxG.switchState(new FreeplayState()); + } + }); + } + }); + } + } + + menuItems.forEach(function(spr:FlxSprite) + { + spr.screenCenter(X); + }); + } + + function changeItem(huh:Int = 0) + { + curSelected += huh; + + if (curSelected >= menuItems.length) + curSelected = 0; + if (curSelected < 0) + curSelected = menuItems.length - 1; + + menuItems.forEach(function(spr:FlxSprite) + { + spr.animation.play('idle'); + + if (spr.ID == curSelected) + { + spr.animation.play('selected'); + camFollow.setPosition(spr.getGraphicMidpoint().x, spr.getGraphicMidpoint().y); + } + + spr.updateHitbox(); + }); + } +} diff --git a/source/MenuCharacter.hx b/source/MenuCharacter.hx new file mode 100644 index 000000000..6835daa9a --- /dev/null +++ b/source/MenuCharacter.hx @@ -0,0 +1,28 @@ +package; + +import flixel.FlxSprite; +import flixel.graphics.frames.FlxAtlasFrames; + +class MenuCharacter extends FlxSprite +{ + public var character:String; + + public function new(x:Float, character:String = 'bf') + { + super(x); + + this.character = character; + + var tex = FlxAtlasFrames.fromSparrow(AssetPaths.campaign_menu_UI_characters__png, AssetPaths.campaign_menu_UI_characters__xml); + frames = tex; + + animation.addByPrefix('bf', "BF idle dance white", 24); + animation.addByPrefix('bfConfirm', 'BF HEY!!', 24, false); + animation.addByPrefix('gf', "GF Dancing Beat WHITE", 24); + animation.addByPrefix('dad', "Dad idle dance BLACK LINE", 24); + animation.addByPrefix('spooky', "spooky dance idle BLACK LINES", 24); + + animation.play(character); + updateHitbox(); + } +} diff --git a/source/MenuItem.hx b/source/MenuItem.hx index 5e7ddc763..5aae4ee28 100644 --- a/source/MenuItem.hx +++ b/source/MenuItem.hx @@ -8,6 +8,7 @@ import flixel.math.FlxMath; class MenuItem extends FlxSpriteGroup { public var targetY:Float = 0; + public var week:FlxSprite; public function new(x:Float, y:Float, weekNum:Int = 0, unlocked:Bool = false) { @@ -15,13 +16,14 @@ class MenuItem extends FlxSpriteGroup var tex = FlxAtlasFrames.fromSparrow(AssetPaths.campaign_menu_UI_assets__png, AssetPaths.campaign_menu_UI_assets__xml); - var week:FlxSprite = new FlxSprite(); + week = new FlxSprite(); week.frames = tex; week.animation.addByPrefix('week0', "WEEK1 select", 24); week.animation.addByPrefix('week1', "week2 select", 24); add(week); week.animation.play('week' + weekNum); + week.animation.pause(); week.updateHitbox(); if (!unlocked) diff --git a/source/PlayState.hx b/source/PlayState.hx index 9f4784dfb..29aeb7335 100644 --- a/source/PlayState.hx +++ b/source/PlayState.hx @@ -16,6 +16,7 @@ import flixel.graphics.atlas.FlxAtlas; import flixel.graphics.frames.FlxAtlasFrames; import flixel.group.FlxGroup.FlxTypedGroup; import flixel.math.FlxMath; +import flixel.math.FlxPoint; import flixel.system.FlxSound; import flixel.text.FlxText; import flixel.tweens.FlxEase; @@ -33,8 +34,13 @@ using StringTools; class PlayState extends MusicBeatState { - public static var curLevel:String = 'Bopeebo'; + public static var curLevel:String = 'Tutorial'; public static var SONG:SwagSong; + public static var isStoryMode:Bool = false; + public static var storyPlaylist:Array = []; + public static var storyDifficulty:Int = 1; + + var halloweenLevel:Bool = false; private var vocals:FlxSound; @@ -69,6 +75,12 @@ class PlayState extends MusicBeatState private var camHUD:FlxCamera; private var camGame:FlxCamera; + var dialogue:Array = ['blah blah blah', 'coolswag']; + + var halloweenBG:FlxSprite; + + var talking:Bool = true; + override public function create() { // var gameCam:FlxCamera = FlxG.camera; @@ -89,28 +101,68 @@ class PlayState extends MusicBeatState Conductor.changeBPM(SONG.bpm); - var bg:FlxSprite = new FlxSprite(-600, -200).loadGraphic(AssetPaths.stageback__png); - // bg.setGraphicSize(Std.int(bg.width * 2.5)); - // bg.updateHitbox(); - bg.antialiasing = true; - bg.scrollFactor.set(0.9, 0.9); - bg.active = false; - add(bg); + switch (SONG.song.toLowerCase()) + { + case 'tutorial': + dialogue = ["Hey you're pretty cute.", 'Use the arrow keys to keep up \nwith me singing.']; + case 'bopeebo': + dialogue = [ + 'HEY!', + "You think you can just sing\nwith my daughter like that?", + "If you want to date her...", + "You're going to have to go \nthrough ME first!" + ]; + case 'fresh': + dialogue = ["Not too shabby boy.", ""]; + case 'dadbattle': + dialogue = [ + "gah you think you're hot stuff?", + "If you can beat me here...", + "Only then I will even CONSIDER letting you\ndate my daughter!" + ]; + } - var stageFront:FlxSprite = new FlxSprite(-650, 600).loadGraphic(AssetPaths.stagefront__png); - stageFront.setGraphicSize(Std.int(stageFront.width * 1.1)); - stageFront.updateHitbox(); - stageFront.antialiasing = true; - stageFront.scrollFactor.set(0.9, 0.9); - stageFront.active = false; - add(stageFront); + if (SONG.song.toLowerCase() == 'spookeez' || SONG.song.toLowerCase() == 'monster' || SONG.song.toLowerCase() == 'south') + { + halloweenLevel = true; - var stageCurtains:FlxSprite = new FlxSprite(-500, -300).loadGraphic(AssetPaths.stagecurtains__png); - stageCurtains.setGraphicSize(Std.int(stageCurtains.width * 0.9)); - stageCurtains.updateHitbox(); - stageCurtains.antialiasing = true; - stageCurtains.scrollFactor.set(1.3, 1.3); - stageCurtains.active = false; + var hallowTex = FlxAtlasFrames.fromSparrow(AssetPaths.halloween_bg__png, AssetPaths.halloween_bg__xml); + + halloweenBG = new FlxSprite(-200, -100); + halloweenBG.frames = hallowTex; + halloweenBG.animation.addByPrefix('idle', 'halloweem bg0'); + halloweenBG.animation.addByPrefix('lightning', 'halloweem bg lightning strike', 24, false); + halloweenBG.animation.play('idle'); + halloweenBG.antialiasing = true; + add(halloweenBG); + } + else + { + var bg:FlxSprite = new FlxSprite(-600, -200).loadGraphic(AssetPaths.stageback__png); + // bg.setGraphicSize(Std.int(bg.width * 2.5)); + // bg.updateHitbox(); + bg.antialiasing = true; + bg.scrollFactor.set(0.9, 0.9); + bg.active = false; + add(bg); + + var stageFront:FlxSprite = new FlxSprite(-650, 600).loadGraphic(AssetPaths.stagefront__png); + stageFront.setGraphicSize(Std.int(stageFront.width * 1.1)); + stageFront.updateHitbox(); + stageFront.antialiasing = true; + stageFront.scrollFactor.set(0.9, 0.9); + stageFront.active = false; + add(stageFront); + + var stageCurtains:FlxSprite = new FlxSprite(-500, -300).loadGraphic(AssetPaths.stagecurtains__png); + stageCurtains.setGraphicSize(Std.int(stageCurtains.width * 0.9)); + stageCurtains.updateHitbox(); + stageCurtains.antialiasing = true; + stageCurtains.scrollFactor.set(1.3, 1.3); + stageCurtains.active = false; + + add(stageCurtains); + } gf = new Character(400, 130, 'gf'); gf.scrollFactor.set(0.95, 0.95); @@ -120,21 +172,37 @@ class PlayState extends MusicBeatState dad = new Character(100, 100, SONG.player2); add(dad); + var camPos:FlxPoint = new FlxPoint(dad.getGraphicMidpoint().x, dad.getGraphicMidpoint().y); + switch (SONG.player2) { case 'gf': dad.setPosition(gf.x, gf.y); gf.visible = false; + if (isStoryMode) + { + camPos.x += 600; + tweenCamIn(); + } + case "spooky": dad.y += 200; case "monster": dad.y += 100; + case 'dad': + camPos.x += 400; } boyfriend = new Boyfriend(770, 450); add(boyfriend); - add(stageCurtains); + var doof:DialogueBox = new DialogueBox(false, dialogue); + // doof.x += 70; + doof.y = FlxG.height * 0.5; + doof.scrollFactor.set(); + doof.finishThing = startCountdown; + + Conductor.songPosition = -5000; strumLine = new FlxSprite(0, 50).makeGraphic(FlxG.width, 10); strumLine.scrollFactor.set(); @@ -146,14 +214,15 @@ class PlayState extends MusicBeatState startingSong = true; - startCountdown(); + // startCountdown(); generateSong(SONG.song); // add(strumLine); camFollow = new FlxObject(0, 0, 1, 1); - camFollow.setPosition(dad.getGraphicMidpoint().x, dad.getGraphicMidpoint().y); + + camFollow.setPosition(camPos.x, camPos.y); add(camFollow); FlxG.camera.follow(camFollow, LOCKON, 0.04); @@ -186,11 +255,22 @@ class PlayState extends MusicBeatState healthHeads.antialiasing = true; add(healthHeads); + // healthBar.visible = healthHeads.visible = healthBarBG.visible = false; + if (isStoryMode) + { + // TEMP for now, later get rid of startCountdown() + // add(doof); + startCountdown(); + } + else + startCountdown(); + strumLineNotes.cameras = [camHUD]; notes.cameras = [camHUD]; healthBar.cameras = [camHUD]; healthBarBG.cameras = [camHUD]; healthHeads.cameras = [camHUD]; + doof.cameras = [camHUD]; // if (SONG.song == 'South') // FlxG.camera.alpha = 0.7; @@ -205,6 +285,10 @@ class PlayState extends MusicBeatState function startCountdown():Void { + generateStaticArrows(0); + generateStaticArrows(1); + + talking = false; startedCountdown = true; Conductor.songPosition = 0; Conductor.songPosition -= Conductor.crochet * 5; @@ -278,7 +362,8 @@ class PlayState extends MusicBeatState lastReportedPlayheadPosition = 0; startingSong = false; - FlxG.sound.playMusic("assets/music/" + SONG.song + "_Inst" + TitleState.soundExt); + FlxG.sound.playMusic("assets/music/" + SONG.song + "_Inst" + TitleState.soundExt, 1, false); + FlxG.sound.music.onComplete = endSong; vocals.play(); } @@ -288,9 +373,6 @@ class PlayState extends MusicBeatState { // FlxG.log.add(ChartParser.parse()); - generateStaticArrows(0); - generateStaticArrows(1); - var songData = SONG; Conductor.changeBPM(songData.bpm); @@ -448,6 +530,11 @@ class PlayState extends MusicBeatState } } + function tweenCamIn():Void + { + FlxTween.tween(FlxG.camera, {zoom: 1.3}, (Conductor.stepCrochet * 4 / 1000), {ease: FlxEase.elasticInOut}); + } + override function openSubState(SubState:FlxSubState) { if (paused) @@ -495,7 +582,7 @@ class PlayState extends MusicBeatState // trace("SONG POS: " + Conductor.songPosition); // FlxG.sound.music.pitch = 2; - if (FlxG.keys.justPressed.ENTER) + if (FlxG.keys.justPressed.ENTER && startedCountdown) { persistentUpdate = false; persistentDraw = true; @@ -570,7 +657,7 @@ class PlayState extends MusicBeatState if (SONG.song.toLowerCase() == 'tutorial') { - FlxTween.tween(FlxG.camera, {zoom: 1.3}, (Conductor.stepCrochet * 4 / 1000), {ease: FlxEase.elasticInOut}); + tweenCamIn(); } } @@ -606,9 +693,9 @@ class PlayState extends MusicBeatState case 112: gfSpeed = 1; case 163: - FlxG.sound.music.stop(); - curLevel = 'Bopeebo'; - FlxG.switchState(new TitleState()); + // FlxG.sound.music.stop(); + // curLevel = 'Bopeebo'; + // FlxG.switchState(new TitleState()); } } @@ -617,9 +704,9 @@ class PlayState extends MusicBeatState switch (totalBeats) { case 127: - FlxG.sound.music.stop(); - curLevel = 'Fresh'; - FlxG.switchState(new PlayState()); + // FlxG.sound.music.stop(); + // curLevel = 'Fresh'; + // FlxG.switchState(new PlayState()); } } // better streaming of shit @@ -697,7 +784,7 @@ class PlayState extends MusicBeatState { if (daNote.tooLate) { - health -= 0.03; + health -= 0.04; vocals.volume = 0; } @@ -714,6 +801,42 @@ class PlayState extends MusicBeatState keyShit(); } + function endSong():Void + { + trace('SONG DONE' + isStoryMode); + + if (isStoryMode) + { + storyPlaylist.remove(storyPlaylist[0]); + + if (storyPlaylist.length <= 0) + { + FlxG.switchState(new TitleState()); + + StoryMenuState.weekUnlocked[1] = true; + } + else + { + var difficulty:String = ""; + + if (storyDifficulty == 0) + difficulty = '-easy'; + + if (storyDifficulty == 2) + difficulty == '-hard'; + + PlayState.SONG = Song.loadFromJson(PlayState.storyPlaylist[0].toLowerCase() + difficulty, PlayState.storyPlaylist[0]); + FlxG.switchState(new PlayState()); + } + } + else + { + FlxG.switchState(new FreeplayState()); + } + } + + var endingSong:Bool = false; + private function popUpScore(strumtime:Float):Void { var noteDiff:Float = Math.abs(strumtime - Conductor.songPosition); @@ -972,7 +1095,7 @@ class PlayState extends MusicBeatState { if (!boyfriend.stunned) { - health -= 0.055; + health -= 0.06; if (combo > 5) { gf.playAnim('sad'); @@ -1068,9 +1191,9 @@ class PlayState extends MusicBeatState } if (note.noteData >= 0) - health += 0.03; + health += 0.023; else - health += 0.007; + health += 0.004; switch (note.noteData) { @@ -1131,22 +1254,25 @@ class PlayState extends MusicBeatState notes.sort(FlxSort.byY, FlxSort.DESCENDING); } - FlxG.log.add('change bpm' + SONG.notes[Std.int(curStep / 16)].changeBPM); - if (SONG.notes[Std.int(curStep / 16)].changeBPM) + if (SONG.notes[Math.floor(curStep / 16)] != null) { - Conductor.changeBPM(SONG.notes[Std.int(curStep / 16)].bpm); - FlxG.log.add('CHANGED BPM!'); + if (SONG.notes[Math.floor(curStep / 16)].changeBPM) + { + Conductor.changeBPM(SONG.notes[Math.floor(curStep / 16)].bpm); + FlxG.log.add('CHANGED BPM!'); + } + else + Conductor.changeBPM(SONG.bpm); + + // Dad doesnt interupt his own notes + if (SONG.notes[Math.floor(curStep / 16)].mustHitSection) + dad.dance(); } - else - Conductor.changeBPM(SONG.bpm); + // FlxG.log.add('change bpm' + SONG.notes[Std.int(curStep / 16)].changeBPM); if (camZooming && FlxG.camera.zoom < 1.35 && totalBeats % 4 == 0) FlxG.camera.zoom += 0.025; - // Dad doesnt interupt his own notes - if (SONG.notes[Std.int(curStep / 16)].mustHitSection) - dad.dance(); - healthHeads.setGraphicSize(Std.int(healthHeads.width + 20)); if (totalBeats % gfSpeed == 0) diff --git a/source/Song.hx b/source/Song.hx index 3c2ac41be..aa75d3951 100644 --- a/source/Song.hx +++ b/source/Song.hx @@ -47,9 +47,9 @@ class Song } } - public static function loadFromJson(jsonInput:String):SwagSong + public static function loadFromJson(jsonInput:String, ?folder:String):SwagSong { - var rawJson = Assets.getText('assets/data/' + jsonInput.toLowerCase() + '/' + jsonInput.toLowerCase() + '.json').trim(); + var rawJson = Assets.getText('assets/data/' + folder.toLowerCase() + '/' + jsonInput.toLowerCase() + '.json').trim(); while (!rawJson.endsWith("}")) { diff --git a/source/StoryMenuState.hx b/source/StoryMenuState.hx index bae5703c0..093746479 100644 --- a/source/StoryMenuState.hx +++ b/source/StoryMenuState.hx @@ -6,6 +6,9 @@ import flixel.graphics.frames.FlxAtlasFrames; import flixel.group.FlxGroup.FlxTypedGroup; import flixel.group.FlxGroup; import flixel.text.FlxText; +import flixel.tweens.FlxTween; +import flixel.util.FlxTimer; +import lime.net.curl.CURLCode; using StringTools; @@ -13,31 +16,38 @@ class StoryMenuState extends MusicBeatState { var scoreText:FlxText; - var weekData:Array = [['Tutorial', 'Bopeebo', 'Fresh', 'Dad Battle'], ['Spookeez', 'South', 'Monster']]; - var weekUnlocked:Array = [true, false]; + var weekData:Array = [['Tutorial', 'Bopeebo', 'Fresh', 'Dadbattle'], ['Spookeez', 'South', 'Monster']]; + var curDifficulty:Int = 1; + public static var weekUnlocked:Array = [true, false]; + + var weekCharacters:Array = [['dad', 'bf', 'gf'], ['spooky', 'bf', 'gf']]; var curWeek:Int = 0; var txtTracklist:FlxText; var grpWeekText:FlxTypedGroup; + var grpWeekCharacters:FlxTypedGroup; var grpLocks:FlxTypedGroup; var difficultySelectors:FlxGroup; + var sprDifficulty:FlxSprite; + var leftArrow:FlxSprite; + var rightArrow:FlxSprite; override function create() { + persistentUpdate = persistentDraw = true; + scoreText = new FlxText(10, 10, 0, "SCORE: 49324858", 36); scoreText.setFormat("VCR OSD Mono", 32); - add(scoreText); var rankText:FlxText = new FlxText(0, 10); rankText.text = 'RANK: GREAT'; rankText.setFormat("assets/fonts/vcr.ttf", 32); rankText.size = scoreText.size; rankText.screenCenter(X); - add(rankText); var ui_tex = FlxAtlasFrames.fromSparrow(AssetPaths.campaign_menu_UI_assets__png, AssetPaths.campaign_menu_UI_assets__xml); var yellowBG:FlxSprite = new FlxSprite(0, 56).makeGraphic(FlxG.width, 400, 0xFFF9CF51); @@ -45,6 +55,8 @@ class StoryMenuState extends MusicBeatState grpWeekText = new FlxTypedGroup(); add(grpWeekText); + grpWeekCharacters = new FlxTypedGroup(); + grpLocks = new FlxTypedGroup(); add(grpLocks); @@ -76,36 +88,65 @@ class StoryMenuState extends MusicBeatState } } + for (char in 0...3) + { + var weekCharacterThing:MenuCharacter = new MenuCharacter((FlxG.width * 0.25) * (1 + char) - 150, weekCharacters[curWeek][char]); + weekCharacterThing.y += 70; + weekCharacterThing.antialiasing = true; + switch (weekCharacterThing.character) + { + case 'dad': + weekCharacterThing.setGraphicSize(Std.int(weekCharacterThing.width * 0.5)); + weekCharacterThing.updateHitbox(); + + case 'bf': + weekCharacterThing.setGraphicSize(Std.int(weekCharacterThing.width * 0.9)); + weekCharacterThing.updateHitbox(); + case 'gf': + weekCharacterThing.setGraphicSize(Std.int(weekCharacterThing.width * 0.5)); + weekCharacterThing.updateHitbox(); + } + + grpWeekCharacters.add(weekCharacterThing); + } + difficultySelectors = new FlxGroup(); add(difficultySelectors); - var leftArrow:FlxSprite = new FlxSprite(grpWeekText.members[0].x + 400, grpWeekText.members[0].y + 10); + leftArrow = new FlxSprite(grpWeekText.members[0].x + 370, grpWeekText.members[0].y + 10); leftArrow.frames = ui_tex; leftArrow.animation.addByPrefix('idle', "arrow left"); + leftArrow.animation.addByPrefix('press', "arrow push left"); leftArrow.animation.play('idle'); difficultySelectors.add(leftArrow); - var sprDifficulty:FlxSprite = new FlxSprite(leftArrow.x + 70, leftArrow.y); + sprDifficulty = new FlxSprite(leftArrow.x + 130, leftArrow.y); sprDifficulty.frames = ui_tex; sprDifficulty.animation.addByPrefix('easy', 'EASY'); sprDifficulty.animation.addByPrefix('normal', 'NORMAL'); sprDifficulty.animation.addByPrefix('hard', 'HARD'); sprDifficulty.animation.play('easy'); + changeDifficulty(); + difficultySelectors.add(sprDifficulty); - var rightArrow:FlxSprite = new FlxSprite(sprDifficulty.x + sprDifficulty.width + 20, sprDifficulty.y); + rightArrow = new FlxSprite(sprDifficulty.x + sprDifficulty.width + 50, leftArrow.y); rightArrow.frames = ui_tex; rightArrow.animation.addByPrefix('idle', 'arrow right'); + rightArrow.animation.addByPrefix('press', "arrow push right", 24, false); rightArrow.animation.play('idle'); difficultySelectors.add(rightArrow); add(yellowBG); + add(grpWeekCharacters); txtTracklist = new FlxText(FlxG.width * 0.05, yellowBG.x + yellowBG.height + 100, 0, "Tracks", 32); txtTracklist.alignment = CENTER; txtTracklist.font = rankText.font; txtTracklist.color = 0xFFe55777; add(txtTracklist); + add(rankText); + add(scoreText); updateText(); @@ -125,14 +166,119 @@ class StoryMenuState extends MusicBeatState lock.y = grpWeekText.members[lock.ID].y; }); - if (controls.UP_P) - changeWeek(-1); - if (controls.DOWN_P) - changeWeek(1); + if (!movedBack) + { + if (!selectedWeek) + { + if (controls.UP_P) + { + changeWeek(-1); + } + + if (controls.DOWN_P) + { + changeWeek(1); + } + + if (controls.RIGHT) + rightArrow.animation.play('press') + else + rightArrow.animation.play('idle'); + + if (controls.LEFT) + leftArrow.animation.play('press'); + else + leftArrow.animation.play('idle'); + + if (controls.RIGHT_P) + changeDifficulty(1); + if (controls.LEFT_P) + changeDifficulty(-1); + } + + if (controls.ACCEPT) + { + selectWeek(); + } + } + + if (controls.BACK && !movedBack && !selectedWeek) + { + FlxG.sound.play('assets/sounds/cancelMenu' + TitleState.soundExt); + movedBack = true; + FlxG.switchState(new MainMenuState()); + } super.update(elapsed); } + var movedBack:Bool = false; + var selectedWeek:Bool = false; + + function selectWeek() + { + if (weekUnlocked[curWeek]) + { + FlxG.sound.play('assets/sounds/confirmMenu' + TitleState.soundExt); + + grpWeekText.members[curWeek].week.animation.resume(); + grpWeekCharacters.members[1].animation.play('bfConfirm'); + + PlayState.storyPlaylist = weekData[curWeek]; + PlayState.isStoryMode = true; + selectedWeek = true; + + var diffic = ""; + + switch (curDifficulty) + { + case 0: + diffic = '-easy'; + case 2: + diffic = '-hard'; + } + + PlayState.storyDifficulty = curDifficulty; + + PlayState.SONG = Song.loadFromJson(PlayState.storyPlaylist[0].toLowerCase() + diffic, PlayState.storyPlaylist[0].toLowerCase()); + new FlxTimer().start(1, function(tmr:FlxTimer) + { + FlxG.sound.music.stop(); + FlxG.switchState(new PlayState()); + }); + } + } + + function changeDifficulty(change:Int = 0):Void + { + curDifficulty += change; + + if (curDifficulty < 0) + curDifficulty = 2; + if (curDifficulty > 2) + curDifficulty = 0; + + sprDifficulty.offset.x = 0; + + switch (curDifficulty) + { + case 0: + sprDifficulty.animation.play('easy'); + sprDifficulty.offset.x = 20; + case 1: + sprDifficulty.animation.play('normal'); + sprDifficulty.offset.x = 70; + case 2: + sprDifficulty.animation.play('hard'); + sprDifficulty.offset.x = 20; + } + + sprDifficulty.alpha = 0; + sprDifficulty.y -= 15; + + FlxTween.tween(sprDifficulty, {y: sprDifficulty.y + 15, alpha: 1}, 0.07); + } + function changeWeek(change:Int = 0):Void { curWeek += change; @@ -150,11 +296,16 @@ class StoryMenuState extends MusicBeatState bullShit++; } + FlxG.sound.play('assets/sounds/scrollMenu' + TitleState.soundExt); + updateText(); } function updateText() { + grpWeekCharacters.members[0].animation.play(weekCharacters[curWeek][0]); + grpWeekCharacters.members[1].animation.play(weekCharacters[curWeek][1]); + grpWeekCharacters.members[2].animation.play(weekCharacters[curWeek][2]); txtTracklist.text = "Tracks\n"; var stringThing:Array = weekData[curWeek]; diff --git a/source/TitleState.hx b/source/TitleState.hx index beb3c9d64..72cae1944 100644 --- a/source/TitleState.hx +++ b/source/TitleState.hx @@ -27,6 +27,17 @@ class TitleState extends MusicBeatState var credGroup:FlxGroup; var credTextShit:Alphabet; var textGroup:FlxGroup; + var ngSpr:FlxSprite; + + var wackyIntros:Array> = [ + ['Shoutouts to tom fulp', 'lmao'], ["Ludum dare", "extraordinaire"], ['Cyberzone', 'coming soon'], ['love to thriftman', 'swag'], + ['ULTIMATE RHYTHM GAMING', 'probably'], ['DOPE ASS GAME', 'playstation magazine'], ['in loving memory of', 'henryeyes'], ['dancin', 'forever'], + ['Ritz dx', 'rest in peace'], ['rate five', 'pls no blam'], ['rhythm gaming', 'ultimate'], ['game of the year', 'forever'], + ['you already know', 'we really out here'], ['rise and grind', 'love to luis'], ['like parappa', 'but cooler'], + ['album of the year', 'chuckie finster'], ["free gitaroo man", "with love to wandaboy"], ['better than geometry dash', 'fight me robtop'], + ['kiddbrute for president', 'vote now']]; + + var curWacky:Array = []; override public function create():Void { @@ -36,6 +47,8 @@ class TitleState extends MusicBeatState PlayerSettings.init(); + curWacky = FlxG.random.getObject(wackyIntros); + // DEBUG BULLSHIT super.create(); @@ -55,18 +68,20 @@ class TitleState extends MusicBeatState diamond.persist = true; diamond.destroyOnNoUse = false; - FlxTransitionableState.defaultTransIn = new TransitionData(FADE, FlxColor.BLACK, 2, new FlxPoint(0, -1), {asset: diamond, width: 32, height: 32}, + FlxTransitionableState.defaultTransIn = new TransitionData(FADE, FlxColor.BLACK, 1, new FlxPoint(0, -1), {asset: diamond, width: 32, height: 32}, new FlxRect(0, 0, FlxG.width, FlxG.height)); - FlxTransitionableState.defaultTransOut = new TransitionData(FADE, FlxColor.BLACK, 1.3, new FlxPoint(0, 1), + FlxTransitionableState.defaultTransOut = new TransitionData(FADE, FlxColor.BLACK, 0.7, new FlxPoint(0, 1), {asset: diamond, width: 32, height: 32}, new FlxRect(0, 0, FlxG.width, FlxG.height)); - initialized = true; - FlxTransitionableState.defaultTransIn.tileData = {asset: diamond, width: 32, height: 32}; FlxTransitionableState.defaultTransOut.tileData = {asset: diamond, width: 32, height: 32}; transIn = FlxTransitionableState.defaultTransIn; transOut = FlxTransitionableState.defaultTransOut; + + FlxG.sound.playMusic('assets/music/freakyMenu' + TitleState.soundExt, 0); + + FlxG.sound.music.fadeIn(4, 0, 0.7); } persistentUpdate = true; @@ -104,13 +119,22 @@ class TitleState extends MusicBeatState credTextShit.visible = false; + ngSpr = new FlxSprite(0, FlxG.height * 0.52).loadGraphic(AssetPaths.newgrounds_logo__png); + add(ngSpr); + ngSpr.visible = false; + ngSpr.setGraphicSize(Std.int(ngSpr.width * 0.8)); + ngSpr.updateHitbox(); + ngSpr.screenCenter(X); + ngSpr.antialiasing = true; + FlxTween.tween(credTextShit, {y: credTextShit.y + 20}, 2.9, {ease: FlxEase.quadInOut, type: PINGPONG}); + if (initialized) + skipIntro(); + else + initialized = true; + // credGroup.add(credTextShit); - - FlxG.sound.playMusic('assets/music/freakyMenu' + TitleState.soundExt, 0, false); - - FlxG.sound.music.fadeIn(4, 0, 0.7); } var transitioning:Bool = false; @@ -129,23 +153,24 @@ class TitleState extends MusicBeatState pressedEnter = true; } - if (pressedEnter && !skippedIntro) - { - skipIntro(); - } - if (pressedEnter && !transitioning && skippedIntro) { FlxG.camera.flash(FlxColor.WHITE, 1); + FlxG.sound.play('assets/sounds/confirmMenu' + TitleState.soundExt, 0.7); transitioning = true; - FlxG.sound.music.stop(); + // FlxG.sound.music.stop(); new FlxTimer().start(2, function(tmr:FlxTimer) { - FlxG.switchState(new FreeplayState()); + FlxG.switchState(new MainMenuState()); }); - FlxG.sound.play('assets/music/titleShoot' + TitleState.soundExt, 0.7); + // FlxG.sound.play('assets/music/titleShoot' + TitleState.soundExt, 0.7); + } + + if (pressedEnter && !skippedIntro) + { + skipIntro(); } super.update(elapsed); @@ -205,18 +230,20 @@ class TitleState extends MusicBeatState createCoolText(['In association', 'with']); case 7: addMoreText('newgrounds'); + ngSpr.visible = true; // credTextShit.text += '\nNewgrounds'; case 8: deleteCoolText(); + ngSpr.visible = false; // credTextShit.visible = false; // credTextShit.text = 'Shoutouts Tom Fulp'; // credTextShit.screenCenter(); case 9: - createCoolText(['Shoutouts Tom Fulp']); + createCoolText([curWacky[0]]); // credTextShit.visible = true; case 11: - addMoreText('lmao'); + addMoreText(curWacky[1]); // credTextShit.text += '\nlmao'; case 12: deleteCoolText(); @@ -243,6 +270,8 @@ class TitleState extends MusicBeatState { if (!skippedIntro) { + remove(ngSpr); + FlxG.camera.flash(FlxColor.WHITE, 4); remove(credGroup); skippedIntro = true; From e293c0c2a6acd78c0b23b592b9a909590a315705 Mon Sep 17 00:00:00 2001 From: Brandon Date: Sat, 31 Oct 2020 21:44:10 -0400 Subject: [PATCH 08/16] old school syntax --- source/Controls.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/Controls.hx b/source/Controls.hx index b5715a614..92b1fccb2 100644 --- a/source/Controls.hx +++ b/source/Controls.hx @@ -278,7 +278,7 @@ class Controls extends FlxActionSet * @param func * @return ->Void) */ - function forEachBound(control:Control, func:(FlxActionDigital, FlxInputState) -> Void) + function forEachBound(control:Control, func:FlxActionDigital -> Void, func:FlxInputState -> Void) { switch (control) { From 81db7718d2731d74064d4581bae9d7450d1c0f29 Mon Sep 17 00:00:00 2001 From: Brandon Date: Sun, 1 Nov 2020 14:16:22 -0500 Subject: [PATCH 09/16] ng release and blank controls --- .gitignore | 3 +- CHANGELOG.md | 6 +- Project.xml | 3 +- source/Alphabet.hx | 12 + source/FreeplayState.hx | 98 ++-- source/GameOverSubstate.hx | 14 +- source/MainMenuState.hx | 116 +++-- source/NGio.hx | 158 ++++++ source/PlayState.hx | 35 +- source/StoryMenuState.hx | 19 +- source/TitleState.hx | 73 ++- source/io/newgrounds/Call.hx | 227 +++++++++ source/io/newgrounds/NG.hx | 475 ++++++++++++++++++ source/io/newgrounds/NGLite.hx | 287 +++++++++++ .../io/newgrounds/components/AppComponent.hx | 44 ++ source/io/newgrounds/components/Component.hx | 13 + .../io/newgrounds/components/ComponentList.hx | 25 + .../newgrounds/components/EventComponent.hx | 16 + .../newgrounds/components/GatewayComponent.hx | 25 + .../newgrounds/components/LoaderComponent.hx | 44 ++ .../newgrounds/components/MedalComponent.hx | 21 + .../components/ScoreBoardComponent.hx | 114 +++++ source/io/newgrounds/crypto/Cipher.hx | 8 + .../io/newgrounds/crypto/EncryptionFormat.hx | 7 + source/io/newgrounds/crypto/Rc4.hx | 68 +++ source/io/newgrounds/objects/Error.hx | 20 + source/io/newgrounds/objects/Medal.hx | 118 +++++ source/io/newgrounds/objects/Object.hx | 33 ++ source/io/newgrounds/objects/Score.hx | 17 + source/io/newgrounds/objects/ScoreBoard.hx | 76 +++ source/io/newgrounds/objects/Session.hx | 65 +++ source/io/newgrounds/objects/User.hx | 19 + source/io/newgrounds/objects/UserIcons.hx | 14 + .../io/newgrounds/objects/events/Response.hx | 43 ++ source/io/newgrounds/objects/events/Result.hx | 109 ++++ source/io/newgrounds/swf/LoadingBar.hx | 23 + source/io/newgrounds/swf/MedalPopup.hx | 151 ++++++ source/io/newgrounds/swf/ScoreBrowser.hx | 250 +++++++++ source/io/newgrounds/swf/common/BaseAsset.hx | 35 ++ source/io/newgrounds/swf/common/Button.hx | 151 ++++++ source/io/newgrounds/swf/common/DropDown.hx | 88 ++++ source/io/newgrounds/utils/AsyncHttp.hx | 203 ++++++++ source/io/newgrounds/utils/Dispatcher.hx | 118 +++++ 43 files changed, 3322 insertions(+), 122 deletions(-) create mode 100644 source/NGio.hx create mode 100644 source/io/newgrounds/Call.hx create mode 100644 source/io/newgrounds/NG.hx create mode 100644 source/io/newgrounds/NGLite.hx create mode 100644 source/io/newgrounds/components/AppComponent.hx create mode 100644 source/io/newgrounds/components/Component.hx create mode 100644 source/io/newgrounds/components/ComponentList.hx create mode 100644 source/io/newgrounds/components/EventComponent.hx create mode 100644 source/io/newgrounds/components/GatewayComponent.hx create mode 100644 source/io/newgrounds/components/LoaderComponent.hx create mode 100644 source/io/newgrounds/components/MedalComponent.hx create mode 100644 source/io/newgrounds/components/ScoreBoardComponent.hx create mode 100644 source/io/newgrounds/crypto/Cipher.hx create mode 100644 source/io/newgrounds/crypto/EncryptionFormat.hx create mode 100644 source/io/newgrounds/crypto/Rc4.hx create mode 100644 source/io/newgrounds/objects/Error.hx create mode 100644 source/io/newgrounds/objects/Medal.hx create mode 100644 source/io/newgrounds/objects/Object.hx create mode 100644 source/io/newgrounds/objects/Score.hx create mode 100644 source/io/newgrounds/objects/ScoreBoard.hx create mode 100644 source/io/newgrounds/objects/Session.hx create mode 100644 source/io/newgrounds/objects/User.hx create mode 100644 source/io/newgrounds/objects/UserIcons.hx create mode 100644 source/io/newgrounds/objects/events/Response.hx create mode 100644 source/io/newgrounds/objects/events/Result.hx create mode 100644 source/io/newgrounds/swf/LoadingBar.hx create mode 100644 source/io/newgrounds/swf/MedalPopup.hx create mode 100644 source/io/newgrounds/swf/ScoreBrowser.hx create mode 100644 source/io/newgrounds/swf/common/BaseAsset.hx create mode 100644 source/io/newgrounds/swf/common/Button.hx create mode 100644 source/io/newgrounds/swf/common/DropDown.hx create mode 100644 source/io/newgrounds/utils/AsyncHttp.hx create mode 100644 source/io/newgrounds/utils/Dispatcher.hx diff --git a/.gitignore b/.gitignore index 5f82e547e..7dbb7d607 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ export/ -.vscode/ \ No newline at end of file +.vscode/ +APIStuff.hx \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 3898230a1..875bc460f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,14 +1,16 @@ # Changelog All notable changes will be documented in this file. -## [Unreleased] +## [1.1.0] ### Added - 32bit support - Controller (dancepads) support - Pause screen - Main Menu overhaul - Cool intro screen thing +- Uh lots of bullshit +- Remind me to actually add this later lmaooo -## [0.1.0] - 2020-10-05 +## [1.0.0] - 2020-10-05 ### Added - Uh, everything. This the game's initial gamejam release. We put it out \ No newline at end of file diff --git a/Project.xml b/Project.xml index 06de8f4ac..24bf57393 100644 --- a/Project.xml +++ b/Project.xml @@ -97,5 +97,6 @@ - + + diff --git a/source/Alphabet.hx b/source/Alphabet.hx index 4e04631ef..9824e2f35 100644 --- a/source/Alphabet.hx +++ b/source/Alphabet.hx @@ -17,6 +17,10 @@ class Alphabet extends FlxSpriteGroup public var delay:Float = 0.05; public var paused:Bool = false; + // for menu shit + public var targetY:Float = 0; + public var isMenuItem:Bool = false; + public var text:String = ""; var _finalText:String = ""; @@ -207,6 +211,14 @@ class Alphabet extends FlxSpriteGroup override function update(elapsed:Float) { + if (isMenuItem) + { + var scaledY = FlxMath.remapToRange(targetY, 0, 1, 0, 1.3); + + y = FlxMath.lerp(y, (scaledY * 120) + (FlxG.height * 0.48), 0.16); + x = FlxMath.lerp(x, (targetY * 20) + 90, 0.16); + } + super.update(elapsed); } } diff --git a/source/FreeplayState.hx b/source/FreeplayState.hx index 0927dd7e6..ecdf81edf 100644 --- a/source/FreeplayState.hx +++ b/source/FreeplayState.hx @@ -3,47 +3,59 @@ package; import flixel.FlxG; import flixel.FlxSprite; import flixel.addons.display.FlxGridOverlay; +import flixel.group.FlxGroup.FlxTypedGroup; import flixel.text.FlxText; -#if switch - import openfl.events.GameInputEvent; - import openfl.ui.GameInput; - import openfl.ui.GameInputDevice; - import openfl.ui.GameInputControl; -#end - class FreeplayState extends MusicBeatState { - var songs:Array = ["Bopeebo", "Dadbattle", "Fresh", "Tutorial", "Spookeez", "South", "Monster"]; + var songs:Array = ["Bopeebo", "Dadbattle", "Fresh", "Tutorial"]; var selector:FlxText; var curSelected:Int = 0; + private var grpSongs:FlxTypedGroup; + override function create() { + if (!FlxG.sound.music.playing) + FlxG.sound.playMusic('assets/music/freakyMenu' + TitleState.soundExt); + + if (StoryMenuState.weekUnlocked[1]) + { + songs.push('Spookeez'); + songs.push('South'); + } + // LOAD MUSIC // LOAD CHARACTERS - var bg:FlxSprite = FlxGridOverlay.create(20, 20); + var bg:FlxSprite = new FlxSprite().loadGraphic(AssetPaths.menuBGBlue__png); add(bg); + grpSongs = new FlxTypedGroup(); + add(grpSongs); + for (i in 0...songs.length) { var songText:Alphabet = new Alphabet(0, (70 * i) + 30, songs[i], true, false); - add(songText); - songText.x += 40; + songText.isMenuItem = true; + songText.targetY = i; + grpSongs.add(songText); + // songText.x += 40; // DONT PUT X IN THE FIRST PARAMETER OF new ALPHABET() !! // songText.screenCenter(X); } - FlxG.sound.playMusic('assets/music/title' + TitleState.soundExt, 0); - FlxG.sound.music.fadeIn(2, 0, 0.8); + changeSelection(); + + // FlxG.sound.playMusic('assets/music/title' + TitleState.soundExt, 0); + // FlxG.sound.music.fadeIn(2, 0, 0.8); selector = new FlxText(); selector.size = 40; selector.text = ">"; - add(selector); + // add(selector); var swag:Alphabet = new Alphabet(1, 0, "swag"); @@ -59,47 +71,53 @@ class FreeplayState extends MusicBeatState if (upP) { - curSelected -= 1; + changeSelection(-1); } if (downP) { - curSelected += 1; + changeSelection(1); } - #if switch - if (gamepad.anyJustPressed(["UP", "DPAD_UP", "LEFT_STICK_DIGITAL_UP"])) - { - curSelected -= 1; - } - if (gamepad.anyJustPressed(["DOWN", "DPAD_DOWN", "LEFT_STICK_DIGITAL_DOWN"])) - { - curSelected += 1; - } - #end + if (controls.BACK) + { + FlxG.switchState(new MainMenuState()); + } + + if (accepted) + { + PlayState.SONG = Song.loadFromJson(songs[curSelected].toLowerCase(), songs[curSelected].toLowerCase()); + PlayState.isStoryMode = false; + FlxG.switchState(new PlayState()); + FlxG.sound.music.stop(); + } + } + + function changeSelection(change:Int = 0) + { + curSelected += change; if (curSelected < 0) curSelected = songs.length - 1; if (curSelected >= songs.length) curSelected = 0; - selector.y = (70 * curSelected) + 30; + // selector.y = (70 * curSelected) + 30; - if (accepted) + var bullShit:Int = 0; + + for (item in grpSongs.members) { - PlayState.SONG = Song.loadFromJson(songs[curSelected].toLowerCase()); - PlayState.isStoryMode = false; - FlxG.switchState(new PlayState()); - FlxG.sound.music.stop(); - } + item.targetY = bullShit - curSelected; + bullShit++; - #if switch - if (gamepad.anyJustPressed(["B"])) //"B" is swapped with "A" on Switch + item.alpha = 0.6; + // item.setGraphicSize(Std.int(item.width * 0.8)); + + if (item.targetY == 0) { - PlayState.SONG = Song.loadFromJson(songs[curSelected].toLowerCase()); - PlayState.isStoryMode = false; - FlxG.switchState(new PlayState()); - FlxG.sound.music.stop(); + item.alpha = 1; + // item.setGraphicSize(Std.int(item.width)); } - #end + } } } diff --git a/source/GameOverSubstate.hx b/source/GameOverSubstate.hx index 1a7d89d58..54df50cdd 100644 --- a/source/GameOverSubstate.hx +++ b/source/GameOverSubstate.hx @@ -12,6 +12,8 @@ class GameOverSubstate extends MusicBeatSubstate var bf:Boyfriend; var camFollow:FlxObject; + // var + public function new(x:Float, y:Float) { super(); @@ -39,11 +41,21 @@ class GameOverSubstate extends MusicBeatSubstate { super.update(elapsed); - if (FlxG.keys.justPressed.ENTER) + if (controls.ACCEPT) { endBullshit(); } + if (controls.BACK) + { + FlxG.sound.music.stop(); + + if (PlayState.isStoryMode) + FlxG.switchState(new StoryMenuState()); + else + FlxG.switchState(new FreeplayState()); + } + if (bf.animation.curAnim.name == 'firstDeath' && bf.animation.curAnim.curFrame == 12) { FlxG.camera.follow(camFollow, LOCKON, 0.01); diff --git a/source/MainMenuState.hx b/source/MainMenuState.hx index 687f6191b..19ddb46bb 100644 --- a/source/MainMenuState.hx +++ b/source/MainMenuState.hx @@ -73,70 +73,76 @@ class MainMenuState extends MusicBeatState super.create(); } + var selectedSomethin:Bool = false; + override function update(elapsed:Float) { - if (controls.UP_P) + if (!selectedSomethin) { - FlxG.sound.play('assets/sounds/scrollMenu' + TitleState.soundExt); - changeItem(-1); - } + if (controls.UP_P) + { + FlxG.sound.play('assets/sounds/scrollMenu' + TitleState.soundExt); + changeItem(-1); + } - if (controls.DOWN_P) - { - FlxG.sound.play('assets/sounds/scrollMenu' + TitleState.soundExt); - changeItem(1); - } + if (controls.DOWN_P) + { + FlxG.sound.play('assets/sounds/scrollMenu' + TitleState.soundExt); + changeItem(1); + } - if (controls.BACK) - { - FlxG.switchState(new TitleState()); + if (controls.BACK) + { + FlxG.switchState(new TitleState()); + } + + if (controls.ACCEPT) + { + if (optionShit[curSelected] == 'donate') + { + FlxG.openURL('https://ninja-muffin24.itch.io/funkin'); + } + else + { + selectedSomethin = true; + FlxG.sound.play('assets/sounds/confirmMenu' + TitleState.soundExt); + + FlxFlicker.flicker(magenta, 1.1, 0.15, false); + + menuItems.forEach(function(spr:FlxSprite) + { + if (curSelected != spr.ID) + { + FlxTween.tween(spr, {alpha: 0}, 0.4, { + ease: FlxEase.quadOut, + onComplete: function(twn:FlxTween) + { + spr.kill(); + } + }); + } + else + { + FlxFlicker.flicker(spr, 1, 0.06, false, false, function(flick:FlxFlicker) + { + var daChoice:String = optionShit[curSelected]; + + switch (daChoice) + { + case 'story mode': + FlxG.switchState(new StoryMenuState()); + case 'freeplay': + FlxG.switchState(new FreeplayState()); + } + }); + } + }); + } + } } super.update(elapsed); - if (controls.ACCEPT) - { - if (optionShit[curSelected] == 'donate') - { - FlxG.openURL('https://ninja-muffin24.itch.io/funkin'); - } - else - { - FlxG.sound.play('assets/sounds/confirmMenu' + TitleState.soundExt); - - FlxFlicker.flicker(magenta, 1.1, 0.15, false); - - menuItems.forEach(function(spr:FlxSprite) - { - if (curSelected != spr.ID) - { - FlxTween.tween(spr, {alpha: 0}, 0.4, { - ease: FlxEase.quadOut, - onComplete: function(twn:FlxTween) - { - spr.kill(); - } - }); - } - else - { - FlxFlicker.flicker(spr, 1, 0.06, false, false, function(flick:FlxFlicker) - { - var daChoice:String = optionShit[curSelected]; - - switch (daChoice) - { - case 'story mode': - FlxG.switchState(new StoryMenuState()); - case 'freeplay': - FlxG.switchState(new FreeplayState()); - } - }); - } - }); - } - } - menuItems.forEach(function(spr:FlxSprite) { spr.screenCenter(X); diff --git a/source/NGio.hx b/source/NGio.hx new file mode 100644 index 000000000..f90bca4c4 --- /dev/null +++ b/source/NGio.hx @@ -0,0 +1,158 @@ +package; + +import flixel.FlxG; +import flixel.util.FlxSignal; +import io.newgrounds.NG; +import io.newgrounds.components.ScoreBoardComponent.Period; +import io.newgrounds.objects.Medal; +import io.newgrounds.objects.Score; +import io.newgrounds.objects.ScoreBoard; +import openfl.display.Stage; + +/** + * MADE BY GEOKURELI THE LEGENED GOD HERO MVP + */ +class NGio +{ + public static var isLoggedIn:Bool = false; + public static var scoreboardsLoaded:Bool = false; + + public static var scoreboardArray:Array = []; + + public static var ngDataLoaded(default, null):FlxSignal = new FlxSignal(); + public static var ngScoresLoaded(default, null):FlxSignal = new FlxSignal(); + + public function new(api:String, encKey:String, ?sessionId:String) + { + trace("connecting to newgrounds"); + + NG.createAndCheckSession(api, sessionId); + + NG.core.verbose = true; + // Set the encryption cipher/format to RC4/Base64. AES128 and Hex are not implemented yet + NG.core.initEncryption(encKey); // Found in you NG project view + + trace(NG.core.attemptingLogin); + + if (NG.core.attemptingLogin) + { + /* a session_id was found in the loadervars, this means the user is playing on newgrounds.com + * and we should login shortly. lets wait for that to happen + */ + trace("attempting login"); + NG.core.onLogin.add(onNGLogin); + } + else + { + /* They are NOT playing on newgrounds.com, no session id was found. We must start one manually, if we want to. + * Note: This will cause a new browser window to pop up where they can log in to newgrounds + */ + NG.core.requestLogin(onNGLogin); + } + } + + function onNGLogin():Void + { + trace('logged in! user:${NG.core.user.name}'); + isLoggedIn = true; + FlxG.save.data.sessionId = NG.core.sessionId; + // FlxG.save.flush(); + // Load medals then call onNGMedalFetch() + NG.core.requestMedals(onNGMedalFetch); + + // Load Scoreboards hten call onNGBoardsFetch() + NG.core.requestScoreBoards(onNGBoardsFetch); + + ngDataLoaded.dispatch(); + } + + // --- MEDALS + function onNGMedalFetch():Void + { + /* + // Reading medal info + for (id in NG.core.medals.keys()) + { + var medal = NG.core.medals.get(id); + trace('loaded medal id:$id, name:${medal.name}, description:${medal.description}'); + } + + // Unlocking medals + var unlockingMedal = NG.core.medals.get(54352);// medal ids are listed in your NG project viewer + if (!unlockingMedal.unlocked) + unlockingMedal.sendUnlock(); + */ + } + + // --- SCOREBOARDS + function onNGBoardsFetch():Void + { + /* + // Reading medal info + for (id in NG.core.scoreBoards.keys()) + { + var board = NG.core.scoreBoards.get(id); + trace('loaded scoreboard id:$id, name:${board.name}'); + } + */ + // var board = NG.core.scoreBoards.get(8004);// ID found in NG project view + + // Posting a score thats OVER 9000! + // board.postScore(FlxG.random.int(0, 1000)); + + // --- To view the scores you first need to select the range of scores you want to see --- + + // add an update listener so we know when we get the new scores + // board.onUpdate.add(onNGScoresFetch); + trace("shoulda got score by NOW!"); + // board.requestScores(20);// get the best 10 scores ever logged + // more info on scores --- http://www.newgrounds.io/help/components/#scoreboard-getscores + } + + inline static public function postScore(score:Int = 0, song:String) + { + if (isLoggedIn) + { + for (id in NG.core.scoreBoards.keys()) + { + var board = NG.core.scoreBoards.get(id); + + if (song == board.name) + { + board.postScore(score, "Uhh meow?"); + } + + // trace('loaded scoreboard id:$id, name:${board.name}'); + } + } + } + + function onNGScoresFetch():Void + { + scoreboardsLoaded = true; + + ngScoresLoaded.dispatch(); + /* + for (score in NG.core.scoreBoards.get(8737).scores) + { + trace('score loaded user:${score.user.name}, score:${score.formatted_value}'); + + } + */ + + // var board = NG.core.scoreBoards.get(8004);// ID found in NG project view + // board.postScore(HighScore.score); + + // NGio.scoreboardArray = NG.core.scoreBoards.get(8004).scores; + } + + inline static public function unlockMedal(id:Int) + { + if (isLoggedIn) + { + var medal = NG.core.medals.get(id); + if (!medal.unlocked) + medal.sendUnlock(); + } + } +} diff --git a/source/PlayState.hx b/source/PlayState.hx index 29aeb7335..2130a37bf 100644 --- a/source/PlayState.hx +++ b/source/PlayState.hx @@ -80,6 +80,7 @@ class PlayState extends MusicBeatState var halloweenBG:FlxSprite; var talking:Bool = true; + var songScore:Int = 0; override public function create() { @@ -593,7 +594,7 @@ class PlayState extends MusicBeatState if (FlxG.keys.justPressed.ESCAPE) { - FlxG.switchState(new ChartingState()); + // FlxG.switchState(new ChartingState()); } // FlxG.watch.addQuick('VOL', vocals.amplitudeLeft); @@ -602,6 +603,9 @@ class PlayState extends MusicBeatState healthHeads.setGraphicSize(Std.int(FlxMath.lerp(100, healthHeads.width, 0.98))); healthHeads.x = healthBar.x + (healthBar.width * (FlxMath.remapToRange(healthBar.percent, 0, 100, 100, 0) * 0.01)) - (healthHeads.width / 2); + if (health > 2) + health = 2; + if (healthBar.percent < 20) healthHeads.animation.play('unhealthy'); else @@ -609,8 +613,8 @@ class PlayState extends MusicBeatState /* if (FlxG.keys.justPressed.NINE) FlxG.switchState(new Charting()); */ - if (FlxG.keys.justPressed.EIGHT) - FlxG.switchState(new AnimationDebug(SONG.player2)); + // if (FlxG.keys.justPressed.EIGHT) + // FlxG.switchState(new AnimationDebug(SONG.player2)); if (startingSong) { @@ -805,15 +809,24 @@ class PlayState extends MusicBeatState { trace('SONG DONE' + isStoryMode); + NGio.postScore(songScore, SONG.song); + if (isStoryMode) { storyPlaylist.remove(storyPlaylist[0]); if (storyPlaylist.length <= 0) { - FlxG.switchState(new TitleState()); + FlxG.sound.playMusic('assets/music/freakyMenu' + TitleState.soundExt); + + FlxG.switchState(new StoryMenuState()); StoryMenuState.weekUnlocked[1] = true; + + NGio.unlockMedal(60961); + + FlxG.save.data.weekUnlocked = StoryMenuState.weekUnlocked; + FlxG.save.flush(); } else { @@ -851,22 +864,28 @@ class PlayState extends MusicBeatState // var rating:FlxSprite = new FlxSprite(); + var score:Int = 350; var daRating:String = "sick"; if (noteDiff > Conductor.safeZoneOffset * 0.9) { daRating = 'shit'; + score = 50; } else if (noteDiff > Conductor.safeZoneOffset * 0.75) { daRating = 'bad'; + score = 100; } else if (noteDiff > Conductor.safeZoneOffset * 0.2) { daRating = 'good'; + score = 200; } + songScore += score; + /* if (combo > 60) daRating = 'sick'; else if (combo > 12) @@ -916,7 +935,9 @@ class PlayState extends MusicBeatState numScore.acceleration.y = FlxG.random.int(200, 300); numScore.velocity.y -= FlxG.random.int(140, 160); numScore.velocity.x = FlxG.random.float(-5, 5); - add(numScore); + + if (combo >= 10 || combo == 0) + add(numScore); FlxTween.tween(numScore, {alpha: 0}, 0.2, { onComplete: function(tween:FlxTween) @@ -1102,6 +1123,8 @@ class PlayState extends MusicBeatState } combo = 0; + songScore -= 10; + FlxG.sound.play('assets/sounds/missnote' + FlxG.random.int(1, 3) + TitleState.soundExt, FlxG.random.float(0.1, 0.2)); // FlxG.sound.play('assets/sounds/missnote1' + TitleState.soundExt, 1, false); // FlxG.log.add('played imss note'); @@ -1283,7 +1306,7 @@ class PlayState extends MusicBeatState if (!boyfriend.animation.curAnim.name.startsWith("sing")) boyfriend.playAnim('idle'); - if (totalBeats % 8 == 6) + if (totalBeats % 8 == 7 && curSong == 'Bopeebo') { boyfriend.playAnim('hey', true); diff --git a/source/StoryMenuState.hx b/source/StoryMenuState.hx index 093746479..d88ab4833 100644 --- a/source/StoryMenuState.hx +++ b/source/StoryMenuState.hx @@ -16,7 +16,7 @@ class StoryMenuState extends MusicBeatState { var scoreText:FlxText; - var weekData:Array = [['Tutorial', 'Bopeebo', 'Fresh', 'Dadbattle'], ['Spookeez', 'South', 'Monster']]; + var weekData:Array = [['Tutorial', 'Bopeebo', 'Fresh', 'Dadbattle'], ['Spookeez', 'South']]; var curDifficulty:Int = 1; public static var weekUnlocked:Array = [true, false]; @@ -38,6 +38,9 @@ class StoryMenuState extends MusicBeatState override function create() { + if (!FlxG.sound.music.playing) + FlxG.sound.playMusic('assets/music/freakyMenu' + TitleState.soundExt); + persistentUpdate = persistentDraw = true; scoreText = new FlxText(10, 10, 0, "SCORE: 49324858", 36); @@ -102,6 +105,7 @@ class StoryMenuState extends MusicBeatState case 'bf': weekCharacterThing.setGraphicSize(Std.int(weekCharacterThing.width * 0.9)); weekCharacterThing.updateHitbox(); + weekCharacterThing.x -= 80; case 'gf': weekCharacterThing.setGraphicSize(Std.int(weekCharacterThing.width * 0.5)); weekCharacterThing.updateHitbox(); @@ -145,8 +149,8 @@ class StoryMenuState extends MusicBeatState txtTracklist.font = rankText.font; txtTracklist.color = 0xFFe55777; add(txtTracklist); - add(rankText); - add(scoreText); + // add(rankText); + // add(scoreText); updateText(); @@ -243,7 +247,8 @@ class StoryMenuState extends MusicBeatState PlayState.SONG = Song.loadFromJson(PlayState.storyPlaylist[0].toLowerCase() + diffic, PlayState.storyPlaylist[0].toLowerCase()); new FlxTimer().start(1, function(tmr:FlxTimer) { - FlxG.sound.music.stop(); + if (FlxG.sound.music != null) + FlxG.sound.music.stop(); FlxG.switchState(new PlayState()); }); } @@ -274,9 +279,11 @@ class StoryMenuState extends MusicBeatState } sprDifficulty.alpha = 0; - sprDifficulty.y -= 15; - FlxTween.tween(sprDifficulty, {y: sprDifficulty.y + 15, alpha: 1}, 0.07); + // USING THESE WEIRD VALUES SO THAT IT DOESNT FLOAT UP + sprDifficulty.y = leftArrow.y - 15; + + FlxTween.tween(sprDifficulty, {y: leftArrow.y + 15, alpha: 1}, 0.07); } function changeWeek(change:Int = 0):Void diff --git a/source/TitleState.hx b/source/TitleState.hx index 72cae1944..12faa41c1 100644 --- a/source/TitleState.hx +++ b/source/TitleState.hx @@ -8,6 +8,7 @@ import flixel.addons.transition.FlxTransitionSprite.GraphicTransTileDiamond; import flixel.addons.transition.FlxTransitionableState; import flixel.addons.transition.TransitionData; import flixel.graphics.FlxGraphic; +import flixel.graphics.frames.FlxAtlasFrames; import flixel.group.FlxGroup; import flixel.input.gamepad.FlxGamepad; import flixel.math.FlxPoint; @@ -53,6 +54,10 @@ class TitleState extends MusicBeatState super.create(); + #if (!debug && NG_LOGIN) + var ng:NGio = new NGio(APIStuff.API, APIStuff.EncKey); + #end + #if SKIP_TO_PLAYSTATE FlxG.switchState(new StoryMenuState()); #else @@ -60,6 +65,11 @@ class TitleState extends MusicBeatState #end } + var logoBl:FlxSprite; + var gfDance:FlxSprite; + var danceLeft:Bool = false; + var titleText:FlxSprite; + function startIntro() { if (!initialized) @@ -82,28 +92,58 @@ class TitleState extends MusicBeatState FlxG.sound.playMusic('assets/music/freakyMenu' + TitleState.soundExt, 0); FlxG.sound.music.fadeIn(4, 0, 0.7); + + FlxG.save.bind('funkin', 'ninjamuffin99'); + + if (FlxG.save.data.weekUnlocked != null) + { + StoryMenuState.weekUnlocked = FlxG.save.data.weekUnlocked; + } } + Conductor.changeBPM(102); persistentUpdate = true; - var bg:FlxSprite = new FlxSprite().loadGraphic(AssetPaths.stageback__png); - bg.antialiasing = true; - bg.setGraphicSize(Std.int(bg.width * 0.6)); - bg.updateHitbox(); + var bg:FlxSprite = new FlxSprite().makeGraphic(FlxG.width, FlxG.height, FlxColor.BLACK); + // bg.antialiasing = true; + // bg.setGraphicSize(Std.int(bg.width * 0.6)); + // bg.updateHitbox(); add(bg); - var logoBl:FlxSprite = new FlxSprite().loadGraphic(AssetPaths.logo__png); - logoBl.screenCenter(); - logoBl.color = FlxColor.BLACK; + logoBl = new FlxSprite(-150, -100); + logoBl.frames = FlxAtlasFrames.fromSparrow(AssetPaths.logoBumpin__png, AssetPaths.logoBumpin__xml); + logoBl.antialiasing = true; + logoBl.animation.addByPrefix('bump', 'logo bumpin', 24); + logoBl.animation.play('bump'); + logoBl.updateHitbox(); + // logoBl.screenCenter(); + // logoBl.color = FlxColor.BLACK; + + gfDance = new FlxSprite(FlxG.width * 0.4, FlxG.height * 0.07); + gfDance.frames = FlxAtlasFrames.fromSparrow(AssetPaths.gfDanceTitle__png, AssetPaths.gfDanceTitle__xml); + gfDance.animation.addByIndices('danceLeft', 'gfDance', [30, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], "", 24, false); + gfDance.animation.addByIndices('danceRight', 'gfDance', [15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29], "", 24, false); + gfDance.antialiasing = true; + add(gfDance); add(logoBl); + titleText = new FlxSprite(100, FlxG.height * 0.8); + titleText.frames = FlxAtlasFrames.fromSparrow(AssetPaths.titleEnter__png, AssetPaths.titleEnter__xml); + titleText.animation.addByPrefix('idle', "Press Enter to Begin", 24); + titleText.animation.addByPrefix('press', "ENTER PRESSED", 24); + titleText.antialiasing = true; + titleText.animation.play('idle'); + titleText.updateHitbox(); + // titleText.screenCenter(X); + add(titleText); + var logo:FlxSprite = new FlxSprite().loadGraphic(AssetPaths.logo__png); logo.screenCenter(); logo.antialiasing = true; - add(logo); + // add(logo); - FlxTween.tween(logoBl, {y: logoBl.y + 50}, 0.6, {ease: FlxEase.quadInOut, type: PINGPONG}); - FlxTween.tween(logo, {y: logoBl.y + 50}, 0.6, {ease: FlxEase.quadInOut, type: PINGPONG, startDelay: 0.1}); + // FlxTween.tween(logoBl, {y: logoBl.y + 50}, 0.6, {ease: FlxEase.quadInOut, type: PINGPONG}); + // FlxTween.tween(logo, {y: logoBl.y + 50}, 0.6, {ease: FlxEase.quadInOut, type: PINGPONG, startDelay: 0.1}); credGroup = new FlxGroup(); add(credGroup); @@ -129,6 +169,8 @@ class TitleState extends MusicBeatState FlxTween.tween(credTextShit, {y: credTextShit.y + 20}, 2.9, {ease: FlxEase.quadInOut, type: PINGPONG}); + FlxG.mouse.visible = false; + if (initialized) skipIntro(); else @@ -155,6 +197,9 @@ class TitleState extends MusicBeatState if (pressedEnter && !transitioning && skippedIntro) { + NGio.unlockMedal(60960); + titleText.animation.play('press'); + FlxG.camera.flash(FlxColor.WHITE, 1); FlxG.sound.play('assets/sounds/confirmMenu' + TitleState.soundExt, 0.7); @@ -210,6 +255,14 @@ class TitleState extends MusicBeatState { super.beatHit(); + logoBl.animation.play('bump'); + danceLeft = !danceLeft; + + if (danceLeft) + gfDance.animation.play('danceRight'); + else + gfDance.animation.play('danceLeft'); + FlxG.log.add(curBeat); switch (curBeat) diff --git a/source/io/newgrounds/Call.hx b/source/io/newgrounds/Call.hx new file mode 100644 index 000000000..bd5d7acaf --- /dev/null +++ b/source/io/newgrounds/Call.hx @@ -0,0 +1,227 @@ +package io.newgrounds; + +import io.newgrounds.utils.Dispatcher; +import io.newgrounds.utils.AsyncHttp; +import io.newgrounds.objects.Error; +import io.newgrounds.objects.events.Result; +import io.newgrounds.objects.events.Result.ResultBase; +import io.newgrounds.objects.events.Response; + +import haxe.ds.StringMap; +import haxe.Json; + +/** A generic way to handle calls agnostic to their type */ +interface ICallable { + + public var component(default, null):String; + + public function send():Void; + public function queue():Void; + public function destroy():Void; +} + +class Call + implements ICallable { + + public var component(default, null):String; + + var _core:NGLite; + var _properties:StringMap; + var _parameters:StringMap; + var _requireSession:Bool; + var _isSecure:Bool; + + // --- BASICALLY SIGNALS + var _dataHandlers:TypedDispatcher>; + var _successHandlers:Dispatcher; + var _httpErrorHandlers:TypedDispatcher; + var _statusHandlers:TypedDispatcher; + + public function new (core:NGLite, component:String, requireSession:Bool = false, isSecure:Bool = false) { + + _core = core; + this.component = component; + _requireSession = requireSession; + _isSecure = isSecure && core.encryptionHandler != null; + } + + /** adds a property to the input's object. **/ + public function addProperty(name:String, value:Dynamic):Call { + + if (_properties == null) + _properties = new StringMap(); + + _properties.set(name, value); + + return this; + } + + /** adds a parameter to the call's component object. **/ + public function addComponentParameter(name:String, value:Dynamic, defaultValue:Dynamic = null):Call { + + if (value == defaultValue)//TODO?: allow sending null value + return this; + + if (_parameters == null) + _parameters = new StringMap(); + + _parameters.set(name, value); + + return this; + } + + /** Handy callback setter for chained call modifiers. Called when ng.io replies successfully */ + public function addDataHandler(handler:Response->Void):Call { + + if (_dataHandlers == null) + _dataHandlers = new TypedDispatcher>(); + + _dataHandlers.add(handler); + return this; + } + + /** Handy callback setter for chained call modifiers. Called when ng.io replies successfully */ + public function addSuccessHandler(handler:Void->Void):Call { + + if (_successHandlers == null) + _successHandlers = new Dispatcher(); + + _successHandlers.add(handler); + return this; + } + + /** Handy callback setter for chained call modifiers. Called when ng.io does not reply for any reason */ + public function addErrorHandler(handler:Error->Void):Call { + + if (_httpErrorHandlers == null) + _httpErrorHandlers = new TypedDispatcher(); + + _httpErrorHandlers.add(handler); + return this; + } + + /** Handy callback setter for chained call modifiers. No idea when this is called; */ + public function addStatusHandler(handler:Int->Void):Call {//TODO:learn what this is for + + if (_statusHandlers == null) + _statusHandlers = new TypedDispatcher(); + + _statusHandlers.add(handler); + return this; + } + + /** + * Sends the call to the server, do not modify this object after calling this + * @param secure If encryption is enabled, it will encrypt the call. + **/ + public function send():Void { + + var data:Dynamic = {}; + data.app_id = _core.appId; + data.call = {}; + data.call.component = component; + + if (_core.debug) + addProperty("debug", true); + + if (_properties == null || !_properties.exists("session_id")) { + // --- HAS NO SESSION ID + + if (_core.sessionId != null) { + // --- AUTO ADD SESSION ID + + addProperty("session_id", _core.sessionId); + + } else if (_requireSession){ + + _core.logError(new Error('cannot send "$component" call without a sessionId')); + return; + } + } + + if (_properties != null) { + + for (field in _properties.keys()) + Reflect.setField(data, field, _properties.get(field)); + } + + if (_parameters != null) { + + data.call.parameters = {}; + + for (field in _parameters.keys()) + Reflect.setField(data.call.parameters, field, _parameters.get(field)); + } + + _core.logVerbose('Post - ${Json.stringify(data)}'); + + if (_isSecure) { + + var secureData = _core.encryptionHandler(Json.stringify(data.call)); + data.call = {}; + data.call.secure = secureData; + + _core.logVerbose(' secure - $secureData'); + } + + _core.markCallPending(this); + + AsyncHttp.send(_core, Json.stringify(data), onData, onHttpError, onStatus); + } + + /** Adds the call to the queue */ + public function queue():Void { + + _core.queueCall(this); + } + + function onData(reply:String):Void { + + _core.logVerbose('Reply - $reply'); + + if (_dataHandlers == null && _successHandlers == null) + return; + + var response = new Response(_core, reply); + + if (_dataHandlers != null) + _dataHandlers.dispatch(response); + + if (response.success && response.result.success && _successHandlers != null) + _successHandlers.dispatch(); + + destroy(); + } + + function onHttpError(message:String):Void { + + _core.logError(message); + + if (_httpErrorHandlers == null) + return; + + var error = new Error(message); + _httpErrorHandlers.dispatch(error); + } + + function onStatus(status:Int):Void { + + if (_statusHandlers == null) + return; + + _statusHandlers.dispatch(status); + } + + public function destroy():Void { + + _core = null; + + _properties = null; + _parameters = null; + + _dataHandlers = null; + _successHandlers = null; + _httpErrorHandlers = null; + _statusHandlers = null; + } +} \ No newline at end of file diff --git a/source/io/newgrounds/NG.hx b/source/io/newgrounds/NG.hx new file mode 100644 index 000000000..3445443ee --- /dev/null +++ b/source/io/newgrounds/NG.hx @@ -0,0 +1,475 @@ +package io.newgrounds; + +#if ng_lite +typedef NG = NGLite; //TODO: test and make lite UI +#else +import io.newgrounds.utils.Dispatcher; +import io.newgrounds.objects.Error; +import io.newgrounds.objects.events.Result.SessionResult; +import io.newgrounds.objects.events.Result.MedalListResult; +import io.newgrounds.objects.events.Result.ScoreBoardResult; +import io.newgrounds.objects.events.Response; +import io.newgrounds.objects.User; +import io.newgrounds.objects.Medal; +import io.newgrounds.objects.Session; +import io.newgrounds.objects.ScoreBoard; + +import haxe.ds.IntMap; +import haxe.Timer; + +/** + * The Newgrounds API for Haxe. + * Contains many things ripped from MSGhero + * - https://github.com/MSGhero/NG.hx + * @author GeoKureli + */ +class NG extends NGLite { + + static public var core(default, null):NG; + static public var onCoreReady(default, null):Dispatcher = new Dispatcher(); + + // --- DATA + + /** The logged in user */ + public var user(get, never):User; + function get_user():User { + + if (_session == null) + return null; + + return _session.user; + } + public var passportUrl(get, never):String; + function get_passportUrl():String { + + if (_session == null || _session.status != SessionStatus.REQUEST_LOGIN) + return null; + + return _session.passportUrl; + } + public var medals(default, null):IntMap; + public var scoreBoards(default, null):IntMap; + + // --- EVENTS + + public var onLogin(default, null):Dispatcher; + public var onLogOut(default, null):Dispatcher; + public var onMedalsLoaded(default, null):Dispatcher; + public var onScoreBoardsLoaded(default, null):Dispatcher; + + // --- MISC + + public var loggedIn(default, null):Bool; + public var attemptingLogin(default, null):Bool; + + var _loginCancelled:Bool; + var _passportCallback:Void->Void; + + var _session:Session; + + /** + * Iniitializes the API, call before utilizing any other component + * @param appId The unique ID of your app as found in the 'API Tools' tab of your Newgrounds.com project. + * @param sessionId A unique session id used to identify the active user. + **/ + public function new(appId = "test", sessionId:String = null, ?onSessionFail:Error->Void) { + + _session = new Session(this); + onLogin = new Dispatcher(); + onLogOut = new Dispatcher(); + onMedalsLoaded = new Dispatcher(); + onScoreBoardsLoaded = new Dispatcher(); + + attemptingLogin = sessionId != null; + + super(appId, sessionId, onSessionFail); + } + + /** + * Creates NG.core, the heart and soul of the API. This is not the only way to create an instance, + * nor is NG a forced singleton, but it's the only way to set the static NG.core. + **/ + static public function create(appId = "test", sessionId:String = null, ?onSessionFail:Error->Void):Void { + + core = new NG(appId, sessionId, onSessionFail); + + onCoreReady.dispatch(); + } + + /** + * Creates NG.core, and tries to create a session. This is not the only way to create an instance, + * nor is NG a forced singleton, but it's the only way to set the static NG.core. + **/ + static public function createAndCheckSession + ( appId = "test" + , backupSession:String = null + , ?onSessionFail:Error->Void + ):Void { + + var session = NGLite.getSessionId(); + if (session == null) + session = backupSession; + + create(appId, session, onSessionFail); + + core.host = getHost(); + if (core.sessionId != null) + core.attemptingLogin = true; + } + + // ------------------------------------------------------------------------------------------- + // APP + // ------------------------------------------------------------------------------------------- + + override function checkInitialSession(failHandler:Error->Void, response:Response):Void { + + onSessionReceive(response, null, null, failHandler); + } + + /** + * Begins the login process + * + * @param onSuccess Called when the login is a success + * @param onPending Called when the passportUrl has been identified, call NG.core.openPassportLink + * to open the link continue the process. Leave as null to open the url automatically + * NOTE: Browser games must open links on click events or else it will be blocked by + * the popup blocker. + * @param onFail + * @param onCancel Called when the user denies the passport connection. + */ + public function requestLogin + ( onSuccess:Void->Void = null + , onPending:Void->Void = null + , onFail :Error->Void = null + , onCancel :Void->Void = null + ):Void { + + if (attemptingLogin) { + + logError("cannot request another login until the previous attempt is complete"); + return; + } + + if (loggedIn) { + + logError("cannot log in, already logged in"); + return; + } + + attemptingLogin = true; + _loginCancelled = false; + _passportCallback = null; + + var call = calls.app.startSession(true) + .addDataHandler(onSessionReceive.bind(_, onSuccess, onPending, onFail, onCancel)); + + if (onFail != null) + call.addErrorHandler(onFail); + + call.send(); + } + + function onSessionReceive + ( response :Response + , onSuccess:Void->Void = null + , onPending:Void->Void = null + , onFail :Error->Void = null + , onCancel :Void->Void = null + ):Void { + + if (!response.success || !response.result.success) { + + sessionId = null; + endLoginAndCall(null); + + if (onFail != null) + onFail(!response.success ? response.error : response.result.error); + + return; + } + + _session.parse(response.result.data.session); + sessionId = _session.id; + + logVerbose('session started - status: ${_session.status}'); + + if (_session.status == SessionStatus.REQUEST_LOGIN) { + + _passportCallback = checkSession.bind(null, onSuccess, onCancel); + if (onPending != null) + onPending(); + else + openPassportUrl(); + + } else + checkSession(null, onSuccess, onCancel); + } + + /** + * Call this once the passport link is established and it will load the passport URL and + * start checking for session connect periodically + */ + public function openPassportUrl():Void { + + if (passportUrl != null) { + + logVerbose('loading passport: ${passportUrl}'); + openPassportHelper(passportUrl); + onPassportUrlOpen(); + + } else + logError("Cannot open passport"); + } + + + static function openPassportHelper(url:String):Void { + var window = "_blank"; + + #if flash + flash.Lib.getURL(new flash.net.URLRequest(url), window); + #elseif (js && html5) + js.Browser.window.open(url, window); + #elseif desktop + + #if (sys && windows) + Sys.command("start", ["", url]); + #elseif mac + Sys.command("/usr/bin/open", [url]); + #elseif linux + Sys.command("/usr/bin/xdg-open", [path, "&"]); + #end + + #elseif android + JNI.createStaticMethod + ( "org/haxe/lime/GameActivity" + , "openURL" + , "(Ljava/lang/String;Ljava/lang/String;)V" + ) (url, window); + #end + } + + /** + * Call this once the passport link is established and it will start checking for session connect periodically + */ + public function onPassportUrlOpen():Void { + + if (_passportCallback != null) + _passportCallback(); + + _passportCallback = null; + } + + function checkSession(response:Response, onSucceess:Void->Void, onCancel:Void->Void):Void { + + if (response != null) { + + if (!response.success || !response.result.success) { + + log("login cancelled via passport"); + + endLoginAndCall(onCancel); + return; + } + + _session.parse(response.result.data.session); + } + + if (_session.status == SessionStatus.USER_LOADED) { + + loggedIn = true; + endLoginAndCall(onSucceess); + onLogin.dispatch(); + + } else if (_session.status == SessionStatus.REQUEST_LOGIN){ + + var call = calls.app.checkSession() + .addDataHandler(checkSession.bind(_, onSucceess, onCancel)); + + // Wait 3 seconds and try again + timer(3.0, + function():Void { + + // Check if cancelLoginRequest was called + if (!_loginCancelled) + call.send(); + else { + + log("login cancelled via cancelLoginRequest"); + endLoginAndCall(onCancel); + } + } + ); + + } else + // The user cancelled the passport + endLoginAndCall(onCancel); + } + + public function cancelLoginRequest():Void { + + if (attemptingLogin) + _loginCancelled = true; + } + + function endLoginAndCall(callback:Void->Void):Void { + + attemptingLogin = false; + _loginCancelled = false; + + if (callback != null) + callback(); + } + + public function logOut(onComplete:Void->Void = null):Void { + + var call = calls.app.endSession() + .addSuccessHandler(onLogOutSuccessful); + + if (onComplete != null) + call.addSuccessHandler(onComplete); + + call.addSuccessHandler(onLogOut.dispatch) + .send(); + } + + function onLogOutSuccessful():Void { + + _session.expire(); + sessionId = null; + loggedIn = false; + } + + // ------------------------------------------------------------------------------------------- + // MEDALS + // ------------------------------------------------------------------------------------------- + + public function requestMedals(onSuccess:Void->Void = null, onFail:Error->Void = null):Void { + + var call = calls.medal.getList() + .addDataHandler(onMedalsReceived); + + if (onSuccess != null) + call.addSuccessHandler(onSuccess); + + if (onFail != null) + call.addErrorHandler(onFail); + + call.send(); + } + + function onMedalsReceived(response:Response):Void { + + if (!response.success || !response.result.success) + return; + + var idList:Array = new Array(); + + if (medals == null) { + + medals = new IntMap(); + + for (medalData in response.result.data.medals) { + + var medal = new Medal(this, medalData); + medals.set(medal.id, medal); + idList.push(medal.id); + } + } else { + + for (medalData in response.result.data.medals) { + + medals.get(medalData.id).parse(medalData); + idList.push(medalData.id); + } + } + + logVerbose('${response.result.data.medals.length} Medals received [${idList.join(", ")}]'); + + onMedalsLoaded.dispatch(); + } + + // ------------------------------------------------------------------------------------------- + // SCOREBOARDS + // ------------------------------------------------------------------------------------------- + + public function requestScoreBoards(onSuccess:Void->Void = null, onFail:Error->Void = null):Void { + + if (scoreBoards != null) { + + log("aborting scoreboard request, all scoreboards are loaded"); + + if (onSuccess != null) + onSuccess(); + + return; + } + + var call = calls.scoreBoard.getBoards() + .addDataHandler(onBoardsReceived); + + if (onSuccess != null) + call.addSuccessHandler(onSuccess); + + if (onFail != null) + call.addErrorHandler(onFail); + + call.send(); + } + + function onBoardsReceived(response:Response):Void { + + if (!response.success || !response.result.success) + return; + + var idList:Array = new Array(); + + if (scoreBoards == null) { + + scoreBoards = new IntMap(); + + for (boardData in response.result.data.scoreboards) { + + var board = new ScoreBoard(this, boardData); + scoreBoards.set(board.id, board); + idList.push(board.id); + } + } + + logVerbose('${response.result.data.scoreboards.length} ScoreBoards received [${idList.join(", ")}]'); + + onScoreBoardsLoaded.dispatch(); + } + + // ------------------------------------------------------------------------------------------- + // HELPERS + // ------------------------------------------------------------------------------------------- + + function timer(delay:Float, callback:Void->Void):Void { + + var timer = new Timer(Std.int(delay * 1000)); + timer.run = function func():Void { + + timer.stop(); + callback(); + } + } + + static var urlParser:EReg = ~/^(?:http[s]?:\/\/)?([^:\/\s]+)(:[0-9]+)?((?:\/\w+)*\/)([\w\-\.]+[^#?\s]+)([^#\s]*)?(#[\w\-]+)?$/i;//TODO:trim + /** Used to get the current web host of your game. */ + static public function getHost():String { + + var url = NGLite.getUrl(); + + if (url == null || url == "") + return ""; + + if (url.indexOf("file") == 0) + return ""; + + if (urlParser.match(url)) + return urlParser.matched(1); + + return ""; + } +} +#end \ No newline at end of file diff --git a/source/io/newgrounds/NGLite.hx b/source/io/newgrounds/NGLite.hx new file mode 100644 index 000000000..0c9d5bdf5 --- /dev/null +++ b/source/io/newgrounds/NGLite.hx @@ -0,0 +1,287 @@ +package io.newgrounds; + +import haxe.crypto.Base64; +import haxe.io.Bytes; +import haxe.PosInfos; + +import io.newgrounds.Call.ICallable; +import io.newgrounds.components.ComponentList; +import io.newgrounds.crypto.EncryptionFormat; +import io.newgrounds.crypto.Cipher; +import io.newgrounds.crypto.Rc4; +import io.newgrounds.objects.Error; +import io.newgrounds.objects.events.Response; +import io.newgrounds.objects.events.Result.ResultBase; +import io.newgrounds.objects.events.Result.SessionResult; +import io.newgrounds.utils.Dispatcher; + +#if !(html5 || flash || desktop || neko) + #error "Target not supported, use: Flash, JS/HTML5, cpp or maybe neko"; +#end + +/** + * The barebones NG.io API. Allows API calls with code completion + * and retrieves server data via strongly typed Objects + * + * Contains many things ripped from MSGhero's repo + * - https://github.com/MSGhero/NG.hx + * + * @author GeoKureli + */ +class NGLite { + + static public var core(default, null):NGLite; + static public var onCoreReady(default, null):Dispatcher = new Dispatcher(); + + /** Enables verbose logging */ + public var verbose:Bool; + public var debug:Bool; + /** The unique ID of your app as found in the 'API Tools' tab of your Newgrounds.com project. */ + public var appId(default, null):String; + /** The name of the host the game is being played on */ + public var host:String; + + @:isVar + public var sessionId(default, set):String; + function set_sessionId(value:String):String { + + return this.sessionId = value == "" ? null : value; + } + + /** Components used to call the NG server directly */ + public var calls(default, null):ComponentList; + + /** + * Converts an object to an encrypted string that can be decrypted by the server. + * Set your preffered encrypter here, + * or just call setDefaultEcryptionHandler with your app's encryption settings + **/ + public var encryptionHandler:String->String; + + /** + * Iniitializes the API, call before utilizing any other component + * @param appId The unique ID of your app as found in the 'API Tools' tab of your Newgrounds.com project. + * @param sessionId A unique session id used to identify the active user. + **/ + public function new(appId = "test", sessionId:String = null, ?onSessionFail:Error->Void) { + + this.appId = appId; + this.sessionId = sessionId; + + calls = new ComponentList(this); + + if (this.sessionId != null) { + + calls.app.checkSession() + .addDataHandler(checkInitialSession.bind(onSessionFail)) + .addErrorHandler(initialSessionFail.bind(onSessionFail)) + .send(); + } + } + + function checkInitialSession(onFail:Error->Void, response:Response):Void { + + if (!response.success || !response.result.success || response.result.data.session.expired) { + + initialSessionFail(onFail, response.success ? response.result.error : response.error); + } + } + + function initialSessionFail(onFail:Error->Void, error:Error):Void { + + sessionId = null; + + if (onFail != null) + onFail(error); + } + + /** + * Creates NG.core, the heart and soul of the API. This is not the only way to create an instance, + * nor is NG a forced singleton, but it's the only way to set the static NG.core. + **/ + static public function create(appId = "test", sessionId:String = null, ?onSessionFail:Error->Void):Void { + + core = new NGLite(appId, sessionId, onSessionFail); + + onCoreReady.dispatch(); + } + + /** + * Creates NG.core, and tries to create a session. This is not the only way to create an instance, + * nor is NG a forced singleton, but it's the only way to set the static NG.core. + **/ + static public function createAndCheckSession + ( appId = "test" + , backupSession:String = null + , ?onSessionFail:Error->Void + ):Void { + + var session = getSessionId(); + if (session == null) + session = backupSession; + + create(appId, session, onSessionFail); + } + + inline static public function getUrl():String { + + #if html5 + return js.Browser.document.location.href; + #elseif flash + return flash.Lib.current.stage.loaderInfo != null + ? flash.Lib.current.stage.loaderInfo.url + : null; + #else + return null; + #end + } + + static public function getSessionId():String { + + #if html5 + + var url = getUrl(); + + // Check for URL params + var index = url.indexOf("?"); + if (index != -1) { + + // Check for session ID in params + for (param in url.substr(index + 1).split("&")) { + + index = param.indexOf("="); + if (index != -1 && param.substr(0, index) == "ngio_session_id") + return param.substr(index + 1); + } + } + + #elseif flash + + if (flash.Lib.current.stage.loaderInfo != null + && Reflect.hasField(flash.Lib.current.stage.loaderInfo.parameters, "ngio_session_id")) + return Reflect.field(flash.Lib.current.stage.loaderInfo.parameters, "ngio_session_id"); + + #end + + return null; + + // --- EXAMPLE LOADER PARAMS + //{ "1517703669" : "" + //, "ng_username" : "GeoKureli" + //, "NewgroundsAPI_SessionID" : "F1LusbG6P8Qf91w7zeUE37c1752563f366688ac6153996d12eeb111a2f60w2xn" + //, "NewgroundsAPI_PublisherID" : 1 + //, "NewgroundsAPI_UserID" : 488329 + //, "NewgroundsAPI_SandboxID" : "5a76520e4ae1e" + //, "ngio_session_id" : "0c6c4e02567a5116734ba1a0cd841dac28a42e79302290" + //, "NewgroundsAPI_UserName" : "GeoKureli" + //} + } + + // ------------------------------------------------------------------------------------------- + // CALLS + // ------------------------------------------------------------------------------------------- + + var _queuedCalls:Array = new Array(); + var _pendingCalls:Array = new Array(); + + @:allow(io.newgrounds.Call) + @:generic + function queueCall(call:Call):Void { + + logVerbose('queued - ${call.component}'); + + _queuedCalls.push(call); + checkQueue(); + } + + @:allow(io.newgrounds.Call) + @:generic + function markCallPending(call:Call):Void { + + _pendingCalls.push(call); + + call.addDataHandler(function (_):Void { onCallComplete(call); }); + call.addErrorHandler(function (_):Void { onCallComplete(call); }); + } + + function onCallComplete(call:ICallable):Void { + + _pendingCalls.remove(call); + checkQueue(); + } + + function checkQueue():Void { + + if (_pendingCalls.length == 0 && _queuedCalls.length > 0) + _queuedCalls.shift().send(); + } + + // ------------------------------------------------------------------------------------------- + // LOGGING / ERRORS + // ------------------------------------------------------------------------------------------- + + /** Called internally, set this to your preferred logging method */ + dynamic public function log(any:Dynamic, ?pos:PosInfos):Void {//TODO: limit access via @:allow + + haxe.Log.trace('[Newgrounds API] :: ${any}', pos); + } + + /** used internally, logs if verbose is true */ + inline public function logVerbose(any:Dynamic, ?pos:PosInfos):Void {//TODO: limit access via @:allow + + if (verbose) + log(any, pos); + } + + /** Used internally. Logs by default, set this to your preferred error handling method */ + dynamic public function logError(any:Dynamic, ?pos:PosInfos):Void {//TODO: limit access via @:allow + + log('Error: $any', pos); + } + + /** used internally, calls log error if the condition is false. EX: if (assert(data != null, "null data")) */ + inline public function assert(condition:Bool, msg:Dynamic, ?pos:PosInfos):Bool {//TODO: limit access via @:allow + if (!condition) + logError(msg, pos); + + return condition; + } + + // ------------------------------------------------------------------------------------------- + // ENCRYPTION + // ------------------------------------------------------------------------------------------- + + /** Sets */ + public function initEncryption + ( key :String + , cipher:Cipher = Cipher.RC4 + , format:EncryptionFormat = EncryptionFormat.BASE_64 + ):Void { + + if (cipher == Cipher.NONE) + encryptionHandler = null; + else if (cipher == Cipher.RC4) + encryptionHandler = encryptRc4.bind(key, format); + else + throw "aes not yet implemented"; + } + + function encryptRc4(key:String, format:EncryptionFormat, data:String):String { + + if (format == EncryptionFormat.HEX) + throw "hex format not yet implemented"; + + var keyBytes:Bytes; + if (format == EncryptionFormat.BASE_64) + keyBytes = Base64.decode(key); + else + keyBytes = null;//TODO + + var dataBytes = new Rc4(keyBytes).crypt(Bytes.ofString(data)); + + if (format == EncryptionFormat.BASE_64) + return Base64.encode(dataBytes); + + return null; + } +} \ No newline at end of file diff --git a/source/io/newgrounds/components/AppComponent.hx b/source/io/newgrounds/components/AppComponent.hx new file mode 100644 index 000000000..1ea5b8b16 --- /dev/null +++ b/source/io/newgrounds/components/AppComponent.hx @@ -0,0 +1,44 @@ +package io.newgrounds.components; + +import io.newgrounds.objects.events.Result; +import io.newgrounds.objects.events.Result.SessionResult; +import io.newgrounds.NGLite; + +class AppComponent extends Component { + + public function new (core:NGLite) { super(core); } + + public function startSession(force:Bool = false):Call { + + return new Call(_core, "App.startSession") + .addComponentParameter("force", force, false); + } + + public function checkSession():Call { + + return new Call(_core, "App.checkSession", true); + } + + public function endSession():Call { + + return new Call(_core, "App.endSession", true); + } + + public function getCurrentVersion(version:String):Call { + + return new Call(_core, "App.getCurrentVersion") + .addComponentParameter("version", version); + } + + public function getHostLicense():Call { + + return new Call(_core, "App.getHostLicense") + .addComponentParameter("host", _core.host); + } + + public function logView():Call { + + return new Call(_core, "App.logView") + .addComponentParameter("host", _core.host); + } +} \ No newline at end of file diff --git a/source/io/newgrounds/components/Component.hx b/source/io/newgrounds/components/Component.hx new file mode 100644 index 000000000..3c588ff19 --- /dev/null +++ b/source/io/newgrounds/components/Component.hx @@ -0,0 +1,13 @@ +package io.newgrounds.components; + +import io.newgrounds.NGLite; + +class Component { + + var _core:NGLite; + + public function new(core:NGLite) { + + this._core = core; + } +} \ No newline at end of file diff --git a/source/io/newgrounds/components/ComponentList.hx b/source/io/newgrounds/components/ComponentList.hx new file mode 100644 index 000000000..315abf4ed --- /dev/null +++ b/source/io/newgrounds/components/ComponentList.hx @@ -0,0 +1,25 @@ +package io.newgrounds.components; +class ComponentList { + + var _core:NGLite; + + // --- COMPONENTS + public var medal : MedalComponent; + public var app : AppComponent; + public var event : EventComponent; + public var scoreBoard: ScoreBoardComponent; + public var loader : LoaderComponent; + public var gateway : GatewayComponent; + + public function new(core:NGLite) { + + _core = core; + + medal = new MedalComponent (_core); + app = new AppComponent (_core); + event = new EventComponent (_core); + scoreBoard = new ScoreBoardComponent(_core); + loader = new LoaderComponent (_core); + gateway = new GatewayComponent (_core); + } +} diff --git a/source/io/newgrounds/components/EventComponent.hx b/source/io/newgrounds/components/EventComponent.hx new file mode 100644 index 000000000..2e631773e --- /dev/null +++ b/source/io/newgrounds/components/EventComponent.hx @@ -0,0 +1,16 @@ +package io.newgrounds.components; + +import io.newgrounds.objects.events.Result.LogEventResult; +import io.newgrounds.NGLite; + +class EventComponent extends Component { + + public function new (core:NGLite){ super(core); } + + public function logEvent(eventName:String):Call { + + return new Call(_core, "Event.logEvent") + .addComponentParameter("event_name", eventName) + .addComponentParameter("host", _core.host); + } +} \ No newline at end of file diff --git a/source/io/newgrounds/components/GatewayComponent.hx b/source/io/newgrounds/components/GatewayComponent.hx new file mode 100644 index 000000000..4f0ece617 --- /dev/null +++ b/source/io/newgrounds/components/GatewayComponent.hx @@ -0,0 +1,25 @@ +package io.newgrounds.components; + +import io.newgrounds.objects.events.Result; +import io.newgrounds.NGLite; + +class GatewayComponent extends Component { + + public function new (core:NGLite){ super(core); } + + public function getDatetime():Call { + + return new Call(_core, "Gateway.getDatetime"); + } + + public function getVersion():Call { + + return new Call(_core, "Gateway.getVersion"); + } + + public function ping():Call { + + return new Call(_core, "Gateway.ping"); + } + +} \ No newline at end of file diff --git a/source/io/newgrounds/components/LoaderComponent.hx b/source/io/newgrounds/components/LoaderComponent.hx new file mode 100644 index 000000000..717cc2eb2 --- /dev/null +++ b/source/io/newgrounds/components/LoaderComponent.hx @@ -0,0 +1,44 @@ +package io.newgrounds.components; + +import io.newgrounds.objects.events.Result; +import io.newgrounds.NGLite; + +class LoaderComponent extends Component { + + public function new (core:NGLite){ super(core); } + + public function loadAuthorUrl(redirect:Bool = false):Call { + + return new Call(_core, "Loader.loadAuthorUrl") + .addComponentParameter("host", _core.host) + .addComponentParameter("redirect", redirect, true); + } + + public function loadMoreGames(redirect:Bool = false):Call { + + return new Call(_core, "Loader.loadMoreGames") + .addComponentParameter("host", _core.host) + .addComponentParameter("redirect", redirect, true); + } + + public function loadNewgrounds(redirect:Bool = false):Call { + + return new Call(_core, "Loader.loadNewgrounds") + .addComponentParameter("host", _core.host) + .addComponentParameter("redirect", redirect, true); + } + + public function loadOfficialUrl(redirect:Bool = false):Call { + + return new Call(_core, "Loader.loadOfficialUrl") + .addComponentParameter("host", _core.host) + .addComponentParameter("redirect", redirect, true); + } + + public function loadReferral(redirect:Bool = false):Call { + + return new Call(_core, "Loader.loadReferral") + .addComponentParameter("host", _core.host) + .addComponentParameter("redirect", redirect, true); + } +} \ No newline at end of file diff --git a/source/io/newgrounds/components/MedalComponent.hx b/source/io/newgrounds/components/MedalComponent.hx new file mode 100644 index 000000000..7e56621c4 --- /dev/null +++ b/source/io/newgrounds/components/MedalComponent.hx @@ -0,0 +1,21 @@ +package io.newgrounds.components; + +import io.newgrounds.objects.events.Result; +import io.newgrounds.Call; +import io.newgrounds.NGLite; + +class MedalComponent extends Component { + + public function new(core:NGLite):Void { super(core); } + + public function unlock(id:Int):Call { + + return new Call(_core, "Medal.unlock", true, true) + .addComponentParameter("id", id); + } + + public function getList():Call { + + return new Call(_core, "Medal.getList"); + } +} \ No newline at end of file diff --git a/source/io/newgrounds/components/ScoreBoardComponent.hx b/source/io/newgrounds/components/ScoreBoardComponent.hx new file mode 100644 index 000000000..7417e67c6 --- /dev/null +++ b/source/io/newgrounds/components/ScoreBoardComponent.hx @@ -0,0 +1,114 @@ +package io.newgrounds.components; + +import io.newgrounds.objects.User; +import io.newgrounds.objects.events.Response; +import io.newgrounds.objects.events.Result; +import io.newgrounds.objects.events.Result.ScoreBoardResult; +import io.newgrounds.objects.events.Result.ScoreResult; +import io.newgrounds.NGLite; +import io.newgrounds.objects.ScoreBoard; + +import haxe.ds.IntMap; + +class ScoreBoardComponent extends Component { + + public var allById:IntMap; + + public function new (core:NGLite){ super(core); } + + // ------------------------------------------------------------------------------------------- + // GET SCORES + // ------------------------------------------------------------------------------------------- + + public function getBoards():Call { + + return new Call(_core, "ScoreBoard.getBoards"); + } + + /*function onBoardsReceive(response:Response):Void { + + if (!response.result.success) + return; + + allById = new IntMap(); + + for (boardData in response.result.scoreboards) + createBoard(boardData); + + _core.log('${response.result.scoreboards.length} ScoreBoards loaded'); + }*/ + + // ------------------------------------------------------------------------------------------- + // GET SCORES + // ------------------------------------------------------------------------------------------- + + public function getScores + ( id :Int + , limit :Int = 10 + , skip :Int = 0 + , period:Period = Period.DAY + , social:Bool = false + , tag :String = null + , user :Dynamic = null + ):Call { + + if (user != null && !Std.is(user, String) && !Std.is(user, Int)) + user = user.id; + + return new Call(_core, "ScoreBoard.getScores") + .addComponentParameter("id" , id ) + .addComponentParameter("limit" , limit , 10) + .addComponentParameter("skip" , skip , 0) + .addComponentParameter("period", period, Period.DAY) + .addComponentParameter("social", social, false) + .addComponentParameter("tag" , tag , null) + .addComponentParameter("user" , user , null); + } + + // ------------------------------------------------------------------------------------------- + // POST SCORE + // ------------------------------------------------------------------------------------------- + + public function postScore(id:Int, value:Int, tag:String = null):Call { + + return new Call(_core, "ScoreBoard.postScore", true, true) + .addComponentParameter("id" , id) + .addComponentParameter("value", value) + .addComponentParameter("tag" , tag , null); + } + + /*function onScorePosted(response:Response):Void { + + if (!response.result.success) + return; + + allById = new IntMap(); + + //createBoard(data.data.scoreBoard).parseScores(data.data.scores); + }*/ + + inline function createBoard(data:Dynamic):ScoreBoard { + + var board = new ScoreBoard(_core, data); + _core.logVerbose('created $board'); + + allById.set(board.id, board); + + return board; + } +} + +@:enum +abstract Period(String) to String from String{ + + /** Indicates scores are from the current day. */ + var DAY = "D"; + /** Indicates scores are from the current week. */ + var WEEK = "W"; + /** Indicates scores are from the current month. */ + var MONTH = "M"; + /** Indicates scores are from the current year. */ + var YEAR = "Y"; + /** Indicates scores are from all-time. */ + var ALL = "A"; +} \ No newline at end of file diff --git a/source/io/newgrounds/crypto/Cipher.hx b/source/io/newgrounds/crypto/Cipher.hx new file mode 100644 index 000000000..2f4c00753 --- /dev/null +++ b/source/io/newgrounds/crypto/Cipher.hx @@ -0,0 +1,8 @@ +package io.newgrounds.crypto; + +@:enum +abstract Cipher(String) to String{ + var NONE = "none"; + var AES_128 = "aes128"; + var RC4 = "rc4"; +} \ No newline at end of file diff --git a/source/io/newgrounds/crypto/EncryptionFormat.hx b/source/io/newgrounds/crypto/EncryptionFormat.hx new file mode 100644 index 000000000..6e8f17fd6 --- /dev/null +++ b/source/io/newgrounds/crypto/EncryptionFormat.hx @@ -0,0 +1,7 @@ +package io.newgrounds.crypto; + +@:enum +abstract EncryptionFormat(String) to String { + var BASE_64 = "base64"; + var HEX = "hex"; +} \ No newline at end of file diff --git a/source/io/newgrounds/crypto/Rc4.hx b/source/io/newgrounds/crypto/Rc4.hx new file mode 100644 index 000000000..54dafa74b --- /dev/null +++ b/source/io/newgrounds/crypto/Rc4.hx @@ -0,0 +1,68 @@ +package io.newgrounds.crypto; + +import haxe.io.Bytes; + +/** + * The following was straight-up ganked from https://github.com/iskolbin/rc4hx + * + * You da real MVP iskolbin... + * + * The MIT License (MIT) + * + * Copyright (c) 2015 iskolbin + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. +**/ +class Rc4 { + var perm = Bytes.alloc( 256 ); + var index1: Int = 0; + var index2: Int = 0; + + public function new( key: Bytes ) { + for ( i in 0...256 ) { + perm.set( i, i ); + } + + var j: Int = 0; + for ( i in 0...256 ) { + j = ( j + perm.get( i ) + key.get( i % key.length )) % 256; + swap( i, j ); + } + } + + inline function swap( i: Int, j: Int ): Void { + var temp = perm.get( i ); + perm.set( i, perm.get( j )); + perm.set( j, temp ); + } + + public function crypt( input: Bytes ): Bytes { + var output = Bytes.alloc( input.length ); + + for ( i in 0...input.length ) { + index1 = ( index1 + 1 ) % 256; + index2 = ( index2 + perm.get( index1 )) % 256; + swap( index1, index2 ); + var j = ( perm.get( index1 ) + perm.get( index2 )) % 256; + output.set( i, input.get( i ) ^ perm.get( j )); + } + + return output; + } +} \ No newline at end of file diff --git a/source/io/newgrounds/objects/Error.hx b/source/io/newgrounds/objects/Error.hx new file mode 100644 index 000000000..6e82e4e2e --- /dev/null +++ b/source/io/newgrounds/objects/Error.hx @@ -0,0 +1,20 @@ +package io.newgrounds.objects; +class Error { + + public var code(default, null):Int; + public var message(default, null):String; + + public function new (message:String, code:Int = 0) { + + this.message = message; + this.code = code; + } + + public function toString():String { + + if (code > 0) + return '#$code - $message'; + + return message; + } +} diff --git a/source/io/newgrounds/objects/Medal.hx b/source/io/newgrounds/objects/Medal.hx new file mode 100644 index 000000000..3cecc7a6b --- /dev/null +++ b/source/io/newgrounds/objects/Medal.hx @@ -0,0 +1,118 @@ +package io.newgrounds.objects; + +import io.newgrounds.objects.events.Response; +import io.newgrounds.objects.events.Result.MedalUnlockResult; +import io.newgrounds.utils.Dispatcher; +import io.newgrounds.NGLite; + +class Medal extends Object { + + inline static public var EASY :Int = 1; + inline static public var MODERATE :Int = 2; + inline static public var CHALLENGING:Int = 3; + inline static public var DIFFICULT :Int = 4; + inline static public var BRUTAL :Int = 5; + + static var difficultyNames:Array = + [ "Easy" + , "Moderate" + , "Challenging" + , "Difficult" + , "Brutal" + ]; + // --- FROM SERVER + public var id (default, null):Int; + public var name (default, null):String; + public var description(default, null):String; + public var icon (default, null):String; + public var value (default, null):Int; + public var difficulty (default, null):Int; + public var secret (default, null):Bool; + public var unlocked (default, null):Bool; + // --- HELPERS + public var difficultyName(get, never):String; + + public var onUnlock:Dispatcher; + + public function new(core:NGLite, data:Dynamic = null):Void { + + onUnlock = new Dispatcher(); + + super(core, data); + } + + @:allow(io.newgrounds.NG) + override function parse(data:Dynamic):Void { + + var wasLocked = !unlocked; + + id = data.id; + name = data.name; + description = data.description; + icon = data.icon; + value = data.value; + difficulty = data.difficulty; + secret = data.secret == 1; + unlocked = data.unlocked; + + super.parse(data); + + if (wasLocked && unlocked) + onUnlock.dispatch(); + + } + + public function sendUnlock():Void { + + if (_core.sessionId == null) { + // --- Unlock regardless, show medal popup to encourage NG signup + unlocked = true; + onUnlock.dispatch(); + //TODO: save unlock in local save + } + + _core.calls.medal.unlock(id) + .addDataHandler(onUnlockResponse) + .send(); + } + + function onUnlockResponse(response:Response):Void { + + if (response.success && response.result.success) { + + parse(response.result.data.medal); + + // --- Unlock response doesn't include unlock=true, so parse won't change it. + if (!unlocked) { + + unlocked = true; + onUnlock.dispatch(); + } + } + } + + /** Locks the medal on the client and sends an unlock request, Server responds the same either way. */ + public function sendDebugUnlock():Void { + + if (NG.core.sessionId == null) { + + onUnlock.dispatch(); + + } else { + + unlocked = false; + + sendUnlock(); + } + } + + public function get_difficultyName():String { + + return difficultyNames[difficulty - 1]; + } + + public function toString():String { + + return 'Medal: $id@$name (${unlocked ? "unlocked" : "locked"}, $value pts, $difficultyName).'; + } +} \ No newline at end of file diff --git a/source/io/newgrounds/objects/Object.hx b/source/io/newgrounds/objects/Object.hx new file mode 100644 index 000000000..32abd0fe5 --- /dev/null +++ b/source/io/newgrounds/objects/Object.hx @@ -0,0 +1,33 @@ +package io.newgrounds.objects; + +import io.newgrounds.utils.Dispatcher; +import io.newgrounds.NGLite; + +class Object { + + var _core:NGLite; + + public var onUpdate(default, null):Dispatcher; + + public function new(core:NGLite, data:Dynamic = null) { + + this._core = core; + + onUpdate = new Dispatcher(); + + if (data != null) + parse(data); + } + + @:allow(io.newgrounds.NGLite) + function parse(data:Dynamic):Void { + + onUpdate.dispatch(); + } + + + public function destroy():Void { + + _core = null; + } +} \ No newline at end of file diff --git a/source/io/newgrounds/objects/Score.hx b/source/io/newgrounds/objects/Score.hx new file mode 100644 index 000000000..0eb698267 --- /dev/null +++ b/source/io/newgrounds/objects/Score.hx @@ -0,0 +1,17 @@ +package io.newgrounds.objects; + +/** We don't want to serialize scores since there's a bajillion of them. */ +typedef Score = { + + /** The value value in the format selected in your scoreboard settings. */ + var formatted_value:String; + + /** The tag attached to this value (if any). */ + var tag:String; + + /** The user who earned value. If this property is absent, the value belongs to the active user. */ + var user:User; + + /** The integer value of the value. */ + var value:Int; +} \ No newline at end of file diff --git a/source/io/newgrounds/objects/ScoreBoard.hx b/source/io/newgrounds/objects/ScoreBoard.hx new file mode 100644 index 000000000..1859b0808 --- /dev/null +++ b/source/io/newgrounds/objects/ScoreBoard.hx @@ -0,0 +1,76 @@ +package io.newgrounds.objects; + +import io.newgrounds.components.ScoreBoardComponent.Period; +import io.newgrounds.objects.events.Response; +import io.newgrounds.objects.events.Result; +import io.newgrounds.objects.events.Result.ScoreResult; +import io.newgrounds.NGLite; + +class ScoreBoard extends Object { + + public var scores(default, null):Array; + + /** The numeric ID of the scoreboard.*/ + public var id(default, null):Int; + + /** The name of the scoreboard. */ + public var name(default, null):String; + + public function new(core:NGLite, data:Dynamic):Void {super(core, data); } + + override function parse(data:Dynamic):Void { + + id = data.id; + name = data.name; + + super.parse(data); + } + + /** + * Fetches score data from the server, this removes all of the existing scores cached + * + * We don't unify the old and new scores because a user's rank or score may change between requests + */ + public function requestScores + ( limit :Int = 10 + , skip :Int = 0 + , period:Period = Period.ALL + , social:Bool = false + , tag :String = null + , user :Dynamic = null + ):Void { + + _core.calls.scoreBoard.getScores(id, limit, skip, period, social, tag, user) + .addDataHandler(onScoresReceived) + .send(); + } + + function onScoresReceived(response:Response):Void { + + if (!response.success || !response.result.success) + return; + + scores = response.result.data.scores; + _core.logVerbose('received ${scores.length} scores'); + + onUpdate.dispatch(); + } + + public function postScore(value :Int, tag:String = null):Void { + + _core.calls.scoreBoard.postScore(id, value, tag) + .addDataHandler(onScorePosted) + .send(); + } + + function onScorePosted(response:Response):Void { + + + } + + public function toString():String { + + return 'ScoreBoard: $id@$name'; + } + +} \ No newline at end of file diff --git a/source/io/newgrounds/objects/Session.hx b/source/io/newgrounds/objects/Session.hx new file mode 100644 index 000000000..887df7927 --- /dev/null +++ b/source/io/newgrounds/objects/Session.hx @@ -0,0 +1,65 @@ +package io.newgrounds.objects; + +class Session extends Object { + + /** If true, the session_id is expired. Use App.startSession to get a new one.*/ + public var expired(default, null):Bool; + + /** A unique session identifier */ + public var id(default, null):String; + + /** If the session has no associated user but is not expired, this property will provide a URL that can be used to sign the user in. */ + public var passportUrl(default, null):String; + + /** If true, the user would like you to remember their session id. */ + public var remember(default, null):Bool; + + /** If the user has not signed in, or granted access to your app, this will be null */ + public var user(default, null):User; + + //TODO:desciption + public var status(get, never):SessionStatus; + + public function new(core:NGLite, data:Dynamic = null) { super(core, data); } + + override public function parse(data:Dynamic):Void { + + id = data.id; + expired = data.expired; + passportUrl = data.passport_url; + remember = data.remember; + + // --- KEEP THE SAME INSTANCE + if (user == null) + user = data.user; + // TODO?: update original user instance with new data. (probly not) + + super.parse(data); + } + + public function get_status():SessionStatus { + + if (expired || id == null || id == "") + return SessionStatus.SESSION_EXPIRED; + + if (user != null && user.name != null && user.name != "") + return SessionStatus.USER_LOADED; + + return SessionStatus.REQUEST_LOGIN; + } + + public function expire():Void { + + expired = true; + id = null; + user = null; + } +} + +@:enum +abstract SessionStatus(String) { + + var SESSION_EXPIRED = "session-expired"; + var REQUEST_LOGIN = "request-login"; + var USER_LOADED = "user-loaded"; +} \ No newline at end of file diff --git a/source/io/newgrounds/objects/User.hx b/source/io/newgrounds/objects/User.hx new file mode 100644 index 000000000..1ff7ce00c --- /dev/null +++ b/source/io/newgrounds/objects/User.hx @@ -0,0 +1,19 @@ +package io.newgrounds.objects; + +typedef User = { + + /** The user's icon images. */ + var icons:UserIcons; + + /** The user's numeric ID. */ + var id:Int; + + /** The user's textual name. */ + var name:String; + + /** Returns true if the user has a Newgrounds Supporter upgrade. */ + var supporter:Bool; + + /** The user's NG profile url. */ + var url:String; +} diff --git a/source/io/newgrounds/objects/UserIcons.hx b/source/io/newgrounds/objects/UserIcons.hx new file mode 100644 index 000000000..b5e56b22a --- /dev/null +++ b/source/io/newgrounds/objects/UserIcons.hx @@ -0,0 +1,14 @@ +package io.newgrounds.objects; + +typedef UserIcons = { + + /**The URL of the user's large icon. */ + var large:String; + + /** The URL of the user's medium icon. */ + var medium:String; + + /** The URL of the user's small icon. */ + var small:String; +} + diff --git a/source/io/newgrounds/objects/events/Response.hx b/source/io/newgrounds/objects/events/Response.hx new file mode 100644 index 000000000..107dc2be3 --- /dev/null +++ b/source/io/newgrounds/objects/events/Response.hx @@ -0,0 +1,43 @@ +package io.newgrounds.objects.events; + +import io.newgrounds.objects.events.Result.ResultBase; +import haxe.Json; +import io.newgrounds.objects.Error; + +typedef DebugResponse = { + + var exec_time:Int; + var input:Dynamic; +} + +class Response { + + public var success(default, null):Bool; + public var error(default, null):Error; + public var debug(default, null):DebugResponse; + public var result(default, null):Result; + + public function new (core:NGLite, reply:String) { + + var data:Dynamic; + + try { + data = Json.parse(reply); + + } catch (e:Dynamic) { + + data = Json.parse('{"success":false,"error":{"message":"${Std.string(reply)}","code":0}}'); + } + + success = data.success; + debug = data.debug; + + if (!success) { + error = new Error(data.error.message, data.error.code); + core.logError('Call unseccessful: $error'); + return; + } + + result = new Result(core, data.result); + } +} diff --git a/source/io/newgrounds/objects/events/Result.hx b/source/io/newgrounds/objects/events/Result.hx new file mode 100644 index 000000000..eaf872616 --- /dev/null +++ b/source/io/newgrounds/objects/events/Result.hx @@ -0,0 +1,109 @@ +package io.newgrounds.objects.events; + +class Result { + + public var echo(default, null):String; + public var component(default, null):String; + + public var data(default, null):T; + public var success(default, null):Bool; + public var debug(default, null):Bool; + public var error(default, null):Error; + + public function new(core:NGLite, data:Dynamic) { + + echo = data.echo; + component = data.component; + + data = data.data; + success = data.success; + debug = data.debug; + + if(!data.success) { + + error = new Error(data.error.message, data.error.code); + core.logError('$component fail: $error'); + + } else + this.data = data; + } +} + +typedef ResultBase = { }; + +typedef SessionResult = { + > ResultBase, + + var session:Dynamic; +} + +typedef GetHostResult = { + > ResultBase, + + var host_approved:Bool; +} + +typedef GetCurrentVersionResult = { + > ResultBase, + + var current_version:String; + var client_deprecated:Bool; +} + +typedef LogEventResult = { + > ResultBase, + + var event_name:String; +} + +typedef GetDateTimeResult = { + > ResultBase, + + var datetime:String; +} + +typedef GetVersionResult = { + > ResultBase, + + var version:String; +} + +typedef PingResult = { + > ResultBase, + + var pong:String; +} + +typedef MedalListResult = { + > ResultBase, + + var medals:Array; +} + +typedef MedalUnlockResult = { + > ResultBase, + + var medal_score:String; + var medal:Dynamic; +} + +typedef ScoreBoardResult = { + > ResultBase, + + var scoreboards:Array; +} + +typedef ScoreResult = { + > ResultBase, + + var scores:Array; + var scoreboard:Dynamic; +} + +typedef PostScoreResult = { + > ResultBase, + + var tag:String; + var scoreboard:Dynamic; + var score:Score; +} \ No newline at end of file diff --git a/source/io/newgrounds/swf/LoadingBar.hx b/source/io/newgrounds/swf/LoadingBar.hx new file mode 100644 index 000000000..9c7c590a8 --- /dev/null +++ b/source/io/newgrounds/swf/LoadingBar.hx @@ -0,0 +1,23 @@ +package io.newgrounds.swf; + +import openfl.display.MovieClip; + +class LoadingBar extends MovieClip { + + public var bar(default, null):MovieClip; + + public function new() { + super(); + + setProgress(0.0); + } + + /** + * + * @param value The ratio of bytes loaded to bytes total + */ + public function setProgress(value:Float):Void { + + bar.gotoAndStop(1 + Std.int(value * (bar.totalFrames - 1))); + } +} diff --git a/source/io/newgrounds/swf/MedalPopup.hx b/source/io/newgrounds/swf/MedalPopup.hx new file mode 100644 index 000000000..f55ef4266 --- /dev/null +++ b/source/io/newgrounds/swf/MedalPopup.hx @@ -0,0 +1,151 @@ +package io.newgrounds.swf; + +import io.newgrounds.swf.common.BaseAsset; +import io.newgrounds.objects.Medal; + +import openfl.text.TextFieldAutoSize; +import openfl.text.TextField; +import openfl.display.DisplayObject; +import openfl.display.Loader; +import openfl.display.MovieClip; +import openfl.net.URLRequest; +import openfl.events.Event; + +class MedalPopup extends BaseAsset { + + static inline var FRAME_HIDDEN:String = "hidden"; + static inline var FRAME_MEDAL_UNLOCKED:String = "medalUnlocked"; + static inline var FRAME_INTRO_COMPLETE:String = "introComplete"; + static inline var FRAME_UNLOCK_COMPLETE:String = "unlockComplete"; + static inline var MIN_TEXT_SIZE:Int = 12; + + public var medalIcon(default, null):MovieClip; + public var medalName(default, null):MovieClip; + public var medalPoints(default, null):MovieClip; + + public var alwaysOnTop:Bool; + #if !ng_lite + public var requiresSession:Bool; + #end + + var _animQueue = new ArrayVoid>(); + var _scrollSpeed:Float; + + public function new() { + super(); + + mouseEnabled = false; + mouseChildren = false; + + hide(); + addFrameScript(totalFrames - 1, onUnlockAnimComplete); + } + + function hide():Void { + + visible = false; + gotoAndStop(FRAME_HIDDEN); + } + + #if !ng_lite + override function onReady():Void { + super.onReady(); + + if (NG.core.medals != null) + onMedalsLoaded(); + else + NG.core.onLogin.addOnce(NG.core.requestMedals.bind(onMedalsLoaded)); + } + + function onMedalsLoaded():Void { + + for (medal in NG.core.medals) + medal.onUnlock.add(onMedalOnlock.bind(medal)); + } + + function onMedalOnlock(medal:Medal):Void { + + if (requiresSession && !NG.core.loggedIn) + return; + + var loader = new Loader(); + loader.load(new URLRequest(medal.icon)); + + playAnim(loader, medal.name, medal.value); + } + + #end + + public function playAnim(icon:DisplayObject, name:String, value:Int):Void { + + if (currentLabel == FRAME_HIDDEN) + playNextAnim(icon, name, value); + else + _animQueue.push(playNextAnim.bind(icon, name, value)); + } + + function playNextAnim(icon:DisplayObject, name:String, value:Int):Void { + + visible = true; + gotoAndPlay(FRAME_MEDAL_UNLOCKED); + + if (alwaysOnTop && parent != null) { + + parent.setChildIndex(this, parent.numChildren - 1); + } + + while(medalIcon.numChildren > 0) + medalIcon.removeChildAt(0); + + cast(medalPoints.getChildByName("field"), TextField).text = Std.string(value); + + var field:TextField = cast medalName.getChildByName("field"); + field.autoSize = TextFieldAutoSize.LEFT; + field.x = 0; + field.text = ""; + var oldWidth = medalName.width; + field.text = name; + + _scrollSpeed = 0; + if (field.width > oldWidth + 4) { + + field.x = oldWidth + 4; + initScroll(field); + } + + medalIcon.addChild(icon); + } + + function initScroll(field:TextField):Void { + //TODO: Find out why scrollrect didn't work + + var animDuration = 0; + + for (frame in currentLabels){ + + if (frame.name == FRAME_INTRO_COMPLETE ) + animDuration -= frame.frame; + else if (frame.name == FRAME_UNLOCK_COMPLETE) + animDuration += frame.frame; + } + + _scrollSpeed = (field.width + field.x + 4) / animDuration; + field.addEventListener(Event.ENTER_FRAME, updateScroll); + } + + function updateScroll(e:Event):Void{ + + if (currentLabel == FRAME_INTRO_COMPLETE) + cast (e.currentTarget, TextField).x -= _scrollSpeed; + } + + function onUnlockAnimComplete():Void { + + cast (medalName.getChildByName("field"), TextField).removeEventListener(Event.ENTER_FRAME, updateScroll); + + if (_animQueue.length == 0) + hide(); + else + (_animQueue.shift())(); + } +} diff --git a/source/io/newgrounds/swf/ScoreBrowser.hx b/source/io/newgrounds/swf/ScoreBrowser.hx new file mode 100644 index 000000000..c4232c22b --- /dev/null +++ b/source/io/newgrounds/swf/ScoreBrowser.hx @@ -0,0 +1,250 @@ +package io.newgrounds.swf; + +import openfl.events.Event; +import io.newgrounds.swf.common.DropDown; +import io.newgrounds.objects.Score; +import io.newgrounds.objects.events.Result.ScoreBoardResult; +import io.newgrounds.objects.events.Result.ScoreResult; +import io.newgrounds.objects.events.Response; +import io.newgrounds.swf.common.BaseAsset; +import io.newgrounds.swf.common.Button; +import io.newgrounds.components.ScoreBoardComponent.Period; + +import openfl.display.MovieClip; +import openfl.text.TextField; + +class ScoreBrowser extends BaseAsset { + + public var prevButton (default, null):MovieClip; + public var nextButton (default, null):MovieClip; + public var reloadButton (default, null):MovieClip; + public var listBox (default, null):MovieClip; + public var loadingIcon (default, null):MovieClip; + public var errorIcon (default, null):MovieClip; + public var scoreContainer(default, null):MovieClip; + public var titleField (default, null):TextField; + public var pageField (default, null):TextField; + + public var period(get, set):Period; + function get_period():Period { return _periodDropDown.value; } + function set_period(value:Period):Period { return _periodDropDown.value = value; } + + public var title(get, set):String; + function get_title():String { return titleField.text; } + function set_title(value:String):String { return titleField.text = value; } + + public var tag(default, set):String; + function set_tag(value:String):String { + + if (this.tag != value) { + + this.tag = value; + delayReload(); + } + + return value; + } + + public var social(default, set):Bool; + function set_social(value:Bool):Bool { + + if (this.social != value) { + + this.social = value; + delayReload(); + } + + return value; + } + + public var boardId(default, set):Int; + function set_boardId(value:Int):Int { + + _boardIDSet = true; + + if (this.boardId != value) { + + this.boardId = value; + delayReload(); + } + + return value; + } + + public var page(default, set):Int; + function set_page(value:Int):Int { + + if (this.page != value) { + + this.page = value; + delayReload(); + } + + return value; + } + + var _scores:Array; + var _limit:Int = 0; + var _periodDropDown:DropDown; + var _boardIDSet:Bool; + + public function new() { super(); } + + override function setDefaults():Void { + super.setDefaults(); + + boardId = -1; + _boardIDSet = false; + + scoreContainer.visible = false; + loadingIcon.visible = false; + reloadButton.visible = false; + errorIcon.visible = false; + errorIcon.addFrameScript(errorIcon.totalFrames - 1, errorIcon.stop); + + //TODO: prevent memory leaks? + new Button(prevButton, onPrevClick); + new Button(nextButton, onNextClick); + new Button(reloadButton, reload); + _periodDropDown = new DropDown(listBox, delayReload); + _periodDropDown.addItem("Current day" , Period.DAY ); + _periodDropDown.addItem("Current week" , Period.WEEK ); + _periodDropDown.addItem("Current month", Period.MONTH); + _periodDropDown.addItem("Current year" , Period.YEAR ); + _periodDropDown.addItem("All time" , Period.ALL ); + _periodDropDown.value = Period.ALL; + + _scores = new Array(); + while(true) { + + var score:MovieClip = cast scoreContainer.getChildByName('score${_scores.length}'); + if (score == null) + break; + + new Button(score); + _scores.push(score); + } + + _limit = _scores.length; + } + + override function onReady():Void { + super.onReady(); + + if (boardId == -1 && !_boardIDSet) { + + #if ng_lite + NG.core.calls.scoreBoard.getBoards() + .addDataHandler(onBoardsRecieved) + .queue(); + #else + if (NG.core.scoreBoards != null) + onBoardsLoaded(); + else + NG.core.requestScoreBoards(onBoardsLoaded); + #end + } + + reload(); + } + + #if ng_lite + function onBoardsRecieved(response:Response):Void { + + if (response.success && response.result.success) { + + for (board in response.result.data.scoreboards) { + + NG.core.log('No boardId specified defaulting to ${board.name}'); + boardId = board.id; + return; + } + } + } + #else + function onBoardsLoaded():Void { + + for (board in NG.core.scoreBoards) { + + NG.core.log('No boardId specified defaulting to ${board.name}'); + boardId = board.id; + return; + } + } + #end + + /** Used internally to avoid multiple server requests from various property changes in a small time-frame. **/ + function delayReload():Void { + + addEventListener(Event.EXIT_FRAME, onDelayComplete); + } + + function onDelayComplete(e:Event):Void { reload(); } + + public function reload():Void { + removeEventListener(Event.EXIT_FRAME, onDelayComplete); + + errorIcon.visible = false; + scoreContainer.visible = false; + pageField.text = 'page ${page + 1}'; + + if (_coreReady && boardId != -1 && _limit > 0 && period != null) { + + loadingIcon.visible = true; + + NG.core.calls.scoreBoard.getScores(boardId, _limit, _limit * page, period, social, tag) + .addDataHandler(onScoresReceive) + .send(); + } + } + + function onScoresReceive(response:Response):Void { + + loadingIcon.visible = false; + + if (response.success && response.result.success) { + + scoreContainer.visible = true; + + var i = _limit; + while(i > 0) { + i--; + + if (i < response.result.data.scores.length) + drawScore(i, response.result.data.scores[i], _scores[i]); + else + drawScore(i, null, _scores[i]); + } + + } else { + + errorIcon.visible = true; + errorIcon.gotoAndPlay(1); + reloadButton.visible = true; + } + } + + inline function drawScore(rank:Int, score:Score, asset:MovieClip):Void { + + if (score == null) + asset.visible = false; + else { + + asset.visible = true; + cast (asset.getChildByName("nameField" ), TextField).text = score.user.name; + cast (asset.getChildByName("scoreField"), TextField).text = score.formatted_value; + cast (asset.getChildByName("rankField" ), TextField).text = Std.string(rank + 1); + } + } + + function onPrevClick():Void { + + if (page > 0) + page--; + } + + function onNextClick():Void { + + page++; + } +} diff --git a/source/io/newgrounds/swf/common/BaseAsset.hx b/source/io/newgrounds/swf/common/BaseAsset.hx new file mode 100644 index 000000000..da1f6126f --- /dev/null +++ b/source/io/newgrounds/swf/common/BaseAsset.hx @@ -0,0 +1,35 @@ +package io.newgrounds.swf.common; + +import openfl.events.Event; +import openfl.display.MovieClip; + +class BaseAsset extends MovieClip { + + var _coreReady:Bool = false; + + public function new() { + super(); + + setDefaults(); + + if (stage != null) + onAdded(null); + else + addEventListener(Event.ADDED_TO_STAGE, onAdded); + } + + function setDefaults():Void { } + + function onAdded(e:Event):Void { + + if (NG.core != null) + onReady(); + else + NG.onCoreReady.add(onReady); + } + + function onReady():Void { + + _coreReady = true; + } +} diff --git a/source/io/newgrounds/swf/common/Button.hx b/source/io/newgrounds/swf/common/Button.hx new file mode 100644 index 000000000..5f4617528 --- /dev/null +++ b/source/io/newgrounds/swf/common/Button.hx @@ -0,0 +1,151 @@ +package io.newgrounds.swf.common; + +import openfl.display.Stage; +import openfl.events.Event; +import openfl.events.MouseEvent; +import openfl.display.MovieClip; + +class Button { + + var _enabled:Bool; + public var enabled(get, set):Bool; + function get_enabled():Bool { return _enabled; } + function set_enabled(value:Bool):Bool { + + if (value != _enabled) { + + _enabled = value; + updateEnabled(); + } + + return value; + } + + public var onClick:Void->Void; + public var onOver:Void->Void; + public var onOut:Void->Void; + + var _target:MovieClip; + var _down:Bool; + var _over:Bool; + var _foundLabels:Array; + + public function new(target:MovieClip, onClick:Void->Void = null, onOver:Void->Void = null, onOut:Void->Void = null) { + + _target = target; + this.onClick = onClick; + this.onOver = onOver; + this.onOut = onOut; + + _foundLabels = new Array(); + for (label in _target.currentLabels) + _foundLabels.push(label.name); + + _target.stop(); + _target.addEventListener(Event.ADDED_TO_STAGE, onAdded); + if (target.stage != null) + onAdded(null); + + enabled = true; + } + + function onAdded(e:Event):Void { + + var stage = _target.stage; + stage.addEventListener(MouseEvent.MOUSE_UP, mouseHandler); + _target.addEventListener(MouseEvent.MOUSE_OVER, mouseHandler); + _target.addEventListener(MouseEvent.MOUSE_OUT, mouseHandler); + _target.addEventListener(MouseEvent.MOUSE_DOWN, mouseHandler); + _target.addEventListener(MouseEvent.CLICK, mouseHandler); + + function selfRemoveEvent(e:Event):Void { + + _target.removeEventListener(Event.REMOVED_FROM_STAGE, selfRemoveEvent); + onRemove(e, stage); + } + _target.addEventListener(Event.REMOVED_FROM_STAGE, selfRemoveEvent); + } + + function onRemove(e:Event, stage:Stage):Void { + + stage.removeEventListener(MouseEvent.MOUSE_UP, mouseHandler); + _target.removeEventListener(MouseEvent.MOUSE_OVER, mouseHandler); + _target.removeEventListener(MouseEvent.MOUSE_OUT, mouseHandler); + _target.removeEventListener(MouseEvent.MOUSE_DOWN, mouseHandler); + _target.removeEventListener(MouseEvent.CLICK, mouseHandler); + } + + function mouseHandler(event:MouseEvent):Void { + + switch(event.type) { + + case MouseEvent.MOUSE_OVER: + + _over = true; + + if (onOver != null) + onOver(); + + case MouseEvent.MOUSE_OUT: + + _over = false; + + if (onOut != null) + onOut(); + + case MouseEvent.MOUSE_DOWN: + + _down = true; + + case MouseEvent.MOUSE_UP: + + _down = false; + + case MouseEvent.CLICK: + + if (enabled && onClick != null) + onClick(); + } + updateState(); + } + + function updateEnabled():Void { + + updateState(); + + _target.useHandCursor = enabled; + _target.buttonMode = enabled; + } + + function updateState():Void { + + var state = determineState(); + + if (_target.currentLabel != state && _foundLabels.indexOf(state) != -1) + _target.gotoAndStop(state); + } + + function determineState():String { + + if (enabled) { + + if (_over) + return _down ? "down" : "over"; + + return "up"; + + } + return "disabled"; + } + + public function destroy():Void { + + _target.removeEventListener(Event.ADDED_TO_STAGE, onAdded); + + _target = null; + onClick = null; + onOver = null; + onOut = null; + _foundLabels = null; + } +} diff --git a/source/io/newgrounds/swf/common/DropDown.hx b/source/io/newgrounds/swf/common/DropDown.hx new file mode 100644 index 000000000..a882cf39b --- /dev/null +++ b/source/io/newgrounds/swf/common/DropDown.hx @@ -0,0 +1,88 @@ +package io.newgrounds.swf.common; + + +import haxe.ds.StringMap; + +import openfl.display.MovieClip; +import openfl.display.Sprite; +import openfl.text.TextField; + +class DropDown { + + public var value(default, set):String; + function set_value(v:String):String { + + if (this.value == v) + return v; + + this.value = v; + _selectedLabel.text = _values.get(v); + + if (_onChange != null) + _onChange(); + + return v; + } + + var _choiceContainer:Sprite; + var _selectedLabel:TextField; + var _onChange:Void->Void; + var _values:StringMap; + var _unusedChoices:Array; + + public function new(target:MovieClip, label:String = "", onChange:Void->Void = null) { + + _onChange = onChange; + + _selectedLabel = cast cast(target.getChildByName("currentItem"), MovieClip).getChildByName("label"); + _selectedLabel.text = label; + + _values = new StringMap(); + + new Button(cast target.getChildByName("button"), onClickExpand); + new Button(cast target.getChildByName("currentItem"), onClickExpand); + _choiceContainer = new Sprite(); + _choiceContainer.visible = false; + target.addChild(_choiceContainer); + + _unusedChoices = new Array(); + while(true) { + + var item:MovieClip = cast target.getChildByName('item${_unusedChoices.length}'); + if (item == null) + break; + + target.removeChild(item); + _unusedChoices.push(item); + } + } + + public function addItem(name:String, value:String):Void { + + _values.set(value, name); + + if (_unusedChoices.length == 0) { + + NG.core.logError('cannot create another dropBox item max=${_choiceContainer.numChildren}'); + return; + } + + var button = _unusedChoices.shift(); + cast(button.getChildByName("label"), TextField).text = name; + _choiceContainer.addChild(button); + + new Button(button, onChoiceClick.bind(value)); + } + + function onClickExpand():Void { + + _choiceContainer.visible = !_choiceContainer.visible; + } + + function onChoiceClick(name:String):Void { + + value = name; + + _choiceContainer.visible = false; + } +} \ No newline at end of file diff --git a/source/io/newgrounds/utils/AsyncHttp.hx b/source/io/newgrounds/utils/AsyncHttp.hx new file mode 100644 index 000000000..4fbe14388 --- /dev/null +++ b/source/io/newgrounds/utils/AsyncHttp.hx @@ -0,0 +1,203 @@ +package io.newgrounds.utils; + +import io.newgrounds.NGLite; + +import haxe.Http; +import haxe.Timer; + +#if neko +import neko.vm.Thread; +#elseif java +import java.vm.Thread; +#elseif cpp +import cpp.vm.Thread; +#end + +/** + * Uses Threading to turn hxcpp's synchronous http requests into asynchronous processes + * + * @author GeoKureli + */ +class AsyncHttp { + + inline static var PATH:String = "https://newgrounds.io/gateway_v3.php"; + + static public function send + ( core:NGLite + , data:String + , onData:String->Void + , onError:String->Void + , onStatus:Int->Void + ) { + + core.logVerbose('sending: $data'); + + #if (neko || java || cpp) + sendAsync(core, data, onData, onError, onStatus); + #else + sendSync(core, data, onData, onError, onStatus); + #end + } + + static function sendSync + ( core:NGLite + , data:String + , onData:String->Void + , onError:String->Void + , onStatus:Int->Void + ):Void { + + var http = new Http(PATH); + http.setParameter("input", data); + http.onData = onData; + http.onError = onError; + http.onStatus = onStatus; + // #if js http.async = async; #end + http.request(true); + } + + #if (neko || java || cpp) + static var _deadPool:Array = []; + static var _livePool:Array = []; + static var _map:Map = new Map(); + static var _timer:Timer; + + static var _count:Int = 0; + + var _core:NGLite; + var _key:Int; + var _onData:String->Void; + var _onError:String->Void; + var _onStatus:Int->Void; + var _worker:Thread; + + public function new (core:NGLite) { + + _core = core; + _worker = Thread.create(sendThreaded); + _key = _count++; + _map[_key] = this; + _core.logVerbose('async http created: $_key'); + } + + function start(data:String, onData:String->Void, onError:String->Void, onStatus:Int->Void) { + + _core.logVerbose('async http started: $_key'); + + if (_livePool.length == 0) + startTimer(); + + _deadPool.remove(this); + _livePool.push(this); + + _onData = onData; + _onError = onError; + _onStatus = onStatus; + _worker.sendMessage({ source:Thread.current(), args:data, key:_key, core:_core }); + } + + function handleMessage(data:ReplyData):Void { + + _core.logVerbose('handling message: $_key'); + + if (data.status != null) { + + _core.logVerbose('\t- status: ${data.status}'); + _onStatus(cast data.status); + return; + } + + var tempFunc:Void->Void; + if (data.data != null) { + + _core.logVerbose('\t- data'); + tempFunc = _onData.bind(data.data); + + } else { + + _core.logVerbose('\t- error'); + tempFunc = _onError.bind(data.error); + } + + cleanUp(); + // Delay the call until destroy so that we're more likely to use a single + // thread on daisy-chained calls + tempFunc(); + } + + inline function cleanUp():Void { + + _onData = null; + _onError = null; + + _deadPool.push(this); + _livePool.remove(this); + + if (_livePool.length == 0) + stopTimer(); + } + + static function sendAsync + ( core:NGLite + , data:String + , onData:String->Void + , onError:String->Void + , onStatus:Int->Void + ):Void { + + var http:AsyncHttp; + if (_deadPool.length == 0) + http = new AsyncHttp(core); + else + http = _deadPool[0]; + + http.start(data, onData, onError, onStatus); + } + + static function startTimer():Void { + + if (_timer != null) + return; + + _timer = new Timer(1000 / 60.0); + _timer.run = update; + } + + static function stopTimer():Void { + + _timer.stop(); + _timer = null; + } + + static public function update():Void { + + var message:ReplyData = cast Thread.readMessage(false); + if (message != null) + _map[message.key].handleMessage(message); + } + + static function sendThreaded():Void { + + while(true) { + + var data:LoaderData = cast Thread.readMessage(true); + data.core.logVerbose('start message received: ${data.key}'); + + sendSync + ( data.core + , data.args + , function(reply ) { data.source.sendMessage({ key:data.key, data :reply }); } + , function(error ) { data.source.sendMessage({ key:data.key, error :error }); } + , function(status) { data.source.sendMessage({ key:data.key, status:status }); } + ); + } + } + + #end +} + + +#if (neko || java || cpp) +typedef LoaderData = { source:Thread, key:Int, args:String, core:NGLite }; +typedef ReplyData = { key:Int, ?data:String, ?error:String, ?status:Null }; +#end \ No newline at end of file diff --git a/source/io/newgrounds/utils/Dispatcher.hx b/source/io/newgrounds/utils/Dispatcher.hx new file mode 100644 index 000000000..699da01da --- /dev/null +++ b/source/io/newgrounds/utils/Dispatcher.hx @@ -0,0 +1,118 @@ +package io.newgrounds.utils; + +/** + * Basically shitty signals, but I didn't want to have external references. +**/ +class Dispatcher { + + var _list:ArrayVoid>; + var _once:ArrayVoid>; + + public function new() { + + _list = new ArrayVoid>(); + _once = new ArrayVoid>(); + } + + public function add(handler:Void->Void, once:Bool = false):Bool { + + if (_list.indexOf(handler) != -1) { + + // ---- REMOVE ONCE + if (!once && _once.indexOf(handler) != -1) + _once.remove(handler); + + return false; + } + + _list.unshift(handler); + if (once) + _once.unshift(handler); + + return true; + } + + inline public function addOnce(handler:Void->Void):Bool { + + return add(handler, true); + } + + public function remove(handler:Void->Void):Bool { + + _once.remove(handler); + return _list.remove(handler); + } + + public function dispatch():Void { + + var i = _list.length - 1; + while(i >= 0) { + + var handler = _list[i]; + + if (_once.remove(handler)) + _list.remove(handler); + + handler(); + + i--; + } + } +} + +class TypedDispatcher { + + var _list:ArrayVoid>; + var _once:ArrayVoid>; + + public function new() { + + _list = new ArrayVoid>(); + _once = new ArrayVoid>(); + } + + public function add(handler:T->Void, once:Bool = false):Bool { + + if (_list.indexOf(handler) != -1) { + + // ---- REMOVE ONCE + if (!once && _once.indexOf(handler) != -1) + _once.remove(handler); + + return false; + } + + _list.unshift(handler); + if (once) + _once.unshift(handler); + + return true; + } + + inline public function addOnce(handler:T->Void):Bool { + + return add(handler, true); + } + + public function remove(handler:T->Void):Bool { + + _once.remove(handler); + return _list.remove(handler); + } + + public function dispatch(arg:T):Void { + + var i = _list.length - 1; + while(i >= 0) { + + var handler = _list[i]; + + if (_once.remove(handler)) + _list.remove(handler); + + handler(arg); + + i--; + } + } +} \ No newline at end of file From 88b08d3dc361bed7feaf873f375be116ac8c0de5 Mon Sep 17 00:00:00 2001 From: Brandon Date: Mon, 2 Nov 2020 00:39:28 -0500 Subject: [PATCH 10/16] shot in the dark 4.0 stuff --- source/Controls.hx | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/source/Controls.hx b/source/Controls.hx index 92b1fccb2..2ac71e0ae 100644 --- a/source/Controls.hx +++ b/source/Controls.hx @@ -278,7 +278,7 @@ class Controls extends FlxActionSet * @param func * @return ->Void) */ - function forEachBound(control:Control, func:FlxActionDigital -> Void, func:FlxInputState -> Void) + function forEachBound(control:Control, func:FlxActionDigital -> Void, func:FlxInputState -> Void) { switch (control) { @@ -383,7 +383,11 @@ class Controls extends FlxActionSet */ public function bindKeys(control:Control, keys:Array) { - inline forEachBound(control, (action, state) -> addKeys(action, keys, state)); + #if (haxe >= "4.0.0") + inline forEachBound(control, (action, state) -> addKeys(action, keys, state)); + #else + forEachBound(control, (action, state) -> addKeys(action, keys, state)); + #end } /** @@ -392,7 +396,11 @@ class Controls extends FlxActionSet */ public function unbindKeys(control:Control, keys:Array) { - inline forEachBound(control, (action, _) -> removeKeys(action, keys)); + #if (haxe >= "4.0.0") + inline forEachBound(control, (action, _) -> removeKeys(action, keys)); + #else + forEachBound(control, (action, _) -> removeKeys(action, keys)); + #end } inline static function addKeys(action:FlxActionDigital, keys:Array, state:FlxInputState) @@ -516,7 +524,11 @@ class Controls extends FlxActionSet */ public function bindButtons(control:Control, id, buttons) { - inline forEachBound(control, (action, state) -> addButtons(action, buttons, state, id)); + #if (haxe >= "4.0.0") + inline forEachBound(control, (action, state) -> addButtons(action, buttons, state, id)); + #else + forEachBound(control, (action, state) -> addButtons(action, buttons, state, id)); + #end } /** @@ -525,7 +537,11 @@ class Controls extends FlxActionSet */ public function unbindButtons(control:Control, gamepadID:Int, buttons) { - inline forEachBound(control, (action, _) -> removeButtons(action, gamepadID, buttons)); + #if (haxe >= "4.0.0") + inline forEachBound(control, (action, _) -> removeButtons(action, gamepadID, buttons)); + #else + forEachBound(control, (action, _) -> removeButtons(action, gamepadID, buttons)); + #end } inline static function addButtons(action:FlxActionDigital, buttons:Array, state, id) From 0590fc59b6ec2e097977074a9c20fc1ae38caf69 Mon Sep 17 00:00:00 2001 From: Brandon Date: Mon, 2 Nov 2020 01:38:01 -0500 Subject: [PATCH 11/16] me sad cause haxe 4.0.0 --- source/Controls.hx | 60 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 52 insertions(+), 8 deletions(-) diff --git a/source/Controls.hx b/source/Controls.hx index 2ac71e0ae..37d7caa71 100644 --- a/source/Controls.hx +++ b/source/Controls.hx @@ -108,7 +108,11 @@ class Controls extends FlxActionSet var _pause = new FlxActionDigital(Action.PAUSE); var _reset = new FlxActionDigital(Action.RESET); + #if (haxe >= "4.0.0") var byName:Map = []; + #else + var byName:Map = new Map(); + #end public var gamepadsAdded:Array = []; public var keyboardScheme = KeyboardScheme.None; @@ -384,9 +388,9 @@ class Controls extends FlxActionSet public function bindKeys(control:Control, keys:Array) { #if (haxe >= "4.0.0") - inline forEachBound(control, (action, state) -> addKeys(action, keys, state)); + inline forEachBound(control, (action, state) -> addKeys(action, keys, state)); #else - forEachBound(control, (action, state) -> addKeys(action, keys, state)); + forEachBound(control, action => addKeys(action, keys, state), state => addKeys(action, keys, state)); #end } @@ -397,9 +401,9 @@ class Controls extends FlxActionSet public function unbindKeys(control:Control, keys:Array) { #if (haxe >= "4.0.0") - inline forEachBound(control, (action, _) -> removeKeys(action, keys)); + inline forEachBound(control, (action, _) -> removeKeys(action, keys)); #else - forEachBound(control, (action, _) -> removeKeys(action, keys)); + forEachBound(control, action => removeKeys(action, keys), _ => removeKeys(action, keys)); #end } @@ -426,6 +430,8 @@ class Controls extends FlxActionSet removeKeyboard(); keyboardScheme = scheme; + + #if (haxe >= "4.0.0") switch (scheme) { case Solo: @@ -458,6 +464,40 @@ class Controls extends FlxActionSet case None: // nothing case Custom: // nothing } + #else + switch (scheme) + { + case Solo: + bindKeys(Control.UP, [W, FlxKey.UP]); + bindKeys(Control.DOWN, [S, FlxKey.DOWN]); + bindKeys(Control.LEFT, [A, FlxKey.LEFT]); + bindKeys(Control.RIGHT, [D, FlxKey.RIGHT]); + bindKeys(Control.ACCEPT, [Z, SPACE, ENTER]); + bindKeys(Control.BACK, [BACKSPACE, ESCAPE]); + bindKeys(Control.PAUSE, [P, ENTER, ESCAPE]); + bindKeys(Control.RESET, [R]); + case Duo(true): + bindKeys(Control.UP, [W]); + bindKeys(Control.DOWN, [S]); + bindKeys(Control.LEFT, [A]); + bindKeys(Control.RIGHT, [D]); + bindKeys(Control.ACCEPT, [G, Z]); + bindKeys(Control.BACK, [H, X]); + bindKeys(Control.PAUSE, [ONE]); + bindKeys(Control.RESET, [R]); + case Duo(false): + bindKeys(Control.UP, [FlxKey.UP]); + bindKeys(Control.DOWN, [FlxKey.DOWN]); + bindKeys(Control.LEFT, [FlxKey.LEFT]); + bindKeys(Control.RIGHT, [FlxKey.RIGHT]); + bindKeys(Control.ACCEPT, [O]); + bindKeys(Control.BACK, [P]); + bindKeys(Control.PAUSE, [ENTER]); + bindKeys(Control.RESET, [BACKSPACE]); + case None: // nothing + case Custom: // nothing + } + #end } function removeKeyboard() @@ -485,7 +525,11 @@ class Controls extends FlxActionSet { gamepadsAdded.push(id); for (control => buttons in buttonMap) + #if (haxe >= "4.0.0") inline bindButtons(control, id, buttons); + #else + bindButtons(control, id, buttons); + #end } public function removeGamepad(deviceID:Int = FlxInputDeviceID.ALL):Void @@ -525,9 +569,9 @@ class Controls extends FlxActionSet public function bindButtons(control:Control, id, buttons) { #if (haxe >= "4.0.0") - inline forEachBound(control, (action, state) -> addButtons(action, buttons, state, id)); + inline forEachBound(control, (action, state) -> addButtons(action, buttons, state, id)); #else - forEachBound(control, (action, state) -> addButtons(action, buttons, state, id)); + forEachBound(control, action => addButtons(action, buttons, state, id), state => addButtons(action, buttons, state, id)); #end } @@ -538,9 +582,9 @@ class Controls extends FlxActionSet public function unbindButtons(control:Control, gamepadID:Int, buttons) { #if (haxe >= "4.0.0") - inline forEachBound(control, (action, _) -> removeButtons(action, gamepadID, buttons)); + inline forEachBound(control, (action, _) -> removeButtons(action, gamepadID, buttons)); #else - forEachBound(control, (action, _) -> removeButtons(action, gamepadID, buttons)); + forEachBound(control, action => removeButtons(action, gamepadID, buttons), _ => removeButtons(action, gamepadID, buttons)); #end } From 07acc538889e43cfb6cf9c4cbe7e191f4351d6a2 Mon Sep 17 00:00:00 2001 From: Brandon Date: Mon, 2 Nov 2020 01:46:37 -0500 Subject: [PATCH 12/16] ng release --- README.md | 1 + source/Boyfriend.hx | 3 +++ source/Character.hx | 7 +++++- source/FreeplayState.hx | 8 ++++++- source/PlayState.hx | 47 +++++++++++++++++++++++++++++++++++------ 5 files changed, 58 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 37cae901f..84ae25647 100644 --- a/README.md +++ b/README.md @@ -3,3 +3,4 @@ This is the repository for Friday Night Funkin, a game originally made for Ludum Dare 47 "Stuck In a Loop". Play the Ludum Dare prototype here: https://ninja-muffin24.itch.io/friday-night-funkin +Play the Newgrounds one here: https://www.newgrounds.com/portal/view/770371 diff --git a/source/Boyfriend.hx b/source/Boyfriend.hx index 483f4d0e6..1a8c6cf07 100644 --- a/source/Boyfriend.hx +++ b/source/Boyfriend.hx @@ -30,6 +30,8 @@ class Boyfriend extends Character animation.addByPrefix('firstDeath', "BF dies", 24, false); animation.addByPrefix('deathLoop', "BF Dead Loop", 24, true); animation.addByPrefix('deathConfirm', "BF Dead confirm", 24, false); + + animation.addByPrefix('scared', 'BF idle shaking', 24); playAnim('idle'); antialiasing = true; @@ -47,6 +49,7 @@ class Boyfriend extends Character addOffset('firstDeath', 37, 11); addOffset('deathLoop', 37, 5); addOffset('deathConfirm', 37, 69); + addOffset('scared', -4); } override function update(elapsed:Float) diff --git a/source/Character.hx b/source/Character.hx index af441f023..937fd310c 100644 --- a/source/Character.hx +++ b/source/Character.hx @@ -24,7 +24,6 @@ class Character extends FlxSprite switch (curCharacter) { - case 'bf': case 'gf': // GIRLFRIEND CODE tex = FlxAtlasFrames.fromSparrow(AssetPaths.GF_assets__png, AssetPaths.GF_assets__xml); @@ -38,6 +37,8 @@ class Character extends FlxSprite animation.addByIndices('danceLeft', 'GF Dancing Beat', [30, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], "", 24, false); animation.addByIndices('danceRight', 'GF Dancing Beat', [15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29], "", 24, false); + animation.addByPrefix('scared', 'GF FEAR', 24); + addOffset('cheer'); addOffset('sad', -2, -2); addOffset('danceLeft', 0, -9); @@ -48,6 +49,8 @@ class Character extends FlxSprite addOffset("singLEFT", 0, -19); addOffset("singDOWN", 0, -20); + addOffset('scared'); + playAnim('danceRight'); case 'dad': @@ -112,6 +115,8 @@ class Character extends FlxSprite { switch (curCharacter) { + case 'bf': + case 'gf': danced = !danced; diff --git a/source/FreeplayState.hx b/source/FreeplayState.hx index ecdf81edf..193744704 100644 --- a/source/FreeplayState.hx +++ b/source/FreeplayState.hx @@ -20,7 +20,13 @@ class FreeplayState extends MusicBeatState if (!FlxG.sound.music.playing) FlxG.sound.playMusic('assets/music/freakyMenu' + TitleState.soundExt); - if (StoryMenuState.weekUnlocked[1]) + var isDebug:Bool = false; + + #if debug + isDebug = true; + #end + + if (StoryMenuState.weekUnlocked[1] || isDebug) { songs.push('Spookeez'); songs.push('South'); diff --git a/source/PlayState.hx b/source/PlayState.hx index 2130a37bf..9bc96a88d 100644 --- a/source/PlayState.hx +++ b/source/PlayState.hx @@ -78,6 +78,7 @@ class PlayState extends MusicBeatState var dialogue:Array = ['blah blah blah', 'coolswag']; var halloweenBG:FlxSprite; + var isHalloween:Bool = false; var talking:Bool = true; var songScore:Int = 0; @@ -136,6 +137,8 @@ class PlayState extends MusicBeatState halloweenBG.animation.play('idle'); halloweenBG.antialiasing = true; add(halloweenBG); + + isHalloween = true; } else { @@ -600,7 +603,8 @@ class PlayState extends MusicBeatState // FlxG.watch.addQuick('VOL', vocals.amplitudeLeft); // FlxG.watch.addQuick('VOLRight', vocals.amplitudeRight); - healthHeads.setGraphicSize(Std.int(FlxMath.lerp(100, healthHeads.width, 0.98))); + healthHeads.setGraphicSize(Std.int(FlxMath.lerp(150, healthHeads.width, 0.50))); + healthHeads.updateHitbox(); healthHeads.x = healthBar.x + (healthBar.width * (FlxMath.remapToRange(healthBar.percent, 0, 100, 100, 0) * 0.01)) - (healthHeads.width / 2); if (health > 2) @@ -613,8 +617,11 @@ class PlayState extends MusicBeatState /* if (FlxG.keys.justPressed.NINE) FlxG.switchState(new Charting()); */ - // if (FlxG.keys.justPressed.EIGHT) - // FlxG.switchState(new AnimationDebug(SONG.player2)); + + #if debug + if (FlxG.keys.justPressed.EIGHT) + FlxG.switchState(new AnimationDebug(SONG.player1)); + #end if (startingSong) { @@ -678,7 +685,8 @@ class PlayState extends MusicBeatState if (camZooming) { - FlxG.camera.zoom = FlxMath.lerp(1.05, FlxG.camera.zoom, 0.96); + FlxG.camera.zoom = FlxMath.lerp(1.05, FlxG.camera.zoom, 0.95); + camHUD.zoom = FlxMath.lerp(1, camHUD.zoom, 0.95); } FlxG.watch.addQuick("beatShit", totalBeats); @@ -760,6 +768,9 @@ class PlayState extends MusicBeatState if (!daNote.mustPress && daNote.wasGoodHit) { + if (SONG.song != 'Tutorial') + camZooming = true; + switch (Math.abs(daNote.noteData)) { case 2: @@ -1247,6 +1258,18 @@ class PlayState extends MusicBeatState } } + function lightningStrikeShit():Void + { + FlxG.sound.play('assets/sounds/thunder_' + FlxG.random.int(1, 2) + TitleState.soundExt); + halloweenBG.animation.play('lightning'); + + lightningStrikeBeat = curBeat; + lightningOffset = FlxG.random.int(8, 24); + + boyfriend.playAnim('scared', true); + gf.playAnim('scared', true); + } + override function stepHit() { if (SONG.needsVoices) @@ -1268,6 +1291,9 @@ class PlayState extends MusicBeatState super.stepHit(); } + var lightningStrikeBeat:Int = 0; + var lightningOffset:Int = 8; + override function beatHit() { super.beatHit(); @@ -1294,9 +1320,13 @@ class PlayState extends MusicBeatState // FlxG.log.add('change bpm' + SONG.notes[Std.int(curStep / 16)].changeBPM); if (camZooming && FlxG.camera.zoom < 1.35 && totalBeats % 4 == 0) - FlxG.camera.zoom += 0.025; + { + FlxG.camera.zoom += 0.015; + camHUD.zoom += 0.03; + } - healthHeads.setGraphicSize(Std.int(healthHeads.width + 20)); + healthHeads.setGraphicSize(Std.int(healthHeads.width + 30)); + healthHeads.updateHitbox(); if (totalBeats % gfSpeed == 0) { @@ -1315,5 +1345,10 @@ class PlayState extends MusicBeatState dad.playAnim('cheer', true); } } + + if (isHalloween && FlxG.random.bool(10) && curBeat > lightningStrikeBeat + lightningOffset) + { + lightningStrikeShit(); + } } } From 5ebe1654f25fd027c89461222c6367df748ab20f Mon Sep 17 00:00:00 2001 From: Brandon Date: Mon, 2 Nov 2020 14:37:23 -0500 Subject: [PATCH 13/16] george the janitor --- source/Controls.hx | 46 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 36 insertions(+), 10 deletions(-) diff --git a/source/Controls.hx b/source/Controls.hx index 37d7caa71..b598ab981 100644 --- a/source/Controls.hx +++ b/source/Controls.hx @@ -282,7 +282,7 @@ class Controls extends FlxActionSet * @param func * @return ->Void) */ - function forEachBound(control:Control, func:FlxActionDigital -> Void, func:FlxInputState -> Void) + function forEachBound(control:Control, func:FlxActionDigital->FlxInputState->Void) { switch (control) { @@ -336,6 +336,7 @@ class Controls extends FlxActionSet public function copyFrom(controls:Controls, ?device:Device) { + #if (haxe >= "4.0.0") for (name => action in controls.byName) { for (input in action.inputs) @@ -344,14 +345,31 @@ class Controls extends FlxActionSet byName[name].add(cast input); } } + #else + for (name in controls.byName.keys()) + { + var action = controls.byName[name]; + for (input in action.inputs) + { + if (device == null || isDevice(input, device)) + byName[name].add(cast input); + } + } + #end switch (device) { case null: // add all + #if (haxe >= "4.0.0") for (gamepad in controls.gamepadsAdded) if (!gamepadsAdded.contains(gamepad)) gamepadsAdded.push(gamepad); + #else + for (gamepad in controls.gamepadsAdded) + if (gamepadsAdded.indexOf(gamepad) == -1) + gamepadsAdded.push(gamepad); + #end mergeKeyboardScheme(controls.keyboardScheme); @@ -390,7 +408,7 @@ class Controls extends FlxActionSet #if (haxe >= "4.0.0") inline forEachBound(control, (action, state) -> addKeys(action, keys, state)); #else - forEachBound(control, action => addKeys(action, keys, state), state => addKeys(action, keys, state)); + forEachBound(control, function(action, state) addKeys(action, keys, state)); #end } @@ -403,7 +421,7 @@ class Controls extends FlxActionSet #if (haxe >= "4.0.0") inline forEachBound(control, (action, _) -> removeKeys(action, keys)); #else - forEachBound(control, action => removeKeys(action, keys), _ => removeKeys(action, keys)); + forEachBound(control, function(action, _) removeKeys(action, keys)); #end } @@ -517,19 +535,27 @@ class Controls extends FlxActionSet public function addGamepad(id:Int, ?buttonMap:Map>):Void { gamepadsAdded.push(id); + + #if (haxe >= "4.0.0") for (control => buttons in buttonMap) - bindButtons(control, id, buttons); + inline bindButtons(control, id, buttons); + #else + for (control in buttonMap.keys()) + bindButtons(control, id, buttonMap[control]); + #end } inline function addGamepadLiteral(id:Int, ?buttonMap:Map>):Void { gamepadsAdded.push(id); + + #if (haxe >= "4.0.0") for (control => buttons in buttonMap) - #if (haxe >= "4.0.0") inline bindButtons(control, id, buttons); - #else - bindButtons(control, id, buttons); - #end + #else + for (control in buttonMap.keys()) + bindButtons(control, id, buttonMap[control]); + #end } public function removeGamepad(deviceID:Int = FlxInputDeviceID.ALL):Void @@ -571,7 +597,7 @@ class Controls extends FlxActionSet #if (haxe >= "4.0.0") inline forEachBound(control, (action, state) -> addButtons(action, buttons, state, id)); #else - forEachBound(control, action => addButtons(action, buttons, state, id), state => addButtons(action, buttons, state, id)); + forEachBound(control, function(action, state) addButtons(action, buttons, state, id)); #end } @@ -584,7 +610,7 @@ class Controls extends FlxActionSet #if (haxe >= "4.0.0") inline forEachBound(control, (action, _) -> removeButtons(action, gamepadID, buttons)); #else - forEachBound(control, action => removeButtons(action, gamepadID, buttons), _ => removeButtons(action, gamepadID, buttons)); + forEachBound(control, function(action, _) removeButtons(action, gamepadID, buttons)); #end } From 0d1bd527e2e75dedb805701bc2e22959307aabee Mon Sep 17 00:00:00 2001 From: Brandon Date: Mon, 2 Nov 2020 15:04:25 -0500 Subject: [PATCH 14/16] controls fixed for 3.4.7 --- source/Controls.hx | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/source/Controls.hx b/source/Controls.hx index b598ab981..09e78be79 100644 --- a/source/Controls.hx +++ b/source/Controls.hx @@ -197,6 +197,7 @@ class Controls extends FlxActionSet inline function get_RESET() return _reset.check(); + #if (haxe >= "4.0.0") public function new(name, scheme = None) { super(name); @@ -223,6 +224,36 @@ class Controls extends FlxActionSet setKeyboardScheme(scheme, false); } + #else + public function new(name, scheme:KeyboardScheme = null) + { + super(name); + + add(_up); + add(_left); + add(_right); + add(_down); + add(_upP); + add(_leftP); + add(_rightP); + add(_downP); + add(_upR); + add(_leftR); + add(_rightR); + add(_downR); + add(_accept); + add(_back); + add(_pause); + add(_reset); + + for (action in digitalActions) + byName[action.name] = action; + + if (scheme == null) + scheme = None; + setKeyboardScheme(scheme, false); + } + #end override function update() { From 3a121ec4f8abbea243ec7f86e115c9fb3620d7be Mon Sep 17 00:00:00 2001 From: Brandon Date: Mon, 2 Nov 2020 15:31:28 -0500 Subject: [PATCH 15/16] cleansed of NG --- source/Alphabet.hx | 12 ++++++++++-- source/PlayState.hx | 4 ++++ source/PlayerSettings.hx | 9 +++++++++ source/TitleState.hx | 4 +++- 4 files changed, 26 insertions(+), 3 deletions(-) diff --git a/source/Alphabet.hx b/source/Alphabet.hx index 9824e2f35..07f35313e 100644 --- a/source/Alphabet.hx +++ b/source/Alphabet.hx @@ -77,7 +77,8 @@ class Alphabet extends FlxSpriteGroup lastWasSpace = true; } - if (AlphaCharacter.alphabet.contains(character.toLowerCase())) + if (AlphaCharacter.alphabet.indexOf(character.toLowerCase()) != -1) + //if (AlphaCharacter.alphabet.contains(character.toLowerCase())) { if (lastSprite != null) { @@ -144,9 +145,16 @@ class Alphabet extends FlxSpriteGroup lastWasSpace = true; } + #if (haxe >= "4.0.0") var isNumber:Bool = AlphaCharacter.numbers.contains(splitWords[loopNum]); var isSymbol:Bool = AlphaCharacter.symbols.contains(splitWords[loopNum]); - if (AlphaCharacter.alphabet.contains(splitWords[loopNum].toLowerCase()) || isNumber || isSymbol) + #else + var isNumber:Bool = AlphaCharacter.numbers.indexOf(splitWords[loopNum]) != -1; + var isSymbol:Bool = AlphaCharacter.symbols.indexOf(splitWords[loopNum]) != -1; + #end + + if (AlphaCharacter.alphabet.indexOf(splitWords[loopNum].toLowerCase()) != -1 || isNumber || isSymbol) + //if (AlphaCharacter.alphabet.contains(splitWords[loopNum].toLowerCase()) || isNumber || isSymbol) { if (lastSprite != null && !xPosResetted) { diff --git a/source/PlayState.hx b/source/PlayState.hx index 9bc96a88d..43ba3d3c5 100644 --- a/source/PlayState.hx +++ b/source/PlayState.hx @@ -820,7 +820,9 @@ class PlayState extends MusicBeatState { trace('SONG DONE' + isStoryMode); + #if !switch NGio.postScore(songScore, SONG.song); + #end if (isStoryMode) { @@ -834,7 +836,9 @@ class PlayState extends MusicBeatState StoryMenuState.weekUnlocked[1] = true; + #if !switch NGio.unlockMedal(60961); + #end FlxG.save.data.weekUnlocked = StoryMenuState.weekUnlocked; FlxG.save.flush(); diff --git a/source/PlayerSettings.hx b/source/PlayerSettings.hx index fa034581d..b275b9b51 100644 --- a/source/PlayerSettings.hx +++ b/source/PlayerSettings.hx @@ -14,12 +14,21 @@ class PlayerSettings static public var player1(default, null):PlayerSettings; static public var player2(default, null):PlayerSettings; + #if (haxe >= "4.0.0") static public final onAvatarAdd = new FlxTypedSignalVoid>(); static public final onAvatarRemove = new FlxTypedSignalVoid>(); + #else + static public var onAvatarAdd = new FlxTypedSignalVoid>(); + static public var onAvatarRemove = new FlxTypedSignalVoid>(); + #end public var id(default, null):Int; + #if (haxe >= "4.0.0") public final controls:Controls; + #else + public var controls:Controls; + #end // public var avatar:Player; // public var camera(get, never):PlayCamera; diff --git a/source/TitleState.hx b/source/TitleState.hx index 12faa41c1..3f8e0e6d9 100644 --- a/source/TitleState.hx +++ b/source/TitleState.hx @@ -54,7 +54,7 @@ class TitleState extends MusicBeatState super.create(); - #if (!debug && NG_LOGIN) + #if (!switch && !debug && NG_LOGIN) var ng:NGio = new NGio(APIStuff.API, APIStuff.EncKey); #end @@ -197,7 +197,9 @@ class TitleState extends MusicBeatState if (pressedEnter && !transitioning && skippedIntro) { + #if !switch NGio.unlockMedal(60960); + #end titleText.animation.play('press'); FlxG.camera.flash(FlxColor.WHITE, 1); From c2f0253f4a7603c1f741f1e3f608c5b74a22387a Mon Sep 17 00:00:00 2001 From: Brandon Date: Mon, 2 Nov 2020 16:56:28 -0500 Subject: [PATCH 16/16] swap A and B for switch --- source/Controls.hx | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/source/Controls.hx b/source/Controls.hx index 09e78be79..b251ac61d 100644 --- a/source/Controls.hx +++ b/source/Controls.hx @@ -607,6 +607,7 @@ class Controls extends FlxActionSet public function addDefaultGamepad(id):Void { + #if !switch addGamepadLiteral(id, [ Control.ACCEPT => [A], Control.BACK => [B], @@ -617,6 +618,19 @@ class Controls extends FlxActionSet Control.PAUSE => [START], Control.RESET => [Y] ]); + #else + addGamepadLiteral(id, [ + //Swap A and B for switch + Control.ACCEPT => [B], + Control.BACK => [A], + Control.UP => [DPAD_UP, LEFT_STICK_DIGITAL_UP], + Control.DOWN => [DPAD_DOWN, LEFT_STICK_DIGITAL_DOWN], + Control.LEFT => [DPAD_LEFT, LEFT_STICK_DIGITAL_LEFT], + Control.RIGHT => [DPAD_RIGHT, LEFT_STICK_DIGITAL_RIGHT], + Control.PAUSE => [START], + Control.RESET => [Y] + ]); + #end } /**