diff --git a/artDUCK/duck_assets.fla b/artDUCK/duck_assets.fla new file mode 100644 index 00000000..bb5b1013 Binary files /dev/null and b/artDUCK/duck_assets.fla differ diff --git a/campaign_menu_UI_characters.png b/campaign_menu_UI_characters.png new file mode 100644 index 00000000..e8453a10 Binary files /dev/null and b/campaign_menu_UI_characters.png differ diff --git a/campaign_menu_UI_characters.xml b/campaign_menu_UI_characters.xml new file mode 100644 index 00000000..79b862b3 --- /dev/null +++ b/campaign_menu_UI_characters.xml @@ -0,0 +1,161 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/duck/Dia/Dialougue Box.png b/duck/Dia/Dialougue Box.png new file mode 100644 index 00000000..059e575d Binary files /dev/null and b/duck/Dia/Dialougue Box.png differ diff --git a/duck/Dia/Dialougue Box.xml b/duck/Dia/Dialougue Box.xml new file mode 100644 index 00000000..15d1b245 --- /dev/null +++ b/duck/Dia/Dialougue Box.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/duck/Portrait/DuckPortrait.png b/duck/Portrait/DuckPortrait.png new file mode 100644 index 00000000..61c06ec2 Binary files /dev/null and b/duck/Portrait/DuckPortrait.png differ diff --git a/duck/Portrait/DuckPortrait.xml b/duck/Portrait/DuckPortrait.xml new file mode 100644 index 00000000..59b7b86e --- /dev/null +++ b/duck/Portrait/DuckPortrait.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/duck/bg.png b/duck/bg.png new file mode 100644 index 00000000..8456c70e Binary files /dev/null and b/duck/bg.png differ diff --git a/duck/duck_assets.png b/duck/duck_assets.png new file mode 100644 index 00000000..b1c80f8a Binary files /dev/null and b/duck/duck_assets.png differ diff --git a/duck/duck_assets.xml b/duck/duck_assets.xml new file mode 100644 index 00000000..164a7fbc --- /dev/null +++ b/duck/duck_assets.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/iconGrid.png b/iconGrid.png new file mode 100644 index 00000000..df92f7ce Binary files /dev/null and b/iconGrid.png differ diff --git a/source_duck/APIStuff.hx b/source_duck/APIStuff.hx new file mode 100644 index 00000000..f05fa065 --- /dev/null +++ b/source_duck/APIStuff.hx @@ -0,0 +1,7 @@ +package; + +class APIStuff +{ + public static var API:String = ""; + public static var EncKey:String = ""; +} diff --git a/source_duck/Alphabet.hx b/source_duck/Alphabet.hx new file mode 100644 index 00000000..6f7e711b --- /dev/null +++ b/source_duck/Alphabet.hx @@ -0,0 +1,309 @@ +package; + +import flixel.FlxG; +import flixel.FlxSprite; +import flixel.graphics.frames.FlxAtlasFrames; +import flixel.group.FlxSpriteGroup; +import flixel.math.FlxMath; +import flixel.util.FlxTimer; + +using StringTools; + +/** + * Loosley based on FlxTypeText lolol + */ +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 = ""; + var _curText:String = ""; + + public var widthOfWords:Float = FlxG.width; + + var yMulti:Float = 1; + + // custom shit + // amp, backslash, question mark, apostrophy, comma, angry faic, period + var lastSprite:AlphaCharacter; + var xPosResetted:Bool = false; + var lastWasSpace:Bool = false; + + 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 (text != "") + { + if (typed) + { + startTypedText(); + } + else + { + addText(); + } + } + } + + public function addText() + { + doSplitWords(); + + var xPos:Float = 0; + for (character in splitWords) + { + // if (character.fastCodeAt() == " ") + // { + // } + + if (character == " " || character == "-") + { + lastWasSpace = true; + } + + if (AlphaCharacter.alphabet.indexOf(character.toLowerCase()) != -1) + // if (AlphaCharacter.alphabet.contains(character.toLowerCase())) + { + if (lastSprite != null) + { + xPos = lastSprite.x + lastSprite.width; + } + + if (lastWasSpace) + { + xPos += 40; + lastWasSpace = false; + } + + // var letter:AlphaCharacter = new AlphaCharacter(30 * loopNum, 0); + var letter:AlphaCharacter = new AlphaCharacter(xPos, 0); + + if (isBold) + letter.createBold(character); + else + { + letter.createLetter(character); + } + + add(letter); + + lastSprite = letter; + } + + // loopNum += 1; + } + } + + function doSplitWords():Void + { + splitWords = _finalText.split(""); + } + + public var personTalking:String = 'gf'; + + public function startTypedText():Void + { + _finalText = text; + doSplitWords(); + + // trace(arrayShit); + + var loopNum:Int = 0; + + var xPos:Float = 0; + var curRow:Int = 0; + + new FlxTimer().start(0.05, function(tmr:FlxTimer) + { + // trace(_finalText.fastCodeAt(loopNum) + " " + _finalText.charAt(loopNum)); + if (_finalText.fastCodeAt(loopNum) == "\n".code) + { + yMulti += 1; + xPosResetted = true; + xPos = 0; + curRow += 1; + } + + if (splitWords[loopNum] == " ") + { + lastWasSpace = true; + } + + #if (haxe >= "4.0.0") + var isNumber:Bool = AlphaCharacter.numbers.contains(splitWords[loopNum]); + var isSymbol:Bool = AlphaCharacter.symbols.contains(splitWords[loopNum]); + #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) + { + 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.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(Paths.soundRandom(daSound, 1, 4)); + } + + add(letter); + + lastSprite = letter; + } + + loopNum += 1; + + tmr.time = FlxG.random.float(0.04, 0.09); + }, splitWords.length); + } + + 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.30); + x = FlxMath.lerp(x, (targetY * 20) + 90, 0.30); + } + + super.update(elapsed); + } +} + +class AlphaCharacter extends FlxSprite +{ + public static var alphabet:String = "abcdefghijklmnopqrstuvwxyz"; + + public static var numbers:String = "1234567890"; + + public static var symbols:String = "|~#$%()*+-:;<=>@[]^_.,'!?"; + + public var row:Int = 0; + + public function new(x:Float, y:Float) + { + super(x, y); + var tex = Paths.getSparrowAtlas('alphabet'); + frames = tex; + + antialiasing = true; + } + + public function createBold(letter:String) + { + animation.addByPrefix(letter, letter.toUpperCase() + " bold", 24); + animation.play(letter); + updateHitbox(); + } + + public function createLetter(letter:String):Void + { + var letterCase:String = "lowercase"; + if (letter.toLowerCase() != letter) + { + letterCase = 'capital'; + } + + 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_duck/AnimationDebug.hx b/source_duck/AnimationDebug.hx new file mode 100644 index 00000000..05b40d63 --- /dev/null +++ b/source_duck/AnimationDebug.hx @@ -0,0 +1,195 @@ +package; + +import flixel.FlxG; +import flixel.FlxObject; +import flixel.FlxSprite; +import flixel.FlxState; +import flixel.addons.display.FlxGridOverlay; +import flixel.group.FlxGroup.FlxTypedGroup; +import flixel.text.FlxText; +import flixel.util.FlxColor; + +/** + *DEBUG MODE + */ +class AnimationDebug extends FlxState +{ + var bf:Boyfriend; + var dad:Character; + var char:Character; + var textAnim:FlxText; + var dumbTexts:FlxTypedGroup; + var animList:Array = []; + var curAnim:Int = 0; + var isDad:Bool = true; + var daAnim:String = 'spooky'; + var camFollow:FlxObject; + + public function new(daAnim:String = 'spooky') + { + super(); + this.daAnim = daAnim; + } + + override function create() + { + FlxG.sound.music.stop(); + + var gridBG:FlxSprite = FlxGridOverlay.create(10, 10); + gridBG.scrollFactor.set(0.5, 0.5); + add(gridBG); + + if (daAnim == 'bf') + isDad = false; + + if (isDad) + { + dad = new Character(0, 0, daAnim); + dad.screenCenter(); + dad.debugMode = true; + add(dad); + + char = dad; + dad.flipX = false; + } + else + { + bf = new Boyfriend(0, 0); + bf.screenCenter(); + bf.debugMode = true; + add(bf); + + char = bf; + bf.flipX = false; + } + + dumbTexts = new FlxTypedGroup(); + add(dumbTexts); + + textAnim = new FlxText(300, 16); + textAnim.size = 26; + textAnim.scrollFactor.set(); + add(textAnim); + + genBoyOffsets(); + + camFollow = new FlxObject(0, 0, 2, 2); + camFollow.screenCenter(); + add(camFollow); + + FlxG.camera.follow(camFollow); + + super.create(); + } + + function genBoyOffsets(pushList:Bool = true):Void + { + var daLoop:Int = 0; + + for (anim => offsets in char.animOffsets) + { + var text:FlxText = new FlxText(10, 20 + (18 * daLoop), 0, anim + ": " + offsets, 15); + text.scrollFactor.set(); + text.color = FlxColor.BLUE; + dumbTexts.add(text); + + if (pushList) + animList.push(anim); + + daLoop++; + } + } + + function updateTexts():Void + { + dumbTexts.forEach(function(text:FlxText) + { + text.kill(); + dumbTexts.remove(text, true); + }); + } + + override function update(elapsed:Float) + { + textAnim.text = char.animation.curAnim.name; + + if (FlxG.keys.justPressed.E) + FlxG.camera.zoom += 0.25; + if (FlxG.keys.justPressed.Q) + FlxG.camera.zoom -= 0.25; + + if (FlxG.keys.pressed.I || FlxG.keys.pressed.J || FlxG.keys.pressed.K || FlxG.keys.pressed.L) + { + if (FlxG.keys.pressed.I) + camFollow.velocity.y = -90; + else if (FlxG.keys.pressed.K) + camFollow.velocity.y = 90; + else + camFollow.velocity.y = 0; + + if (FlxG.keys.pressed.J) + camFollow.velocity.x = -90; + else if (FlxG.keys.pressed.L) + camFollow.velocity.x = 90; + else + camFollow.velocity.x = 0; + } + else + { + camFollow.velocity.set(); + } + + if (FlxG.keys.justPressed.W) + { + curAnim -= 1; + } + + if (FlxG.keys.justPressed.S) + { + curAnim += 1; + } + + if (curAnim < 0) + curAnim = animList.length - 1; + + if (curAnim >= animList.length) + curAnim = 0; + + if (FlxG.keys.justPressed.S || FlxG.keys.justPressed.W || FlxG.keys.justPressed.SPACE) + { + char.playAnim(animList[curAnim]); + + updateTexts(); + genBoyOffsets(false); + } + + var upP = FlxG.keys.anyJustPressed([UP]); + var rightP = FlxG.keys.anyJustPressed([RIGHT]); + var downP = FlxG.keys.anyJustPressed([DOWN]); + var leftP = FlxG.keys.anyJustPressed([LEFT]); + + var holdShift = FlxG.keys.pressed.SHIFT; + var multiplier = 1; + if (holdShift) + multiplier = 10; + + if (upP || rightP || downP || leftP) + { + updateTexts(); + if (upP) + char.animOffsets.get(animList[curAnim])[1] += 1 * multiplier; + if (downP) + char.animOffsets.get(animList[curAnim])[1] -= 1 * multiplier; + if (leftP) + char.animOffsets.get(animList[curAnim])[0] += 1 * multiplier; + if (rightP) + char.animOffsets.get(animList[curAnim])[0] -= 1 * multiplier; + + updateTexts(); + genBoyOffsets(false); + char.playAnim(animList[curAnim]); + } + + super.update(elapsed); + } +} diff --git a/source_duck/AssetPaths.hx b/source_duck/AssetPaths.hx new file mode 100644 index 00000000..5202a17d --- /dev/null +++ b/source_duck/AssetPaths.hx @@ -0,0 +1,4 @@ +package; + +@:build(flixel.system.FlxAssets.buildFileReferences("assets", true)) +class AssetPaths {} diff --git a/source_duck/BackgroundDancer.hx b/source_duck/BackgroundDancer.hx new file mode 100644 index 00000000..a619dacc --- /dev/null +++ b/source_duck/BackgroundDancer.hx @@ -0,0 +1,30 @@ +package; + +import flixel.FlxSprite; +import flixel.graphics.frames.FlxAtlasFrames; + +class BackgroundDancer extends FlxSprite +{ + public function new(x:Float, y:Float) + { + super(x, y); + + frames = Paths.getSparrowAtlas("limo/limoDancer"); + animation.addByIndices('danceLeft', 'bg dancer sketch PINK', [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], "", 24, false); + animation.addByIndices('danceRight', 'bg dancer sketch PINK', [15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29], "", 24, false); + animation.play('danceLeft'); + antialiasing = true; + } + + var danceDir:Bool = false; + + public function dance():Void + { + danceDir = !danceDir; + + if (danceDir) + animation.play('danceRight', true); + else + animation.play('danceLeft', true); + } +} diff --git a/source_duck/BackgroundGirls.hx b/source_duck/BackgroundGirls.hx new file mode 100644 index 00000000..c37b16f9 --- /dev/null +++ b/source_duck/BackgroundGirls.hx @@ -0,0 +1,39 @@ +package; + +import flixel.FlxSprite; +import flixel.graphics.frames.FlxAtlasFrames; + +class BackgroundGirls extends FlxSprite +{ + public function new(x:Float, y:Float) + { + super(x, y); + + // BG fangirls dissuaded + frames = Paths.getSparrowAtlas('weeb/bgFreaks'); + + animation.addByIndices('danceLeft', 'BG girls group', CoolUtil.numberArray(14), "", 24, false); + animation.addByIndices('danceRight', 'BG girls group', CoolUtil.numberArray(30, 15), "", 24, false); + + animation.play('danceLeft'); + } + + var danceDir:Bool = false; + + public function getScared():Void + { + animation.addByIndices('danceLeft', 'BG fangirls dissuaded', CoolUtil.numberArray(14), "", 24, false); + animation.addByIndices('danceRight', 'BG fangirls dissuaded', CoolUtil.numberArray(30, 15), "", 24, false); + dance(); + } + + public function dance():Void + { + danceDir = !danceDir; + + if (danceDir) + animation.play('danceRight', true); + else + animation.play('danceLeft', true); + } +} diff --git a/source_duck/BlendModeEffect.hx b/source_duck/BlendModeEffect.hx new file mode 100644 index 00000000..1c9038be --- /dev/null +++ b/source_duck/BlendModeEffect.hx @@ -0,0 +1,34 @@ +package; + +import flixel.util.FlxColor; +import openfl.display.ShaderParameter; + +typedef BlendModeShader = +{ + var uBlendColor:ShaderParameter; +} + +class BlendModeEffect +{ + public var shader(default, null):BlendModeShader; + + @:isVar + public var color(default, set):FlxColor; + + public function new(shader:BlendModeShader, color:FlxColor):Void + { + shader.uBlendColor.value = []; + this.shader = shader; + this.color = color; + } + + function set_color(color:FlxColor):FlxColor + { + shader.uBlendColor.value[0] = color.redFloat; + shader.uBlendColor.value[1] = color.greenFloat; + shader.uBlendColor.value[2] = color.blueFloat; + shader.uBlendColor.value[3] = color.alphaFloat; + + return this.color = color; + } +} diff --git a/source_duck/Boyfriend.hx b/source_duck/Boyfriend.hx new file mode 100644 index 00000000..c9f87f9b --- /dev/null +++ b/source_duck/Boyfriend.hx @@ -0,0 +1,43 @@ +package; + +import flixel.FlxG; +import flixel.FlxSprite; +import flixel.graphics.frames.FlxAtlasFrames; +import flixel.util.FlxTimer; + +using StringTools; + +class Boyfriend extends Character +{ + public var stunned:Bool = false; + + public function new(x:Float, y:Float, ?char:String = 'bf') + { + super(x, y, char, true); + } + + override function update(elapsed:Float) + { + if (!debugMode) + { + if (animation.curAnim.name.startsWith('sing')) + { + holdTimer += elapsed; + } + else + holdTimer = 0; + + if (animation.curAnim.name.endsWith('miss') && animation.curAnim.finished && !debugMode) + { + playAnim('idle', true, false, 10); + } + + if (animation.curAnim.name == 'firstDeath' && animation.curAnim.finished) + { + playAnim('deathLoop'); + } + } + + super.update(elapsed); + } +} diff --git a/source_duck/ButtonRemapSubstate.hx b/source_duck/ButtonRemapSubstate.hx new file mode 100644 index 00000000..64ff0794 --- /dev/null +++ b/source_duck/ButtonRemapSubstate.hx @@ -0,0 +1,11 @@ +package; + +import flixel.FlxSubState; + +class ButtonRemapSubstate extends FlxSubState +{ + public function new() + { + super(); + } +} diff --git a/source_duck/Caching.hx b/source_duck/Caching.hx new file mode 100644 index 00000000..1cbbcd66 --- /dev/null +++ b/source_duck/Caching.hx @@ -0,0 +1,131 @@ +package; + +import haxe.Exception; +import flixel.tweens.FlxEase; +import flixel.tweens.FlxTween; +import sys.FileSystem; +import sys.io.File; +import flixel.FlxG; +import flixel.FlxSprite; +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.math.FlxPoint; +import flixel.math.FlxRect; +import flixel.util.FlxColor; +import flixel.util.FlxTimer; +import flixel.text.FlxText; + +using StringTools; + +class Caching extends MusicBeatState +{ + var toBeDone = 0; + var done = 0; + + var text:FlxText; + var kadeLogo:FlxSprite; + + override function create() + { + FlxG.mouse.visible = false; + + FlxG.worldBounds.set(0,0); + + text = new FlxText(FlxG.width / 2, FlxG.height / 2 + 300,0,"Loading..."); + text.size = 34; + text.alignment = FlxTextAlign.CENTER; + text.alpha = 0; + + kadeLogo = new FlxSprite(FlxG.width / 2, FlxG.height / 2).loadGraphic(Paths.image('KadeEngineLogo')); + kadeLogo.x -= kadeLogo.width / 2; + kadeLogo.y -= kadeLogo.height / 2 + 100; + text.y -= kadeLogo.height / 2 - 125; + text.x -= 170; + kadeLogo.setGraphicSize(Std.int(kadeLogo.width * 0.6)); + + kadeLogo.alpha = 0; + + add(kadeLogo); + add(text); + + trace('starting caching..'); + + sys.thread.Thread.create(() -> { + cache(); + }); + + + super.create(); + } + + var calledDone = false; + + override function update(elapsed) + { + + if (toBeDone != 0 && done != toBeDone) + { + var alpha = HelperFunctions.truncateFloat(done / toBeDone * 100,2) / 100; + kadeLogo.alpha = alpha; + text.alpha = alpha; + text.text = "Loading... (" + done + "/" + toBeDone + ")"; + } + + super.update(elapsed); + } + + + function cache() + { + + var images = []; + var music = []; + var charts = []; + + trace("caching images..."); + + for (i in FileSystem.readDirectory(FileSystem.absolutePath("assets/shared/images/characters"))) + { + if (!i.endsWith(".png")) + continue; + images.push(i); + } + + trace("caching music..."); + + for (i in FileSystem.readDirectory(FileSystem.absolutePath("assets/songs"))) + { + music.push(i); + } + + + toBeDone = Lambda.count(images) + Lambda.count(music); + + trace("LOADING: " + toBeDone + " OBJECTS."); + + for (i in images) + { + var replaced = i.replace(".png",""); + FlxG.bitmap.add(Paths.image("characters/" + replaced,"shared")); + trace("cached " + replaced); + done++; + } + + for (i in music) + { + FlxG.sound.cache(Paths.inst(i)); + FlxG.sound.cache(Paths.voices(i)); + trace("cached " + i); + done++; + } + + + trace("Finished caching..."); + + FlxG.switchState(new TitleState()); + } + +} \ No newline at end of file diff --git a/source_duck/Character.hx b/source_duck/Character.hx new file mode 100644 index 00000000..a2f88274 --- /dev/null +++ b/source_duck/Character.hx @@ -0,0 +1,672 @@ +package; + +import flixel.FlxG; +import flixel.FlxSprite; +import flixel.animation.FlxBaseAnimation; +import flixel.graphics.frames.FlxAtlasFrames; + +using StringTools; + +class Character extends FlxSprite +{ + public var animOffsets:Map>; + public var debugMode:Bool = false; + + public var isPlayer:Bool = false; + public var curCharacter:String = 'bf'; + + public var holdTimer:Float = 0; + + public function new(x:Float, y:Float, ?character:String = "bf", ?isPlayer:Bool = false) + { + super(x, y); + + animOffsets = new Map>(); + curCharacter = character; + this.isPlayer = isPlayer; + + var tex:FlxAtlasFrames; + antialiasing = true; + + switch (curCharacter) + { + case 'gf': + // GIRLFRIEND CODE + tex = Paths.getSparrowAtlas('characters/GF_assets'); + frames = tex; + animation.addByPrefix('cheer', 'GF Cheer', 24, false); + animation.addByPrefix('singLEFT', 'GF left note', 24, false); + animation.addByPrefix('singRIGHT', 'GF Right Note', 24, false); + animation.addByPrefix('singUP', 'GF Up Note', 24, false); + animation.addByPrefix('singDOWN', 'GF Down Note', 24, false); + animation.addByIndices('sad', 'gf sad', [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], "", 24, false); + 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.addByIndices('hairBlow', "GF Dancing Beat Hair blowing", [0, 1, 2, 3], "", 24); + animation.addByIndices('hairFall', "GF Dancing Beat Hair Landing", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], "", 24, false); + animation.addByPrefix('scared', 'GF FEAR', 24); + + addOffset('cheer'); + addOffset('sad', -2, -2); + addOffset('danceLeft', 0, -9); + addOffset('danceRight', 0, -9); + + addOffset("singUP", 0, 4); + addOffset("singRIGHT", 0, -20); + addOffset("singLEFT", 0, -19); + addOffset("singDOWN", 0, -20); + addOffset('hairBlow', 45, -8); + addOffset('hairFall', 0, -9); + + addOffset('scared', -2, -17); + + playAnim('danceRight'); + + case 'gf-christmas': + tex = Paths.getSparrowAtlas('characters/gfChristmas'); + frames = tex; + animation.addByPrefix('cheer', 'GF Cheer', 24, false); + animation.addByPrefix('singLEFT', 'GF left note', 24, false); + animation.addByPrefix('singRIGHT', 'GF Right Note', 24, false); + animation.addByPrefix('singUP', 'GF Up Note', 24, false); + animation.addByPrefix('singDOWN', 'GF Down Note', 24, false); + animation.addByIndices('sad', 'gf sad', [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], "", 24, false); + 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.addByIndices('hairBlow', "GF Dancing Beat Hair blowing", [0, 1, 2, 3], "", 24); + animation.addByIndices('hairFall', "GF Dancing Beat Hair Landing", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], "", 24, false); + animation.addByPrefix('scared', 'GF FEAR', 24); + + addOffset('cheer'); + addOffset('sad', -2, -2); + addOffset('danceLeft', 0, -9); + addOffset('danceRight', 0, -9); + + addOffset("singUP", 0, 4); + addOffset("singRIGHT", 0, -20); + addOffset("singLEFT", 0, -19); + addOffset("singDOWN", 0, -20); + addOffset('hairBlow', 45, -8); + addOffset('hairFall', 0, -9); + + addOffset('scared', -2, -17); + + playAnim('danceRight'); + + case 'gf-car': + tex = Paths.getSparrowAtlas('characters/gfCar'); + frames = tex; + animation.addByIndices('singUP', 'GF Dancing Beat Hair blowing CAR', [0], "", 24, false); + animation.addByIndices('danceLeft', 'GF Dancing Beat Hair blowing CAR', [30, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], "", 24, false); + animation.addByIndices('danceRight', 'GF Dancing Beat Hair blowing CAR', [15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29], "", 24, + false); + + addOffset('danceLeft', 0); + addOffset('danceRight', 0); + + playAnim('danceRight'); + + case 'gf-pixel': + tex = Paths.getSparrowAtlas('characters/gfPixel'); + frames = tex; + animation.addByIndices('singUP', 'GF IDLE', [2], "", 24, false); + animation.addByIndices('danceLeft', 'GF IDLE', [30, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], "", 24, false); + animation.addByIndices('danceRight', 'GF IDLE', [15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29], "", 24, false); + + addOffset('danceLeft', 0); + addOffset('danceRight', 0); + + playAnim('danceRight'); + + setGraphicSize(Std.int(width * PlayState.daPixelZoom)); + updateHitbox(); + antialiasing = false; + + case 'dad': + // DAD ANIMATION LOADING CODE + tex = Paths.getSparrowAtlas('characters/DADDY_DEAREST'); + frames = tex; + animation.addByPrefix('idle', 'Dad idle dance', 24); + animation.addByPrefix('singUP', 'Dad Sing Note UP', 24); + animation.addByPrefix('singRIGHT', 'Dad Sing Note RIGHT', 24); + animation.addByPrefix('singDOWN', 'Dad Sing Note DOWN', 24); + animation.addByPrefix('singLEFT', 'Dad Sing Note LEFT', 24); + + addOffset('idle'); + addOffset("singUP", -6, 50); + addOffset("singRIGHT", 0, 27); + addOffset("singLEFT", -10, 10); + addOffset("singDOWN", 0, -30); + + playAnim('idle'); + case 'spooky': + tex = Paths.getSparrowAtlas('characters/spooky_kids_assets'); + frames = tex; + animation.addByPrefix('singUP', 'spooky UP NOTE', 24, false); + 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', [0, 2, 6], "", 12, false); + animation.addByIndices('danceRight', 'spooky dance idle', [8, 10, 12, 14], "", 12, false); + + addOffset('danceLeft'); + addOffset('danceRight'); + + addOffset("singUP", -20, 26); + addOffset("singRIGHT", -130, -14); + addOffset("singLEFT", 130, -10); + addOffset("singDOWN", -50, -130); + + playAnim('danceRight'); + case 'mom': + tex = Paths.getSparrowAtlas('characters/Mom_Assets'); + frames = tex; + + animation.addByPrefix('idle', "Mom Idle", 24, false); + animation.addByPrefix('singUP', "Mom Up Pose", 24, false); + animation.addByPrefix('singDOWN', "MOM DOWN POSE", 24, false); + animation.addByPrefix('singLEFT', 'Mom Left Pose', 24, false); + // ANIMATION IS CALLED MOM LEFT POSE BUT ITS FOR THE RIGHT + // CUZ DAVE IS DUMB! + animation.addByPrefix('singRIGHT', 'Mom Pose Left', 24, false); + + addOffset('idle'); + addOffset("singUP", 14, 71); + addOffset("singRIGHT", 10, -60); + addOffset("singLEFT", 250, -23); + addOffset("singDOWN", 20, -160); + + playAnim('idle'); + + case 'mom-car': + tex = Paths.getSparrowAtlas('characters/momCar'); + frames = tex; + + animation.addByPrefix('idle', "Mom Idle", 24, false); + animation.addByPrefix('singUP', "Mom Up Pose", 24, false); + animation.addByPrefix('singDOWN', "MOM DOWN POSE", 24, false); + animation.addByPrefix('singLEFT', 'Mom Left Pose', 24, false); + // ANIMATION IS CALLED MOM LEFT POSE BUT ITS FOR THE RIGHT + // CUZ DAVE IS DUMB! + animation.addByPrefix('singRIGHT', 'Mom Pose Left', 24, false); + + addOffset('idle'); + addOffset("singUP", 14, 71); + addOffset("singRIGHT", 10, -60); + addOffset("singLEFT", 250, -23); + addOffset("singDOWN", 20, -160); + + playAnim('idle'); + case 'monster': + tex = Paths.getSparrowAtlas('characters/Monster_Assets'); + frames = tex; + animation.addByPrefix('idle', 'monster idle', 24, false); + 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'); + case 'monster-christmas': + tex = Paths.getSparrowAtlas('characters/monsterChristmas'); + frames = tex; + animation.addByPrefix('idle', 'monster idle', 24, false); + 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", -40, -94); + playAnim('idle'); + case 'pico': + tex = Paths.getSparrowAtlas('characters/Pico_FNF_assetss'); + frames = tex; + animation.addByPrefix('idle', "Pico Idle Dance", 24); + animation.addByPrefix('singUP', 'pico Up note0', 24, false); + animation.addByPrefix('singDOWN', 'Pico Down Note0', 24, false); + if (isPlayer) + { + animation.addByPrefix('singLEFT', 'Pico NOTE LEFT0', 24, false); + animation.addByPrefix('singRIGHT', 'Pico Note Right0', 24, false); + animation.addByPrefix('singRIGHTmiss', 'Pico Note Right Miss', 24, false); + animation.addByPrefix('singLEFTmiss', 'Pico NOTE LEFT miss', 24, false); + } + else + { + // Need to be flipped! REDO THIS LATER! + animation.addByPrefix('singLEFT', 'Pico Note Right0', 24, false); + animation.addByPrefix('singRIGHT', 'Pico NOTE LEFT0', 24, false); + animation.addByPrefix('singRIGHTmiss', 'Pico NOTE LEFT miss', 24, false); + animation.addByPrefix('singLEFTmiss', 'Pico Note Right Miss', 24, false); + } + + animation.addByPrefix('singUPmiss', 'pico Up note miss', 24); + animation.addByPrefix('singDOWNmiss', 'Pico Down Note MISS', 24); + + addOffset('idle'); + addOffset("singUP", -29, 27); + addOffset("singRIGHT", -68, -7); + addOffset("singLEFT", 65, 9); + addOffset("singDOWN", 200, -70); + addOffset("singUPmiss", -19, 67); + addOffset("singRIGHTmiss", -60, 41); + addOffset("singLEFTmiss", 62, 64); + addOffset("singDOWNmiss", 210, -28); + + playAnim('idle'); + + flipX = true; + + case 'bf': + var tex = Paths.getSparrowAtlas('characters/BOYFRIEND'); + frames = tex; + animation.addByPrefix('idle', 'BF idle dance', 24, false); + animation.addByPrefix('singUP', 'BF NOTE UP0', 24, false); + animation.addByPrefix('singLEFT', 'BF NOTE LEFT0', 24, false); + animation.addByPrefix('singRIGHT', 'BF NOTE RIGHT0', 24, false); + animation.addByPrefix('singDOWN', 'BF NOTE DOWN0', 24, false); + animation.addByPrefix('singUPmiss', 'BF NOTE UP MISS', 24, false); + animation.addByPrefix('singLEFTmiss', 'BF NOTE LEFT MISS', 24, false); + 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); + + animation.addByPrefix('scared', 'BF idle shaking', 24); + + addOffset('idle', -5); + addOffset("singUP", -29, 27); + addOffset("singRIGHT", -38, -7); + addOffset("singLEFT", 12, -6); + addOffset("singDOWN", -10, -50); + addOffset("singUPmiss", -29, 27); + addOffset("singRIGHTmiss", -30, 21); + addOffset("singLEFTmiss", 12, 24); + addOffset("singDOWNmiss", -11, -19); + addOffset("hey", 7, 4); + addOffset('firstDeath', 37, 11); + addOffset('deathLoop', 37, 5); + addOffset('deathConfirm', 37, 69); + addOffset('scared', -4); + + playAnim('idle'); + + flipX = true; + + case 'bf-christmas': + var tex = Paths.getSparrowAtlas('characters/bfChristmas'); + frames = tex; + animation.addByPrefix('idle', 'BF idle dance', 24, false); + animation.addByPrefix('singUP', 'BF NOTE UP0', 24, false); + animation.addByPrefix('singLEFT', 'BF NOTE LEFT0', 24, false); + animation.addByPrefix('singRIGHT', 'BF NOTE RIGHT0', 24, false); + animation.addByPrefix('singDOWN', 'BF NOTE DOWN0', 24, false); + animation.addByPrefix('singUPmiss', 'BF NOTE UP MISS', 24, false); + animation.addByPrefix('singLEFTmiss', 'BF NOTE LEFT MISS', 24, false); + 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); + + addOffset('idle', -5); + addOffset("singUP", -29, 27); + addOffset("singRIGHT", -38, -7); + addOffset("singLEFT", 12, -6); + addOffset("singDOWN", -10, -50); + addOffset("singUPmiss", -29, 27); + addOffset("singRIGHTmiss", -30, 21); + addOffset("singLEFTmiss", 12, 24); + addOffset("singDOWNmiss", -11, -19); + addOffset("hey", 7, 4); + + playAnim('idle'); + + flipX = true; + case 'bf-car': + var tex = Paths.getSparrowAtlas('characters/bfCar'); + frames = tex; + animation.addByPrefix('idle', 'BF idle dance', 24, false); + animation.addByPrefix('singUP', 'BF NOTE UP0', 24, false); + animation.addByPrefix('singLEFT', 'BF NOTE LEFT0', 24, false); + animation.addByPrefix('singRIGHT', 'BF NOTE RIGHT0', 24, false); + animation.addByPrefix('singDOWN', 'BF NOTE DOWN0', 24, false); + animation.addByPrefix('singUPmiss', 'BF NOTE UP MISS', 24, false); + animation.addByPrefix('singLEFTmiss', 'BF NOTE LEFT MISS', 24, false); + animation.addByPrefix('singRIGHTmiss', 'BF NOTE RIGHT MISS', 24, false); + animation.addByPrefix('singDOWNmiss', 'BF NOTE DOWN MISS', 24, false); + + addOffset('idle', -5); + addOffset("singUP", -29, 27); + addOffset("singRIGHT", -38, -7); + addOffset("singLEFT", 12, -6); + addOffset("singDOWN", -10, -50); + addOffset("singUPmiss", -29, 27); + addOffset("singRIGHTmiss", -30, 21); + addOffset("singLEFTmiss", 12, 24); + addOffset("singDOWNmiss", -11, -19); + playAnim('idle'); + + flipX = true; + case 'bf-pixel': + frames = Paths.getSparrowAtlas('characters/bfPixel'); + animation.addByPrefix('idle', 'BF IDLE', 24, false); + animation.addByPrefix('singUP', 'BF UP NOTE', 24, false); + animation.addByPrefix('singLEFT', 'BF LEFT NOTE', 24, false); + animation.addByPrefix('singRIGHT', 'BF RIGHT NOTE', 24, false); + animation.addByPrefix('singDOWN', 'BF DOWN NOTE', 24, false); + animation.addByPrefix('singUPmiss', 'BF UP MISS', 24, false); + animation.addByPrefix('singLEFTmiss', 'BF LEFT MISS', 24, false); + animation.addByPrefix('singRIGHTmiss', 'BF RIGHT MISS', 24, false); + animation.addByPrefix('singDOWNmiss', 'BF DOWN MISS', 24, false); + + addOffset('idle'); + addOffset("singUP"); + addOffset("singRIGHT"); + addOffset("singLEFT"); + addOffset("singDOWN"); + addOffset("singUPmiss"); + addOffset("singRIGHTmiss"); + addOffset("singLEFTmiss"); + addOffset("singDOWNmiss"); + + setGraphicSize(Std.int(width * 6)); + updateHitbox(); + + playAnim('idle'); + + width -= 100; + height -= 100; + + antialiasing = false; + + flipX = true; + case 'bf-pixel-dead': + frames = Paths.getSparrowAtlas('characters/bfPixelsDEAD'); + animation.addByPrefix('singUP', "BF Dies pixel", 24, false); + animation.addByPrefix('firstDeath', "BF Dies pixel", 24, false); + animation.addByPrefix('deathLoop', "Retry Loop", 24, true); + animation.addByPrefix('deathConfirm', "RETRY CONFIRM", 24, false); + animation.play('firstDeath'); + + addOffset('firstDeath'); + addOffset('deathLoop', -37); + addOffset('deathConfirm', -37); + playAnim('firstDeath'); + // pixel bullshit + setGraphicSize(Std.int(width * 6)); + updateHitbox(); + antialiasing = false; + flipX = true; + + case 'senpai': + frames = Paths.getSparrowAtlas('characters/senpai'); + animation.addByPrefix('idle', 'Senpai Idle', 24, false); + animation.addByPrefix('singUP', 'SENPAI UP NOTE', 24, false); + animation.addByPrefix('singLEFT', 'SENPAI LEFT NOTE', 24, false); + animation.addByPrefix('singRIGHT', 'SENPAI RIGHT NOTE', 24, false); + animation.addByPrefix('singDOWN', 'SENPAI DOWN NOTE', 24, false); + + addOffset('idle'); + addOffset("singUP", 5, 37); + addOffset("singRIGHT"); + addOffset("singLEFT", 40); + addOffset("singDOWN", 14); + + playAnim('idle'); + + setGraphicSize(Std.int(width * 6)); + updateHitbox(); + + antialiasing = false; + case 'senpai-angry': + frames = Paths.getSparrowAtlas('characters/senpai'); + animation.addByPrefix('idle', 'Angry Senpai Idle', 24, false); + animation.addByPrefix('singUP', 'Angry Senpai UP NOTE', 24, false); + animation.addByPrefix('singLEFT', 'Angry Senpai LEFT NOTE', 24, false); + animation.addByPrefix('singRIGHT', 'Angry Senpai RIGHT NOTE', 24, false); + animation.addByPrefix('singDOWN', 'Angry Senpai DOWN NOTE', 24, false); + + addOffset('idle'); + addOffset("singUP", 5, 37); + addOffset("singRIGHT"); + addOffset("singLEFT", 40); + addOffset("singDOWN", 14); + playAnim('idle'); + + setGraphicSize(Std.int(width * 6)); + updateHitbox(); + + antialiasing = false; + + case 'spirit': + frames = Paths.getPackerAtlas('characters/spirit'); + animation.addByPrefix('idle', "idle spirit_", 24, false); + animation.addByPrefix('singUP', "up_", 24, false); + animation.addByPrefix('singRIGHT', "right_", 24, false); + animation.addByPrefix('singLEFT', "left_", 24, false); + animation.addByPrefix('singDOWN', "spirit down_", 24, false); + + addOffset('idle', -220, -280); + addOffset('singUP', -220, -240); + addOffset("singRIGHT", -220, -280); + addOffset("singLEFT", -200, -280); + addOffset("singDOWN", 170, 110); + + setGraphicSize(Std.int(width * 6)); + updateHitbox(); + + playAnim('idle'); + + antialiasing = false; + + case 'parents-christmas': + frames = Paths.getSparrowAtlas('characters/mom_dad_christmas_assets'); + animation.addByPrefix('idle', 'Parent Christmas Idle', 24, false); + animation.addByPrefix('singUP', 'Parent Up Note Dad', 24, false); + animation.addByPrefix('singDOWN', 'Parent Down Note Dad', 24, false); + animation.addByPrefix('singLEFT', 'Parent Left Note Dad', 24, false); + animation.addByPrefix('singRIGHT', 'Parent Right Note Dad', 24, false); + + animation.addByPrefix('singUP-alt', 'Parent Up Note Mom', 24, false); + + animation.addByPrefix('singDOWN-alt', 'Parent Down Note Mom', 24, false); + animation.addByPrefix('singLEFT-alt', 'Parent Left Note Mom', 24, false); + animation.addByPrefix('singRIGHT-alt', 'Parent Right Note Mom', 24, false); + + addOffset('idle'); + addOffset("singUP", -47, 24); + addOffset("singRIGHT", -1, -23); + addOffset("singLEFT", -30, 16); + addOffset("singDOWN", -31, -29); + addOffset("singUP-alt", -47, 24); + addOffset("singRIGHT-alt", -1, -24); + addOffset("singLEFT-alt", -30, 15); + addOffset("singDOWN-alt", -30, -27); + + playAnim('idle'); + + case 'duck': + frames = Paths.getSparrowAtlas('duck/duck_assets'); + animation.addByPrefix('idle', 'duck idle', 24, false); + animation.addByPrefix('singUP', 'duck up', 24, false); + animation.addByPrefix('singDOWN', 'duck down', 24, false); + animation.addByPrefix('singLEFT', 'duck left', 24, false); + animation.addByPrefix('singRIGHT', 'duck right', 24, false); + + addOffset('idle'); + addOffset("singUP"); + addOffset('singRIGHT'); + addOffset('singLEFT'); + addOffset('singDOWN'); + + playAnim('idle'); + } + + dance(); + + if (isPlayer) + { + flipX = !flipX; + + // Doesn't flip for BF, since his are already in the right place??? + if (!curCharacter.startsWith('bf')) + { + // var animArray + var oldRight = animation.getByName('singRIGHT').frames; + animation.getByName('singRIGHT').frames = animation.getByName('singLEFT').frames; + animation.getByName('singLEFT').frames = oldRight; + + // IF THEY HAVE MISS ANIMATIONS?? + if (animation.getByName('singRIGHTmiss') != null) + { + var oldMiss = animation.getByName('singRIGHTmiss').frames; + animation.getByName('singRIGHTmiss').frames = animation.getByName('singLEFTmiss').frames; + animation.getByName('singLEFTmiss').frames = oldMiss; + } + } + } + } + + override function update(elapsed:Float) + { + if (!curCharacter.startsWith('bf')) + { + if (animation.curAnim.name.startsWith('sing')) + { + holdTimer += elapsed; + } + + var dadVar:Float = 4; + + if (curCharacter == 'dad') + dadVar = 6.1; + if (holdTimer >= Conductor.stepCrochet * dadVar * 0.001) + { + dance(); + holdTimer = 0; + } + } + + switch (curCharacter) + { + case 'gf': + if (animation.curAnim.name == 'hairFall' && animation.curAnim.finished) + playAnim('danceRight'); + } + + super.update(elapsed); + } + + private var danced:Bool = false; + + /** + * FOR GF DANCING SHIT + */ + public function dance() + { + if (!debugMode) + { + switch (curCharacter) + { + case 'gf': + if (!animation.curAnim.name.startsWith('hair')) + { + danced = !danced; + + if (danced) + playAnim('danceRight'); + else + playAnim('danceLeft'); + } + + case 'gf-christmas': + if (!animation.curAnim.name.startsWith('hair')) + { + danced = !danced; + + if (danced) + playAnim('danceRight'); + else + playAnim('danceLeft'); + } + + case 'gf-car': + if (!animation.curAnim.name.startsWith('hair')) + { + danced = !danced; + + if (danced) + playAnim('danceRight'); + else + playAnim('danceLeft'); + } + case 'gf-pixel': + if (!animation.curAnim.name.startsWith('hair')) + { + danced = !danced; + + if (danced) + playAnim('danceRight'); + else + playAnim('danceLeft'); + } + + case 'spooky': + danced = !danced; + + if (danced) + playAnim('danceRight'); + else + playAnim('danceLeft'); + default: + playAnim('idle'); + } + } + } + + public function playAnim(AnimName:String, Force:Bool = false, Reversed:Bool = false, Frame:Int = 0):Void + { + animation.play(AnimName, Force, Reversed, Frame); + + var daOffset = animOffsets.get(AnimName); + if (animOffsets.exists(AnimName)) + { + offset.set(daOffset[0], daOffset[1]); + } + else + offset.set(0, 0); + + 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) + { + animOffsets[name] = [x, y]; + } +} diff --git a/source_duck/ChartParser.hx b/source_duck/ChartParser.hx new file mode 100644 index 00000000..3884c0e5 --- /dev/null +++ b/source_duck/ChartParser.hx @@ -0,0 +1,80 @@ +package; + +import flixel.util.FlxStringUtil; + +using StringTools; + +class ChartParser +{ + static public function parse(songName:String, section:Int):Array + { + var IMG_WIDTH:Int = 8; + var regex:EReg = new EReg("[ \t]*((\r\n)|\r|\n)[ \t]*", "g"); + + var csvData = FlxStringUtil.imageToCSV(Paths.file('data/' + songName + '/' + songName + '_section' + section + '.png')); + + var lines:Array = regex.split(csvData); + var rows:Array = lines.filter(function(line) return line != ""); + csvData.replace("\n", ','); + + var heightInTiles = rows.length; + var widthInTiles = 0; + + var row:Int = 0; + + // LMAOOOO STOLE ALL THIS FROM FLXBASETILEMAP LOLOL + + var dopeArray:Array = []; + while (row < heightInTiles) + { + var rowString = rows[row]; + if (rowString.endsWith(",")) + rowString = rowString.substr(0, rowString.length - 1); + var columns = rowString.split(","); + + if (columns.length == 0) + { + heightInTiles--; + continue; + } + if (widthInTiles == 0) + { + widthInTiles = columns.length; + } + + var column = 0; + var pushedInColumn:Bool = false; + while (column < widthInTiles) + { + // the current tile to be added: + var columnString = columns[column]; + var curTile = Std.parseInt(columnString); + + if (curTile == null) + throw 'String in row $row, column $column is not a valid integer: "$columnString"'; + + if (curTile == 1) + { + if (column < 4) + dopeArray.push(column + 1); + else + { + var tempCol = (column + 1) * -1; + tempCol += 4; + dopeArray.push(tempCol); + } + + pushedInColumn = true; + } + + column++; + } + + if (!pushedInColumn) + dopeArray.push(0); + + row++; + } + return dopeArray; + } +} diff --git a/source_duck/ChartingState.hx b/source_duck/ChartingState.hx new file mode 100644 index 00000000..93c5cfa1 --- /dev/null +++ b/source_duck/ChartingState.hx @@ -0,0 +1,1351 @@ +package; + +import flixel.addons.ui.FlxUIText; +import haxe.zip.Writer; +import Conductor.BPMChangeEvent; +import Section.SwagSection; +import Song.SwagSong; +import flixel.FlxG; +import flixel.FlxSprite; +import flixel.addons.display.FlxGridOverlay; +import flixel.addons.ui.FlxInputText; +import flixel.addons.ui.FlxUI9SliceSprite; +import flixel.addons.ui.FlxUI; +import flixel.addons.ui.FlxUICheckBox; +import flixel.addons.ui.FlxUIDropDownMenu; +import flixel.addons.ui.FlxUIInputText; +import flixel.addons.ui.FlxUINumericStepper; +import flixel.addons.ui.FlxUITabMenu; +import flixel.addons.ui.FlxUITooltip.FlxUITooltipStyle; +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; +import flixel.util.FlxColor; +import haxe.Json; +import lime.utils.Assets; +import openfl.events.Event; +import openfl.events.IOErrorEvent; +import openfl.events.IOErrorEvent; +import openfl.events.IOErrorEvent; +import openfl.media.Sound; +import openfl.net.FileReference; +import openfl.utils.ByteArray; + +using StringTools; + +class ChartingState extends MusicBeatState +{ + var _file:FileReference; + + var UI_box:FlxUITabMenu; + + /** + * Array of notes showing when each section STARTS in STEPS + * Usually rounded up?? + */ + var curSection:Int = 0; + + public static var lastSection:Int = 0; + + var bpmTxt:FlxText; + + var strumLine:FlxSprite; + var curSong:String = 'Dadbattle'; + var amountSteps:Int = 0; + var bullshitUI:FlxGroup; + var writingNotesText:FlxText; + var highlight:FlxSprite; + + var GRID_SIZE:Int = 40; + + var dummyArrow:FlxSprite; + + var curRenderedNotes:FlxTypedGroup; + var curRenderedSustains:FlxTypedGroup; + + var gridBG:FlxSprite; + + var _song:SwagSong; + + var typingShit:FlxInputText; + /* + * WILL BE THE CURRENT / LAST PLACED NOTE + **/ + var curSelectedNote:Array; + + var tempBpm:Int = 0; + var gridBlackLine:FlxSprite; + var vocals:FlxSound; + + var player2:Character = new Character(0,0, "dad"); + var player1:Boyfriend = new Boyfriend(0,0, "bf"); + + var leftIcon:HealthIcon; + var rightIcon:HealthIcon; + + private var lastNote:Note; + + override function create() + { + curSection = lastSection; + + if (PlayState.SONG != null) + _song = PlayState.SONG; + else + { + _song = { + song: 'Test', + notes: [], + bpm: 150, + needsVoices: true, + player1: 'bf', + player2: 'dad', + gfVersion: 'gf', + noteStyle: 'normal', + stage: 'stage', + speed: 1, + validScore: false + }; + } + + gridBG = FlxGridOverlay.create(GRID_SIZE, GRID_SIZE, GRID_SIZE * 8, GRID_SIZE * 16); + add(gridBG); + + gridBlackLine = new FlxSprite(gridBG.x + gridBG.width / 2).makeGraphic(2, Std.int(gridBG.height), FlxColor.BLACK); + add(gridBlackLine); + + curRenderedNotes = new FlxTypedGroup(); + curRenderedSustains = new FlxTypedGroup(); + + FlxG.mouse.visible = true; + FlxG.save.bind('funkin', 'ninjamuffin99'); + + tempBpm = _song.bpm; + + addSection(); + + // sections = _song.notes; + + updateGrid(); + + loadSong(_song.song); + Conductor.changeBPM(_song.bpm); + Conductor.mapBPMChanges(_song); + + leftIcon = new HealthIcon(_song.player1); + rightIcon = new HealthIcon(_song.player2); + leftIcon.scrollFactor.set(1, 1); + rightIcon.scrollFactor.set(1, 1); + + leftIcon.setGraphicSize(0, 45); + rightIcon.setGraphicSize(0, 45); + + add(leftIcon); + add(rightIcon); + + leftIcon.setPosition(0, -100); + rightIcon.setPosition(gridBG.width / 2, -100); + + bpmTxt = new FlxText(1000, 50, 0, "", 16); + bpmTxt.scrollFactor.set(); + add(bpmTxt); + + strumLine = new FlxSprite(0, 50).makeGraphic(Std.int(FlxG.width / 2), 4); + add(strumLine); + + dummyArrow = new FlxSprite().makeGraphic(GRID_SIZE, GRID_SIZE); + add(dummyArrow); + + var tabs = [ + {name: "Song", label: 'Song Data'}, + {name: "Section", label: 'Section Data'}, + {name: "Note", label: 'Note Data'}, + {name: "Assets", label: 'Assets'} + ]; + + UI_box = new FlxUITabMenu(null, tabs, true); + + UI_box.resize(300, 400); + UI_box.x = FlxG.width / 2; + UI_box.y = 20; + add(UI_box); + + addSongUI(); + addSectionUI(); + addNoteUI(); + + add(curRenderedNotes); + add(curRenderedSustains); + + super.create(); + } + + function addSongUI():Void + { + var UI_songTitle = new FlxUIInputText(10, 10, 70, _song.song, 8); + typingShit = UI_songTitle; + + var check_voices = new FlxUICheckBox(10, 25, null, null, "Has voice track", 100); + check_voices.checked = _song.needsVoices; + // _song.needsVoices = check_voices.checked; + check_voices.callback = function() + { + _song.needsVoices = check_voices.checked; + trace('CHECKED!'); + }; + + var check_mute_inst = new FlxUICheckBox(10, 200, null, null, "Mute Instrumental (in editor)", 100); + check_mute_inst.checked = false; + check_mute_inst.callback = function() + { + var vol:Float = 1; + + if (check_mute_inst.checked) + vol = 0; + + FlxG.sound.music.volume = vol; + }; + + var saveButton:FlxButton = new FlxButton(110, 8, "Save", function() + { + saveLevel(); + }); + + var reloadSong:FlxButton = new FlxButton(saveButton.x + saveButton.width + 10, saveButton.y, "Reload Audio", function() + { + loadSong(_song.song); + }); + + var reloadSongJson:FlxButton = new FlxButton(reloadSong.x, saveButton.y + 30, "Reload JSON", function() + { + loadJson(_song.song.toLowerCase()); + }); + + + var restart = new FlxButton(10,140,"Reset Chart", function() + { + for (ii in 0..._song.notes.length) + { + for (i in 0..._song.notes[ii].sectionNotes.length) + { + _song.notes[ii].sectionNotes = []; + } + } + resetSection(true); + }); + + var loadAutosaveBtn:FlxButton = new FlxButton(reloadSongJson.x, reloadSongJson.y + 30, 'load autosave', loadAutosave); + + var stepperSpeed:FlxUINumericStepper = new FlxUINumericStepper(10, 80, 0.1, 1, 0.1, 10, 1); + stepperSpeed.value = _song.speed; + stepperSpeed.name = 'song_speed'; + + var stepperSpeedLabel = new FlxText(74,80,'Scroll Speed'); + + var stepperBPM:FlxUINumericStepper = new FlxUINumericStepper(10, 65, 0.1, 1, 1.0, 5000.0, 1); + stepperBPM.value = Conductor.bpm; + stepperBPM.name = 'song_bpm'; + + var stepperBPMLabel = new FlxText(74,65,'BPM'); + + var characters:Array = CoolUtil.coolTextFile(Paths.txt('characterList')); + var gfVersions:Array = CoolUtil.coolTextFile(Paths.txt('gfVersionList')); + var stages:Array = CoolUtil.coolTextFile(Paths.txt('stageList')); + var noteStyles:Array = CoolUtil.coolTextFile(Paths.txt('noteStyleList')); + + var player1DropDown = new FlxUIDropDownMenu(10, 100, FlxUIDropDownMenu.makeStrIdLabelArray(characters, true), function(character:String) + { + _song.player1 = characters[Std.parseInt(character)]; + }); + player1DropDown.selectedLabel = _song.player1; + + var player1Label = new FlxText(10,80,64,'Player 1'); + + var player2DropDown = new FlxUIDropDownMenu(140, 100, FlxUIDropDownMenu.makeStrIdLabelArray(characters, true), function(character:String) + { + _song.player2 = characters[Std.parseInt(character)]; + }); + player2DropDown.selectedLabel = _song.player2; + + var player2Label = new FlxText(140,80,64,'Player 2'); + + var gfVersionDropDown = new FlxUIDropDownMenu(10, 200, FlxUIDropDownMenu.makeStrIdLabelArray(gfVersions, true), function(gfVersion:String) + { + _song.gfVersion = gfVersions[Std.parseInt(gfVersion)]; + }); + gfVersionDropDown.selectedLabel = _song.gfVersion; + + var gfVersionLabel = new FlxText(10,180,64,'Girlfriend'); + + var stageDropDown = new FlxUIDropDownMenu(140, 200, FlxUIDropDownMenu.makeStrIdLabelArray(stages, true), function(stage:String) + { + _song.stage = stages[Std.parseInt(stage)]; + }); + stageDropDown.selectedLabel = _song.stage; + + var stageLabel = new FlxText(140,180,64,'Stage'); + + var noteStyleDropDown = new FlxUIDropDownMenu(10, 300, FlxUIDropDownMenu.makeStrIdLabelArray(noteStyles, true), function(noteStyle:String) + { + _song.noteStyle = noteStyles[Std.parseInt(noteStyle)]; + }); + noteStyleDropDown.selectedLabel = _song.noteStyle; + + var noteStyleLabel = new FlxText(10,280,64,'Note Skin'); + + var tab_group_song = new FlxUI(null, UI_box); + tab_group_song.name = "Song"; + tab_group_song.add(UI_songTitle); + tab_group_song.add(restart); + tab_group_song.add(check_voices); + tab_group_song.add(check_mute_inst); + tab_group_song.add(saveButton); + tab_group_song.add(reloadSong); + tab_group_song.add(reloadSongJson); + tab_group_song.add(loadAutosaveBtn); + tab_group_song.add(stepperBPM); + tab_group_song.add(stepperBPMLabel); + tab_group_song.add(stepperSpeed); + tab_group_song.add(stepperSpeedLabel); + + var tab_group_assets = new FlxUI(null, UI_box); + tab_group_assets.name = "Assets"; + tab_group_assets.add(noteStyleDropDown); + tab_group_assets.add(noteStyleLabel); + tab_group_assets.add(gfVersionDropDown); + tab_group_assets.add(gfVersionLabel); + tab_group_assets.add(stageDropDown); + tab_group_assets.add(stageLabel); + tab_group_assets.add(player1DropDown); + tab_group_assets.add(player2DropDown); + tab_group_assets.add(player1Label); + tab_group_assets.add(player2Label); + + UI_box.addGroup(tab_group_song); + UI_box.addGroup(tab_group_assets); + UI_box.scrollFactor.set(); + + FlxG.camera.follow(strumLine); + } + + var stepperLength:FlxUINumericStepper; + var check_mustHitSection:FlxUICheckBox; + var check_changeBPM:FlxUICheckBox; + var stepperSectionBPM:FlxUINumericStepper; + var check_altAnim:FlxUICheckBox; + + function addSectionUI():Void + { + var tab_group_section = new FlxUI(null, UI_box); + tab_group_section.name = 'Section'; + + stepperLength = new FlxUINumericStepper(10, 10, 4, 0, 0, 999, 0); + stepperLength.value = _song.notes[curSection].lengthInSteps; + stepperLength.name = "section_length"; + + var stepperLengthLabel = new FlxText(74,10,'Section Length (in steps)'); + + 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, 132, 1, 1, -999, 999, 0); + var stepperCopyLabel = new FlxText(174,132,'sections back'); + + var copyButton:FlxButton = new FlxButton(10, 130, "Copy last section", function() + { + copySection(Std.int(stepperCopy.value)); + }); + + var clearSectionButton:FlxButton = new FlxButton(10, 150, "Clear Section", clearSection); + + var swapSection:FlxButton = new FlxButton(10, 170, "Swap Section", function() + { + for (i in 0..._song.notes[curSection].sectionNotes.length) + { + var note = _song.notes[curSection].sectionNotes[i]; + note[1] = (note[1] + 4) % 8; + _song.notes[curSection].sectionNotes[i] = note; + updateGrid(); + } + }); + check_mustHitSection = new FlxUICheckBox(10, 30, null, null, "Camera Points to P1?", 100); + check_mustHitSection.name = 'check_mustHit'; + check_mustHitSection.checked = true; + // _song.needsVoices = check_mustHit.checked; + + check_altAnim = new FlxUICheckBox(10, 400, null, null, "Alternate Animation", 100); + check_altAnim.name = 'check_altAnim'; + + 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(stepperLengthLabel); + tab_group_section.add(stepperSectionBPM); + tab_group_section.add(stepperCopy); + tab_group_section.add(stepperCopyLabel); + tab_group_section.add(check_mustHitSection); + tab_group_section.add(check_altAnim); + tab_group_section.add(check_changeBPM); + tab_group_section.add(copyButton); + tab_group_section.add(clearSectionButton); + tab_group_section.add(swapSection); + + UI_box.addGroup(tab_group_section); + } + + var stepperSusLength:FlxUINumericStepper; + + var tab_group_note:FlxUI; + + function addNoteUI():Void + { + tab_group_note = new FlxUI(null, UI_box); + tab_group_note.name = 'Note'; + + writingNotesText = new FlxUIText(20,100, 0, ""); + writingNotesText.setFormat("Arial",20,FlxColor.WHITE,FlxTextAlign.LEFT,FlxTextBorderStyle.OUTLINE,FlxColor.BLACK); + + stepperSusLength = new FlxUINumericStepper(10, 10, Conductor.stepCrochet / 2, 0, 0, Conductor.stepCrochet * _song.notes[curSection].lengthInSteps * 4); + stepperSusLength.value = 0; + stepperSusLength.name = 'note_susLength'; + + var stepperSusLengthLabel = new FlxText(74,10,'Note Sustain Length'); + + var applyLength:FlxButton = new FlxButton(10, 100, 'Apply Data'); + + tab_group_note.add(writingNotesText); + tab_group_note.add(stepperSusLength); + tab_group_note.add(stepperSusLengthLabel); + tab_group_note.add(applyLength); + + UI_box.addGroup(tab_group_note); + + /*player2 = new Character(0,gridBG.y, _song.player2); + player1 = new Boyfriend(player2.width * 0.2,gridBG.y + player2.height, _song.player1); + + player1.y = player1.y - player1.height; + + player2.setGraphicSize(Std.int(player2.width * 0.2)); + player1.setGraphicSize(Std.int(player1.width * 0.2)); + + UI_box.add(player1); + UI_box.add(player2);*/ + + } + + function loadSong(daSong:String):Void + { + if (FlxG.sound.music != null) + { + FlxG.sound.music.stop(); + // vocals.stop(); + } + + FlxG.sound.playMusic(Paths.inst(daSong), 0.6); + + // WONT WORK FOR TUTORIAL OR TEST SONG!!! REDO LATER + vocals = new FlxSound().loadEmbedded(Paths.voices(daSong)); + FlxG.sound.list.add(vocals); + + 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; + changeSection(); + }; + } + + function generateUI():Void + { + while (bullshitUI.members.length > 0) + { + bullshitUI.remove(bullshitUI.members[0], true); + } + + // general shit + var title:FlxText = new FlxText(UI_box.x + 20, UI_box.y + 20, 0); + bullshitUI.add(title); + /* + var loopCheck = new FlxUICheckBox(UI_box.x + 10, UI_box.y + 50, null, null, "Loops", 100, ['loop check']); + loopCheck.checked = curNoteSelected.doesLoop; + tooltips.add(loopCheck, {title: 'Section looping', body: "Whether or not it's a simon says style section", style: tooltipType}); + bullshitUI.add(loopCheck); + + */ + } + + override function getEvent(id:String, sender:Dynamic, data:Dynamic, ?params:Array) + { + if (id == FlxUICheckBox.CLICK_EVENT) + { + var check:FlxUICheckBox = cast sender; + var label = check.getLabel().text; + switch (label) + { + 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'); + case "Alt Animation": + _song.notes[curSection].altAnim = check.checked; + } + } + else if (id == FlxUINumericStepper.CHANGE_EVENT && (sender is FlxUINumericStepper)) + { + var nums:FlxUINumericStepper = cast sender; + var wname = nums.name; + FlxG.log.add(wname); + if (wname == 'section_length') + { + if (nums.value <= 4) + nums.value = 4; + _song.notes[curSection].lengthInSteps = Std.int(nums.value); + updateGrid(); + } + else if (wname == 'song_speed') + { + if (nums.value <= 0) + nums.value = 0; + _song.speed = nums.value; + } + else if (wname == 'song_bpm') + { + if (nums.value <= 0) + nums.value = 1; + tempBpm = Std.int(nums.value); + Conductor.mapBPMChanges(_song); + Conductor.changeBPM(Std.int(nums.value)); + } + else if (wname == 'note_susLength') + { + if (curSelectedNote == null) + return; + + if (nums.value <= 0) + nums.value = 0; + curSelectedNote[2] = nums.value; + updateGrid(); + } + else if (wname == 'section_bpm') + { + if (nums.value <= 0.1) + nums.value = 0.1; + _song.notes[curSection].bpm = Std.int(nums.value); + updateGrid(); + } + } + + // FlxG.log.add(id + " WEED " + sender + " WEED " + data + " WEED " + params); + } + + var updatedSection:Bool = false; + + /* this function got owned LOL + function lengthBpmBullshit():Float + { + if (_song.notes[curSection].changeBPM) + return _song.notes[curSection].lengthInSteps * (_song.notes[curSection].bpm / _song.bpm); + else + return _song.notes[curSection].lengthInSteps; + }*/ + function sectionStartTime():Float + { + var daBPM:Int = _song.bpm; + var daPos:Float = 0; + for (i in 0...curSection) + { + if (_song.notes[i].changeBPM) + { + daBPM = _song.notes[i].bpm; + } + daPos += 4 * (1000 * 60 / daBPM); + } + return daPos; + } + + var writingNotes:Bool = false; + + override function update(elapsed:Float) + { + updateHeads(); + + curStep = recalculateSteps(); + + if (FlxG.keys.justPressed.ALT && UI_box.selected_tab == 0) + { + writingNotes = !writingNotes; + } + + if (writingNotes) + writingNotesText.text = "WRITING NOTES"; + else + writingNotesText.text = ""; + + Conductor.songPosition = FlxG.sound.music.time; + _song.song = typingShit.text; + + var upP = controls.UP_P; + var rightP = controls.RIGHT_P; + var downP = controls.DOWN_P; + var leftP = controls.LEFT_P; + + var controlArray:Array = [leftP, downP, upP, rightP]; + + if ((upP || rightP || downP || leftP) && writingNotes) + { + for(i in 0...controlArray.length) + { + if (controlArray[i]) + { + for (n in 0..._song.notes[curSection].sectionNotes.length) + { + var note = _song.notes[curSection].sectionNotes[n]; + if (note == null) + continue; + if (note[0] == Conductor.songPosition && note[1] % 4 == i) + { + trace('GAMING'); + _song.notes[curSection].sectionNotes.remove(note); + } + } + trace('adding note'); + _song.notes[curSection].sectionNotes.push([Conductor.songPosition, i, 0]); + updateGrid(); + } + } + + } + + strumLine.y = getYfromStrum((Conductor.songPosition - sectionStartTime()) % (Conductor.stepCrochet * _song.notes[curSection].lengthInSteps)); + + + /*curRenderedNotes.forEach(function(note:Note) { + if (strumLine.overlaps(note) && strumLine.y == note.y) // yandere dev type shit + { + if (_song.notes[curSection].mustHitSection) + { + trace('must hit ' + Math.abs(note.noteData)); + if (note.noteData < 4) + { + switch (Math.abs(note.noteData)) + { + case 2: + player1.playAnim('singUP', true); + case 3: + player1.playAnim('singRIGHT', true); + case 1: + player1.playAnim('singDOWN', true); + case 0: + player1.playAnim('singLEFT', true); + } + } + if (note.noteData >= 4) + { + switch (note.noteData) + { + case 6: + player2.playAnim('singUP', true); + case 7: + player2.playAnim('singRIGHT', true); + case 5: + player2.playAnim('singDOWN', true); + case 4: + player2.playAnim('singLEFT', true); + } + } + } + else + { + trace('hit ' + Math.abs(note.noteData)); + if (note.noteData < 4) + { + switch (Math.abs(note.noteData)) + { + case 2: + player2.playAnim('singUP', true); + case 3: + player2.playAnim('singRIGHT', true); + case 1: + player2.playAnim('singDOWN', true); + case 0: + player2.playAnim('singLEFT', true); + } + } + if (note.noteData >= 4) + { + switch (note.noteData) + { + case 6: + player1.playAnim('singUP', true); + case 7: + player1.playAnim('singRIGHT', true); + case 5: + player1.playAnim('singDOWN', true); + case 4: + player1.playAnim('singLEFT', true); + } + } + } + } + });*/ + + if (curBeat % 4 == 0 && curStep >= 16 * (curSection + 1)) + { + trace(curStep); + trace((_song.notes[curSection].lengthInSteps) * (curSection + 1)); + trace('DUMBSHIT'); + + if (_song.notes[curSection + 1] == null) + { + addSection(); + } + + changeSection(curSection + 1, false); + } + + FlxG.watch.addQuick('daBeat', curBeat); + FlxG.watch.addQuick('daStep', curStep); + + if (FlxG.mouse.justPressed) + { + if (FlxG.mouse.overlaps(curRenderedNotes)) + { + curRenderedNotes.forEach(function(note:Note) + { + if (FlxG.mouse.overlaps(note)) + { + if (FlxG.keys.pressed.CONTROL) + { + selectNote(note); + } + else + { + deleteNote(note); + } + } + }); + } + else + { + if (FlxG.mouse.x > gridBG.x + && FlxG.mouse.x < gridBG.x + gridBG.width + && FlxG.mouse.y > gridBG.y + && FlxG.mouse.y < gridBG.y + (GRID_SIZE * _song.notes[curSection].lengthInSteps)) + { + FlxG.log.add('added note'); + addNote(); + } + } + } + + if (FlxG.mouse.x > gridBG.x + && FlxG.mouse.x < gridBG.x + gridBG.width + && FlxG.mouse.y > gridBG.y + && FlxG.mouse.y < gridBG.y + (GRID_SIZE * _song.notes[curSection].lengthInSteps)) + { + dummyArrow.x = Math.floor(FlxG.mouse.x / GRID_SIZE) * GRID_SIZE; + if (FlxG.keys.pressed.SHIFT) + dummyArrow.y = FlxG.mouse.y; + else + dummyArrow.y = Math.floor(FlxG.mouse.y / GRID_SIZE) * GRID_SIZE; + } + + if (FlxG.keys.justPressed.ENTER) + { + lastSection = curSection; + + PlayState.SONG = _song; + FlxG.sound.music.stop(); + vocals.stop(); + LoadingState.loadAndSwitchState(new PlayState()); + } + + if (FlxG.keys.justPressed.E) + { + changeNoteSustain(Conductor.stepCrochet); + } + if (FlxG.keys.justPressed.Q) + { + changeNoteSustain(-Conductor.stepCrochet); + } + + if (FlxG.keys.justPressed.TAB) + { + if (FlxG.keys.pressed.SHIFT) + { + UI_box.selected_tab -= 1; + if (UI_box.selected_tab < 0) + UI_box.selected_tab = 2; + } + else + { + UI_box.selected_tab += 1; + if (UI_box.selected_tab >= 3) + UI_box.selected_tab = 0; + } + } + + if (!typingShit.hasFocus) + { + + if (FlxG.keys.pressed.CONTROL) + { + if (FlxG.keys.justPressed.Z && lastNote != null) + { + trace(curRenderedNotes.members.contains(lastNote) ? "delete note" : "add note"); + if (curRenderedNotes.members.contains(lastNote)) + deleteNote(lastNote); + else + addNote(lastNote); + } + } + + var shiftThing:Int = 1; + if (FlxG.keys.pressed.SHIFT) + shiftThing = 4; + if (!writingNotes) + { + if (FlxG.keys.justPressed.RIGHT || FlxG.keys.justPressed.D) + changeSection(curSection + shiftThing); + if (FlxG.keys.justPressed.LEFT || FlxG.keys.justPressed.A) + changeSection(curSection - shiftThing); + } + if (FlxG.keys.justPressed.SPACE) + { + if (FlxG.sound.music.playing) + { + FlxG.sound.music.pause(); + vocals.pause(); + } + else + { + vocals.play(); + FlxG.sound.music.play(); + } + } + + if (FlxG.keys.justPressed.R) + { + if (FlxG.keys.pressed.SHIFT) + resetSection(true); + else + resetSection(); + } + + if (FlxG.mouse.wheel != 0) + { + FlxG.sound.music.pause(); + vocals.pause(); + + FlxG.sound.music.time -= (FlxG.mouse.wheel * Conductor.stepCrochet * 0.4); + vocals.time = FlxG.sound.music.time; + } + + if (!FlxG.keys.pressed.SHIFT) + { + if (FlxG.keys.pressed.W || FlxG.keys.pressed.S) + { + FlxG.sound.music.pause(); + vocals.pause(); + + var daTime:Float = 700 * FlxG.elapsed; + + if (FlxG.keys.pressed.W) + { + FlxG.sound.music.time -= daTime; + } + else + FlxG.sound.music.time += daTime; + + vocals.time = FlxG.sound.music.time; + } + } + else + { + if (FlxG.keys.justPressed.W || FlxG.keys.justPressed.S) + { + FlxG.sound.music.pause(); + vocals.pause(); + + var daTime:Float = Conductor.stepCrochet * 2; + + if (FlxG.keys.justPressed.W) + { + FlxG.sound.music.time -= daTime; + } + else + FlxG.sound.music.time += daTime; + + vocals.time = FlxG.sound.music.time; + } + } + } + + _song.bpm = tempBpm; + + /* if (FlxG.keys.justPressed.UP) + Conductor.changeBPM(Conductor.bpm + 1); + if (FlxG.keys.justPressed.DOWN) + Conductor.changeBPM(Conductor.bpm - 1); */ + + bpmTxt.text = bpmTxt.text = Std.string(FlxMath.roundDecimal(Conductor.songPosition / 1000, 2)) + + " / " + + Std.string(FlxMath.roundDecimal(FlxG.sound.music.length / 1000, 2)) + + "\nSection: " + + curSection + + "\nCurStep: " + + curStep; + super.update(elapsed); + } + + function changeNoteSustain(value:Float):Void + { + if (curSelectedNote != null) + { + if (curSelectedNote[2] != null) + { + curSelectedNote[2] += value; + curSelectedNote[2] = Math.max(curSelectedNote[2], 0); + } + } + + updateNoteUI(); + updateGrid(); + } + + override function beatHit() + { + trace('beat'); + + super.beatHit(); + if (!player2.animation.curAnim.name.startsWith("sing")) + { + player2.playAnim('idle'); + } + player1.dance(); + } + + function recalculateSteps():Int + { + var lastChange:BPMChangeEvent = { + stepTime: 0, + songTime: 0, + bpm: 0 + } + for (i in 0...Conductor.bpmChangeMap.length) + { + if (FlxG.sound.music.time > Conductor.bpmChangeMap[i].songTime) + lastChange = Conductor.bpmChangeMap[i]; + } + + curStep = lastChange.stepTime + Math.floor((FlxG.sound.music.time - lastChange.songTime) / Conductor.stepCrochet); + updateBeat(); + + return curStep; + } + + function resetSection(songBeginning:Bool = false):Void + { + updateGrid(); + + FlxG.sound.music.pause(); + vocals.pause(); + + // Basically old shit from changeSection??? + FlxG.sound.music.time = sectionStartTime(); + + if (songBeginning) + { + FlxG.sound.music.time = 0; + curSection = 0; + } + + vocals.time = FlxG.sound.music.time; + updateCurStep(); + + updateGrid(); + updateSectionUI(); + } + + function changeSection(sec:Int = 0, ?updateMusic:Bool = true):Void + { + trace('changing section' + sec); + + if (_song.notes[sec] != null) + { + trace('naw im not null'); + curSection = sec; + + updateGrid(); + + if (updateMusic) + { + FlxG.sound.music.pause(); + vocals.pause(); + + /*var daNum:Int = 0; + var daLength:Float = 0; + while (daNum <= sec) + { + daLength += lengthBpmBullshit(); + daNum++; + }*/ + + FlxG.sound.music.time = sectionStartTime(); + vocals.time = FlxG.sound.music.time; + updateCurStep(); + } + + updateGrid(); + updateSectionUI(); + } + else + trace('bro wtf I AM NULL'); + } + + function copySection(?sectionNum:Int = 1) + { + var daSec = FlxMath.maxInt(curSection, sectionNum); + + for (note in _song.notes[daSec - sectionNum].sectionNotes) + { + var strum = note[0] + Conductor.stepCrochet * (_song.notes[daSec].lengthInSteps * sectionNum); + + var copiedNote:Array = [strum, note[1], note[2]]; + _song.notes[daSec].sectionNotes.push(copiedNote); + } + + updateGrid(); + } + + function updateSectionUI():Void + { + var sec = _song.notes[curSection]; + + stepperLength.value = sec.lengthInSteps; + check_mustHitSection.checked = sec.mustHitSection; + check_altAnim.checked = sec.altAnim; + check_changeBPM.checked = sec.changeBPM; + stepperSectionBPM.value = sec.bpm; + } + + function updateHeads():Void + { + if (check_mustHitSection.checked) + { + leftIcon.animation.play(_song.player1); + rightIcon.animation.play(_song.player2); + } + else + { + leftIcon.animation.play(_song.player2); + rightIcon.animation.play(_song.player1); + } + } + + function updateNoteUI():Void + { + if (curSelectedNote != null) + stepperSusLength.value = curSelectedNote[2]; + } + + function updateGrid():Void + { + remove(gridBG); + gridBG = FlxGridOverlay.create(GRID_SIZE, GRID_SIZE, GRID_SIZE * 8, GRID_SIZE * _song.notes[curSection].lengthInSteps); + add(gridBG); + + remove(gridBlackLine); + gridBlackLine = new FlxSprite(gridBG.x + gridBG.width / 2).makeGraphic(2, Std.int(gridBG.height), FlxColor.BLACK); + add(gridBlackLine); + + while (curRenderedNotes.members.length > 0) + { + curRenderedNotes.remove(curRenderedNotes.members[0], true); + } + + while (curRenderedSustains.members.length > 0) + { + curRenderedSustains.remove(curRenderedSustains.members[0], true); + } + + var sectionInfo:Array = _song.notes[curSection].sectionNotes; + + if (_song.notes[curSection].changeBPM && _song.notes[curSection].bpm > 0) + { + Conductor.changeBPM(_song.notes[curSection].bpm); + FlxG.log.add('CHANGED BPM!'); + } + else + { + // get last bpm + var daBPM:Int = _song.bpm; + for (i in 0...curSection) + if (_song.notes[i].changeBPM) + daBPM = _song.notes[i].bpm; + Conductor.changeBPM(daBPM); + } + + /* // PORT BULLSHIT, INCASE THERE'S NO SUSTAIN DATA FOR A NOTE + for (sec in 0..._song.notes.length) + { + for (notesse in 0..._song.notes[sec].sectionNotes.length) + { + if (_song.notes[sec].sectionNotes[notesse][2] == null) + { + trace('SUS NULL'); + _song.notes[sec].sectionNotes[notesse][2] = 0; + } + } + } + */ + + for (i in sectionInfo) + { + var daNoteInfo = i[1]; + var daStrumTime = i[0]; + var daSus = i[2]; + + var note:Note = new Note(daStrumTime, daNoteInfo % 4); + note.sustainLength = daSus; + note.setGraphicSize(GRID_SIZE, GRID_SIZE); + note.updateHitbox(); + note.x = Math.floor(daNoteInfo * GRID_SIZE); + note.y = Math.floor(getYfromStrum((daStrumTime - sectionStartTime()) % (Conductor.stepCrochet * _song.notes[curSection].lengthInSteps))); + + if (curSelectedNote != null) + if (curSelectedNote[0] == note.strumTime) + lastNote = note; + + curRenderedNotes.add(note); + + if (daSus > 0) + { + var sustainVis:FlxSprite = new FlxSprite(note.x + (GRID_SIZE / 2), + note.y + GRID_SIZE).makeGraphic(8, Math.floor(FlxMath.remapToRange(daSus, 0, Conductor.stepCrochet * _song.notes[curSection].lengthInSteps, 0, gridBG.height))); + curRenderedSustains.add(sustainVis); + } + } + } + + private function addSection(lengthInSteps:Int = 16):Void + { + var sec:SwagSection = { + lengthInSteps: lengthInSteps, + bpm: _song.bpm, + changeBPM: false, + mustHitSection: true, + sectionNotes: [], + typeOfSection: 0, + altAnim: false + }; + + _song.notes.push(sec); + } + + function selectNote(note:Note):Void + { + var swagNum:Int = 0; + + for (i in _song.notes[curSection].sectionNotes) + { + if (i.strumTime == note.strumTime && i.noteData % 4 == note.noteData) + { + curSelectedNote = _song.notes[curSection].sectionNotes[swagNum]; + } + + swagNum += 1; + } + + updateGrid(); + updateNoteUI(); + } + + + function deleteNote(note:Note):Void + { + lastNote = note; + for (i in _song.notes[curSection].sectionNotes) + { + if (i[0] == note.strumTime && i[1] % 4 == note.noteData) + { + _song.notes[curSection].sectionNotes.remove(i); + } + } + + updateGrid(); + } + + function clearSection():Void + { + _song.notes[curSection].sectionNotes = []; + + updateGrid(); + } + + function clearSong():Void + { + for (daSection in 0..._song.notes.length) + { + _song.notes[daSection].sectionNotes = []; + } + + updateGrid(); + } + + private function addNote(?n:Note):Void + { + var noteStrum = getStrumTime(dummyArrow.y) + sectionStartTime(); + var noteData = Math.floor(FlxG.mouse.x / GRID_SIZE); + var noteSus = 0; + + if (n != null) + _song.notes[curSection].sectionNotes.push([n.strumTime, n.noteData, n.sustainLength]); + else + _song.notes[curSection].sectionNotes.push([noteStrum, noteData, noteSus]); + + var thingy = _song.notes[curSection].sectionNotes[_song.notes[curSection].sectionNotes.length - 1]; + + curSelectedNote = thingy; + + updateGrid(); + updateNoteUI(); + + autosaveSong(); + } + + function getStrumTime(yPos:Float):Float + { + return FlxMath.remapToRange(yPos, gridBG.y, gridBG.y + gridBG.height, 0, 16 * Conductor.stepCrochet); + } + + function getYfromStrum(strumTime:Float):Float + { + return FlxMath.remapToRange(strumTime, 0, 16 * Conductor.stepCrochet, gridBG.y, gridBG.y + gridBG.height); + } + + /* + function calculateSectionLengths(?sec:SwagSection):Int + { + var daLength:Int = 0; + + for (i in _song.notes) + { + var swagLength = i.lengthInSteps; + + if (i.typeOfSection == Section.COPYCAT) + swagLength * 2; + + daLength += swagLength; + + if (sec != null && sec == i) + { + trace('swag loop??'); + break; + } + } + + return daLength; + }*/ + private var daSpacing:Float = 0.3; + + function loadLevel():Void + { + trace(_song.notes); + } + + function getNotes():Array + { + var noteData:Array = []; + + for (i in _song.notes) + { + noteData.push(i.sectionNotes); + } + + return noteData; + } + + function loadJson(song:String):Void + { + PlayState.SONG = Song.loadFromJson(song.toLowerCase(), song.toLowerCase()); + LoadingState.loadAndSwitchState(new ChartingState()); + } + + function loadAutosave():Void + { + PlayState.SONG = Song.parseJSONshit(FlxG.save.data.autosave); + LoadingState.loadAndSwitchState(new ChartingState()); + } + + function autosaveSong():Void + { + FlxG.save.data.autosave = Json.stringify({ + "song": _song + }); + FlxG.save.flush(); + } + + private function saveLevel() + { + var json = { + "song": _song + }; + + var data:String = Json.stringify(json); + + if ((data != null) && (data.length > 0)) + { + _file = new FileReference(); + _file.addEventListener(Event.COMPLETE, onSaveComplete); + _file.addEventListener(Event.CANCEL, onSaveCancel); + _file.addEventListener(IOErrorEvent.IO_ERROR, onSaveError); + _file.save(data.trim(), _song.song.toLowerCase() + ".json"); + } + } + + function onSaveComplete(_):Void + { + _file.removeEventListener(Event.COMPLETE, onSaveComplete); + _file.removeEventListener(Event.CANCEL, onSaveCancel); + _file.removeEventListener(IOErrorEvent.IO_ERROR, onSaveError); + _file = null; + FlxG.log.notice("Successfully saved LEVEL DATA."); + } + + /** + * Called when the save file dialog is cancelled. + */ + function onSaveCancel(_):Void + { + _file.removeEventListener(Event.COMPLETE, onSaveComplete); + _file.removeEventListener(Event.CANCEL, onSaveCancel); + _file.removeEventListener(IOErrorEvent.IO_ERROR, onSaveError); + _file = null; + } + + /** + * Called if there is an error while saving the gameplay recording. + */ + function onSaveError(_):Void + { + _file.removeEventListener(Event.COMPLETE, onSaveComplete); + _file.removeEventListener(Event.CANCEL, onSaveCancel); + _file.removeEventListener(IOErrorEvent.IO_ERROR, onSaveError); + _file = null; + FlxG.log.error("Problem saving Level data"); + } +} diff --git a/source_duck/Conductor.hx b/source_duck/Conductor.hx new file mode 100644 index 00000000..68634a3b --- /dev/null +++ b/source_duck/Conductor.hx @@ -0,0 +1,78 @@ +package; + +import Song.SwagSong; +import flixel.FlxG; + +/** + * ... + * @author + */ + +typedef BPMChangeEvent = +{ + var stepTime:Int; + var songTime:Float; + var bpm:Int; +} + +class Conductor +{ + public static var bpm:Int = 100; + public static var crochet:Float = ((60 / bpm) * 1000); // beats in milliseconds + public static var stepCrochet:Float = crochet / 4; // steps in milliseconds + public static var songPosition:Float; + public static var lastSongPos:Float; + public static var offset:Float = 0; + + public static var safeFrames:Int = 10; + public static var safeZoneOffset:Float = Math.floor((safeFrames / 60) * 1000); // is calculated in create(), is safeFrames in milliseconds + public static var timeScale:Float = Conductor.safeZoneOffset / 166; + + public static var bpmChangeMap:Array = []; + + public function new() + { + } + + public static function recalculateTimings() + { + Conductor.safeFrames = FlxG.save.data.frames; + Conductor.safeZoneOffset = Math.floor((Conductor.safeFrames / 60) * 1000); + Conductor.timeScale = Conductor.safeZoneOffset / 166; + } + + public static function mapBPMChanges(song:SwagSong) + { + bpmChangeMap = []; + + var curBPM:Int = song.bpm; + var totalSteps:Int = 0; + var totalPos:Float = 0; + for (i in 0...song.notes.length) + { + if(song.notes[i].changeBPM && song.notes[i].bpm != curBPM) + { + curBPM = song.notes[i].bpm; + var event:BPMChangeEvent = { + stepTime: totalSteps, + songTime: totalPos, + bpm: curBPM + }; + bpmChangeMap.push(event); + } + + var deltaSteps:Int = song.notes[i].lengthInSteps; + totalSteps += deltaSteps; + totalPos += ((60 / curBPM) * 1000 / 4) * deltaSteps; + } + trace("new BPM map BUDDY " + bpmChangeMap); + } + + public static function changeBPM(newBpm:Int) + { + bpm = newBpm; + + crochet = ((60 / bpm) * 1000); + stepCrochet = crochet / 4; + } +} \ No newline at end of file diff --git a/source_duck/Controls.hx b/source_duck/Controls.hx new file mode 100644 index 00000000..49776674 --- /dev/null +++ b/source_duck/Controls.hx @@ -0,0 +1,741 @@ +package; + +import flixel.FlxG; +import flixel.input.FlxInput; +import flixel.input.actions.FlxAction; +import flixel.input.actions.FlxActionInput; +import flixel.input.actions.FlxActionInputDigital; +import flixel.input.actions.FlxActionManager; +import flixel.input.actions.FlxActionSet; +import flixel.input.gamepad.FlxGamepadButton; +import flixel.input.gamepad.FlxGamepadInputID; +import flixel.input.keyboard.FlxKey; + +#if (haxe >= "4.0.0") +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"; + var CHEAT = "cheat"; +} +#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"; + var CHEAT = "cheat"; +} +#end + +enum Device +{ + Keys; + Gamepad(id:Int); +} + +/** + * Since, in many cases multiple actions should use similar keys, we don't want the + * rebinding UI to list every action. ActionBinders are what the user percieves as + * an input so, for instance, they can't set jump-press and jump-release to different keys. + */ +enum Control +{ + UP; + LEFT; + RIGHT; + DOWN; + RESET; + ACCEPT; + BACK; + PAUSE; + CHEAT; +} + +enum KeyboardScheme +{ + Solo; + Duo(first:Bool); + None; + Custom; +} + +/** + * A list of actions that a player would invoke via some input device. + * Uses FlxActions to funnel various inputs to a single action. + */ +class Controls extends FlxActionSet +{ + var _up = new FlxActionDigital(Action.UP); + var _left = new FlxActionDigital(Action.LEFT); + var _right = new FlxActionDigital(Action.RIGHT); + var _down = new FlxActionDigital(Action.DOWN); + var _upP = new FlxActionDigital(Action.UP_P); + var _leftP = new FlxActionDigital(Action.LEFT_P); + var _rightP = new FlxActionDigital(Action.RIGHT_P); + var _downP = new FlxActionDigital(Action.DOWN_P); + var _upR = new FlxActionDigital(Action.UP_R); + var _leftR = new FlxActionDigital(Action.LEFT_R); + var _rightR = new FlxActionDigital(Action.RIGHT_R); + var _downR = new FlxActionDigital(Action.DOWN_R); + var _accept = new FlxActionDigital(Action.ACCEPT); + var _back = new FlxActionDigital(Action.BACK); + var _pause = new FlxActionDigital(Action.PAUSE); + var _reset = new FlxActionDigital(Action.RESET); + var _cheat = new FlxActionDigital(Action.CHEAT); + + #if (haxe >= "4.0.0") + var byName:Map = []; + #else + var byName:Map = new Map(); + #end + + public var gamepadsAdded:Array = []; + public var keyboardScheme = KeyboardScheme.None; + + public var UP(get, never):Bool; + + inline function get_UP() + return _up.check(); + + public var LEFT(get, never):Bool; + + inline function get_LEFT() + return _left.check(); + + public var RIGHT(get, never):Bool; + + inline function get_RIGHT() + return _right.check(); + + public var DOWN(get, never):Bool; + + inline function get_DOWN() + return _down.check(); + + public var UP_P(get, never):Bool; + + inline function get_UP_P() + return _upP.check(); + + public var LEFT_P(get, never):Bool; + + inline function get_LEFT_P() + return _leftP.check(); + + public var RIGHT_P(get, never):Bool; + + inline function get_RIGHT_P() + return _rightP.check(); + + public var DOWN_P(get, never):Bool; + + inline function get_DOWN_P() + return _downP.check(); + + public var UP_R(get, never):Bool; + + inline function get_UP_R() + return _upR.check(); + + public var LEFT_R(get, never):Bool; + + inline function get_LEFT_R() + return _leftR.check(); + + public var RIGHT_R(get, never):Bool; + + inline function get_RIGHT_R() + return _rightR.check(); + + public var DOWN_R(get, never):Bool; + + inline function get_DOWN_R() + return _downR.check(); + + public var ACCEPT(get, never):Bool; + + inline function get_ACCEPT() + return _accept.check(); + + public var BACK(get, never):Bool; + + inline function get_BACK() + return _back.check(); + + public var PAUSE(get, never):Bool; + + inline function get_PAUSE() + return _pause.check(); + + public var RESET(get, never):Bool; + + inline function get_RESET() + return _reset.check(); + + public var CHEAT(get, never):Bool; + + inline function get_CHEAT() + return _cheat.check(); + + #if (haxe >= "4.0.0") + public function new(name, scheme = None) + { + 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); + add(_cheat); + + for (action in digitalActions) + byName[action.name] = action; + + 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); + add(_cheat); + + for (action in digitalActions) + byName[action.name] = action; + + if (scheme == null) + scheme = None; + setKeyboardScheme(scheme, false); + } + #end + + override function update() + { + super.update(); + } + + // inline + public function checkByName(name:Action):Bool + { + #if debug + if (!byName.exists(name)) + throw 'Invalid name: $name'; + #end + return byName[name].check(); + } + + public function getDialogueName(action:FlxActionDigital):String + { + var input = action.inputs[0]; + return switch input.device + { + case KEYBOARD: return '[${(input.inputID : FlxKey)}]'; + case GAMEPAD: return '(${(input.inputID : FlxGamepadInputID)})'; + case device: throw 'unhandled device: $device'; + } + } + + public function getDialogueNameFromToken(token:String):String + { + return getDialogueName(getActionFromControl(Control.createByName(token.toUpperCase()))); + } + + function getActionFromControl(control:Control):FlxActionDigital + { + return switch (control) + { + case UP: _up; + case DOWN: _down; + case LEFT: _left; + case RIGHT: _right; + case ACCEPT: _accept; + case BACK: _back; + case PAUSE: _pause; + case RESET: _reset; + case CHEAT: _cheat; + } + } + + static function init():Void + { + var actions = new FlxActionManager(); + FlxG.inputs.add(actions); + } + + /** + * Calls a function passing each action bound by the specified control + * @param control + * @param func + * @return ->Void) + */ + function forEachBound(control:Control, func:FlxActionDigital->FlxInputState->Void) + { + switch (control) + { + case UP: + func(_up, PRESSED); + func(_upP, JUST_PRESSED); + func(_upR, JUST_RELEASED); + case LEFT: + func(_left, PRESSED); + func(_leftP, JUST_PRESSED); + func(_leftR, JUST_RELEASED); + case RIGHT: + func(_right, PRESSED); + func(_rightP, JUST_PRESSED); + func(_rightR, JUST_RELEASED); + case DOWN: + func(_down, PRESSED); + func(_downP, JUST_PRESSED); + func(_downR, JUST_RELEASED); + case ACCEPT: + func(_accept, JUST_PRESSED); + case BACK: + func(_back, JUST_PRESSED); + case PAUSE: + func(_pause, JUST_PRESSED); + case RESET: + func(_reset, JUST_PRESSED); + case CHEAT: + func(_cheat, JUST_PRESSED); + } + } + + public function replaceBinding(control:Control, device:Device, ?toAdd:Int, ?toRemove:Int) + { + if (toAdd == toRemove) + return; + + switch (device) + { + case Keys: + if (toRemove != null) + unbindKeys(control, [toRemove]); + if (toAdd != null) + bindKeys(control, [toAdd]); + + case Gamepad(id): + if (toRemove != null) + unbindButtons(control, id, [toRemove]); + if (toAdd != null) + bindButtons(control, id, [toAdd]); + } + } + + public function copyFrom(controls:Controls, ?device:Device) + { + #if (haxe >= "4.0.0") + for (name => action in controls.byName) + { + for (input in action.inputs) + { + if (device == null || isDevice(input, device)) + 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); + + case Gamepad(id): + gamepadsAdded.push(id); + case Keys: + mergeKeyboardScheme(controls.keyboardScheme); + } + } + + inline public function copyTo(controls:Controls, ?device:Device) + { + controls.copyFrom(this, device); + } + + function mergeKeyboardScheme(scheme:KeyboardScheme):Void + { + if (scheme != None) + { + switch (keyboardScheme) + { + case None: + keyboardScheme = scheme; + default: + keyboardScheme = Custom; + } + } + } + + /** + * Sets all actions that pertain to the binder to trigger when the supplied keys are used. + * If binder is a literal you can inline this + */ + public function bindKeys(control:Control, keys:Array) + { + #if (haxe >= "4.0.0") + inline forEachBound(control, (action, state) -> addKeys(action, keys, state)); + #else + forEachBound(control, function(action, state) addKeys(action, keys, state)); + #end + } + + /** + * Sets all actions that pertain to the binder to trigger when the supplied keys are used. + * If binder is a literal you can inline this + */ + public function unbindKeys(control:Control, keys:Array) + { + #if (haxe >= "4.0.0") + inline forEachBound(control, (action, _) -> removeKeys(action, keys)); + #else + forEachBound(control, function(action, _) removeKeys(action, keys)); + #end + } + + inline static function addKeys(action:FlxActionDigital, keys:Array, state:FlxInputState) + { + for (key in keys) + action.addKey(key, state); + } + + static function removeKeys(action:FlxActionDigital, keys:Array) + { + var i = action.inputs.length; + while (i-- > 0) + { + var input = action.inputs[i]; + if (input.device == KEYBOARD && keys.indexOf(cast input.inputID) != -1) + action.remove(input); + } + } + + public function setKeyboardScheme(scheme:KeyboardScheme, reset = true) + { + if (reset) + removeKeyboard(); + + keyboardScheme = scheme; + + #if (haxe >= "4.0.0") + switch (scheme) + { + case Solo: + inline bindKeys(Control.UP, [J, FlxKey.UP]); + inline bindKeys(Control.DOWN, [F, FlxKey.DOWN]); + inline bindKeys(Control.LEFT, [D, FlxKey.LEFT]); + inline bindKeys(Control.RIGHT, [K, FlxKey.RIGHT]); + 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, FlxKey.UP]); + 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, [G, Z, SPACE, ENTER]); + inline bindKeys(Control.BACK, [BACKSPACE, ESCAPE]); + inline bindKeys(Control.RESET, [R]); + case Duo(false): + inline bindKeys(Control.UP, [FlxKey.UP]); + inline bindKeys(Control.DOWN, [FlxKey.DOWN]); + inline bindKeys(Control.LEFT, [FlxKey.LEFT]); + inline bindKeys(Control.RIGHT, [FlxKey.RIGHT]); + inline bindKeys(Control.ACCEPT, [O]); + inline bindKeys(Control.BACK, [P]); + inline bindKeys(Control.PAUSE, [ENTER]); + inline bindKeys(Control.RESET, [BACKSPACE]); + 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() + { + for (action in this.digitalActions) + { + var i = action.inputs.length; + while (i-- > 0) + { + var input = action.inputs[i]; + if (input.device == KEYBOARD) + action.remove(input); + } + } + } + + public function addGamepad(id:Int, ?buttonMap:Map>):Void + { + gamepadsAdded.push(id); + + #if (haxe >= "4.0.0") + for (control => buttons in buttonMap) + 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) + inline bindButtons(control, id, buttons); + #else + for (control in buttonMap.keys()) + bindButtons(control, id, buttonMap[control]); + #end + } + + public function removeGamepad(deviceID:Int = FlxInputDeviceID.ALL):Void + { + for (action in this.digitalActions) + { + var i = action.inputs.length; + while (i-- > 0) + { + var input = action.inputs[i]; + if (input.device == GAMEPAD && (deviceID == FlxInputDeviceID.ALL || input.deviceID == deviceID)) + action.remove(input); + } + } + + gamepadsAdded.remove(deviceID); + } + + public function addDefaultGamepad(id):Void + { + #if !switch + addGamepadLiteral(id, [ + Control.ACCEPT => [A], + Control.BACK => [B], + 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] + ]); + #else + addGamepadLiteral(id, [ + //Swap A and B for switch + Control.ACCEPT => [B], + Control.BACK => [A], + Control.UP => [DPAD_UP, LEFT_STICK_DIGITAL_UP, RIGHT_STICK_DIGITAL_UP], + Control.DOWN => [DPAD_DOWN, LEFT_STICK_DIGITAL_DOWN, RIGHT_STICK_DIGITAL_DOWN], + Control.LEFT => [DPAD_LEFT, LEFT_STICK_DIGITAL_LEFT, RIGHT_STICK_DIGITAL_LEFT], + Control.RIGHT => [DPAD_RIGHT, LEFT_STICK_DIGITAL_RIGHT, RIGHT_STICK_DIGITAL_RIGHT], + Control.PAUSE => [START], + //Swap Y and X for switch + Control.RESET => [Y], + Control.CHEAT => [X] + ]); + #end + } + + /** + * Sets all actions that pertain to the binder to trigger when the supplied keys are used. + * If binder is a literal you can inline this + */ + public function bindButtons(control:Control, id, buttons) + { + #if (haxe >= "4.0.0") + inline forEachBound(control, (action, state) -> addButtons(action, buttons, state, id)); + #else + forEachBound(control, function(action, state) addButtons(action, buttons, state, id)); + #end + } + + /** + * Sets all actions that pertain to the binder to trigger when the supplied keys are used. + * If binder is a literal you can inline this + */ + public function unbindButtons(control:Control, gamepadID:Int, buttons) + { + #if (haxe >= "4.0.0") + inline forEachBound(control, (action, _) -> removeButtons(action, gamepadID, buttons)); + #else + forEachBound(control, function(action, _) removeButtons(action, gamepadID, buttons)); + #end + } + + inline static function addButtons(action:FlxActionDigital, buttons:Array, state, id) + { + for (button in buttons) + action.addGamepad(button, state, id); + } + + static function removeButtons(action:FlxActionDigital, gamepadID:Int, buttons:Array) + { + var i = action.inputs.length; + while (i-- > 0) + { + var input = action.inputs[i]; + if (isGamepad(input, gamepadID) && buttons.indexOf(cast input.inputID) != -1) + action.remove(input); + } + } + + public function getInputsFor(control:Control, device:Device, ?list:Array):Array + { + if (list == null) + list = []; + + switch (device) + { + case Keys: + for (input in getActionFromControl(control).inputs) + { + if (input.device == KEYBOARD) + list.push(input.inputID); + } + case Gamepad(id): + for (input in getActionFromControl(control).inputs) + { + if (input.deviceID == id) + list.push(input.inputID); + } + } + return list; + } + + public function removeDevice(device:Device) + { + switch (device) + { + case Keys: + setKeyboardScheme(None); + case Gamepad(id): + removeGamepad(id); + } + } + + static function isDevice(input:FlxActionInput, device:Device) + { + return switch device + { + case Keys: input.device == KEYBOARD; + case Gamepad(id): isGamepad(input, id); + } + } + + inline static function isGamepad(input:FlxActionInput, deviceID:Int) + { + return input.device == GAMEPAD && (deviceID == FlxInputDeviceID.ALL || input.deviceID == deviceID); + } +} diff --git a/source_duck/ControlsSubState.hx b/source_duck/ControlsSubState.hx new file mode 100644 index 00000000..f35d74cc --- /dev/null +++ b/source_duck/ControlsSubState.hx @@ -0,0 +1,12 @@ +package; + +import flixel.FlxSprite; +import flixel.FlxSubState; + +class ControlsSubState extends FlxSubState +{ + public function new() + { + super(); + } +} diff --git a/source_duck/ConvertScore.hx b/source_duck/ConvertScore.hx new file mode 100644 index 00000000..9eba969f --- /dev/null +++ b/source_duck/ConvertScore.hx @@ -0,0 +1,21 @@ +class ConvertScore +{ + public static function convertScore(noteDiff:Float):Int + { + var daRating:String = Ratings.CalculateRating(noteDiff, 166); + + switch(daRating) + { + case 'shit': + return -300; + case 'bad': + return 0; + case 'good': + return 200; + case 'sick': + return 350; + } + return 0; + } + +} \ No newline at end of file diff --git a/source_duck/CoolUtil.hx b/source_duck/CoolUtil.hx new file mode 100644 index 00000000..92cc2c0c --- /dev/null +++ b/source_duck/CoolUtil.hx @@ -0,0 +1,49 @@ +package; + +import lime.utils.Assets; + +using StringTools; + +class CoolUtil +{ + public static var difficultyArray:Array = ['EASY', "NORMAL", "HARD"]; + + public static function difficultyString():String + { + return difficultyArray[PlayState.storyDifficulty]; + } + + public static function coolTextFile(path:String):Array + { + var daList:Array = Assets.getText(path).trim().split('\n'); + + for (i in 0...daList.length) + { + daList[i] = daList[i].trim(); + } + + return daList; + } + + public static function coolStringFile(path:String):Array + { + var daList:Array = path.trim().split('\n'); + + for (i in 0...daList.length) + { + daList[i] = daList[i].trim(); + } + + return daList; + } + + public static function numberArray(max:Int, ?min = 0):Array + { + var dumbArray:Array = []; + for (i in min...max) + { + dumbArray.push(i); + } + return dumbArray; + } +} diff --git a/source_duck/DialogueBox.hx b/source_duck/DialogueBox.hx new file mode 100644 index 00000000..edd67d12 --- /dev/null +++ b/source_duck/DialogueBox.hx @@ -0,0 +1,265 @@ +package; + +import flixel.FlxG; +import flixel.FlxSprite; +import flixel.addons.text.FlxTypeText; +import flixel.graphics.frames.FlxAtlasFrames; +import flixel.group.FlxSpriteGroup; +import flixel.input.FlxKeyManager; +import flixel.text.FlxText; +import flixel.util.FlxColor; +import flixel.util.FlxTimer; + +using StringTools; + +class DialogueBox extends FlxSpriteGroup +{ + var box:FlxSprite; + + var curCharacter:String = ''; + + var dialogue:Alphabet; + var dialogueList:Array = []; + + // SECOND DIALOGUE FOR THE PIXEL SHIT INSTEAD??? + var swagDialogue:FlxTypeText; + + var dropText:FlxText; + + public var finishThing:Void->Void; + + var portraitLeft:FlxSprite; + var portraitRight:FlxSprite; + + var handSelect:FlxSprite; + var bgFade:FlxSprite; + + public function new(talkingRight:Bool = true, ?dialogueList:Array) + { + super(); + + switch (PlayState.SONG.song.toLowerCase()) + { + case 'senpai': + FlxG.sound.playMusic(Paths.music('Lunchbox'), 0); + FlxG.sound.music.fadeIn(1, 0, 0.8); + case 'thorns': + FlxG.sound.playMusic(Paths.music('LunchboxScary'), 0); + FlxG.sound.music.fadeIn(1, 0, 0.8); + } + + bgFade = new FlxSprite(-200, -200).makeGraphic(Std.int(FlxG.width * 1.3), Std.int(FlxG.height * 1.3), 0xFFB3DFd8); + bgFade.scrollFactor.set(); + bgFade.alpha = 0; + add(bgFade); + + new FlxTimer().start(0.83, function(tmr:FlxTimer) + { + bgFade.alpha += (1 / 5) * 0.7; + if (bgFade.alpha > 0.7) + bgFade.alpha = 0.7; + }, 5); + + box = new FlxSprite(-20, 45); + + var hasDialog = false; + switch (PlayState.SONG.song.toLowerCase()) + { + case 'senpai': + hasDialog = true; + box.frames = Paths.getSparrowAtlas('weeb/pixelUI/dialogueBox-pixel'); + box.animation.addByPrefix('normalOpen', 'Text Box Appear', 24, false); + box.animation.addByIndices('normal', 'Text Box Appear', [4], "", 24); + case 'roses': + hasDialog = true; + FlxG.sound.play(Paths.sound('ANGRY_TEXT_BOX')); + + box.frames = Paths.getSparrowAtlas('weeb/pixelUI/dialogueBox-senpaiMad'); + box.animation.addByPrefix('normalOpen', 'SENPAI ANGRY IMPACT SPEECH', 24, false); + box.animation.addByIndices('normal', 'SENPAI ANGRY IMPACT SPEECH', [4], "", 24); + + case 'thorns': + hasDialog = true; + box.frames = Paths.getSparrowAtlas('weeb/pixelUI/dialogueBox-evil'); + box.animation.addByPrefix('normalOpen', 'Spirit Textbox spawn', 24, false); + box.animation.addByIndices('normal', 'Spirit Textbox spawn', [11], "", 24); + + case 'timer': + hasDialog = true; + box.frames = Paths.getSparrowAtlas('duck/Dia/Dialougue Box'); + box.animation.addByPrefix('normalOpen', 'Dialogue Box', 24, false); + box.animation.addByIndices('normal', 'Dialogue Box', [11], "", 24); + + var face:FlxSprite = new FlxSprite(320, 170).loadGraphic(Paths.image('weeb/spiritFaceForward')); + face.setGraphicSize(Std.int(face.width * 6)); + add(face); + } + + this.dialogueList = dialogueList; + + if (!hasDialog) + return; + + portraitLeft = new FlxSprite(-20, 40); + portraitLeft.frames = Paths.getSparrowAtlas('weeb/senpaiPortrait'); + portraitLeft.animation.addByPrefix('enter', 'Senpai Portrait Enter', 24, false); + portraitLeft.setGraphicSize(Std.int(portraitLeft.width * PlayState.daPixelZoom * 0.9)); + portraitLeft.updateHitbox(); + portraitLeft.scrollFactor.set(); + add(portraitLeft); + portraitLeft.visible = false; + + portraitRight = new FlxSprite(0, 40); + portraitRight.frames = Paths.getSparrowAtlas('weeb/bfPortrait'); + portraitRight.animation.addByPrefix('enter', 'Boyfriend portrait enter', 24, false); + portraitRight.setGraphicSize(Std.int(portraitRight.width * PlayState.daPixelZoom * 0.9)); + portraitRight.updateHitbox(); + portraitRight.scrollFactor.set(); + add(portraitRight); + portraitRight.visible = false; + + box.animation.play('normalOpen'); + box.setGraphicSize(Std.int(box.width * PlayState.daPixelZoom * 0.9)); + box.updateHitbox(); + add(box); + + box.screenCenter(X); + portraitLeft.screenCenter(X); + + handSelect = new FlxSprite(FlxG.width * 0.9, FlxG.height * 0.9).loadGraphic(Paths.image('weeb/pixelUI/hand_textbox')); + add(handSelect); + + if (!talkingRight) + { + // box.flipX = true; + } + + dropText = new FlxText(242, 502, Std.int(FlxG.width * 0.6), "", 32); + dropText.font = 'Pixel Arial 11 Bold'; + dropText.color = 0xFFD89494; + add(dropText); + + swagDialogue = new FlxTypeText(240, 500, Std.int(FlxG.width * 0.6), "", 32); + swagDialogue.font = 'Pixel Arial 11 Bold'; + swagDialogue.color = 0xFF3F2021; + swagDialogue.sounds = [FlxG.sound.load(Paths.sound('pixelText'), 0.6)]; + add(swagDialogue); + + dialogue = new Alphabet(0, 80, "", false, true); + // dialogue.x = 90; + // add(dialogue); + } + + var dialogueOpened:Bool = false; + var dialogueStarted:Bool = false; + + override function update(elapsed:Float) + { + // HARD CODING CUZ IM STUPDI + if (PlayState.SONG.song.toLowerCase() == 'roses') + portraitLeft.visible = false; + if (PlayState.SONG.song.toLowerCase() == 'thorns') + { + portraitLeft.color = FlxColor.BLACK; + swagDialogue.color = FlxColor.WHITE; + dropText.color = FlxColor.BLACK; + } + + dropText.text = swagDialogue.text; + + 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.ANY && dialogueStarted == true) + { + remove(dialogue); + + FlxG.sound.play(Paths.sound('clickText'), 0.8); + + if (dialogueList[1] == null && dialogueList[0] != null) + { + if (!isEnding) + { + isEnding = true; + + if (PlayState.SONG.song.toLowerCase() == 'senpai' || PlayState.SONG.song.toLowerCase() == 'thorns') + FlxG.sound.music.fadeOut(2.2, 0); + + new FlxTimer().start(0.2, function(tmr:FlxTimer) + { + box.alpha -= 1 / 5; + bgFade.alpha -= 1 / 5 * 0.7; + portraitLeft.visible = false; + portraitRight.visible = false; + swagDialogue.alpha -= 1 / 5; + dropText.alpha = swagDialogue.alpha; + }, 5); + + new FlxTimer().start(1.2, function(tmr:FlxTimer) + { + finishThing(); + kill(); + }); + } + } + else + { + dialogueList.remove(dialogueList[0]); + startDialogue(); + } + } + + super.update(elapsed); + } + + var isEnding:Bool = false; + + function startDialogue():Void + { + cleanDialog(); + // var theDialog:Alphabet = new Alphabet(0, 70, dialogueList[0], false, true); + // dialogue = theDialog; + // add(theDialog); + + // swagDialogue.text = ; + swagDialogue.resetText(dialogueList[0]); + swagDialogue.start(0.04, true); + + switch (curCharacter) + { + case 'dad': + portraitRight.visible = false; + if (!portraitLeft.visible) + { + portraitLeft.visible = true; + portraitLeft.animation.play('enter'); + } + case 'bf': + portraitLeft.visible = false; + if (!portraitRight.visible) + { + portraitRight.visible = true; + portraitRight.animation.play('enter'); + } + } + } + + function cleanDialog():Void + { + var splitName:Array = dialogueList[0].split(":"); + curCharacter = splitName[1]; + dialogueList[0] = dialogueList[0].substr(splitName[1].length + 2).trim(); + } +} diff --git a/source_duck/DiffCalc.hx b/source_duck/DiffCalc.hx new file mode 100644 index 00000000..a1c7a7a5 --- /dev/null +++ b/source_duck/DiffCalc.hx @@ -0,0 +1,140 @@ +import Song.SwagSong; + +class SmallNote // basically Note.hx but small as fuck +{ + public var strumTime:Float; + public var noteData:Int; + + public function new(strum,data) + { + strumTime = strum; + noteData = data; + } +} + +class DiffCalc +{ + public static function CalculateDiff(song:SwagSong) + { + // cleaned notes + var cleanedNotes:Array = []; + + // find all of the notes + for(i in song.notes) // sections + { + for (ii in i.sectionNotes) // notes + { + if (ii[2] != 0) // skip helds + continue; + var gottaHitNote:Bool = i.mustHitSection; + + if (ii[1] > 3) + gottaHitNote = !i.mustHitSection; + + if (gottaHitNote) + cleanedNotes.push(new SmallNote(ii[0],Math.floor(Math.abs(ii[1])))); + } + } + + var handOne:Array = []; + var handTwo:Array = []; + + cleanedNotes.sort((a, b) -> Std.int(a.strumTime - b.strumTime)); + + var firstNoteTime = cleanedNotes[0].strumTime; + + // normalize the notes + + for(i in cleanedNotes) + { + i.strumTime = (i.strumTime - firstNoteTime) * 2; + } + + for (i in cleanedNotes) + { + switch(i.noteData) + { + case 0: + handOne.push(i); + case 1: + handTwo.push(i); + case 2: + handTwo.push(i); + case 3: + handOne.push(i); + } + } + + + // length in segments of the song + var length = ((cleanedNotes[cleanedNotes.length - 1].strumTime / 1000) / 0.5); + + // hackey way of creating a array with a length + var segmentsOne:Array = new_Array(1,Std.int(length)); + var segmentsTwo:Array = new_Array(1,Std.int(length)); + + // algo loop + for(i in handOne) + { + var index = Std.int(((i.strumTime / 1000))); + if (index + 1 > segmentsOne.length) + continue; + segmentsOne[index] = segmentsOne[index] + 1; + } + + for(i in handTwo) + { + var index = Std.int(((i.strumTime / 1000))); + if (index + 1 > segmentsTwo.length) + continue; + segmentsTwo[index] = segmentsTwo[index] + 1; + } + + // get the average of all of the segments + var sumOne:Float = 0; + var sumTwo:Float = 0; + + + var lone = segmentsOne.length; + var ltwo = segmentsOne.length; + + for (i in segmentsOne) + { + if (i == 0) // remove empty/breaks + { + lone--; + continue; + } + //trace(i); + sumOne += i / .5; // half it because otherwise instead of nps its just fucking notes per half second which is dumb and stupid + } + + for (i in segmentsTwo) + { + if (i == 0) // remove empty/breaks + { + ltwo--; + continue; + } + //trace(i); + sumTwo += i / .5; // half it because otherwise instead of nps its just fucking notes per half second which is dumb and stupid + } + + + var handOneAvg = sumOne / lone; + var handTwoAvg = sumTwo / ltwo; + + return HelperFunctions.truncateFloat(handOneAvg > handTwoAvg ? handOneAvg : handTwoAvg,2); + } + + static public function new_Array( ArrayType:T, Length:Int ):Array { + var empty:Null = null; + var newArray:Array = new Array(); + + for ( i in 0...Length ) { + newArray.push( empty ); + } + + return newArray; + } +} \ No newline at end of file diff --git a/source_duck/Discord.hx b/source_duck/Discord.hx new file mode 100644 index 00000000..00018e78 --- /dev/null +++ b/source_duck/Discord.hx @@ -0,0 +1,89 @@ +package; + +#if windows +import Sys.sleep; +import discord_rpc.DiscordRpc; + +using StringTools; + +class DiscordClient +{ + public function new() + { + trace("Discord Client starting..."); + DiscordRpc.start({ + clientID: "557069829501091850", // change this to what ever the fuck you want lol + onReady: onReady, + onError: onError, + onDisconnected: onDisconnected + }); + trace("Discord Client started."); + + while (true) + { + DiscordRpc.process(); + sleep(2); + //trace("Discord Client Update"); + } + + DiscordRpc.shutdown(); + } + + public static function shutdown() + { + DiscordRpc.shutdown(); + } + + static function onReady() + { + DiscordRpc.presence({ + details: "In the Menus", + state: null, + largeImageKey: 'icon', + largeImageText: "fridaynightfunkin" + }); + } + + static function onError(_code:Int, _message:String) + { + trace('Error! $_code : $_message'); + } + + static function onDisconnected(_code:Int, _message:String) + { + trace('Disconnected! $_code : $_message'); + } + + public static function initialize() + { + var DiscordDaemon = sys.thread.Thread.create(() -> + { + new DiscordClient(); + }); + trace("Discord Client initialized"); + } + + public static function changePresence(details:String, state:Null, ?smallImageKey : String, ?hasStartTimestamp : Bool, ?endTimestamp: Float) + { + var startTimestamp:Float = if(hasStartTimestamp) Date.now().getTime() else 0; + + if (endTimestamp > 0) + { + endTimestamp = startTimestamp + endTimestamp; + } + + DiscordRpc.presence({ + details: details, + state: state, + largeImageKey: 'icon', + largeImageText: "fridaynightfunkin", + smallImageKey : smallImageKey, + // Obtained times are in milliseconds so they are divided so Discord can use it + startTimestamp : Std.int(startTimestamp / 1000), + endTimestamp : Std.int(endTimestamp / 1000) + }); + + //trace('Discord RPC Updated. Arguments: $details, $state, $smallImageKey, $hasStartTimestamp, $endTimestamp'); + } +} +#end \ No newline at end of file diff --git a/source_duck/EtternaFunctions.hx b/source_duck/EtternaFunctions.hx new file mode 100644 index 00000000..9fcfbaea --- /dev/null +++ b/source_duck/EtternaFunctions.hx @@ -0,0 +1,84 @@ +class EtternaFunctions +{ + // erf constants + public static var a1 = 0.254829592; + public static var a2 = -0.284496736; + public static var a3 = 1.421413741; + public static var a4 = -1.453152027; + public static var a5 = 1.061405429; + public static var p = 0.3275911; + + public static function erf(x:Float):Float + { + // Save the sign of x + var sign = 1; + if (x < 0) + sign = -1; + x = Math.abs(x); + + // A&S formula 7.1.26 + var t = 1.0/(1.0 + p*x); + var y = 1.0 - (((((a5*t + a4)*t) + a3)*t + a2)*t + a1)*t*Math.exp(-x*x); + + return sign*y; + } + + public static function getNotes():Int + { + var notes:Int = 0; + for (i in 0...PlayState.SONG.notes.length) + { + for (ii in 0...PlayState.SONG.notes[i].sectionNotes.length) + { + var n = PlayState.SONG.notes[i].sectionNotes[ii]; + if (n[1] <= 0) + notes++; + } + } + return notes; + } + + public static function getHolds():Int + { + var notes:Int = 0; + for (i in 0...PlayState.SONG.notes.length) + { + trace(PlayState.SONG.notes[i]); + for (ii in 0...PlayState.SONG.notes[i].sectionNotes.length) + { + var n = PlayState.SONG.notes[i].sectionNotes[ii]; + trace(n); + if (n[1] > 0) + notes++; + } + } + return notes; + } + + public static function getMapMaxScore():Int + { + return (getNotes() * 350); + } + + public static function wife3(maxms:Float, ts:Float) + { + var max_points = 1.0; + var miss_weight = -5.5; + var ridic= 5 * ts; + var max_boo_weight = 180 * ts; + var ts_pow = 0.75; + var zero = 65 * (Math.pow(ts,ts_pow)); + var power = 2.5; + var dev = 22.7 * (Math.pow(ts,ts_pow)); + + if (maxms <= ridic) // anything below this (judge scaled) threshold is counted as full pts + return max_points; + else if (maxms <= zero) // ma/pa region, exponential + return max_points * erf((zero - maxms) / dev); + else if (maxms <= max_boo_weight)// cb region, linear + return (maxms - zero) * miss_weight / (max_boo_weight - zero); + else + return miss_weight; + } + +} \ No newline at end of file diff --git a/source_duck/FreeplayState.hx b/source_duck/FreeplayState.hx new file mode 100644 index 00000000..59ecc708 --- /dev/null +++ b/source_duck/FreeplayState.hx @@ -0,0 +1,307 @@ +package; + +import flash.text.TextField; +import flixel.FlxG; +import flixel.FlxSprite; +import flixel.addons.display.FlxGridOverlay; +import flixel.group.FlxGroup.FlxTypedGroup; +import flixel.math.FlxMath; +import flixel.text.FlxText; +import flixel.util.FlxColor; +import lime.utils.Assets; + + +#if windows +import Discord.DiscordClient; +#end + +using StringTools; + +class FreeplayState extends MusicBeatState +{ + var songs:Array = []; + + var selector:FlxText; + var curSelected:Int = 0; + var curDifficulty:Int = 1; + + var scoreText:FlxText; + var diffText:FlxText; + var lerpScore:Int = 0; + var intendedScore:Int = 0; + + private var grpSongs:FlxTypedGroup; + private var curPlaying:Bool = false; + + private var iconArray:Array = []; + + override function create() + { + var initSonglist = CoolUtil.coolTextFile(Paths.txt('freeplaySonglist')); + + for (i in 0...initSonglist.length) + { + var data:Array = initSonglist[i].split(':'); + songs.push(new SongMetadata(data[0], Std.parseInt(data[2]), data[1])); + } + + /* + if (FlxG.sound.music != null) + { + if (!FlxG.sound.music.playing) + FlxG.sound.playMusic(Paths.music('freakyMenu')); + } + */ + + #if windows + // Updating Discord Rich Presence + DiscordClient.changePresence("In the Freeplay Menu", null); + #end + + var isDebug:Bool = false; + + #if debug + isDebug = true; + #end + + // LOAD MUSIC + + // LOAD CHARACTERS + + var bg:FlxSprite = new FlxSprite().loadGraphic(Paths.image('menuBGBlue')); + add(bg); + + grpSongs = new FlxTypedGroup(); + add(grpSongs); + + for (i in 0...songs.length) + { + var songText:Alphabet = new Alphabet(0, (70 * i) + 30, songs[i].songName, true, false); + songText.isMenuItem = true; + songText.targetY = i; + grpSongs.add(songText); + + var icon:HealthIcon = new HealthIcon(songs[i].songCharacter); + icon.sprTracker = songText; + + // using a FlxGroup is too much fuss! + iconArray.push(icon); + add(icon); + + // songText.x += 40; + // DONT PUT X IN THE FIRST PARAMETER OF new ALPHABET() !! + // songText.screenCenter(X); + } + + scoreText = new FlxText(FlxG.width * 0.7, 5, 0, "", 32); + // scoreText.autoSize = false; + scoreText.setFormat(Paths.font("vcr.ttf"), 32, FlxColor.WHITE, RIGHT); + // scoreText.alignment = RIGHT; + + var scoreBG:FlxSprite = new FlxSprite(scoreText.x - 6, 0).makeGraphic(Std.int(FlxG.width * 0.35), 66, 0xFF000000); + scoreBG.alpha = 0.6; + add(scoreBG); + + diffText = new FlxText(scoreText.x, scoreText.y + 36, 0, "", 24); + diffText.font = scoreText.font; + add(diffText); + + add(scoreText); + + changeSelection(); + changeDiff(); + + // FlxG.sound.playMusic(Paths.music('title'), 0); + // FlxG.sound.music.fadeIn(2, 0, 0.8); + selector = new FlxText(); + + selector.size = 40; + selector.text = ">"; + // add(selector); + + var swag:Alphabet = new Alphabet(1, 0, "swag"); + + // JUST DOIN THIS SHIT FOR TESTING!!! + /* + var md:String = Markdown.markdownToHtml(Assets.getText('CHANGELOG.md')); + + var texFel:TextField = new TextField(); + texFel.width = FlxG.width; + texFel.height = FlxG.height; + // texFel. + texFel.htmlText = md; + + FlxG.stage.addChild(texFel); + + // scoreText.textField.htmlText = md; + + trace(md); + */ + + super.create(); + } + + public function addSong(songName:String, weekNum:Int, songCharacter:String) + { + songs.push(new SongMetadata(songName, weekNum, songCharacter)); + } + + public function addWeek(songs:Array, weekNum:Int, ?songCharacters:Array) + { + if (songCharacters == null) + songCharacters = ['bf']; + + var num:Int = 0; + for (song in songs) + { + addSong(song, weekNum, songCharacters[num]); + + if (songCharacters.length != 1) + num++; + } + } + + override function update(elapsed:Float) + { + super.update(elapsed); + + if (FlxG.sound.music.volume < 0.7) + { + FlxG.sound.music.volume += 0.5 * FlxG.elapsed; + } + + lerpScore = Math.floor(FlxMath.lerp(lerpScore, intendedScore, 0.4)); + + if (Math.abs(lerpScore - intendedScore) <= 10) + lerpScore = intendedScore; + + scoreText.text = "PERSONAL BEST:" + lerpScore; + + var upP = controls.UP_P; + var downP = controls.DOWN_P; + var accepted = controls.ACCEPT; + + if (upP) + { + changeSelection(-1); + } + if (downP) + { + changeSelection(1); + } + + if (controls.LEFT_P) + changeDiff(-1); + if (controls.RIGHT_P) + changeDiff(1); + + if (controls.BACK) + { + FlxG.switchState(new MainMenuState()); + } + + if (accepted) + { + var poop:String = Highscore.formatSong(songs[curSelected].songName.toLowerCase(), curDifficulty); + + trace(poop); + + PlayState.SONG = Song.loadFromJson(poop, songs[curSelected].songName.toLowerCase()); + PlayState.isStoryMode = false; + PlayState.storyDifficulty = curDifficulty; + PlayState.storyWeek = songs[curSelected].week; + trace('CUR WEEK' + PlayState.storyWeek); + LoadingState.loadAndSwitchState(new PlayState()); + } + } + + function changeDiff(change:Int = 0) + { + curDifficulty += change; + + if (curDifficulty < 0) + curDifficulty = 2; + if (curDifficulty > 2) + curDifficulty = 0; + + #if !switch + intendedScore = Highscore.getScore(songs[curSelected].songName, curDifficulty); + #end + + switch (curDifficulty) + { + case 0: + diffText.text = "EASY"; + case 1: + diffText.text = 'NORMAL'; + case 2: + diffText.text = "HARD"; + } + } + + function changeSelection(change:Int = 0) + { + #if !switch + // NGio.logEvent('Fresh'); + #end + + // NGio.logEvent('Fresh'); + FlxG.sound.play(Paths.sound('scrollMenu'), 0.4); + + curSelected += change; + + if (curSelected < 0) + curSelected = songs.length - 1; + if (curSelected >= songs.length) + curSelected = 0; + + // selector.y = (70 * curSelected) + 30; + + #if !switch + intendedScore = Highscore.getScore(songs[curSelected].songName, curDifficulty); + // lerpScore = 0; + #end + + #if PRELOAD_ALL + FlxG.sound.playMusic(Paths.inst(songs[curSelected].songName), 0); + #end + + var bullShit:Int = 0; + + for (i in 0...iconArray.length) + { + iconArray[i].alpha = 0.6; + } + + iconArray[curSelected].alpha = 1; + + for (item in grpSongs.members) + { + item.targetY = bullShit - curSelected; + bullShit++; + + item.alpha = 0.6; + // item.setGraphicSize(Std.int(item.width * 0.8)); + + if (item.targetY == 0) + { + item.alpha = 1; + // item.setGraphicSize(Std.int(item.width)); + } + } + } +} + +class SongMetadata +{ + public var songName:String = ""; + public var week:Int = 0; + public var songCharacter:String = ""; + + public function new(song:String, week:Int, songCharacter:String) + { + this.songName = song; + this.week = week; + this.songCharacter = songCharacter; + } +} diff --git a/source_duck/GameOverState.hx b/source_duck/GameOverState.hx new file mode 100644 index 00000000..57c60d1f --- /dev/null +++ b/source_duck/GameOverState.hx @@ -0,0 +1,82 @@ +package; + +import flixel.FlxG; +import flixel.FlxSprite; +import flixel.addons.transition.FlxTransitionableState; +import flixel.graphics.frames.FlxAtlasFrames; +import flixel.input.gamepad.FlxGamepad; +import flixel.tweens.FlxEase; +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 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}); + + super.create(); + } + + private var fading:Bool = false; + + override function update(elapsed:Float) + { + var pressed:Bool = FlxG.keys.justPressed.ANY; + + var gamepad:FlxGamepad = FlxG.gamepads.lastActive; + + if (gamepad != null) + { + if (gamepad.justPressed.ANY) + pressed = true; + } + + pressed = false; + + if (pressed && !fading) + { + fading = true; + FlxG.sound.music.fadeOut(0.5, 0, function(twn:FlxTween) + { + FlxG.sound.music.stop(); + LoadingState.loadAndSwitchState(new PlayState()); + }); + } + super.update(elapsed); + } +} diff --git a/source_duck/GameOverSubstate.hx b/source_duck/GameOverSubstate.hx new file mode 100644 index 00000000..1a2752e9 --- /dev/null +++ b/source_duck/GameOverSubstate.hx @@ -0,0 +1,116 @@ +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; + + var stageSuffix:String = ""; + + public function new(x:Float, y:Float) + { + var daStage = PlayState.curStage; + var daBf:String = ''; + switch (daStage) + { + case 'school': + stageSuffix = '-pixel'; + daBf = 'bf-pixel-dead'; + case 'schoolEvil': + stageSuffix = '-pixel'; + daBf = 'bf-pixel-dead'; + default: + daBf = 'bf'; + } + + super(); + + Conductor.songPosition = 0; + + bf = new Boyfriend(x, y, daBf); + add(bf); + + camFollow = new FlxObject(bf.getGraphicMidpoint().x, bf.getGraphicMidpoint().y, 1, 1); + add(camFollow); + + FlxG.sound.play(Paths.sound('fnf_loss_sfx' + stageSuffix)); + 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 (controls.ACCEPT) + { + endBullshit(); + } + + if (controls.BACK) + { + FlxG.sound.music.stop(); + + if (PlayState.isStoryMode) + FlxG.switchState(new StoryMenuState()); + else + FlxG.switchState(new FreeplayState()); + PlayState.loadRep = false; + } + + 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(Paths.music('gameOver' + stageSuffix)); + } + + 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(Paths.music('gameOverEnd' + stageSuffix)); + new FlxTimer().start(0.7, function(tmr:FlxTimer) + { + FlxG.camera.fade(FlxColor.BLACK, 2, false, function() + { + LoadingState.loadAndSwitchState(new PlayState()); + }); + }); + } + } +} diff --git a/source_duck/GameplayCustomizeState.hx b/source_duck/GameplayCustomizeState.hx new file mode 100644 index 00000000..b3991c80 --- /dev/null +++ b/source_duck/GameplayCustomizeState.hx @@ -0,0 +1,213 @@ +import flixel.math.FlxMath; +import flixel.FlxCamera; +import flixel.math.FlxPoint; +import flixel.FlxObject; +#if windows +import Discord.DiscordClient; +import sys.thread.Thread; +#end + +import flixel.group.FlxGroup.FlxTypedGroup; +import openfl.ui.Keyboard; +import flixel.FlxSprite; +import flixel.FlxG; + +class GameplayCustomizeState extends MusicBeatState +{ + + var defaultX:Float = FlxG.width * 0.55 - 135; + var defaultY:Float = FlxG.height / 2 - 50; + + var background:FlxSprite = new FlxSprite(-600, -200).loadGraphic(Paths.image('stageback','shared')); + var curt:FlxSprite = new FlxSprite(-500, -300).loadGraphic(Paths.image('stagecurtains','shared')); + var front:FlxSprite = new FlxSprite(-650, 600).loadGraphic(Paths.image('stagefront','shared')); + + var sick:FlxSprite = new FlxSprite().loadGraphic(Paths.image('sick','shared')); + + var bf:Boyfriend = new Boyfriend(770, 450, 'bf'); + var dad:Character; + + var strumLine:FlxSprite; + var strumLineNotes:FlxTypedGroup; + var playerStrums:FlxTypedGroup; + private var camHUD:FlxCamera; + + public override function create() { + #if windows + // Updating Discord Rich Presence + DiscordClient.changePresence("Customizing Gameplay", null); + #end + + Conductor.changeBPM(102); + persistentUpdate = true; + + super.create(); + + camHUD = new FlxCamera(); + camHUD.bgColor.alpha = 0; + FlxG.cameras.add(camHUD); + + background.scrollFactor.set(0.9,0.9); + curt.scrollFactor.set(0.9,0.9); + front.scrollFactor.set(0.9,0.9); + + add(background); + add(front); + add(curt); + + var camFollow = new FlxObject(0, 0, 1, 1); + + dad = new Character(100, 100, 'dad'); + + var camPos:FlxPoint = new FlxPoint(dad.getGraphicMidpoint().x + 400, dad.getGraphicMidpoint().y); + + camFollow.setPosition(camPos.x, camPos.y); + + add(bf); + add(dad); + + add(sick); + + add(camFollow); + + FlxG.camera.follow(camFollow, LOCKON, 0.01); + // FlxG.camera.setScrollBounds(0, FlxG.width, 0, FlxG.height); + FlxG.camera.zoom = 0.9; + FlxG.camera.focusOn(camFollow.getPosition()); + + strumLine = new FlxSprite(0, 25).makeGraphic(FlxG.width, 10); + strumLine.scrollFactor.set(); + + if (FlxG.save.data.downscroll) + strumLine.y = FlxG.height - 165; + + strumLineNotes = new FlxTypedGroup(); + add(strumLineNotes); + + playerStrums = new FlxTypedGroup(); + + sick.cameras = [camHUD]; + strumLine.cameras = [camHUD]; + playerStrums.cameras = [camHUD]; + + generateStaticArrows(0); + generateStaticArrows(1); + + + if (!FlxG.save.data.changedHit) + { + FlxG.save.data.changedHitX = defaultX; + FlxG.save.data.changedHitY = defaultY; + } + + sick.x = FlxG.save.data.changedHitX; + sick.y = FlxG.save.data.changedHitY; + + sick.updateHitbox(); + + FlxG.mouse.visible = true; + + } + + override function update(elapsed:Float) { + if (FlxG.sound.music != null) + Conductor.songPosition = FlxG.sound.music.time; + + super.update(elapsed); + + FlxG.camera.zoom = FlxMath.lerp(0.9, FlxG.camera.zoom, 0.95); + camHUD.zoom = FlxMath.lerp(1, camHUD.zoom, 0.95); + + if (FlxG.mouse.overlaps(sick) && FlxG.mouse.pressed) + { + sick.x = FlxG.mouse.x - sick.width / 2; + sick.y = FlxG.mouse.y - sick.height / 2; + } + + if (FlxG.mouse.overlaps(sick) && FlxG.mouse.justReleased) + { + FlxG.save.data.changedHitX = sick.x; + FlxG.save.data.changedHitY = sick.y; + FlxG.save.data.changedHit = true; + } + + if (controls.BACK) + { + FlxG.mouse.visible = false; + FlxG.sound.play(Paths.sound('cancelMenu')); + FlxG.switchState(new OptionsMenu()); + } + + } + + override function beatHit() + { + super.beatHit(); + + bf.playAnim('idle'); + dad.dance(); + + FlxG.camera.zoom += 0.015; + camHUD.zoom += 0.010; + + trace('beat'); + + } + + + // ripped from play state cuz im lazy + + private function generateStaticArrows(player:Int):Void + { + for (i in 0...4) + { + // FlxG.log.add(i); + var babyArrow:FlxSprite = new FlxSprite(0, strumLine.y); + babyArrow.frames = Paths.getSparrowAtlas('NOTE_assets', 'shared'); + babyArrow.animation.addByPrefix('green', 'arrowUP'); + babyArrow.animation.addByPrefix('blue', 'arrowDOWN'); + babyArrow.animation.addByPrefix('purple', 'arrowLEFT'); + babyArrow.animation.addByPrefix('red', 'arrowRIGHT'); + babyArrow.antialiasing = true; + babyArrow.setGraphicSize(Std.int(babyArrow.width * 0.7)); + switch (Math.abs(i)) + { + case 0: + babyArrow.x += Note.swagWidth * 0; + babyArrow.animation.addByPrefix('static', 'arrowLEFT'); + babyArrow.animation.addByPrefix('pressed', 'left press', 24, false); + babyArrow.animation.addByPrefix('confirm', 'left confirm', 24, false); + case 1: + babyArrow.x += Note.swagWidth * 1; + babyArrow.animation.addByPrefix('static', 'arrowDOWN'); + babyArrow.animation.addByPrefix('pressed', 'down press', 24, false); + babyArrow.animation.addByPrefix('confirm', 'down confirm', 24, false); + case 2: + babyArrow.x += Note.swagWidth * 2; + babyArrow.animation.addByPrefix('static', 'arrowUP'); + babyArrow.animation.addByPrefix('pressed', 'up press', 24, false); + babyArrow.animation.addByPrefix('confirm', 'up confirm', 24, false); + case 3: + babyArrow.x += Note.swagWidth * 3; + babyArrow.animation.addByPrefix('static', 'arrowRIGHT'); + babyArrow.animation.addByPrefix('pressed', 'right press', 24, false); + babyArrow.animation.addByPrefix('confirm', 'right confirm', 24, false); + } + babyArrow.updateHitbox(); + babyArrow.scrollFactor.set(); + + babyArrow.ID = i; + + if (player == 1) + { + playerStrums.add(babyArrow); + } + + babyArrow.animation.play('static'); + babyArrow.x += 50; + babyArrow.x += ((FlxG.width / 2) * player); + + strumLineNotes.add(babyArrow); + } + } +} \ No newline at end of file diff --git a/source_duck/GitarooPause.hx b/source_duck/GitarooPause.hx new file mode 100644 index 00000000..cb9705c5 --- /dev/null +++ b/source_duck/GitarooPause.hx @@ -0,0 +1,88 @@ +package; + +import flixel.FlxG; +import flixel.FlxSprite; +import flixel.graphics.frames.FlxAtlasFrames; + +class GitarooPause extends MusicBeatState +{ + var replayButton:FlxSprite; + var cancelButton:FlxSprite; + + var replaySelect:Bool = false; + + public function new():Void + { + super(); + } + + override function create() + { + if (FlxG.sound.music != null) + FlxG.sound.music.stop(); + + var bg:FlxSprite = new FlxSprite().loadGraphic(Paths.image('pauseAlt/pauseBG')); + add(bg); + + var bf:FlxSprite = new FlxSprite(0, 30); + bf.frames = Paths.getSparrowAtlas('pauseAlt/bfLol'); + bf.animation.addByPrefix('lol', "funnyThing", 13); + bf.animation.play('lol'); + add(bf); + bf.screenCenter(X); + + replayButton = new FlxSprite(FlxG.width * 0.28, FlxG.height * 0.7); + replayButton.frames = Paths.getSparrowAtlas('pauseAlt/pauseUI'); + replayButton.animation.addByPrefix('selected', 'bluereplay', 0, false); + replayButton.animation.appendByPrefix('selected', 'yellowreplay'); + replayButton.animation.play('selected'); + add(replayButton); + + cancelButton = new FlxSprite(FlxG.width * 0.58, replayButton.y); + cancelButton.frames = Paths.getSparrowAtlas('pauseAlt/pauseUI'); + cancelButton.animation.addByPrefix('selected', 'bluecancel', 0, false); + cancelButton.animation.appendByPrefix('selected', 'cancelyellow'); + cancelButton.animation.play('selected'); + add(cancelButton); + + changeThing(); + + super.create(); + } + + override function update(elapsed:Float) + { + if (controls.LEFT_P || controls.RIGHT_P) + changeThing(); + + if (controls.ACCEPT) + { + if (replaySelect) + { + FlxG.switchState(new PlayState()); + } + else + { + FlxG.switchState(new MainMenuState()); + } + } + + super.update(elapsed); + } + + function changeThing():Void + { + replaySelect = !replaySelect; + + if (replaySelect) + { + cancelButton.animation.curAnim.curFrame = 0; + replayButton.animation.curAnim.curFrame = 1; + } + else + { + cancelButton.animation.curAnim.curFrame = 1; + replayButton.animation.curAnim.curFrame = 0; + } + } +} diff --git a/source_duck/GlobalVideo.hx b/source_duck/GlobalVideo.hx new file mode 100644 index 00000000..2c4f7078 --- /dev/null +++ b/source_duck/GlobalVideo.hx @@ -0,0 +1,95 @@ +package; + +import openfl.Lib; + +class GlobalVideo +{ + private static var video:VideoHandler; + private static var webm:WebmHandler; + public static var isWebm:Bool = false; + public static var isAndroid:Bool = false; + public static var daAlpha1:Float = 0.2; + public static var daAlpha2:Float = 1; + + public static function setVid(vid:VideoHandler):Void + { + video = vid; + } + + public static function getVid():VideoHandler + { + return video; + } + + public static function setWebm(vid:WebmHandler):Void + { + webm = vid; + isWebm = true; + } + + public static function getWebm():WebmHandler + { + return webm; + } + + public static function get():Dynamic + { + if (isWebm) + { + return getWebm(); + } else { + return getVid(); + } + } + + public static function calc(ind:Int):Dynamic + { + var stageWidth:Int = Lib.current.stage.stageWidth; + var stageHeight:Int = Lib.current.stage.stageHeight; + + var width:Float = GameDimensions.width; + var height:Float = GameDimensions.height; + + //trace("AH: " + stageWidth); + //trace(width); + + var ratioX:Float = height / width; + var ratioY:Float = width / height; + var appliedWidth:Float = stageHeight * ratioY; + var appliedHeight:Float = stageWidth * ratioX; + //trace(appliedWidth); + var remainingX:Float = stageWidth - appliedWidth; + var remainingY:Float = stageHeight - appliedHeight; + remainingX = remainingX / 2; + remainingY = remainingY / 2; + + appliedWidth = Std.int(appliedWidth); + appliedHeight = Std.int(appliedHeight); + + if (appliedHeight > stageHeight) + { + remainingY = 0; + appliedHeight = stageHeight; + } + + if (appliedWidth > stageWidth) + { + remainingX = 0; + appliedWidth = stageWidth; + } + + switch(ind) + { + case 0: + return remainingX; + case 1: + return remainingY; + case 2: + return appliedWidth; + case 3: + return appliedHeight; + } + + return null; + } +} \ No newline at end of file diff --git a/source_duck/HealthIcon.hx b/source_duck/HealthIcon.hx new file mode 100644 index 00000000..a443be8a --- /dev/null +++ b/source_duck/HealthIcon.hx @@ -0,0 +1,59 @@ +package; + +import flixel.FlxSprite; + +class HealthIcon extends FlxSprite +{ + /** + * Used for FreeplayState! If you use it elsewhere, prob gonna annoying + */ + public var sprTracker:FlxSprite; + + public function new(char:String = 'bf', isPlayer:Bool = false) + { + super(); + + loadGraphic(Paths.image('iconGrid'), true, 150, 150); + + antialiasing = true; + animation.add('bf', [0, 1], 0, false, isPlayer); + animation.add('bf-car', [0, 1], 0, false, isPlayer); + animation.add('bf-christmas', [0, 1], 0, false, isPlayer); + animation.add('bf-pixel', [21, 21], 0, false, isPlayer); + animation.add('spooky', [2, 3], 0, false, isPlayer); + animation.add('pico', [4, 5], 0, false, isPlayer); + animation.add('mom', [6, 7], 0, false, isPlayer); + animation.add('mom-car', [6, 7], 0, false, isPlayer); + animation.add('tankman', [8, 9], 0, false, isPlayer); + animation.add('face', [10, 11], 0, false, isPlayer); + animation.add('dad', [12, 13], 0, false, isPlayer); + animation.add('senpai', [22, 22], 0, false, isPlayer); + animation.add('senpai-angry', [22, 22], 0, false, isPlayer); + animation.add('spirit', [23, 23], 0, false, isPlayer); + animation.add('bf-old', [14, 15], 0, false, isPlayer); + animation.add('gf', [16], 0, false, isPlayer); + animation.add('gf-christmas', [16], 0, false, isPlayer); + animation.add('gf-pixel', [16], 0, false, isPlayer); + animation.add('parents-christmas', [17, 18], 0, false, isPlayer); + animation.add('monster', [19, 20], 0, false, isPlayer); + animation.add('monster-christmas', [19, 20], 0, false, isPlayer); + animation.add('duck', [24, 25], 0, false, isPlayer); + animation.play(char); + + switch (char) + { + case 'bf-pixel' | 'senpai' | 'senpai-angry' | 'spirit' | 'gf-pixel': + antialiasing = false; + } + + scrollFactor.set(); + } + + override function update(elapsed:Float) + { + super.update(elapsed); + + if (sprTracker != null) + setPosition(sprTracker.x + sprTracker.width + 10, sprTracker.y - 30); + } +} diff --git a/source_duck/HelperFunctions.hx b/source_duck/HelperFunctions.hx new file mode 100644 index 00000000..8f6c6fca --- /dev/null +++ b/source_duck/HelperFunctions.hx @@ -0,0 +1,9 @@ +class HelperFunctions +{ + public static function truncateFloat( number : Float, precision : Int): Float { + var num = number; + num = num * Math.pow(10, precision); + num = Math.round( num ) / Math.pow(10, precision); + return num; + } +} \ No newline at end of file diff --git a/source_duck/Highscore.hx b/source_duck/Highscore.hx new file mode 100644 index 00000000..5fbc7329 --- /dev/null +++ b/source_duck/Highscore.hx @@ -0,0 +1,98 @@ +package; + +import flixel.FlxG; + +class Highscore +{ + #if (haxe >= "4.0.0") + public static var songScores:Map = new Map(); + #else + public static var songScores:Map = new Map(); + #end + + + public static function saveScore(song:String, score:Int = 0, ?diff:Int = 0):Void + { + var daSong:String = formatSong(song, diff); + + + #if !switch + NGio.postScore(score, song); + #end + + + if (songScores.exists(daSong)) + { + if (songScores.get(daSong) < score) + setScore(daSong, score); + } + else + setScore(daSong, score); + } + + public static function saveWeekScore(week:Int = 1, score:Int = 0, ?diff:Int = 0):Void + { + + #if !switch + NGio.postScore(score, "Week " + week); + #end + + + var daWeek:String = formatSong('week' + week, diff); + + if (songScores.exists(daWeek)) + { + if (songScores.get(daWeek) < score) + setScore(daWeek, score); + } + else + setScore(daWeek, score); + } + + /** + * YOU SHOULD FORMAT SONG WITH formatSong() BEFORE TOSSING IN SONG VARIABLE + */ + static function setScore(song:String, score:Int):Void + { + // Reminder that I don't need to format this song, it should come formatted! + songScores.set(song, score); + FlxG.save.data.songScores = songScores; + FlxG.save.flush(); + } + + public static function formatSong(song:String, diff:Int):String + { + var daSong:String = song; + + if (diff == 0) + daSong += '-easy'; + else if (diff == 2) + daSong += '-hard'; + + return daSong; + } + + public static function getScore(song:String, diff:Int):Int + { + if (!songScores.exists(formatSong(song, diff))) + setScore(formatSong(song, diff), 0); + + return songScores.get(formatSong(song, diff)); + } + + public static function getWeekScore(week:Int, diff:Int):Int + { + if (!songScores.exists(formatSong('week' + week, diff))) + setScore(formatSong('week' + week, diff), 0); + + return songScores.get(formatSong('week' + week, diff)); + } + + public static function load():Void + { + if (FlxG.save.data.songScores != null) + { + songScores = FlxG.save.data.songScores; + } + } +} diff --git a/source_duck/HitGraph.hx b/source_duck/HitGraph.hx new file mode 100644 index 00000000..169d8231 --- /dev/null +++ b/source_duck/HitGraph.hx @@ -0,0 +1,279 @@ +import flixel.FlxG; +import openfl.display.Bitmap; +import openfl.display.BitmapData; +import openfl.text.TextFieldAutoSize; +import flixel.system.FlxAssets; +import openfl.text.TextFormat; +import flash.display.Graphics; +import flash.display.Shape; +import flash.display.Sprite; +import flash.text.TextField; +import flash.text.TextFormatAlign; +import flixel.math.FlxMath; +import flixel.util.FlxColor; +import flixel.util.FlxDestroyUtil; + +/** + * stolen from https://github.com/HaxeFlixel/flixel/blob/master/flixel/system/debug/stats/StatsGraph.hx + */ +class HitGraph extends Sprite +{ + static inline var AXIS_COLOR:FlxColor = 0xffffff; + static inline var AXIS_ALPHA:Float = 0.5; + inline static var HISTORY_MAX:Int = 30; + + public var minLabel:TextField; + public var curLabel:TextField; + public var maxLabel:TextField; + public var avgLabel:TextField; + + public var minValue:Float = -(Math.floor((PlayState.rep.replay.sf / 60) * 1000) + 95); + public var maxValue:Float = Math.floor((PlayState.rep.replay.sf / 60) * 1000) + 95; + + public var showInput:Bool = FlxG.save.data.inputShow; + + public var graphColor:FlxColor; + + public var history:Array = []; + + public var bitmap:Bitmap; + + var _axis:Shape; + var _width:Int; + var _height:Int; + var _unit:String; + var _labelWidth:Int; + var _label:String; + + public function new(X:Int, Y:Int, Width:Int, Height:Int) + { + super(); + x = X; + y = Y; + _width = Width; + _height = Height; + + var bm = new BitmapData(Width,Height); + bm.draw(this); + bitmap = new Bitmap(bm); + + _axis = new Shape(); + _axis.x = _labelWidth + 10; + + var ts = Math.floor((PlayState.rep.replay.sf / 60) * 1000) / 166; + + var early = createTextField(10,10,FlxColor.WHITE,12); + var late = createTextField(10,_height - 20,FlxColor.WHITE,12); + + early.text = "Early (" + -166 * ts + "ms)"; + late.text = "Late (" + 166 * ts + "ms)"; + + addChild(early); + addChild(late); + + addChild(_axis); + + drawAxes(); + } + + /** + * Redraws the axes of the graph. + */ + function drawAxes():Void + { + var gfx = _axis.graphics; + gfx.clear(); + gfx.lineStyle(1, AXIS_COLOR, AXIS_ALPHA); + + // y-Axis + gfx.moveTo(0, 0); + gfx.lineTo(0, _height); + + // x-Axis + gfx.moveTo(0, _height); + gfx.lineTo(_width, _height); + + gfx.moveTo(0, _height / 2); + gfx.lineTo(_width, _height / 2); + + } + + public static function createTextField(X:Float = 0, Y:Float = 0, Color:FlxColor = FlxColor.WHITE, Size:Int = 12):TextField + { + return initTextField(new TextField(), X, Y, Color, Size); + } + + public static function initTextField(tf:T, X:Float = 0, Y:Float = 0, Color:FlxColor = FlxColor.WHITE, Size:Int = 12):T + { + tf.x = X; + tf.y = Y; + tf.multiline = false; + tf.wordWrap = false; + tf.embedFonts = true; + tf.selectable = false; + #if flash + tf.antiAliasType = AntiAliasType.NORMAL; + tf.gridFitType = GridFitType.PIXEL; + #end + tf.defaultTextFormat = new TextFormat("assets/fonts/vcr.ttf", Size, Color.to24Bit()); + tf.alpha = Color.alphaFloat; + tf.autoSize = TextFieldAutoSize.LEFT; + return tf; + } + + function drawJudgementLine(ms:Float):Void + { + + var gfx:Graphics = graphics; + + gfx.lineStyle(1, graphColor, 0.3); + + var ts = Math.floor((PlayState.rep.replay.sf / 60) * 1000) / 166; + var range:Float = Math.max(maxValue - minValue, maxValue * 0.1); + + var value = ((ms * ts) - minValue) / range; + + var pointY = _axis.y + ((-value * _height - 1) + _height); + + var graphX = _axis.x + 1; + + if (ms == 45) + gfx.moveTo(graphX, _axis.y + pointY); + + var graphX = _axis.x + 1; + + gfx.drawRect(graphX,pointY, _width,1); + + gfx.lineStyle(1, graphColor, 1); + } + + /** + * Redraws the graph based on the values stored in the history. + */ + function drawGraph():Void + { + var gfx:Graphics = graphics; + gfx.clear(); + gfx.lineStyle(1, graphColor, 1); + + gfx.beginFill(0x00FF00); + drawJudgementLine(45); + gfx.endFill(); + + gfx.beginFill(0xFF0000); + drawJudgementLine(90); + gfx.endFill(); + + gfx.beginFill(0x8b0000); + drawJudgementLine(135); + gfx.endFill(); + + gfx.beginFill(0x580000); + drawJudgementLine(166); + gfx.endFill(); + + gfx.beginFill(0x00FF00); + drawJudgementLine(-45); + gfx.endFill(); + + gfx.beginFill(0xFF0000); + drawJudgementLine(-90); + gfx.endFill(); + + gfx.beginFill(0x8b0000); + drawJudgementLine(-135); + gfx.endFill(); + + gfx.beginFill(0x580000); + drawJudgementLine(-166); + gfx.endFill(); + + var range:Float = Math.max(maxValue - minValue, maxValue * 0.1); + var graphX = _axis.x + 1; + + if (showInput) + { + for (i in 0...PlayState.rep.replay.ana.anaArray.length) + { + var ana = PlayState.rep.replay.ana.anaArray[i]; + + var value = (ana.key * 25 - minValue) / range; + + if (ana.hit) + gfx.beginFill(0xFFFF00); + else + gfx.beginFill(0xC2B280); + + if (ana.hitTime < 0) + continue; + + var pointY = (-value * _height - 1) + _height; + gfx.drawRect(graphX + fitX(ana.hitTime), pointY,2,2); + gfx.endFill(); + } + } + + for (i in 0...history.length) + { + var value = (history[i][0] - minValue) / range; + var judge = history[i][1]; + + switch(judge) + { + case "sick": + gfx.beginFill(0x00FFFF); + case "good": + gfx.beginFill(0x00FF00); + case "bad": + gfx.beginFill(0xFF0000); + case "shit": + gfx.beginFill(0x8b0000); + case "miss": + gfx.beginFill(0x580000); + default: + gfx.beginFill(0xFFFFFF); + } + var pointY = (-value * _height - 1) + _height; + + /*if (i == 0) + gfx.moveTo(graphX, _axis.y + pointY);*/ + gfx.drawRect(fitX(history[i][2]), pointY,4,4); + + gfx.endFill(); + } + + + var bm = new BitmapData(_width,_height); + bm.draw(this); + bitmap = new Bitmap(bm); + } + + public function fitX(x:Float) + { + return (x / FlxG.sound.music.length) * width; + } + + public function addToHistory(diff:Float, judge:String, time:Float) + { + history.push([diff,judge, time]); + } + + public function update():Void + { + drawGraph(); + } + + public function average():Float + { + var sum:Float = 0; + for (value in history) + sum += value; + return sum / history.length; + } + + public function destroy():Void + { + _axis = FlxDestroyUtil.removeChild(this, _axis); + history = null; + } +} \ No newline at end of file diff --git a/source_duck/KadeEngineData.hx b/source_duck/KadeEngineData.hx new file mode 100644 index 00000000..7978a418 --- /dev/null +++ b/source_duck/KadeEngineData.hx @@ -0,0 +1,72 @@ +import openfl.Lib; +import flixel.FlxG; + +class KadeEngineData +{ + public static function initSave() + { + if (FlxG.save.data.newInput == null) + FlxG.save.data.newInput = true; + + if (FlxG.save.data.downscroll == null) + FlxG.save.data.downscroll = false; + + if (FlxG.save.data.dfjk == null) + FlxG.save.data.dfjk = false; + + if (FlxG.save.data.accuracyDisplay == null) + FlxG.save.data.accuracyDisplay = true; + + if (FlxG.save.data.offset == null) + FlxG.save.data.offset = 0; + + if (FlxG.save.data.songPosition == null) + FlxG.save.data.songPosition = false; + + if (FlxG.save.data.fps == null) + FlxG.save.data.fps = false; + + if (FlxG.save.data.changedHit == null) + { + FlxG.save.data.changedHitX = -1; + FlxG.save.data.changedHitY = -1; + FlxG.save.data.changedHit = false; + } + + if (FlxG.save.data.fpsRain == null) + FlxG.save.data.fpsRain = false; + + if (FlxG.save.data.fpsCap == null) + FlxG.save.data.fpsCap = 120; + + if (FlxG.save.data.fpsCap > 285 || FlxG.save.data.fpsCap < 60) + FlxG.save.data.fpsCap = 120; // baby proof so you can't hard lock ur copy of kade engine + + if (FlxG.save.data.scrollSpeed == null) + FlxG.save.data.scrollSpeed = 1; + + if (FlxG.save.data.npsDisplay == null) + FlxG.save.data.npsDisplay = false; + + if (FlxG.save.data.frames == null) + FlxG.save.data.frames = 10; + + if (FlxG.save.data.accuracyMod == null) + FlxG.save.data.accuracyMod = 1; + + if (FlxG.save.data.watermark == null) + FlxG.save.data.watermark = true; + + if (FlxG.save.data.ghost == null) + FlxG.save.data.ghost = true; + + if (FlxG.save.data.distractions == null) + FlxG.save.data.distractions = true; + + Conductor.recalculateTimings(); + + Main.watermarks = FlxG.save.data.watermark; + + (cast (Lib.current.getChildAt(0), Main)).setFPSCap(FlxG.save.data.fpsCap); + } +} diff --git a/source_duck/KeyBindMenu.hx b/source_duck/KeyBindMenu.hx new file mode 100644 index 00000000..d5a3d41f --- /dev/null +++ b/source_duck/KeyBindMenu.hx @@ -0,0 +1,402 @@ +package; + +/// Code created by Rozebud for FPS Plus (thanks rozebud) +// modified by KadeDev for use in Kade Engine/Tricky + +import flixel.input.gamepad.FlxGamepad; +import flixel.util.FlxAxes; +import flixel.FlxSubState; +import Options.Option; +import flixel.input.FlxInput; +import flixel.input.keyboard.FlxKey; +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.text.FlxText; +import flixel.tweens.FlxEase; +import flixel.tweens.FlxTween; +import flixel.util.FlxColor; +import io.newgrounds.NG; +import lime.app.Application; +import lime.utils.Assets; +import flixel.math.FlxMath; +import flixel.text.FlxText; +import flixel.input.FlxKeyManager; + + +using StringTools; + +class KeyBindMenu extends FlxSubState +{ + + var keyTextDisplay:FlxText; + var keyWarning:FlxText; + var warningTween:FlxTween; + var keyText:Array = ["LEFT", "DOWN", "UP", "RIGHT"]; + var defaultKeys:Array = ["A", "S", "W", "D", "R"]; + var defaultGpKeys:Array = ["DPAD_LEFT", "DPAD_DOWN", "DPAD_UP", "DPAD_RIGHT"]; + var curSelected:Int = 0; + + var keys:Array = [FlxG.save.data.leftBind, + FlxG.save.data.downBind, + FlxG.save.data.upBind, + FlxG.save.data.rightBind]; + var gpKeys:Array = [FlxG.save.data.gpleftBind, + FlxG.save.data.gpdownBind, + FlxG.save.data.gpupBind, + FlxG.save.data.gprightBind]; + var tempKey:String = ""; + var blacklist:Array = ["ESCAPE", "ENTER", "BACKSPACE", "SPACE", "TAB"]; + + var blackBox:FlxSprite; + var infoText:FlxText; + + var state:String = "select"; + + override function create() + { + + for (i in 0...keys.length) + { + var k = keys[i]; + if (k == null) + keys[i] = defaultKeys[i]; + } + + for (i in 0...gpKeys.length) + { + var k = gpKeys[i]; + if (k == null) + gpKeys[i] = defaultGpKeys[i]; + } + + //FlxG.sound.playMusic('assets/music/configurator' + TitleState.soundExt); + + persistentUpdate = true; + + keyTextDisplay = new FlxText(-10, 0, 1280, "", 72); + keyTextDisplay.scrollFactor.set(0, 0); + keyTextDisplay.setFormat("VCR OSD Mono", 42, FlxColor.WHITE, FlxTextAlign.CENTER, FlxTextBorderStyle.OUTLINE, FlxColor.BLACK); + keyTextDisplay.borderSize = 2; + keyTextDisplay.borderQuality = 3; + + blackBox = new FlxSprite(0,0).makeGraphic(FlxG.width,FlxG.height,FlxColor.BLACK); + add(blackBox); + + infoText = new FlxText(-10, 580, 1280, 'Current Mode: ${KeyBinds.gamepad ? 'GAMEPAD' : 'KEYBOARD'}. Press TAB to switch\n(${KeyBinds.gamepad ? 'RIGHT Trigger' : 'Escape'} to save, ${KeyBinds.gamepad ? 'LEFT Trigger' : 'Backspace'} to leave without saving. ${KeyBinds.gamepad ? 'START To change a keybind' : ''})', 72); + infoText.scrollFactor.set(0, 0); + infoText.setFormat("VCR OSD Mono", 24, FlxColor.WHITE, FlxTextAlign.CENTER, FlxTextBorderStyle.OUTLINE, FlxColor.BLACK); + infoText.borderSize = 2; + infoText.borderQuality = 3; + infoText.alpha = 0; + infoText.screenCenter(FlxAxes.X); + add(infoText); + add(keyTextDisplay); + + blackBox.alpha = 0; + keyTextDisplay.alpha = 0; + + FlxTween.tween(keyTextDisplay, {alpha: 1}, 1, {ease: FlxEase.expoInOut}); + FlxTween.tween(infoText, {alpha: 1}, 1.4, {ease: FlxEase.expoInOut}); + FlxTween.tween(blackBox, {alpha: 0.7}, 1, {ease: FlxEase.expoInOut}); + + OptionsMenu.instance.acceptInput = false; + + textUpdate(); + + super.create(); + } + + var frames = 0; + + override function update(elapsed:Float) + { + var gamepad:FlxGamepad = FlxG.gamepads.lastActive; + + if (frames <= 10) + frames++; + + switch(state){ + + case "select": + if (FlxG.keys.justPressed.UP) + { + FlxG.sound.play(Paths.sound('scrollMenu')); + changeItem(-1); + } + + if (FlxG.keys.justPressed.DOWN) + { + FlxG.sound.play(Paths.sound('scrollMenu')); + changeItem(1); + } + + if (FlxG.keys.justPressed.TAB) + { + KeyBinds.gamepad = !KeyBinds.gamepad; + infoText.text = 'Current Mode: ${KeyBinds.gamepad ? 'GAMEPAD' : 'KEYBOARD'}. Press TAB to switch\n(${KeyBinds.gamepad ? 'RIGHT Trigger' : 'Escape'} to save, ${KeyBinds.gamepad ? 'LEFT Trigger' : 'Backspace'} to leave without saving. ${KeyBinds.gamepad ? 'START To change a keybind' : ''})'; + textUpdate(); + } + + if (FlxG.keys.justPressed.ENTER){ + FlxG.sound.play(Paths.sound('scrollMenu')); + state = "input"; + } + else if(FlxG.keys.justPressed.ESCAPE){ + quit(); + } + else if (FlxG.keys.justPressed.BACKSPACE){ + reset(); + } + if (gamepad != null) // GP Logic + { + if (gamepad.justPressed.DPAD_UP) + { + FlxG.sound.play(Paths.sound('scrollMenu')); + changeItem(-1); + textUpdate(); + } + if (gamepad.justPressed.DPAD_DOWN) + { + FlxG.sound.play(Paths.sound('scrollMenu')); + changeItem(1); + textUpdate(); + } + + if (gamepad.justPressed.START && frames > 10){ + FlxG.sound.play(Paths.sound('scrollMenu')); + state = "input"; + } + else if(gamepad.justPressed.LEFT_TRIGGER){ + quit(); + } + else if (gamepad.justPressed.RIGHT_TRIGGER){ + reset(); + } + } + + case "input": + tempKey = keys[curSelected]; + keys[curSelected] = "?"; + if (KeyBinds.gamepad) + gpKeys[curSelected] = "?"; + textUpdate(); + state = "waiting"; + + case "waiting": + if (gamepad != null && KeyBinds.gamepad) // GP Logic + { + if(FlxG.keys.justPressed.ESCAPE){ // just in case you get stuck + gpKeys[curSelected] = tempKey; + state = "select"; + FlxG.sound.play(Paths.sound('confirmMenu')); + } + + if (gamepad.justPressed.START) + { + addKeyGamepad(defaultKeys[curSelected]); + save(); + state = "select"; + } + + if (gamepad.justPressed.ANY) + { + trace(gamepad.firstJustPressedID()); + addKeyGamepad(gamepad.firstJustPressedID()); + save(); + state = "select"; + textUpdate(); + } + + } + else + { + if(FlxG.keys.justPressed.ESCAPE){ + keys[curSelected] = tempKey; + state = "select"; + FlxG.sound.play(Paths.sound('confirmMenu')); + } + else if(FlxG.keys.justPressed.ENTER){ + addKey(defaultKeys[curSelected]); + save(); + state = "select"; + } + else if(FlxG.keys.justPressed.ANY){ + addKey(FlxG.keys.getIsDown()[0].ID.toString()); + save(); + state = "select"; + } + } + + + case "exiting": + + + default: + state = "select"; + + } + + if(FlxG.keys.justPressed.ANY) + textUpdate(); + + super.update(elapsed); + + } + + function textUpdate(){ + + keyTextDisplay.text = "\n\n"; + + if (KeyBinds.gamepad) + { + for(i in 0...4){ + + var textStart = (i == curSelected) ? "> " : " "; + trace(gpKeys[i]); + keyTextDisplay.text += textStart + keyText[i] + ": " + gpKeys[i] + "\n"; + + } + } + else + { + for(i in 0...4){ + + var textStart = (i == curSelected) ? "> " : " "; + keyTextDisplay.text += textStart + keyText[i] + ": " + ((keys[i] != keyText[i]) ? (keys[i] + " / ") : "" ) + keyText[i] + " ARROW\n"; + + } + } + + keyTextDisplay.screenCenter(); + + } + + function save(){ + + FlxG.save.data.upBind = keys[2]; + FlxG.save.data.downBind = keys[1]; + FlxG.save.data.leftBind = keys[0]; + FlxG.save.data.rightBind = keys[3]; + + FlxG.save.data.gpupBind = gpKeys[2]; + FlxG.save.data.gpdownBind = gpKeys[1]; + FlxG.save.data.gpleftBind = gpKeys[0]; + FlxG.save.data.gprightBind = gpKeys[3]; + + FlxG.save.flush(); + + PlayerSettings.player1.controls.loadKeyBinds(); + + } + + function reset(){ + + for(i in 0...5){ + keys[i] = defaultKeys[i]; + } + quit(); + + } + + function quit(){ + + state = "exiting"; + + save(); + + OptionsMenu.instance.acceptInput = true; + + FlxTween.tween(keyTextDisplay, {alpha: 0}, 1, {ease: FlxEase.expoInOut}); + FlxTween.tween(blackBox, {alpha: 0}, 1.1, {ease: FlxEase.expoInOut, onComplete: function(flx:FlxTween){close();}}); + FlxTween.tween(infoText, {alpha: 0}, 1, {ease: FlxEase.expoInOut}); + } + + + function addKeyGamepad(r:String){ + + var shouldReturn:Bool = true; + + var notAllowed:Array = ["START", "RIGHT_TRIGGER", "LEFT_TRIGGER"]; + + for(x in 0...gpKeys.length) + { + var oK = gpKeys[x]; + if(oK == r) + gpKeys[x] = null; + if (notAllowed.contains(oK)) + { + gpKeys[x] = null; + return; + } + } + + if(shouldReturn){ + gpKeys[curSelected] = r; + FlxG.sound.play(Paths.sound('scrollMenu')); + } + else{ + gpKeys[curSelected] = tempKey; + FlxG.sound.play(Paths.sound('scrollMenu')); + keyWarning.alpha = 1; + warningTween.cancel(); + warningTween = FlxTween.tween(keyWarning, {alpha: 0}, 0.5, {ease: FlxEase.circOut, startDelay: 2}); + } + + } + + function addKey(r:String){ + + var shouldReturn:Bool = true; + + var notAllowed:Array = []; + + for(x in blacklist){notAllowed.push(x);} + + trace(notAllowed); + + for(x in 0...keys.length) + { + var oK = keys[x]; + if(oK == r) + keys[x] = null; + if (notAllowed.contains(oK)) + { + keys[x] = null; + return; + } + } + + if (r.contains("NUMPAD")) + { + keys[curSelected] = null; + return; + } + + if(shouldReturn){ + keys[curSelected] = r; + FlxG.sound.play(Paths.sound('scrollMenu')); + } + else{ + keys[curSelected] = tempKey; + FlxG.sound.play(Paths.sound('scrollMenu')); + keyWarning.alpha = 1; + warningTween.cancel(); + warningTween = FlxTween.tween(keyWarning, {alpha: 0}, 0.5, {ease: FlxEase.circOut, startDelay: 2}); + } + + } + + function changeItem(_amount:Int = 0) + { + curSelected += _amount; + + if (curSelected > 3) + curSelected = 0; + if (curSelected < 0) + curSelected = 3; + } +} diff --git a/source_duck/KeyBinds.hx b/source_duck/KeyBinds.hx new file mode 100644 index 00000000..b9c704d2 --- /dev/null +++ b/source_duck/KeyBinds.hx @@ -0,0 +1,83 @@ +import flixel.FlxG; +import flixel.input.FlxInput; +import flixel.input.actions.FlxAction; +import flixel.input.actions.FlxActionInput; +import flixel.input.actions.FlxActionInputDigital; +import flixel.input.actions.FlxActionManager; +import flixel.input.actions.FlxActionSet; +import flixel.input.gamepad.FlxGamepadButton; +import flixel.input.gamepad.FlxGamepadInputID; +import flixel.input.keyboard.FlxKey; + +class KeyBinds +{ + + public static var gamepad:Bool = false; + + public static function resetBinds():Void{ + + FlxG.save.data.upBind = "W"; + FlxG.save.data.downBind = "S"; + FlxG.save.data.leftBind = "A"; + FlxG.save.data.rightBind = "D"; + FlxG.save.data.killBind = "R"; + FlxG.save.data.gpupBind = "DPAD_UP"; + FlxG.save.data.gpdownBind = "DPAD_DOWN"; + FlxG.save.data.gpleftBind = "DPAD_LEFT"; + FlxG.save.data.gprightBind = "DPAD_RIGHT"; + PlayerSettings.player1.controls.loadKeyBinds(); + + } + + public static function keyCheck():Void + { + if(FlxG.save.data.upBind == null){ + FlxG.save.data.upBind = "W"; + trace("No UP"); + } + if (StringTools.contains(FlxG.save.data.upBind,"NUMPAD")) + FlxG.save.data.upBind = "W"; + if(FlxG.save.data.downBind == null){ + FlxG.save.data.downBind = "S"; + trace("No DOWN"); + } + if (StringTools.contains(FlxG.save.data.downBind,"NUMPAD")) + FlxG.save.data.downBind = "S"; + if(FlxG.save.data.leftBind == null){ + FlxG.save.data.leftBind = "A"; + trace("No LEFT"); + } + if (StringTools.contains(FlxG.save.data.leftBind,"NUMPAD")) + FlxG.save.data.leftBind = "A"; + if(FlxG.save.data.rightBind == null){ + FlxG.save.data.rightBind = "D"; + trace("No RIGHT"); + } + if (StringTools.contains(FlxG.save.data.rightBind,"NUMPAD")) + FlxG.save.data.rightBind = "D"; + + if(FlxG.save.data.gpupBind == null){ + FlxG.save.data.gpupBind = "DPAD_UP"; + trace("No GUP"); + } + if(FlxG.save.data.gpdownBind == null){ + FlxG.save.data.gpdownBind = "DPAD_DOWN"; + trace("No GDOWN"); + } + if(FlxG.save.data.gpleftBind == null){ + FlxG.save.data.gpleftBind = "DPAD_LEFT"; + trace("No GLEFT"); + } + if(FlxG.save.data.gprightBind == null){ + FlxG.save.data.gprightBind = "DPAD_RIGHT"; + trace("No GRIGHT"); + } + if(FlxG.save.data.killBind == null){ + FlxG.save.data.killBind = "R"; + trace("No KILL"); + } + + trace('${FlxG.save.data.leftBind}-${FlxG.save.data.downBind}-${FlxG.save.data.upBind}-${FlxG.save.data.rightBind}'); + } + +} diff --git a/source_duck/LoadReplayState.hx b/source_duck/LoadReplayState.hx new file mode 100644 index 00000000..1be8679e --- /dev/null +++ b/source_duck/LoadReplayState.hx @@ -0,0 +1,201 @@ +package; + +import Controls.KeyboardScheme; +import Controls.Control; +import flash.text.TextField; +import flixel.FlxG; +import flixel.FlxSprite; +import flixel.addons.display.FlxGridOverlay; +import flixel.group.FlxGroup.FlxTypedGroup; +import flixel.input.keyboard.FlxKey; +import flixel.math.FlxMath; +import flixel.text.FlxText; +import flixel.util.FlxColor; +import lime.utils.Assets; +#if sys +import sys.io.File; +#end + +class LoadReplayState extends MusicBeatState +{ + var selector:FlxText; + var curSelected:Int = 0; + + var songs:Array = []; + + var controlsStrings:Array = []; + var actualNames:Array = []; + + private var grpControls:FlxTypedGroup; + var versionShit:FlxText; + var poggerDetails:FlxText; + override function create() + { + var menuBG:FlxSprite = new FlxSprite().loadGraphic(Paths.image('menuDesat')); + #if sys + controlsStrings = sys.FileSystem.readDirectory(Sys.getCwd() + "/assets/replays/"); + #end + trace(controlsStrings); + + controlsStrings.sort(Reflect.compare); + + addWeek(['Bopeebo', 'Fresh', 'Dadbattle'], 1, ['dad']); + addWeek(['Spookeez', 'South', 'Monster'], 2, ['spooky']); + addWeek(['Pico', 'Philly', 'Blammed'], 3, ['pico']); + + addWeek(['Satin-Panties', 'High', 'Milf'], 4, ['mom']); + addWeek(['Cocoa', 'Eggnog', 'Winter-Horrorland'], 5, ['parents-christmas', 'parents-christmas', 'monster-christmas']); + + addWeek(['Senpai', 'Roses', 'Thorns'], 6, ['senpai', 'senpai', 'spirit']); + + + for(i in 0...controlsStrings.length) + { + var string:String = controlsStrings[i]; + actualNames[i] = string; + var rep:Replay = Replay.LoadReplay(string); + controlsStrings[i] = string.split("time")[0] + " " + (rep.replay.songDiff == 2 ? "HARD" : rep.replay.songDiff == 1 ? "EASY" : "NORMAL"); + } + + if (controlsStrings.length == 0) + controlsStrings.push("No Replays..."); + + menuBG.color = 0xFFea71fd; + menuBG.setGraphicSize(Std.int(menuBG.width * 1.1)); + menuBG.updateHitbox(); + menuBG.screenCenter(); + menuBG.antialiasing = true; + add(menuBG); + + grpControls = new FlxTypedGroup(); + add(grpControls); + + for (i in 0...controlsStrings.length) + { + var controlLabel:Alphabet = new Alphabet(0, (70 * i) + 30, controlsStrings[i], true, false); + controlLabel.isMenuItem = true; + controlLabel.targetY = i; + grpControls.add(controlLabel); + // DONT PUT X IN THE FIRST PARAMETER OF new ALPHABET() !! + } + + + versionShit = new FlxText(5, FlxG.height - 34, 0, "Replay Loader (ESCAPE TO GO BACK)\nNOTICE!!!! Replays are in a beta stage, and they are probably not 100% correct. expect misses and other stuff that isn't there!", 12); + versionShit.scrollFactor.set(); + versionShit.setFormat("VCR OSD Mono", 16, FlxColor.WHITE, LEFT, FlxTextBorderStyle.OUTLINE, FlxColor.BLACK); + add(versionShit); + + + poggerDetails = new FlxText(5, 34, 0, "Replay Details - \nnone", 12); + poggerDetails.scrollFactor.set(); + poggerDetails.setFormat("VCR OSD Mono", 16, FlxColor.WHITE, LEFT, FlxTextBorderStyle.OUTLINE, FlxColor.BLACK); + add(poggerDetails); + + changeSelection(0); + + super.create(); + } + + public function getWeekNumbFromSong(songName:String):Int + { + var week:Int = 0; + for (i in 0...songs.length) + { + var pog:FreeplayState.SongMetadata = songs[i]; + if (pog.songName.toLowerCase() == songName) + week = pog.week; + } + return week; + } + + public function addSong(songName:String, weekNum:Int, songCharacter:String) + { + songs.push(new FreeplayState.SongMetadata(songName, weekNum, songCharacter)); + } + + public function addWeek(songs:Array, weekNum:Int, ?songCharacters:Array) + { + if (songCharacters == null) + songCharacters = ['bf']; + + var num:Int = 0; + for (song in songs) + { + addSong(song, weekNum, songCharacters[num]); + + if (songCharacters.length != 1) + num++; + } + } + + + override function update(elapsed:Float) + { + super.update(elapsed); + + if (controls.BACK) + FlxG.switchState(new OptionsMenu()); + if (controls.UP_P) + changeSelection(-1); + if (controls.DOWN_P) + changeSelection(1); + + + if (controls.ACCEPT && grpControls.members[curSelected].text != "No Replays...") + { + trace('loading ' + actualNames[curSelected]); + PlayState.rep = Replay.LoadReplay(actualNames[curSelected]); + + PlayState.loadRep = true; + + var poop:String = Highscore.formatSong(PlayState.rep.replay.songName.toLowerCase(), PlayState.rep.replay.songDiff); + + PlayState.SONG = Song.loadFromJson(poop, PlayState.rep.replay.songName.toLowerCase()); + PlayState.isStoryMode = false; + PlayState.storyDifficulty = PlayState.rep.replay.songDiff; + PlayState.storyWeek = getWeekNumbFromSong(PlayState.rep.replay.songName); + LoadingState.loadAndSwitchState(new PlayState()); + } + } + + var isSettingControl:Bool = false; + + function changeSelection(change:Int = 0) + { + #if !switch + // NGio.logEvent('Fresh'); + #end + + FlxG.sound.play(Paths.sound('scrollMenu'), 0.4); + + curSelected += change; + + if (curSelected < 0) + curSelected = grpControls.length - 1; + if (curSelected >= grpControls.length) + curSelected = 0; + + var rep:Replay = Replay.LoadReplay(actualNames[curSelected]); + + poggerDetails.text = "Replay Details - \nDate Created: " + rep.replay.timestamp + "\nSong: " + rep.replay.songName + "\nReplay Version: " + (rep.replay.replayGameVer != Replay.version ? "OUTDATED" : "Latest"); + + // selector.y = (70 * curSelected) + 30; + + var bullShit:Int = 0; + + for (item in grpControls.members) + { + item.targetY = bullShit - curSelected; + bullShit++; + + item.alpha = 0.6; + // item.setGraphicSize(Std.int(item.width * 0.8)); + + if (item.targetY == 0) + { + item.alpha = 1; + // item.setGraphicSize(Std.int(item.width)); + } + } + } +} diff --git a/source_duck/LoadingState.hx b/source_duck/LoadingState.hx new file mode 100644 index 00000000..23bce2e6 --- /dev/null +++ b/source_duck/LoadingState.hx @@ -0,0 +1,310 @@ +package; + +import lime.app.Promise; +import lime.app.Future; +import flixel.FlxG; +import flixel.FlxState; +import flixel.FlxSprite; +import flixel.graphics.frames.FlxAtlasFrames; +import flixel.util.FlxTimer; + +import openfl.utils.Assets; +import lime.utils.Assets as LimeAssets; +import lime.utils.AssetLibrary; +import lime.utils.AssetManifest; + +import haxe.io.Path; + +class LoadingState extends MusicBeatState +{ + inline static var MIN_TIME = 1.0; + + var target:FlxState; + var stopMusic = false; + var callbacks:MultiCallback; + + var logo:FlxSprite; + var gfDance:FlxSprite; + var danceLeft = false; + + function new(target:FlxState, stopMusic:Bool) + { + super(); + this.target = target; + this.stopMusic = stopMusic; + } + + override function create() + { + logo = new FlxSprite(-150, -100); + logo.frames = Paths.getSparrowAtlas('logoBumpin'); + logo.antialiasing = true; + logo.animation.addByPrefix('bump', 'logo bumpin', 24); + logo.animation.play('bump'); + logo.updateHitbox(); + // logoBl.screenCenter(); + // logoBl.color = FlxColor.BLACK; + + gfDance = new FlxSprite(FlxG.width * 0.4, FlxG.height * 0.07); + gfDance.frames = Paths.getSparrowAtlas('gfDanceTitle'); + 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(logo); + + initSongsManifest().onComplete + ( + function (lib) + { + callbacks = new MultiCallback(onLoad); + var introComplete = callbacks.add("introComplete"); + checkLoadSong(getSongPath()); + if (PlayState.SONG.needsVoices) + checkLoadSong(getVocalPath()); + checkLibrary("shared"); + if (PlayState.storyWeek > 0) + checkLibrary("week" + PlayState.storyWeek); + else + checkLibrary("tutorial"); + + var fadeTime = 0.5; + FlxG.camera.fade(FlxG.camera.bgColor, fadeTime, true); + new FlxTimer().start(fadeTime + MIN_TIME, function(_) introComplete()); + } + ); + } + + function checkLoadSong(path:String) + { + if (!Assets.cache.hasSound(path)) + { + var library = Assets.getLibrary("songs"); + final symbolPath = path.split(":").pop(); + // @:privateAccess + // library.types.set(symbolPath, SOUND); + // @:privateAccess + // library.pathGroups.set(symbolPath, [library.__cacheBreak(symbolPath)]); + var callback = callbacks.add("song:" + path); + Assets.loadSound(path).onComplete(function (_) { callback(); }); + } + } + + function checkLibrary(library:String) + { + trace(Assets.hasLibrary(library)); + if (Assets.getLibrary(library) == null) + { + @:privateAccess + if (!LimeAssets.libraryPaths.exists(library)) + throw "Missing library: " + library; + + var callback = callbacks.add("library:" + library); + Assets.loadLibrary(library).onComplete(function (_) { callback(); }); + } + } + + override function beatHit() + { + super.beatHit(); + + logo.animation.play('bump'); + danceLeft = !danceLeft; + + if (danceLeft) + gfDance.animation.play('danceRight'); + else + gfDance.animation.play('danceLeft'); + } + + override function update(elapsed:Float) + { + super.update(elapsed); + #if debug + if (FlxG.keys.justPressed.SPACE) + trace('fired: ' + callbacks.getFired() + " unfired:" + callbacks.getUnfired()); + #end + } + + function onLoad() + { + if (stopMusic && FlxG.sound.music != null) + FlxG.sound.music.stop(); + + FlxG.switchState(target); + } + + static function getSongPath() + { + return Paths.inst(PlayState.SONG.song); + } + + static function getVocalPath() + { + return Paths.voices(PlayState.SONG.song); + } + + inline static public function loadAndSwitchState(target:FlxState, stopMusic = false) + { + FlxG.switchState(getNextState(target, stopMusic)); + } + + static function getNextState(target:FlxState, stopMusic = false):FlxState + { + Paths.setCurrentLevel("week" + PlayState.storyWeek); + #if NO_PRELOAD_ALL + var loaded = isSoundLoaded(getSongPath()) + && (!PlayState.SONG.needsVoices || isSoundLoaded(getVocalPath())) + && isLibraryLoaded("shared"); + + if (!loaded) + return new LoadingState(target, stopMusic); + #end + if (stopMusic && FlxG.sound.music != null) + FlxG.sound.music.stop(); + + return target; + } + + #if NO_PRELOAD_ALL + static function isSoundLoaded(path:String):Bool + { + return Assets.cache.hasSound(path); + } + + static function isLibraryLoaded(library:String):Bool + { + return Assets.getLibrary(library) != null; + } + #end + + override function destroy() + { + super.destroy(); + + callbacks = null; + } + + static function initSongsManifest() + { + var id = "songs"; + var promise = new Promise(); + + var library = LimeAssets.getLibrary(id); + + if (library != null) + { + return Future.withValue(library); + } + + var path = id; + var rootPath = null; + + @:privateAccess + var libraryPaths = LimeAssets.libraryPaths; + if (libraryPaths.exists(id)) + { + path = libraryPaths[id]; + rootPath = Path.directory(path); + } + else + { + if (StringTools.endsWith(path, ".bundle")) + { + rootPath = path; + path += "/library.json"; + } + else + { + rootPath = Path.directory(path); + } + @:privateAccess + path = LimeAssets.__cacheBreak(path); + } + + AssetManifest.loadFromFile(path, rootPath).onComplete(function(manifest) + { + if (manifest == null) + { + promise.error("Cannot parse asset manifest for library \"" + id + "\""); + return; + } + + var library = AssetLibrary.fromManifest(manifest); + + if (library == null) + { + promise.error("Cannot open library \"" + id + "\""); + } + else + { + @:privateAccess + LimeAssets.libraries.set(id, library); + library.onChange.add(LimeAssets.onChange.dispatch); + promise.completeWith(Future.withValue(library)); + } + }).onError(function(_) + { + promise.error("There is no asset library with an ID of \"" + id + "\""); + }); + + return promise.future; + } +} + +class MultiCallback +{ + public var callback:Void->Void; + public var logId:String = null; + public var length(default, null) = 0; + public var numRemaining(default, null) = 0; + + var unfired = new MapVoid>(); + var fired = new Array(); + + public function new (callback:Void->Void, logId:String = null) + { + this.callback = callback; + this.logId = logId; + } + + public function add(id = "untitled") + { + id = '$length:$id'; + length++; + numRemaining++; + var func:Void->Void = null; + func = function () + { + if (unfired.exists(id)) + { + unfired.remove(id); + fired.push(id); + numRemaining--; + + if (logId != null) + log('fired $id, $numRemaining remaining'); + + if (numRemaining == 0) + { + if (logId != null) + log('all callbacks fired'); + callback(); + } + } + else + log('already fired $id'); + } + unfired[id] = func; + return func; + } + + inline function log(msg):Void + { + if (logId != null) + trace('$logId: $msg'); + } + + public function getFired() return fired.copy(); + public function getUnfired() return [for (id in unfired.keys()) id]; +} \ No newline at end of file diff --git a/source_duck/LuaShader.hx b/source_duck/LuaShader.hx new file mode 100644 index 00000000..4e403c69 --- /dev/null +++ b/source_duck/LuaShader.hx @@ -0,0 +1,72 @@ +/*import flixel.system.FlxAssets.FlxShader; + +class LuaShader extends FlxShader +{ + // SHADER SHIT FOR LUA CODE + + public function new(frag,vert) + { + glFragmentSource = ' + #pragma header + varying float openfl_Alphav; + varying vec4 openfl_ColorMultiplierv; + varying vec4 openfl_ColorOffsetv; + varying vec2 openfl_TextureCoordv; + + uniform bool openfl_HasColorTransform; + uniform vec2 openfl_TextureSize; + uniform sampler2D bitmap; + + uniform bool hasTransform; + uniform bool hasColorTransform; + + uniform vec3 iResolution; // viewport resolution (in pixels) + uniform float iTime; // shader playback time (in seconds) + uniform float iTimeDelta; // render time (in seconds) + uniform int iFrame; // shader playback frame + uniform float iChannelTime[4]; // channel playback time (in seconds) + uniform vec3 iChannelResolution[4]; // channel resolution (in pixels) + uniform vec4 iMouse; // mouse pixel coords. xy: current, zw: click + uniform samplerXX iChannel0..3; // input channel. XX = 2D/Cube + uniform vec4 iDate; // (year, month, day, time in seconds) + uniform float iSampleRate; // sound sample rate (i.e., 44100) + + vec4 flixel_texture2D(sampler2D bitmap, vec2 coord) + { + vec4 color = texture2D(bitmap, coord); + if (!hasTransform) + { + return color; + } + + if (color.a == 0.0) + { + return vec4(0.0, 0.0, 0.0, 0.0); + } + + if (!hasColorTransform) + { + return color * openfl_Alphav; + } + + color = vec4(color.rgb / color.a, color.a); + + mat4 colorMultiplier = mat4(0); + colorMultiplier[0][0] = openfl_ColorMultiplierv.x; + colorMultiplier[1][1] = openfl_ColorMultiplierv.y; + colorMultiplier[2][2] = openfl_ColorMultiplierv.z; + colorMultiplier[3][3] = openfl_ColorMultiplierv.w; + + color = clamp(openfl_ColorOffsetv + (color * colorMultiplier), 0.0, 1.0); + + if (color.a > 0.0) + { + return vec4(0.0, 0.0, 0.0, 0.0); + } + + ' + frag; + + iResolution + super(); + } +}*/ \ No newline at end of file diff --git a/source_duck/Main.hx b/source_duck/Main.hx new file mode 100644 index 00000000..48f0fc34 --- /dev/null +++ b/source_duck/Main.hx @@ -0,0 +1,119 @@ +package; + +import openfl.display.BlendMode; +import openfl.text.TextFormat; +import openfl.display.Application; +import flixel.util.FlxColor; +import flixel.FlxG; +import flixel.FlxGame; +import flixel.FlxState; +import openfl.Assets; +import openfl.Lib; +import openfl.display.FPS; +import openfl.display.Sprite; +import openfl.events.Event; + +class Main extends Sprite +{ + var gameWidth:Int = 1280; // Width of the game in pixels (might be less / more in actual pixels depending on your zoom). + var gameHeight:Int = 720; // Height of the game in pixels (might be less / more in actual pixels depending on your zoom). + var initialState:Class = TitleState; // The FlxState the game starts with. + var zoom:Float = -1; // If -1, zoom is automatically calculated to fit the window dimensions. + var framerate:Int = 120; // How many frames per second the game should run at. + var skipSplash:Bool = true; // Whether to skip the flixel splash screen that appears in release mode. + var startFullscreen:Bool = false; // Whether to start the game in fullscreen on desktop targets + + public static var watermarks = true; // Whether to put Kade Engine liteartly anywhere + + // You can pretty much ignore everything from here on - your code should go in your states. + + public static function main():Void + { + + // quick checks + + Lib.current.addChild(new Main()); + } + + public function new() + { + super(); + + if (stage != null) + { + init(); + } + else + { + addEventListener(Event.ADDED_TO_STAGE, init); + } + } + + private function init(?E:Event):Void + { + if (hasEventListener(Event.ADDED_TO_STAGE)) + { + removeEventListener(Event.ADDED_TO_STAGE, init); + } + + setupGame(); + } + + private function setupGame():Void + { + var stageWidth:Int = Lib.current.stage.stageWidth; + var stageHeight:Int = Lib.current.stage.stageHeight; + + if (zoom == -1) + { + var ratioX:Float = stageWidth / gameWidth; + var ratioY:Float = stageHeight / gameHeight; + zoom = Math.min(ratioX, ratioY); + gameWidth = Math.ceil(stageWidth / zoom); + gameHeight = Math.ceil(stageHeight / zoom); + } + + #if !debug + initialState = TitleState; + #end + + game = new FlxGame(gameWidth, gameHeight, initialState, zoom, framerate, framerate, skipSplash, startFullscreen); + + addChild(game); + + #if !mobile + fpsCounter = new FPS(10, 3, 0xFFFFFF); + addChild(fpsCounter); + toggleFPS(FlxG.save.data.fps); + + #end + } + + var game:FlxGame; + + var fpsCounter:FPS; + + public function toggleFPS(fpsEnabled:Bool):Void { + fpsCounter.visible = fpsEnabled; + } + + public function changeFPSColor(color:FlxColor) + { + fpsCounter.textColor = color; + } + + public function setFPSCap(cap:Float) + { + openfl.Lib.current.stage.frameRate = cap; + } + + public function getFPSCap():Float + { + return openfl.Lib.current.stage.frameRate; + } + + public function getFPS():Float + { + return fpsCounter.currentFPS; + } +} diff --git a/source_duck/MainMenuState.hx b/source_duck/MainMenuState.hx new file mode 100644 index 00000000..d57308b7 --- /dev/null +++ b/source_duck/MainMenuState.hx @@ -0,0 +1,237 @@ +package; + +import Controls.KeyboardScheme; +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.text.FlxText; +import flixel.tweens.FlxEase; +import flixel.tweens.FlxTween; +import flixel.util.FlxColor; +import io.newgrounds.NG; +import lime.app.Application; + +#if windows +import Discord.DiscordClient; +#end + +using StringTools; + +class MainMenuState extends MusicBeatState +{ + var curSelected:Int = 0; + + var menuItems:FlxTypedGroup; + + #if !switch + var optionShit:Array = ['story mode', 'freeplay', 'donate', 'options']; + #else + var optionShit:Array = ['story mode', 'freeplay']; + #end + + var newGaming:FlxText; + var newGaming2:FlxText; + var newInput:Bool = true; + + public static var nightly:String = ""; + + public static var kadeEngineVer:String = "1.4.2" + nightly; + public static var gameVer:String = "0.2.7.1"; + + var magenta:FlxSprite; + var camFollow:FlxObject; + + override function create() + { + #if windows + // Updating Discord Rich Presence + DiscordClient.changePresence("In the Menus", null); + #end + + if (!FlxG.sound.music.playing) + { + FlxG.sound.playMusic(Paths.music('freakyMenu')); + } + + persistentUpdate = persistentDraw = true; + + var bg:FlxSprite = new FlxSprite(-80).loadGraphic(Paths.image('menuBG')); + bg.scrollFactor.x = 0; + bg.scrollFactor.y = 0.15; + 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(Paths.image('menuDesat')); + 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; + magenta.color = 0xFFfd719b; + add(magenta); + // magenta.scrollFactor.set(); + + menuItems = new FlxTypedGroup(); + add(menuItems); + + var tex = Paths.getSparrowAtlas('FNF_main_menu_assets'); + + 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.60 * (60 / FlxG.save.data.fpsCap)); + + var versionShit:FlxText = new FlxText(5, FlxG.height - 18, 0, gameVer + (Main.watermarks ? " FNF - " + kadeEngineVer + " Kade Engine" : ""), 12); + versionShit.scrollFactor.set(); + versionShit.setFormat("VCR OSD Mono", 16, FlxColor.WHITE, LEFT, FlxTextBorderStyle.OUTLINE, FlxColor.BLACK); + add(versionShit); + + // NG.core.calls.event.logEvent('swag').send(); + + + if (FlxG.save.data.dfjk) + controls.setKeyboardScheme(KeyboardScheme.Solo, true); + else + controls.setKeyboardScheme(KeyboardScheme.Duo(true), true); + + changeItem(); + + super.create(); + } + + var selectedSomethin:Bool = false; + + override function update(elapsed:Float) + { + if (FlxG.sound.music.volume < 0.8) + { + FlxG.sound.music.volume += 0.5 * FlxG.elapsed; + } + + if (!selectedSomethin) + { + if (controls.UP_P) + { + FlxG.sound.play(Paths.sound('scrollMenu')); + changeItem(-1); + } + + if (controls.DOWN_P) + { + FlxG.sound.play(Paths.sound('scrollMenu')); + changeItem(1); + } + + if (controls.BACK) + { + FlxG.switchState(new TitleState()); + } + + if (controls.ACCEPT) + { + if (optionShit[curSelected] == 'donate') + { + #if linux + Sys.command('/usr/bin/xdg-open', ["https://ninja-muffin24.itch.io/funkin", "&"]); + #else + FlxG.openURL('https://ninja-muffin24.itch.io/funkin'); + #end + } + else + { + selectedSomethin = true; + FlxG.sound.play(Paths.sound('confirmMenu')); + + FlxFlicker.flicker(magenta, 1.1, 0.15, false); + + menuItems.forEach(function(spr:FlxSprite) + { + if (curSelected != spr.ID) + { + FlxTween.tween(spr, {alpha: 0}, 1.3, { + 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()); + trace("Story Menu Selected"); + case 'freeplay': + FlxG.switchState(new FreeplayState()); + + trace("Freeplay Menu Selected"); + + case 'options': + FlxG.switchState(new OptionsMenu()); + } + }); + } + }); + } + } + } + + super.update(elapsed); + + 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_duck/MenuCharacter.hx b/source_duck/MenuCharacter.hx new file mode 100644 index 00000000..a09c11ad --- /dev/null +++ b/source_duck/MenuCharacter.hx @@ -0,0 +1,81 @@ +package; + +import flixel.FlxSprite; +import flixel.graphics.frames.FlxAtlasFrames; + +class CharacterSetting +{ + public var x(default, null):Int; + public var y(default, null):Int; + public var scale(default, null):Float; + public var flipped(default, null):Bool; + + public function new(x:Int = 0, y:Int = 0, scale:Float = 1.0, flipped:Bool = false) + { + this.x = x; + this.y = y; + this.scale = scale; + this.flipped = flipped; + } +} + +class MenuCharacter extends FlxSprite +{ + private static var settings:Map = [ + 'bf' => new CharacterSetting(0, -20, 1.0, true), + 'gf' => new CharacterSetting(50, 80, 1.5, true), + 'dad' => new CharacterSetting(-15, 130), + 'spooky' => new CharacterSetting(20, 30), + 'pico' => new CharacterSetting(0, 0, 1.0, true), + 'mom' => new CharacterSetting(-30, 140, 0.85), + 'parents-christmas' => new CharacterSetting(100, 130, 1.8), + 'senpai' => new CharacterSetting(-40, -45, 1.4), + 'duck' => new CharacterSetting(-40, -45, 1.4) + ]; + + private var flipped:Bool = false; + + public function new(x:Int, y:Int, scale:Float, flipped:Bool) + { + super(x, y); + this.flipped = flipped; + + antialiasing = true; + + frames = Paths.getSparrowAtlas('campaign_menu_UI_characters'); + + 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.addByPrefix('pico', "Pico Idle Dance", 24); + animation.addByPrefix('mom', "Mom Idle BLACK LINES", 24); + animation.addByPrefix('parents-christmas', "Parent Christmas Idle", 24); + animation.addByPrefix('senpai', "SENPAI idle Black Lines", 24); + animation.addByPrefix('duck', 'Duck Black Lines', 24); + + setGraphicSize(Std.int(width * scale)); + updateHitbox(); + } + + public function setCharacter(character:String):Void + { + if (character == '') + { + visible = false; + return; + } + else + { + visible = true; + } + + animation.play(character); + + var setting:CharacterSetting = settings[character]; + offset.set(setting.x, setting.y); + setGraphicSize(Std.int(width * setting.scale)); + flipX = setting.flipped != flipped; + } +} diff --git a/source_duck/MenuItem.hx b/source_duck/MenuItem.hx new file mode 100644 index 00000000..44353f8b --- /dev/null +++ b/source_duck/MenuItem.hx @@ -0,0 +1,49 @@ +package; + +import flixel.FlxG; +import flixel.FlxSprite; +import flixel.graphics.frames.FlxAtlasFrames; +import flixel.group.FlxSpriteGroup; +import flixel.math.FlxMath; +import flixel.util.FlxColor; + +class MenuItem extends FlxSpriteGroup +{ + public var targetY:Float = 0; + public var week:FlxSprite; + public var flashingInt:Int = 0; + + public function new(x:Float, y:Float, weekNum:Int = 0) + { + super(x, y); + week = new FlxSprite().loadGraphic(Paths.image('storymenu/week' + weekNum)); + add(week); + } + + private var isFlashing:Bool = false; + + public function startFlashing():Void + { + isFlashing = true; + } + + // if it runs at 60fps, fake framerate will be 6 + // if it runs at 144 fps, fake framerate will be like 14, and will update the graphic every 0.016666 * 3 seconds still??? + // so it runs basically every so many seconds, not dependant on framerate?? + // I'm still learning how math works thanks whoever is reading this lol + var fakeFramerate:Int = Math.round((1 / FlxG.elapsed) / 10); + + override function update(elapsed:Float) + { + super.update(elapsed); + y = FlxMath.lerp(y, (targetY * 120) + 480, 0.17 * (60 / FlxG.save.data.fpsCap)); + + if (isFlashing) + flashingInt += 1; + + if (flashingInt % fakeFramerate >= Math.floor(fakeFramerate / 2)) + week.color = 0xFF33ffff; + else + week.color = FlxColor.WHITE; + } +} diff --git a/source_duck/ModchartState.hx b/source_duck/ModchartState.hx new file mode 100644 index 00000000..d3b8e535 --- /dev/null +++ b/source_duck/ModchartState.hx @@ -0,0 +1,667 @@ +// this file is for modchart things, this is to declutter playstate.hx + +// Lua +#if windows +import flixel.tweens.FlxEase; +import openfl.filters.ShaderFilter; +import flixel.tweens.FlxTween; +import flixel.util.FlxColor; +import openfl.geom.Matrix; +import openfl.display.BitmapData; +import lime.app.Application; +import flixel.FlxSprite; +import llua.Convert; +import llua.Lua; +import llua.State; +import llua.LuaL; +import flixel.FlxBasic; +import flixel.FlxCamera; +import flixel.FlxG; + +class ModchartState +{ + //public static var shaders:Array = null; + + public static var lua:State = null; + + function callLua(func_name : String, args : Array, ?type : String) : Dynamic + { + var result : Any = null; + + Lua.getglobal(lua, func_name); + + for( arg in args ) { + Convert.toLua(lua, arg); + } + + result = Lua.pcall(lua, args.length, 1, 0); + var p = Lua.tostring(lua,result); + var e = getLuaErrorMessage(lua); + + if (e != null) + { + if (p != null) + { + Application.current.window.alert("LUA ERROR:\n" + p + "\nhaxe things: " + e,"Kade Engine Modcharts"); + lua = null; + LoadingState.loadAndSwitchState(new MainMenuState()); + } + // trace('err: ' + e); + } + if( result == null) { + return null; + } else { + return convert(result, type); + } + + } + + function getType(l, type):Any + { + return switch Lua.type(l,type) { + case t if (t == Lua.LUA_TNIL): null; + case t if (t == Lua.LUA_TNUMBER): Lua.tonumber(l, type); + case t if (t == Lua.LUA_TSTRING): (Lua.tostring(l, type):String); + case t if (t == Lua.LUA_TBOOLEAN): Lua.toboolean(l, type); + case t: throw 'you don goofed up. lua type error ($t)'; + } + } + + function getReturnValues(l) { + var lua_v:Int; + var v:Any = null; + while((lua_v = Lua.gettop(l)) != 0) { + var type:String = getType(l,lua_v); + v = convert(lua_v, type); + Lua.pop(l, 1); + } + return v; + } + + + private function convert(v : Any, type : String) : Dynamic { // I didn't write this lol + if( Std.is(v, String) && type != null ) { + var v : String = v; + if( type.substr(0, 4) == 'array' ) { + if( type.substr(4) == 'float' ) { + var array : Array = v.split(','); + var array2 : Array = new Array(); + + for( vars in array ) { + array2.push(Std.parseFloat(vars)); + } + + return array2; + } else if( type.substr(4) == 'int' ) { + var array : Array = v.split(','); + var array2 : Array = new Array(); + + for( vars in array ) { + array2.push(Std.parseInt(vars)); + } + + return array2; + } else { + var array : Array = v.split(','); + return array; + } + } else if( type == 'float' ) { + return Std.parseFloat(v); + } else if( type == 'int' ) { + return Std.parseInt(v); + } else if( type == 'bool' ) { + if( v == 'true' ) { + return true; + } else { + return false; + } + } else { + return v; + } + } else { + return v; + } + } + + function getLuaErrorMessage(l) { + var v:String = Lua.tostring(l, -1); + Lua.pop(l, 1); + return v; + } + + public function setVar(var_name : String, object : Dynamic){ + // trace('setting variable ' + var_name + ' to ' + object); + + Lua.pushnumber(lua,object); + Lua.setglobal(lua, var_name); + } + + public function getVar(var_name : String, type : String) : Dynamic { + var result : Any = null; + + // trace('getting variable ' + var_name + ' with a type of ' + type); + + Lua.getglobal(lua, var_name); + result = Convert.fromLua(lua,-1); + Lua.pop(lua,1); + + if( result == null ) { + return null; + } else { + var result = convert(result, type); + //trace(var_name + ' result: ' + result); + return result; + } + } + + function getActorByName(id:String):Dynamic + { + // pre defined names + switch(id) + { + case 'boyfriend': + @:privateAccess + return PlayState.boyfriend; + case 'girlfriend': + @:privateAccess + return PlayState.gf; + case 'dad': + @:privateAccess + return PlayState.dad; + } + // lua objects or what ever + if (luaSprites.get(id) == null) + { + if (Std.parseInt(id) == null) + return Reflect.getProperty(PlayState.instance,id); + return PlayState.PlayState.strumLineNotes.members[Std.parseInt(id)]; + } + return luaSprites.get(id); + } + + public static var luaSprites:Map = []; + + + + function makeLuaSprite(spritePath:String,toBeCalled:String, drawBehind:Bool) + { + #if sys + var data:BitmapData = BitmapData.fromFile(Sys.getCwd() + "assets/data/" + PlayState.SONG.song.toLowerCase() + '/' + spritePath + ".png"); + + var sprite:FlxSprite = new FlxSprite(0,0); + var imgWidth:Float = FlxG.width / data.width; + var imgHeight:Float = FlxG.height / data.height; + var scale:Float = imgWidth <= imgHeight ? imgWidth : imgHeight; + + // Cap the scale at x1 + if (scale > 1) + { + scale = 1; + } + + sprite.makeGraphic(Std.int(data.width * scale),Std.int(data.width * scale),FlxColor.TRANSPARENT); + + var data2:BitmapData = sprite.pixels.clone(); + var matrix:Matrix = new Matrix(); + matrix.identity(); + matrix.scale(scale, scale); + data2.fillRect(data2.rect, FlxColor.TRANSPARENT); + data2.draw(data, matrix, null, null, null, true); + sprite.pixels = data2; + + luaSprites.set(toBeCalled,sprite); + // and I quote: + // shitty layering but it works! + @:privateAccess + { + if (drawBehind) + { + PlayState.instance.removeObject(PlayState.gf); + PlayState.instance.removeObject(PlayState.boyfriend); + PlayState.instance.removeObject(PlayState.dad); + } + PlayState.instance.addObject(sprite); + if (drawBehind) + { + PlayState.instance.addObject(PlayState.gf); + PlayState.instance.addObject(PlayState.boyfriend); + PlayState.instance.addObject(PlayState.dad); + } + } + #end + return toBeCalled; + } + + public function die() + { + Lua.close(lua); + lua = null; + } + + // LUA SHIT + + function new() + { + trace('opening a lua state (because we are cool :))'); + lua = LuaL.newstate(); + LuaL.openlibs(lua); + trace("Lua version: " + Lua.version()); + trace("LuaJIT version: " + Lua.versionJIT()); + Lua.init_callbacks(lua); + + //shaders = new Array(); + + var result = LuaL.dofile(lua, Paths.lua(PlayState.SONG.song.toLowerCase() + "/modchart")); // execute le file + + if (result != 0) + { + Application.current.window.alert("LUA COMPILE ERROR:\n" + Lua.tostring(lua,result),"Kade Engine Modcharts"); + lua = null; + LoadingState.loadAndSwitchState(new MainMenuState()); + } + + // get some fukin globals up in here bois + + setVar("difficulty", PlayState.storyDifficulty); + setVar("bpm", Conductor.bpm); + setVar("scrollspeed", FlxG.save.data.scrollSpeed != 1 ? FlxG.save.data.scrollSpeed : PlayState.SONG.speed); + setVar("fpsCap", FlxG.save.data.fpsCap); + setVar("downscroll", FlxG.save.data.downscroll); + + setVar("curStep", 0); + setVar("curBeat", 0); + setVar("crochet", Conductor.stepCrochet); + setVar("safeZoneOffset", Conductor.safeZoneOffset); + + setVar("hudZoom", PlayState.instance.camHUD.zoom); + setVar("cameraZoom", FlxG.camera.zoom); + + setVar("cameraAngle", FlxG.camera.angle); + setVar("camHudAngle", PlayState.instance.camHUD.angle); + + setVar("followXOffset",0); + setVar("followYOffset",0); + + setVar("showOnlyStrums", false); + setVar("strumLine1Visible", true); + setVar("strumLine2Visible", true); + + setVar("screenWidth",FlxG.width); + setVar("screenHeight",FlxG.height); + setVar("hudWidth", PlayState.instance.camHUD.width); + setVar("hudHeight", PlayState.instance.camHUD.height); + + setVar("mustHit", false); + + setVar("strumLineY", PlayState.instance.strumLine.y); + + // callbacks + + // sprites + + trace(Lua_helper.add_callback(lua,"makeSprite", makeLuaSprite)); + + Lua_helper.add_callback(lua,"destroySprite", function(id:String) { + var sprite = luaSprites.get(id); + if (sprite == null) + return false; + PlayState.instance.removeObject(sprite); + return true; + }); + + // hud/camera + + trace(Lua_helper.add_callback(lua,"setHudAngle", function (x:Float) { + PlayState.instance.camHUD.angle = x; + })); + + trace(Lua_helper.add_callback(lua,"setHudPosition", function (x:Int, y:Int) { + PlayState.instance.camHUD.x = x; + PlayState.instance.camHUD.y = y; + })); + + trace(Lua_helper.add_callback(lua,"getHudX", function () { + return PlayState.instance.camHUD.x; + })); + + trace(Lua_helper.add_callback(lua,"getHudY", function () { + return PlayState.instance.camHUD.y; + })); + + trace(Lua_helper.add_callback(lua,"setCamPosition", function (x:Int, y:Int) { + FlxG.camera.x = x; + FlxG.camera.y = y; + })); + + trace(Lua_helper.add_callback(lua,"getCameraX", function () { + return FlxG.camera.x; + })); + + trace(Lua_helper.add_callback(lua,"getCameraY", function () { + return FlxG.camera.y; + })); + + trace(Lua_helper.add_callback(lua,"setCamZoom", function(zoomAmount:Int) { + FlxG.camera.zoom = zoomAmount; + })); + + trace(Lua_helper.add_callback(lua,"setHudZoom", function(zoomAmount:Int) { + PlayState.instance.camHUD.zoom = zoomAmount; + })); + + // actors + + trace(Lua_helper.add_callback(lua,"getRenderedNotes", function() { + return PlayState.instance.notes.length; + })); + + trace(Lua_helper.add_callback(lua,"getRenderedNoteX", function(id:Int) { + return PlayState.instance.notes.members[id].x; + })); + + trace(Lua_helper.add_callback(lua,"getRenderedNoteY", function(id:Int) { + return PlayState.instance.notes.members[id].y; + })); + + trace(Lua_helper.add_callback(lua,"getRenderedNoteType", function(id:Int) { + return PlayState.instance.notes.members[id].noteData; + })); + + trace(Lua_helper.add_callback(lua,"isSustain", function(id:Int) { + return PlayState.instance.notes.members[id].isSustainNote; + })); + + trace(Lua_helper.add_callback(lua,"isParentSustain", function(id:Int) { + return PlayState.instance.notes.members[id].prevNote.isSustainNote; + })); + + + trace(Lua_helper.add_callback(lua,"getRenderedNoteParentX", function(id:Int) { + return PlayState.instance.notes.members[id].prevNote.x; + })); + + trace(Lua_helper.add_callback(lua,"getRenderedNoteParentY", function(id:Int) { + return PlayState.instance.notes.members[id].prevNote.y; + })); + + trace(Lua_helper.add_callback(lua,"getRenderedNoteHit", function(id:Int) { + return PlayState.instance.notes.members[id].mustPress; + })); + + trace(Lua_helper.add_callback(lua,"getRenderedNoteCalcX", function(id:Int) { + if (PlayState.instance.notes.members[id].mustPress) + return PlayState.playerStrums.members[Math.floor(Math.abs(PlayState.instance.notes.members[id].noteData))].x; + return PlayState.strumLineNotes.members[Math.floor(Math.abs(PlayState.instance.notes.members[id].noteData))].x; + })); + + trace(Lua_helper.add_callback(lua,"anyNotes", function() { + return PlayState.instance.notes.members.length != 0; + })); + + trace(Lua_helper.add_callback(lua,"getRenderedNoteStrumtime", function(id:Int) { + return PlayState.instance.notes.members[id].strumTime; + })); + + trace(Lua_helper.add_callback(lua,"getRenderedNoteScaleX", function(id:Int) { + return PlayState.instance.notes.members[id].scale.x; + })); + + trace(Lua_helper.add_callback(lua,"setRenderedNotePos", function(x:Float,y:Float, id:Int) { + if (PlayState.instance.notes.members[id] == null) + throw('error! you cannot set a rendered notes position when it doesnt exist! ID: ' + id); + else + { + PlayState.instance.notes.members[id].modifiedByLua = true; + PlayState.instance.notes.members[id].x = x; + PlayState.instance.notes.members[id].y = y; + } + })); + + trace(Lua_helper.add_callback(lua,"setRenderedNoteAlpha", function(alpha:Float, id:Int) { + PlayState.instance.notes.members[id].modifiedByLua = true; + PlayState.instance.notes.members[id].alpha = alpha; + })); + + trace(Lua_helper.add_callback(lua,"setRenderedNoteScale", function(scale:Float, id:Int) { + PlayState.instance.notes.members[id].modifiedByLua = true; + PlayState.instance.notes.members[id].setGraphicSize(Std.int(PlayState.instance.notes.members[id].width * scale)); + })); + + trace(Lua_helper.add_callback(lua,"setRenderedNoteScale", function(scaleX:Int, scaleY:Int, id:Int) { + PlayState.instance.notes.members[id].modifiedByLua = true; + PlayState.instance.notes.members[id].setGraphicSize(scaleX,scaleY); + })); + + trace(Lua_helper.add_callback(lua,"getRenderedNoteWidth", function(id:Int) { + return PlayState.instance.notes.members[id].width; + })); + + + trace(Lua_helper.add_callback(lua,"setRenderedNoteAngle", function(angle:Float, id:Int) { + PlayState.instance.notes.members[id].modifiedByLua = true; + PlayState.instance.notes.members[id].angle = angle; + })); + + trace(Lua_helper.add_callback(lua,"setActorX", function(x:Int,id:String) { + getActorByName(id).x = x; + })); + + trace(Lua_helper.add_callback(lua,"setActorAlpha", function(alpha:Float,id:String) { + getActorByName(id).alpha = alpha; + })); + + trace(Lua_helper.add_callback(lua,"setActorY", function(y:Int,id:String) { + getActorByName(id).y = y; + })); + + trace(Lua_helper.add_callback(lua,"setActorAngle", function(angle:Int,id:String) { + getActorByName(id).angle = angle; + })); + + trace(Lua_helper.add_callback(lua,"setActorScale", function(scale:Float,id:String) { + getActorByName(id).setGraphicSize(Std.int(getActorByName(id).width * scale)); + })); + + + trace(Lua_helper.add_callback(lua,"getActorWidth", function (id:String) { + return getActorByName(id).width; + })); + + trace(Lua_helper.add_callback(lua,"getActorHeight", function (id:String) { + return getActorByName(id).height; + })); + + trace(Lua_helper.add_callback(lua,"getActorAlpha", function(id:String) { + return getActorByName(id).alpha; + })); + + trace(Lua_helper.add_callback(lua,"getActorAngle", function(id:String) { + return getActorByName(id).angle; + })); + + trace(Lua_helper.add_callback(lua,"getActorX", function (id:String) { + return getActorByName(id).x; + })); + + trace(Lua_helper.add_callback(lua,"getActorY", function (id:String) { + return getActorByName(id).y; + })); + + + // tweens + + Lua_helper.add_callback(lua,"tweenCameraPos", function(toX:Int, toY:Int, time:Float, onComplete:String) { + FlxTween.tween(FlxG.camera, {x: toX, y: toY}, time, {ease: FlxEase.linear, onComplete: function(flxTween:FlxTween) { if (onComplete != '' && onComplete != null) {callLua(onComplete,["camera"]);}}}); + }); + + Lua_helper.add_callback(lua,"tweenCameraAngle", function(toAngle:Float, time:Float, onComplete:String) { + FlxTween.tween(FlxG.camera, {angle:toAngle}, time, {ease: FlxEase.linear, onComplete: function(flxTween:FlxTween) { if (onComplete != '' && onComplete != null) {callLua(onComplete,["camera"]);}}}); + }); + + Lua_helper.add_callback(lua,"tweenCameraZoom", function(toZoom:Float, time:Float, onComplete:String) { + FlxTween.tween(FlxG.camera, {zoom:toZoom}, time, {ease: FlxEase.linear, onComplete: function(flxTween:FlxTween) { if (onComplete != '' && onComplete != null) {callLua(onComplete,["camera"]);}}}); + }); + + Lua_helper.add_callback(lua,"tweenHudPos", function(toX:Int, toY:Int, time:Float, onComplete:String) { + FlxTween.tween(PlayState.instance.camHUD, {x: toX, y: toY}, time, {ease: FlxEase.linear, onComplete: function(flxTween:FlxTween) { if (onComplete != '' && onComplete != null) {callLua(onComplete,["camera"]);}}}); + }); + + Lua_helper.add_callback(lua,"tweenHudAngle", function(toAngle:Float, time:Float, onComplete:String) { + FlxTween.tween(PlayState.instance.camHUD, {angle:toAngle}, time, {ease: FlxEase.linear, onComplete: function(flxTween:FlxTween) { if (onComplete != '' && onComplete != null) {callLua(onComplete,["camera"]);}}}); + }); + + Lua_helper.add_callback(lua,"tweenHudZoom", function(toZoom:Float, time:Float, onComplete:String) { + FlxTween.tween(PlayState.instance.camHUD, {zoom:toZoom}, time, {ease: FlxEase.linear, onComplete: function(flxTween:FlxTween) { if (onComplete != '' && onComplete != null) {callLua(onComplete,["camera"]);}}}); + }); + + Lua_helper.add_callback(lua,"tweenPos", function(id:String, toX:Int, toY:Int, time:Float, onComplete:String) { + FlxTween.tween(getActorByName(id), {x: toX, y: toY}, time, {ease: FlxEase.linear, onComplete: function(flxTween:FlxTween) { if (onComplete != '' && onComplete != null) {callLua(onComplete,[id]);}}}); + }); + + Lua_helper.add_callback(lua,"tweenPosXAngle", function(id:String, toX:Int, toAngle:Float, time:Float, onComplete:String) { + FlxTween.tween(getActorByName(id), {x: toX, angle: toAngle}, time, {ease: FlxEase.linear, onComplete: function(flxTween:FlxTween) { if (onComplete != '' && onComplete != null) {callLua(onComplete,[id]);}}}); + }); + + Lua_helper.add_callback(lua,"tweenPosYAngle", function(id:String, toY:Int, toAngle:Float, time:Float, onComplete:String) { + FlxTween.tween(getActorByName(id), {y: toY, angle: toAngle}, time, {ease: FlxEase.linear, onComplete: function(flxTween:FlxTween) { if (onComplete != '' && onComplete != null) {callLua(onComplete,[id]);}}}); + }); + + Lua_helper.add_callback(lua,"tweenAngle", function(id:String, toAngle:Int, time:Float, onComplete:String) { + FlxTween.tween(getActorByName(id), {angle: toAngle}, time, {ease: FlxEase.linear, onComplete: function(flxTween:FlxTween) { if (onComplete != '' && onComplete != null) {callLua(onComplete,[id]);}}}); + }); + + Lua_helper.add_callback(lua,"tweenCameraPosOut", function(toX:Int, toY:Int, time:Float, onComplete:String) { + FlxTween.tween(FlxG.camera, {x: toX, y: toY}, time, {ease: FlxEase.cubeOut, onComplete: function(flxTween:FlxTween) { if (onComplete != '' && onComplete != null) {callLua(onComplete,["camera"]);}}}); + }); + + Lua_helper.add_callback(lua,"tweenCameraAngleOut", function(toAngle:Float, time:Float, onComplete:String) { + FlxTween.tween(FlxG.camera, {angle:toAngle}, time, {ease: FlxEase.cubeOut, onComplete: function(flxTween:FlxTween) { if (onComplete != '' && onComplete != null) {callLua(onComplete,["camera"]);}}}); + }); + + Lua_helper.add_callback(lua,"tweenCameraZoomOut", function(toZoom:Float, time:Float, onComplete:String) { + FlxTween.tween(FlxG.camera, {zoom:toZoom}, time, {ease: FlxEase.cubeOut, onComplete: function(flxTween:FlxTween) { if (onComplete != '' && onComplete != null) {callLua(onComplete,["camera"]);}}}); + }); + + Lua_helper.add_callback(lua,"tweenHudPosOut", function(toX:Int, toY:Int, time:Float, onComplete:String) { + FlxTween.tween(PlayState.instance.camHUD, {x: toX, y: toY}, time, {ease: FlxEase.cubeOut, onComplete: function(flxTween:FlxTween) { if (onComplete != '' && onComplete != null) {callLua(onComplete,["camera"]);}}}); + }); + + Lua_helper.add_callback(lua,"tweenHudAngleOut", function(toAngle:Float, time:Float, onComplete:String) { + FlxTween.tween(PlayState.instance.camHUD, {angle:toAngle}, time, {ease: FlxEase.cubeOut, onComplete: function(flxTween:FlxTween) { if (onComplete != '' && onComplete != null) {callLua(onComplete,["camera"]);}}}); + }); + + Lua_helper.add_callback(lua,"tweenHudZoomOut", function(toZoom:Float, time:Float, onComplete:String) { + FlxTween.tween(PlayState.instance.camHUD, {zoom:toZoom}, time, {ease: FlxEase.cubeOut, onComplete: function(flxTween:FlxTween) { if (onComplete != '' && onComplete != null) {callLua(onComplete,["camera"]);}}}); + }); + + Lua_helper.add_callback(lua,"tweenPosOut", function(id:String, toX:Int, toY:Int, time:Float, onComplete:String) { + FlxTween.tween(getActorByName(id), {x: toX, y: toY}, time, {ease: FlxEase.cubeOut, onComplete: function(flxTween:FlxTween) { if (onComplete != '' && onComplete != null) {callLua(onComplete,[id]);}}}); + }); + + Lua_helper.add_callback(lua,"tweenPosXAngleOut", function(id:String, toX:Int, toAngle:Float, time:Float, onComplete:String) { + FlxTween.tween(getActorByName(id), {x: toX, angle: toAngle}, time, {ease: FlxEase.cubeOut, onComplete: function(flxTween:FlxTween) { if (onComplete != '' && onComplete != null) {callLua(onComplete,[id]);}}}); + }); + + Lua_helper.add_callback(lua,"tweenPosYAngleOut", function(id:String, toY:Int, toAngle:Float, time:Float, onComplete:String) { + FlxTween.tween(getActorByName(id), {y: toY, angle: toAngle}, time, {ease: FlxEase.cubeOut, onComplete: function(flxTween:FlxTween) { if (onComplete != '' && onComplete != null) {callLua(onComplete,[id]);}}}); + }); + + Lua_helper.add_callback(lua,"tweenAngleOut", function(id:String, toAngle:Int, time:Float, onComplete:String) { + FlxTween.tween(getActorByName(id), {angle: toAngle}, time, {ease: FlxEase.cubeOut, onComplete: function(flxTween:FlxTween) { if (onComplete != '' && onComplete != null) {callLua(onComplete,[id]);}}}); + }); + + Lua_helper.add_callback(lua,"tweenCameraPosIn", function(toX:Int, toY:Int, time:Float, onComplete:String) { + FlxTween.tween(FlxG.camera, {x: toX, y: toY}, time, {ease: FlxEase.cubeIn, onComplete: function(flxTween:FlxTween) { if (onComplete != '' && onComplete != null) {callLua(onComplete,["camera"]);}}}); + }); + + Lua_helper.add_callback(lua,"tweenCameraAngleIn", function(toAngle:Float, time:Float, onComplete:String) { + FlxTween.tween(FlxG.camera, {angle:toAngle}, time, {ease: FlxEase.cubeIn, onComplete: function(flxTween:FlxTween) { if (onComplete != '' && onComplete != null) {callLua(onComplete,["camera"]);}}}); + }); + + Lua_helper.add_callback(lua,"tweenCameraZoomIn", function(toZoom:Float, time:Float, onComplete:String) { + FlxTween.tween(FlxG.camera, {zoom:toZoom}, time, {ease: FlxEase.cubeIn, onComplete: function(flxTween:FlxTween) { if (onComplete != '' && onComplete != null) {callLua(onComplete,["camera"]);}}}); + }); + + Lua_helper.add_callback(lua,"tweenHudPosIn", function(toX:Int, toY:Int, time:Float, onComplete:String) { + FlxTween.tween(PlayState.instance.camHUD, {x: toX, y: toY}, time, {ease: FlxEase.cubeIn, onComplete: function(flxTween:FlxTween) { if (onComplete != '' && onComplete != null) {callLua(onComplete,["camera"]);}}}); + }); + + Lua_helper.add_callback(lua,"tweenHudAngleIn", function(toAngle:Float, time:Float, onComplete:String) { + FlxTween.tween(PlayState.instance.camHUD, {angle:toAngle}, time, {ease: FlxEase.cubeIn, onComplete: function(flxTween:FlxTween) { if (onComplete != '' && onComplete != null) {callLua(onComplete,["camera"]);}}}); + }); + + Lua_helper.add_callback(lua,"tweenHudZoomIn", function(toZoom:Float, time:Float, onComplete:String) { + FlxTween.tween(PlayState.instance.camHUD, {zoom:toZoom}, time, {ease: FlxEase.cubeIn, onComplete: function(flxTween:FlxTween) { if (onComplete != '' && onComplete != null) {callLua(onComplete,["camera"]);}}}); + }); + + Lua_helper.add_callback(lua,"tweenPosIn", function(id:String, toX:Int, toY:Int, time:Float, onComplete:String) { + FlxTween.tween(getActorByName(id), {x: toX, y: toY}, time, {ease: FlxEase.cubeIn, onComplete: function(flxTween:FlxTween) { if (onComplete != '' && onComplete != null) {callLua(onComplete,[id]);}}}); + }); + + Lua_helper.add_callback(lua,"tweenPosXAngleIn", function(id:String, toX:Int, toAngle:Float, time:Float, onComplete:String) { + FlxTween.tween(getActorByName(id), {x: toX, angle: toAngle}, time, {ease: FlxEase.cubeIn, onComplete: function(flxTween:FlxTween) { if (onComplete != '' && onComplete != null) {callLua(onComplete,[id]);}}}); + }); + + Lua_helper.add_callback(lua,"tweenPosYAngleIn", function(id:String, toY:Int, toAngle:Float, time:Float, onComplete:String) { + FlxTween.tween(getActorByName(id), {y: toY, angle: toAngle}, time, {ease: FlxEase.cubeIn, onComplete: function(flxTween:FlxTween) { if (onComplete != '' && onComplete != null) {callLua(onComplete,[id]);}}}); + }); + + Lua_helper.add_callback(lua,"tweenAngleIn", function(id:String, toAngle:Int, time:Float, onComplete:String) { + FlxTween.tween(getActorByName(id), {angle: toAngle}, time, {ease: FlxEase.cubeIn, onComplete: function(flxTween:FlxTween) { if (onComplete != '' && onComplete != null) {callLua(onComplete,[id]);}}}); + }); + + Lua_helper.add_callback(lua,"tweenFadeIn", function(id:String, toAlpha:Float, time:Float, onComplete:String) { + FlxTween.tween(getActorByName(id), {alpha: toAlpha}, time, {ease: FlxEase.circIn, onComplete: function(flxTween:FlxTween) { if (onComplete != '' && onComplete != null) {callLua(onComplete,[id]);}}}); + }); + + Lua_helper.add_callback(lua,"tweenFadeOut", function(id:String, toAlpha:Float, time:Float, onComplete:String) { + FlxTween.tween(getActorByName(id), {alpha: toAlpha}, time, {ease: FlxEase.circOut, onComplete: function(flxTween:FlxTween) { if (onComplete != '' && onComplete != null) {callLua(onComplete,[id]);}}}); + }); +//forgot and accidentally commit to master branch + // shader + + /*Lua_helper.add_callback(lua,"createShader", function(frag:String,vert:String) { + var shader:LuaShader = new LuaShader(frag,vert); + + trace(shader.glFragmentSource); + + shaders.push(shader); + // if theres 1 shader we want to say theres 0 since 0 index and length returns a 1 index. + return shaders.length == 1 ? 0 : shaders.length; + }); + + + Lua_helper.add_callback(lua,"setFilterHud", function(shaderIndex:Int) { + PlayState.instance.camHUD.setFilters([new ShaderFilter(shaders[shaderIndex])]); + }); + + Lua_helper.add_callback(lua,"setFilterCam", function(shaderIndex:Int) { + FlxG.camera.setFilters([new ShaderFilter(shaders[shaderIndex])]); + });*/ + + // default strums + + for (i in 0...PlayState.strumLineNotes.length) { + var member = PlayState.strumLineNotes.members[i]; + trace(PlayState.strumLineNotes.members[i].x + " " + PlayState.strumLineNotes.members[i].y + " " + PlayState.strumLineNotes.members[i].angle + " | strum" + i); + //setVar("strum" + i + "X", Math.floor(member.x)); + setVar("defaultStrum" + i + "X", Math.floor(member.x)); + //setVar("strum" + i + "Y", Math.floor(member.y)); + setVar("defaultStrum" + i + "Y", Math.floor(member.y)); + //setVar("strum" + i + "Angle", Math.floor(member.angle)); + setVar("defaultStrum" + i + "Angle", Math.floor(member.angle)); + trace("Adding strum" + i); + } + } + + public function executeState(name,args:Array) + { + return Lua.tostring(lua,callLua(name, args)); + } + + public static function createModchartState():ModchartState + { + return new ModchartState(); + } +} +#end diff --git a/source_duck/MusicBeatState.hx b/source_duck/MusicBeatState.hx new file mode 100644 index 00000000..29260f84 --- /dev/null +++ b/source_duck/MusicBeatState.hx @@ -0,0 +1,114 @@ +package; + +#if windows +import Discord.DiscordClient; +#end +import flixel.tweens.FlxTween; +import flixel.util.FlxColor; +import openfl.Lib; +import Conductor.BPMChangeEvent; +import flixel.FlxG; +import flixel.addons.transition.FlxTransitionableState; +import flixel.addons.ui.FlxUIState; +import flixel.math.FlxRect; +import flixel.util.FlxTimer; + +class MusicBeatState extends FlxUIState +{ + private var lastBeat:Float = 0; + private var lastStep:Float = 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() + { + (cast (Lib.current.getChildAt(0), Main)).setFPSCap(FlxG.save.data.fpsCap); + + if (transIn != null) + trace('reg ' + transIn.region); + + super.create(); + } + + + var array:Array = [ + FlxColor.fromRGB(148, 0, 211), + FlxColor.fromRGB(75, 0, 130), + FlxColor.fromRGB(0, 0, 255), + FlxColor.fromRGB(0, 255, 0), + FlxColor.fromRGB(255, 255, 0), + FlxColor.fromRGB(255, 127, 0), + FlxColor.fromRGB(255, 0 , 0) + ]; + + var skippedFrames = 0; + + override function update(elapsed:Float) + { + //everyStep(); + var oldStep:Int = curStep; + + updateCurStep(); + updateBeat(); + + if (oldStep != curStep && curStep > 0) + stepHit(); + + if (FlxG.save.data.fpsRain && skippedFrames >= 6) + { + if (currentColor >= array.length) + currentColor = 0; + (cast (Lib.current.getChildAt(0), Main)).changeFPSColor(array[currentColor]); + currentColor++; + skippedFrames = 0; + } + else + skippedFrames++; + + if ((cast (Lib.current.getChildAt(0), Main)).getFPSCap != FlxG.save.data.fpsCap) + (cast (Lib.current.getChildAt(0), Main)).setFPSCap(FlxG.save.data.fpsCap); + + super.update(elapsed); + } + + private function updateBeat():Void + { + lastBeat = curStep; + curBeat = Math.floor(curStep / 4); + } + + public static var currentColor = 0; + + private function updateCurStep():Void + { + var lastChange:BPMChangeEvent = { + stepTime: 0, + songTime: 0, + bpm: 0 + } + for (i in 0...Conductor.bpmChangeMap.length) + { + if (Conductor.songPosition >= Conductor.bpmChangeMap[i].songTime) + lastChange = Conductor.bpmChangeMap[i]; + } + + curStep = lastChange.stepTime + Math.floor((Conductor.songPosition - lastChange.songTime) / Conductor.stepCrochet); + } + + public function stepHit():Void + { + + if (curStep % 4 == 0) + beatHit(); + } + + public function beatHit():Void + { + //do literally nothing dumbass + } +} diff --git a/source_duck/MusicBeatSubstate.hx b/source_duck/MusicBeatSubstate.hx new file mode 100644 index 00000000..c6da0fb5 --- /dev/null +++ b/source_duck/MusicBeatSubstate.hx @@ -0,0 +1,65 @@ +package; + +import Conductor.BPMChangeEvent; +import flixel.FlxG; +import flixel.FlxSubState; + +class MusicBeatSubstate extends FlxSubState +{ + public function new() + { + super(); + } + + private var lastBeat:Float = 0; + private var lastStep:Float = 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 update(elapsed:Float) + { + //everyStep(); + var oldStep:Int = curStep; + + updateCurStep(); + curBeat = Math.floor(curStep / 4); + + if (oldStep != curStep && curStep > 0) + stepHit(); + + + super.update(elapsed); + } + + private function updateCurStep():Void + { + var lastChange:BPMChangeEvent = { + stepTime: 0, + songTime: 0, + bpm: 0 + } + for (i in 0...Conductor.bpmChangeMap.length) + { + if (Conductor.songPosition > Conductor.bpmChangeMap[i].songTime) + lastChange = Conductor.bpmChangeMap[i]; + } + + curStep = lastChange.stepTime + Math.floor((Conductor.songPosition - lastChange.songTime) / Conductor.stepCrochet); + } + + public function stepHit():Void + { + if (curStep % 4 == 0) + beatHit(); + } + + public function beatHit():Void + { + //do literally nothing dumbass + } +} diff --git a/source_duck/NGio.hx b/source_duck/NGio.hx new file mode 100644 index 00000000..f8d8948e --- /dev/null +++ b/source_duck/NGio.hx @@ -0,0 +1,200 @@ +package; + +import flixel.FlxG; +import flixel.util.FlxSignal; +import flixel.util.FlxTimer; +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 io.newgrounds.objects.events.Response; +import io.newgrounds.objects.events.Result.GetCurrentVersionResult; +import io.newgrounds.objects.events.Result.GetVersionResult; +import lime.app.Application; +import openfl.display.Stage; + +using StringTools; + +/** + * 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 static var GAME_VER:String = ""; + public static var GAME_VER_NUMS:String = ''; + public static var gotOnlineVer:Bool = false; + + public static function noLogin(api:String) + { + trace('INIT NOLOGIN'); + GAME_VER = "v" + Application.current.meta.get('version'); + + if (api.length != 0) + { + NG.create(api); + + new FlxTimer().start(2, function(tmr:FlxTimer) + { + var call = NG.core.calls.app.getCurrentVersion(GAME_VER).addDataHandler(function(response:Response) + { + GAME_VER = response.result.data.currentVersion; + GAME_VER_NUMS = GAME_VER.split(" ")[0].trim(); + trace('CURRENT NG VERSION: ' + GAME_VER); + trace('CURRENT NG VERSION: ' + GAME_VER_NUMS); + gotOnlineVer = true; + }); + + call.send(); + }); + } + } + + 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 logEvent(event:String) + { + NG.core.calls.event.logEvent(event).send(); + trace('should have logged: ' + event); + } + + 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_duck/Note.hx b/source_duck/Note.hx new file mode 100644 index 00000000..544fe3aa --- /dev/null +++ b/source_duck/Note.hx @@ -0,0 +1,217 @@ +package; + +import flixel.addons.effects.FlxSkewedSprite; +import flixel.FlxG; +import flixel.FlxSprite; +import flixel.graphics.frames.FlxAtlasFrames; +import flixel.math.FlxMath; +import flixel.util.FlxColor; +#if polymod +import polymod.format.ParseRules.TargetSignatureElement; +#end +import PlayState; + +using StringTools; + +class Note extends FlxSprite +{ + public var strumTime:Float = 0; + + public var mustPress:Bool = false; + public var noteData:Int = 0; + public var canBeHit:Bool = false; + public var tooLate:Bool = false; + public var wasGoodHit:Bool = false; + public var prevNote:Note; + public var modifiedByLua:Bool = false; + public var sustainLength:Float = 0; + public var isSustainNote:Bool = false; + + public var noteScore:Float = 1; + + public static var swagWidth:Float = 160 * 0.7; + public static var PURP_NOTE:Int = 0; + public static var GREEN_NOTE:Int = 2; + public static var BLUE_NOTE:Int = 1; + public static var RED_NOTE:Int = 3; + + public var rating:String = "shit"; + + public function new(strumTime:Float, noteData:Int, ?prevNote:Note, ?sustainNote:Bool = false) + { + super(); + + if (prevNote == null) + prevNote = this; + + this.prevNote = prevNote; + isSustainNote = sustainNote; + + x += 50; + // MAKE SURE ITS DEFINITELY OFF SCREEN? + y -= 2000; + this.strumTime = strumTime; + + if (this.strumTime < 0 ) + this.strumTime = 0; + + this.noteData = noteData; + + var daStage:String = PlayState.curStage; + + switch (PlayState.SONG.noteStyle) + { + case 'pixel': + loadGraphic(Paths.image('weeb/pixelUI/arrows-pixels','week6'), true, 17, 17); + + animation.add('greenScroll', [6]); + animation.add('redScroll', [7]); + animation.add('blueScroll', [5]); + animation.add('purpleScroll', [4]); + + if (isSustainNote) + { + loadGraphic(Paths.image('weeb/pixelUI/arrowEnds','week6'), true, 7, 6); + + animation.add('purpleholdend', [4]); + animation.add('greenholdend', [6]); + animation.add('redholdend', [7]); + animation.add('blueholdend', [5]); + + animation.add('purplehold', [0]); + animation.add('greenhold', [2]); + animation.add('redhold', [3]); + animation.add('bluehold', [1]); + } + + setGraphicSize(Std.int(width * PlayState.daPixelZoom)); + updateHitbox(); + default: + frames = Paths.getSparrowAtlas('NOTE_assets'); + + animation.addByPrefix('greenScroll', 'green0'); + animation.addByPrefix('redScroll', 'red0'); + animation.addByPrefix('blueScroll', 'blue0'); + animation.addByPrefix('purpleScroll', 'purple0'); + + animation.addByPrefix('purpleholdend', 'pruple end hold'); + animation.addByPrefix('greenholdend', 'green hold end'); + animation.addByPrefix('redholdend', 'red hold end'); + animation.addByPrefix('blueholdend', 'blue hold end'); + + animation.addByPrefix('purplehold', 'purple hold piece'); + animation.addByPrefix('greenhold', 'green hold piece'); + animation.addByPrefix('redhold', 'red hold piece'); + animation.addByPrefix('bluehold', 'blue hold piece'); + + setGraphicSize(Std.int(width * 0.7)); + updateHitbox(); + antialiasing = true; + } + + switch (noteData) + { + case 0: + x += swagWidth * 0; + animation.play('purpleScroll'); + case 1: + x += swagWidth * 1; + animation.play('blueScroll'); + case 2: + x += swagWidth * 2; + animation.play('greenScroll'); + case 3: + x += swagWidth * 3; + animation.play('redScroll'); + } + + // trace(prevNote); + + // we make sure its downscroll and its a SUSTAIN NOTE (aka a trail, not a note) + // and flip it so it doesn't look weird. + // THIS DOESN'T FUCKING FLIP THE NOTE, CONTRIBUTERS DON'T JUST COMMENT THIS OUT JESUS + if (FlxG.save.data.downscroll && sustainNote) + flipY = true; + + if (isSustainNote && prevNote != null) + { + noteScore * 0.2; + alpha = 0.6; + + x += width / 2; + + switch (noteData) + { + case 2: + animation.play('greenholdend'); + case 3: + animation.play('redholdend'); + case 1: + animation.play('blueholdend'); + case 0: + animation.play('purpleholdend'); + } + + updateHitbox(); + + x -= width / 2; + + if (PlayState.curStage.startsWith('school')) + x += 30; + + if (prevNote.isSustainNote) + { + switch (prevNote.noteData) + { + case 0: + prevNote.animation.play('purplehold'); + case 1: + prevNote.animation.play('bluehold'); + case 2: + prevNote.animation.play('greenhold'); + case 3: + prevNote.animation.play('redhold'); + } + + + if(FlxG.save.data.scrollSpeed != 1) + prevNote.scale.y *= Conductor.stepCrochet / 100 * 1.5 * FlxG.save.data.scrollSpeed; + else + prevNote.scale.y *= Conductor.stepCrochet / 100 * 1.5 * PlayState.SONG.speed; + prevNote.updateHitbox(); + // prevNote.setGraphicSize(); + } + } + } + + override function update(elapsed:Float) + { + super.update(elapsed); + + if (mustPress) + { + // The * 0.5 is so that it's easier to hit them too late, instead of too early + if (strumTime > Conductor.songPosition - (Conductor.safeZoneOffset * 1.5) + && strumTime < Conductor.songPosition + (Conductor.safeZoneOffset * 0.5)) + canBeHit = true; + else + canBeHit = false; + + if (strumTime < Conductor.songPosition - Conductor.safeZoneOffset * Conductor.timeScale && !wasGoodHit) + tooLate = true; + } + else + { + canBeHit = false; + + if (strumTime <= Conductor.songPosition) + wasGoodHit = true; + } + + if (tooLate) + { + if (alpha > 0.3) + alpha = 0.3; + } + } +} diff --git a/source_duck/OFLSprite.hx b/source_duck/OFLSprite.hx new file mode 100644 index 00000000..abea3a09 --- /dev/null +++ b/source_duck/OFLSprite.hx @@ -0,0 +1,39 @@ +import flixel.util.FlxColor; +import openfl.display.Sprite; +import flixel.FlxSprite; + +/** + * designed to draw a Open FL Sprite as a FlxSprite (to allow layering and auto sizing for haxe flixel cameras) + * Custom made for Kade Engine + */ +class OFLSprite extends FlxSprite +{ + public var flSprite:Sprite; + + public function new(x,y,width,height,Sprite:Sprite) + { + super(x,y); + + makeGraphic(width,height,FlxColor.TRANSPARENT); + + flSprite = Sprite; + + pixels.draw(flSprite); + } + + private var _frameCount:Int = 0; + + override function update(elapsed:Float) + { + if (_frameCount != 2) + { + pixels.draw(flSprite); + _frameCount++; + } + } + + public function updateDisplay() + { + pixels.draw(flSprite); + } +} \ No newline at end of file diff --git a/source_duck/Options.hx b/source_duck/Options.hx new file mode 100644 index 00000000..3d9c2cd5 --- /dev/null +++ b/source_duck/Options.hx @@ -0,0 +1,538 @@ +package; + +import flixel.util.FlxColor; +import Controls.KeyboardScheme; +import flixel.FlxG; +import openfl.display.FPS; +import openfl.Lib; + +class OptionCatagory +{ + private var _options:Array