Tobee_Night_Funkin_PSYCH/source/PlayState.hx

4015 lines
112 KiB
Haxe

package;
#if desktop
import Discord.DiscordClient;
#end
import Section.SwagSection;
import Song.SwagSong;
import WiggleEffect.WiggleEffectType;
import flixel.FlxBasic;
import flixel.FlxCamera;
import flixel.FlxG;
import flixel.FlxGame;
import flixel.FlxObject;
import flixel.FlxSprite;
import flixel.FlxState;
import flixel.FlxSubState;
import flixel.addons.display.FlxGridOverlay;
import flixel.addons.effects.FlxTrail;
import flixel.addons.effects.FlxTrailArea;
import flixel.addons.effects.chainable.FlxEffectSprite;
import flixel.addons.effects.chainable.FlxWaveEffect;
import flixel.addons.transition.FlxTransitionableState;
import flixel.graphics.atlas.FlxAtlas;
import flixel.graphics.frames.FlxAtlasFrames;
import flixel.group.FlxGroup.FlxTypedGroup;
import flixel.math.FlxMath;
import flixel.math.FlxPoint;
import flixel.math.FlxRect;
import flixel.system.FlxSound;
import flixel.text.FlxText;
import flixel.tweens.FlxEase;
import flixel.tweens.FlxTween;
import flixel.ui.FlxBar;
import flixel.util.FlxCollision;
import flixel.util.FlxColor;
import flixel.util.FlxSort;
import flixel.util.FlxStringUtil;
import flixel.util.FlxTimer;
import haxe.Json;
import lime.utils.Assets;
import openfl.display.BlendMode;
import openfl.display.StageQuality;
import openfl.filters.ShaderFilter;
import openfl.utils.Assets as OpenFlAssets;
import editors.ChartingState;
import editors.CharacterEditorState;
import flixel.group.FlxSpriteGroup;
import Achievements;
import StageData;
import FunkinLua;
import DialogueBoxPsych;
#if sys
import sys.FileSystem;
#end
using StringTools;
class PlayState extends MusicBeatState
{
public static var STRUM_X = 42;
public static var STRUM_X_MIDDLESCROLL = -278;
public static var ratingStuff:Array<Dynamic> = [
['You Suck!', 0.2], //From 0% to 19%
['Shit', 0.4], //From 20% to 39%
['Bad', 0.5], //From 40% to 49%
['Bruh', 0.6], //From 50% to 59%
['Meh', 0.69], //From 60% to 68%
['Nice', 0.7], //69%
['Good', 0.8], //From 70% to 79%
['Great', 0.9], //From 80% to 89%
['Sick!', 1], //From 90% to 99%
['Perfect!!', 1] //The value on this one isn't used actually, since Perfect is always "1"
];
#if (haxe >= "4.0.0")
public var modchartTweens:Map<String, FlxTween> = new Map();
public var modchartSprites:Map<String, ModchartSprite> = new Map();
public var modchartTimers:Map<String, FlxTimer> = new Map();
public var modchartSounds:Map<String, FlxSound> = new Map();
#else
public var modchartTweens:Map<String, FlxTween> = new Map<String, FlxTween>();
public var modchartSprites:Map<String, ModchartSprite> = new Map<String, Dynamic>();
public var modchartTimers:Map<String, FlxTimer> = new Map<String, FlxTimer>();
public var modchartSounds:Map<String, FlxSound> = new Map<String, FlxSound>();
#end
//event variables
private var isCameraOnForcedPos:Bool = false;
#if (haxe >= "4.0.0")
public var boyfriendMap:Map<String, Boyfriend> = new Map();
public var dadMap:Map<String, Character> = new Map();
public var gfMap:Map<String, Character> = new Map();
#else
public var boyfriendMap:Map<String, Boyfriend> = new Map<String, Boyfriend>();
public var dadMap:Map<String, Character> = new Map<String, Character>();
public var gfMap:Map<String, Character> = new Map<String, Character>();
#end
public var BF_X:Float = 770;
public var BF_Y:Float = 100;
public var DAD_X:Float = 100;
public var DAD_Y:Float = 100;
public var GF_X:Float = 400;
public var GF_Y:Float = 130;
public var boyfriendGroup:FlxSpriteGroup;
public var dadGroup:FlxSpriteGroup;
public var gfGroup:FlxSpriteGroup;
public static var curStage:String = '';
public static var isPixelStage:Bool = false;
public static var SONG:SwagSong = null;
public static var isStoryMode:Bool = false;
public static var storyWeek:Int = 0;
public static var storyPlaylist:Array<String> = [];
public static var storyDifficulty:Int = 1;
public var vocals:FlxSound;
public var dad:Character;
public var gf:Character;
public var boyfriend:Boyfriend;
public var notes:FlxTypedGroup<Note>;
public var unspawnNotes:Array<Note> = [];
public var eventNotes:Array<Dynamic> = [];
private var strumLine:FlxSprite;
//Handles the new epic mega sexy cam code that i've done
private var camFollow:FlxPoint;
private var camFollowPos:FlxObject;
private static var prevCamFollow:FlxPoint;
private static var prevCamFollowPos:FlxObject;
private static var resetSpriteCache:Bool = false;
public var strumLineNotes:FlxTypedGroup<StrumNote>;
public var opponentStrums:FlxTypedGroup<StrumNote>;
public var playerStrums:FlxTypedGroup<StrumNote>;
public var grpNoteSplashes:FlxTypedGroup<NoteSplash>;
public var camZooming:Bool = false;
private var curSong:String = "";
public var gfSpeed:Int = 1;
public var health:Float = 1;
public var combo:Int = 0;
private var healthBarBG:AttachedSprite;
public var healthBar:FlxBar;
var songPercent:Float = 0;
private var timeBarBG:AttachedSprite;
public var timeBar:FlxBar;
private var generatedMusic:Bool = false;
public var endingSong:Bool = false;
private var startingSong:Bool = false;
private var updateTime:Bool = false;
public static var practiceMode:Bool = false;
public static var usedPractice:Bool = false;
public static var changedDifficulty:Bool = false;
public static var cpuControlled:Bool = false;
var botplaySine:Float = 0;
var botplayTxt:FlxText;
public var iconP1:HealthIcon;
public var iconP2:HealthIcon;
public var camHUD:FlxCamera;
public var camGame:FlxCamera;
public var camOther:FlxCamera;
public var cameraSpeed:Float = 1;
var dialogue:Array<String> = ['blah blah blah', 'coolswag'];
var dialogueJson:DialogueFile = null;
var halloweenBG:BGSprite;
var halloweenWhite:BGSprite;
var phillyCityLights:FlxTypedGroup<BGSprite>;
var phillyTrain:BGSprite;
var blammedLightsBlack:ModchartSprite;
var blammedLightsBlackTween:FlxTween;
var phillyCityLightsEvent:FlxTypedGroup<BGSprite>;
var phillyCityLightsEventTween:FlxTween;
var trainSound:FlxSound;
var limoKillingState:Int = 0;
var limo:BGSprite;
var limoMetalPole:BGSprite;
var limoLight:BGSprite;
var limoCorpse:BGSprite;
var limoCorpseTwo:BGSprite;
var bgLimo:BGSprite;
var grpLimoParticles:FlxTypedGroup<BGSprite>;
var grpLimoDancers:FlxTypedGroup<BackgroundDancer>;
var fastCar:BGSprite;
var upperBoppers:BGSprite;
var bottomBoppers:BGSprite;
var santa:BGSprite;
var heyTimer:Float;
var bgGirls:BackgroundGirls;
var wiggleShit:WiggleEffect = new WiggleEffect();
var bgGhouls:BGSprite;
public var songScore:Int = 0;
public var songHits:Int = 0;
public var songMisses:Int = 0;
public var ghostMisses:Int = 0;
public var scoreTxt:FlxText;
var timeTxt:FlxText;
var scoreTxtTween:FlxTween;
public static var campaignScore:Int = 0;
public static var campaignMisses:Int = 0;
public static var seenCutscene:Bool = false;
public static var deathCounter:Int = 0;
public var defaultCamZoom:Float = 1.05;
// how big to stretch the pixel art assets
public static var daPixelZoom:Float = 6;
public var inCutscene:Bool = false;
var songLength:Float = 0;
#if desktop
// Discord RPC variables
var storyDifficultyText:String = "";
var detailsText:String = "";
var detailsPausedText:String = "";
#end
private var luaArray:Array<FunkinLua> = [];
//Achievement shit
var keysPressed:Array<Bool> = [false, false, false, false];
var boyfriendIdleTime:Float = 0.0;
var boyfriendIdled:Bool = false;
// Lua shit
private var luaDebugGroup:FlxTypedGroup<DebugLuaText>;
public var introSoundsSuffix:String = '';
override public function create()
{
#if MODS_ALLOWED
Paths.destroyLoadedImages(resetSpriteCache);
#end
resetSpriteCache = false;
if (FlxG.sound.music != null)
FlxG.sound.music.stop();
practiceMode = false;
// var gameCam:FlxCamera = FlxG.camera;
camGame = new FlxCamera();
camHUD = new FlxCamera();
camOther = new FlxCamera();
camHUD.bgColor.alpha = 0;
camOther.bgColor.alpha = 0;
FlxG.cameras.reset(camGame);
FlxG.cameras.add(camHUD);
FlxG.cameras.add(camOther);
grpNoteSplashes = new FlxTypedGroup<NoteSplash>();
FlxCamera.defaultCameras = [camGame];
CustomFadeTransition.nextCamera = camOther;
//FlxG.cameras.setDefaultDrawTarget(camGame, true);
persistentUpdate = true;
persistentDraw = true;
if (SONG == null)
SONG = Song.loadFromJson('tutorial');
Conductor.mapBPMChanges(SONG);
Conductor.changeBPM(SONG.bpm);
#if desktop
storyDifficultyText = '' + CoolUtil.difficultyStuff[storyDifficulty][0];
// String that contains the mode defined here so it isn't necessary to call changePresence for each mode
if (isStoryMode)
{
detailsText = "Story Mode: " + WeekData.getCurrentWeek().weekName;
}
else
{
detailsText = "Freeplay";
}
// String for when the game is paused
detailsPausedText = "Paused - " + detailsText;
#end
GameOverSubstate.resetVariables();
var songName:String = Paths.formatToSongPath(SONG.song);
curStage = PlayState.SONG.stage;
trace('stage is: ' + curStage);
if(PlayState.SONG.stage == null || PlayState.SONG.stage.length < 1) {
switch (songName)
{
case 'spookeez' | 'south' | 'monster':
curStage = 'spooky';
case 'pico' | 'blammed' | 'philly' | 'philly-nice':
curStage = 'philly';
case 'milf' | 'satin-panties' | 'high':
curStage = 'limo';
case 'cocoa' | 'eggnog':
curStage = 'mall';
case 'winter-horrorland':
curStage = 'mallEvil';
case 'senpai' | 'roses':
curStage = 'school';
case 'thorns':
curStage = 'schoolEvil';
default:
curStage = 'stage';
}
}
var stageData:StageFile = StageData.getStageFile(curStage);
if(stageData == null) { //Stage couldn't be found, create a dummy stage for preventing a crash
stageData = {
directory: "",
defaultZoom: 0.9,
isPixelStage: false,
boyfriend: [770, 100],
girlfriend: [400, 130],
opponent: [100, 100]
};
}
defaultCamZoom = stageData.defaultZoom;
isPixelStage = stageData.isPixelStage;
BF_X = stageData.boyfriend[0];
BF_Y = stageData.boyfriend[1];
GF_X = stageData.girlfriend[0];
GF_Y = stageData.girlfriend[1];
DAD_X = stageData.opponent[0];
DAD_Y = stageData.opponent[1];
boyfriendGroup = new FlxSpriteGroup(BF_X, BF_Y);
dadGroup = new FlxSpriteGroup(DAD_X, DAD_Y);
gfGroup = new FlxSpriteGroup(GF_X, GF_Y);
switch (curStage)
{
case 'stage': //Week 1
var bg:BGSprite = new BGSprite('stageback', -600, -200, 0.9, 0.9);
add(bg);
var stageFront:BGSprite = new BGSprite('stagefront', -650, 600, 0.9, 0.9);
stageFront.setGraphicSize(Std.int(stageFront.width * 1.1));
stageFront.updateHitbox();
add(stageFront);
if(!ClientPrefs.lowQuality) {
var stageLight:BGSprite = new BGSprite('stage_light', -125, -100, 0.9, 0.9);
stageLight.setGraphicSize(Std.int(stageLight.width * 1.1));
stageLight.updateHitbox();
add(stageLight);
var stageLight:BGSprite = new BGSprite('stage_light', 1225, -100, 0.9, 0.9);
stageLight.setGraphicSize(Std.int(stageLight.width * 1.1));
stageLight.updateHitbox();
stageLight.flipX = true;
add(stageLight);
var stageCurtains:BGSprite = new BGSprite('stagecurtains', -500, -300, 1.3, 1.3);
stageCurtains.setGraphicSize(Std.int(stageCurtains.width * 0.9));
stageCurtains.updateHitbox();
add(stageCurtains);
}
case 'spooky': //Week 2
if(!ClientPrefs.lowQuality) {
halloweenBG = new BGSprite('halloween_bg', -200, -100, ['halloweem bg0', 'halloweem bg lightning strike']);
} else {
halloweenBG = new BGSprite('halloween_bg_low', -200, -100);
}
add(halloweenBG);
halloweenWhite = new BGSprite(null, -FlxG.width, -FlxG.height, 0, 0);
halloweenWhite.makeGraphic(Std.int(FlxG.width * 3), Std.int(FlxG.height * 3), FlxColor.WHITE);
halloweenWhite.alpha = 0;
halloweenWhite.blend = ADD;
//PRECACHE SOUNDS
CoolUtil.precacheSound('thunder_1');
CoolUtil.precacheSound('thunder_2');
case 'philly': //Week 3
if(!ClientPrefs.lowQuality) {
var bg:BGSprite = new BGSprite('philly/sky', -100, 0, 0.1, 0.1);
add(bg);
}
var city:BGSprite = new BGSprite('philly/city', -10, 0, 0.3, 0.3);
city.setGraphicSize(Std.int(city.width * 0.85));
city.updateHitbox();
add(city);
phillyCityLights = new FlxTypedGroup<BGSprite>();
add(phillyCityLights);
for (i in 0...5)
{
var light:BGSprite = new BGSprite('philly/win' + i, city.x, city.y, 0.3, 0.3);
light.visible = false;
light.setGraphicSize(Std.int(light.width * 0.85));
light.updateHitbox();
phillyCityLights.add(light);
}
if(!ClientPrefs.lowQuality) {
var streetBehind:BGSprite = new BGSprite('philly/behindTrain', -40, 50);
add(streetBehind);
}
phillyTrain = new BGSprite('philly/train', 2000, 360);
add(phillyTrain);
trainSound = new FlxSound().loadEmbedded(Paths.sound('train_passes'));
CoolUtil.precacheSound('train_passes');
FlxG.sound.list.add(trainSound);
var street:BGSprite = new BGSprite('philly/street', -40, 50);
add(street);
case 'limo': //Week 4
var skyBG:BGSprite = new BGSprite('limo/limoSunset', -120, -50, 0.1, 0.1);
add(skyBG);
if(!ClientPrefs.lowQuality) {
limoMetalPole = new BGSprite('gore/metalPole', -500, 220, 0.4, 0.4);
add(limoMetalPole);
bgLimo = new BGSprite('limo/bgLimo', -150, 480, 0.4, 0.4, ['background limo pink'], true);
add(bgLimo);
limoCorpse = new BGSprite('gore/noooooo', -500, limoMetalPole.y - 130, 0.4, 0.4, ['Henchmen on rail'], true);
add(limoCorpse);
limoCorpseTwo = new BGSprite('gore/noooooo', -500, limoMetalPole.y, 0.4, 0.4, ['henchmen death'], true);
add(limoCorpseTwo);
grpLimoDancers = new FlxTypedGroup<BackgroundDancer>();
add(grpLimoDancers);
for (i in 0...5)
{
var dancer:BackgroundDancer = new BackgroundDancer((370 * i) + 130, bgLimo.y - 400);
dancer.scrollFactor.set(0.4, 0.4);
grpLimoDancers.add(dancer);
}
limoLight = new BGSprite('gore/coldHeartKiller', limoMetalPole.x - 180, limoMetalPole.y - 80, 0.4, 0.4);
add(limoLight);
grpLimoParticles = new FlxTypedGroup<BGSprite>();
add(grpLimoParticles);
//PRECACHE BLOOD
var particle:BGSprite = new BGSprite('gore/stupidBlood', -400, -400, 0.4, 0.4, ['blood'], false);
particle.alpha = 0.01;
grpLimoParticles.add(particle);
resetLimoKill();
//PRECACHE SOUND
CoolUtil.precacheSound('dancerdeath');
}
limo = new BGSprite('limo/limoDrive', -120, 550, 1, 1, ['Limo stage'], true);
fastCar = new BGSprite('limo/fastCarLol', -300, 160);
fastCar.active = true;
limoKillingState = 0;
case 'mall': //Week 5 - Cocoa, Eggnog
var bg:BGSprite = new BGSprite('christmas/bgWalls', -1000, -500, 0.2, 0.2);
bg.setGraphicSize(Std.int(bg.width * 0.8));
bg.updateHitbox();
add(bg);
if(!ClientPrefs.lowQuality) {
upperBoppers = new BGSprite('christmas/upperBop', -240, -90, 0.33, 0.33, ['Upper Crowd Bob']);
upperBoppers.setGraphicSize(Std.int(upperBoppers.width * 0.85));
upperBoppers.updateHitbox();
add(upperBoppers);
var bgEscalator:BGSprite = new BGSprite('christmas/bgEscalator', -1100, -600, 0.3, 0.3);
bgEscalator.setGraphicSize(Std.int(bgEscalator.width * 0.9));
bgEscalator.updateHitbox();
add(bgEscalator);
}
var tree:BGSprite = new BGSprite('christmas/christmasTree', 370, -250, 0.40, 0.40);
add(tree);
bottomBoppers = new BGSprite('christmas/bottomBop', -300, 140, 0.9, 0.9, ['Bottom Level Boppers Idle']);
bottomBoppers.animation.addByPrefix('hey', 'Bottom Level Boppers HEY', 24, false);
bottomBoppers.setGraphicSize(Std.int(bottomBoppers.width * 1));
bottomBoppers.updateHitbox();
add(bottomBoppers);
var fgSnow:BGSprite = new BGSprite('christmas/fgSnow', -600, 700);
add(fgSnow);
santa = new BGSprite('christmas/santa', -840, 150, 1, 1, ['santa idle in fear']);
add(santa);
CoolUtil.precacheSound('Lights_Shut_off');
case 'mallEvil': //Week 5 - Winter Horrorland
var bg:BGSprite = new BGSprite('christmas/evilBG', -400, -500, 0.2, 0.2);
bg.setGraphicSize(Std.int(bg.width * 0.8));
bg.updateHitbox();
add(bg);
var evilTree:BGSprite = new BGSprite('christmas/evilTree', 300, -300, 0.2, 0.2);
add(evilTree);
var evilSnow:BGSprite = new BGSprite('christmas/evilSnow', -200, 700);
add(evilSnow);
case 'school': //Week 6 - Senpai, Roses
GameOverSubstate.deathSoundName = 'fnf_loss_sfx-pixel';
GameOverSubstate.loopSoundName = 'gameOver-pixel';
GameOverSubstate.endSoundName = 'gameOverEnd-pixel';
GameOverSubstate.characterName = 'bf-pixel-dead';
var bgSky:BGSprite = new BGSprite('weeb/weebSky', 0, 0, 0.1, 0.1);
add(bgSky);
bgSky.antialiasing = false;
var repositionShit = -200;
var bgSchool:BGSprite = new BGSprite('weeb/weebSchool', repositionShit, 0, 0.6, 0.90);
add(bgSchool);
bgSchool.antialiasing = false;
var bgStreet:BGSprite = new BGSprite('weeb/weebStreet', repositionShit, 0, 0.95, 0.95);
add(bgStreet);
bgStreet.antialiasing = false;
var widShit = Std.int(bgSky.width * 6);
if(!ClientPrefs.lowQuality) {
var fgTrees:BGSprite = new BGSprite('weeb/weebTreesBack', repositionShit + 170, 130, 0.9, 0.9);
fgTrees.setGraphicSize(Std.int(widShit * 0.8));
fgTrees.updateHitbox();
add(fgTrees);
fgTrees.antialiasing = false;
}
var bgTrees:FlxSprite = new FlxSprite(repositionShit - 380, -800);
bgTrees.frames = Paths.getPackerAtlas('weeb/weebTrees');
bgTrees.animation.add('treeLoop', [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18], 12);
bgTrees.animation.play('treeLoop');
bgTrees.scrollFactor.set(0.85, 0.85);
add(bgTrees);
bgTrees.antialiasing = false;
if(!ClientPrefs.lowQuality) {
var treeLeaves:BGSprite = new BGSprite('weeb/petals', repositionShit, -40, 0.85, 0.85, ['PETALS ALL'], true);
treeLeaves.setGraphicSize(widShit);
treeLeaves.updateHitbox();
add(treeLeaves);
treeLeaves.antialiasing = false;
}
bgSky.setGraphicSize(widShit);
bgSchool.setGraphicSize(widShit);
bgStreet.setGraphicSize(widShit);
bgTrees.setGraphicSize(Std.int(widShit * 1.4));
bgSky.updateHitbox();
bgSchool.updateHitbox();
bgStreet.updateHitbox();
bgTrees.updateHitbox();
if(!ClientPrefs.lowQuality) {
bgGirls = new BackgroundGirls(-100, 190);
bgGirls.scrollFactor.set(0.9, 0.9);
bgGirls.setGraphicSize(Std.int(bgGirls.width * daPixelZoom));
bgGirls.updateHitbox();
add(bgGirls);
}
case 'schoolEvil': //Week 6 - Thorns
GameOverSubstate.deathSoundName = 'fnf_loss_sfx-pixel';
GameOverSubstate.loopSoundName = 'gameOver-pixel';
GameOverSubstate.endSoundName = 'gameOverEnd-pixel';
GameOverSubstate.characterName = 'bf-pixel-dead';
/*if(!ClientPrefs.lowQuality) { //Does this even do something?
var waveEffectBG = new FlxWaveEffect(FlxWaveMode.ALL, 2, -1, 3, 2);
var waveEffectFG = new FlxWaveEffect(FlxWaveMode.ALL, 2, -1, 5, 2);
}*/
var posX = 400;
var posY = 200;
if(!ClientPrefs.lowQuality) {
var bg:BGSprite = new BGSprite('weeb/animatedEvilSchool', posX, posY, 0.8, 0.9, ['background 2'], true);
bg.scale.set(6, 6);
bg.antialiasing = false;
add(bg);
bgGhouls = new BGSprite('weeb/bgGhouls', -100, 190, 0.9, 0.9, ['BG freaks glitch instance'], false);
bgGhouls.setGraphicSize(Std.int(bgGhouls.width * daPixelZoom));
bgGhouls.updateHitbox();
bgGhouls.visible = false;
bgGhouls.antialiasing = false;
add(bgGhouls);
} else {
var bg:BGSprite = new BGSprite('weeb/animatedEvilSchool_low', posX, posY, 0.8, 0.9);
bg.scale.set(6, 6);
bg.antialiasing = false;
add(bg);
}
}
if(isPixelStage) {
introSoundsSuffix = '-pixel';
}
add(gfGroup);
// Shitty layering but whatev it works LOL
if (curStage == 'limo')
add(limo);
add(dadGroup);
add(boyfriendGroup);
if(curStage == 'spooky') {
add(halloweenWhite);
}
#if LUA_ALLOWED
luaDebugGroup = new FlxTypedGroup<DebugLuaText>();
luaDebugGroup.cameras = [camOther];
add(luaDebugGroup);
#end
#if (MODS_ALLOWED && LUA_ALLOWED)
var doPush:Bool = false;
var luaFile:String = 'stages/' + curStage + '.lua';
if(FileSystem.exists(Paths.modFolders(luaFile))) {
luaFile = Paths.modFolders(luaFile);
doPush = true;
} else {
luaFile = Paths.getPreloadPath(luaFile);
if(FileSystem.exists(luaFile)) {
doPush = true;
}
}
if(curStage == 'philly') {
phillyCityLightsEvent = new FlxTypedGroup<BGSprite>();
for (i in 0...5)
{
var light:BGSprite = new BGSprite('philly/win' + i, -10, 0, 0.3, 0.3);
light.visible = false;
light.setGraphicSize(Std.int(light.width * 0.85));
light.updateHitbox();
phillyCityLightsEvent.add(light);
}
}
if(doPush)
luaArray.push(new FunkinLua(luaFile));
if(!modchartSprites.exists('blammedLightsBlack')) { //Creates blammed light black fade in case you didn't make your own
blammedLightsBlack = new ModchartSprite(FlxG.width * -0.5, FlxG.height * -0.5);
blammedLightsBlack.makeGraphic(Std.int(FlxG.width * 2), Std.int(FlxG.height * 2), FlxColor.BLACK);
var position:Int = members.indexOf(gfGroup);
if(members.indexOf(boyfriendGroup) < position) {
position = members.indexOf(boyfriendGroup);
} else if(members.indexOf(dadGroup) < position) {
position = members.indexOf(dadGroup);
}
insert(position, blammedLightsBlack);
blammedLightsBlack.wasAdded = true;
modchartSprites.set('blammedLightsBlack', blammedLightsBlack);
}
if(curStage == 'philly') insert(members.indexOf(blammedLightsBlack) + 1, phillyCityLightsEvent);
blammedLightsBlack = modchartSprites.get('blammedLightsBlack');
blammedLightsBlack.alpha = 0.0;
#end
var gfVersion:String = SONG.player3;
if(gfVersion == null || gfVersion.length < 1) {
switch (curStage)
{
case 'limo':
gfVersion = 'gf-car';
case 'mall' | 'mallEvil':
gfVersion = 'gf-christmas';
case 'school' | 'schoolEvil':
gfVersion = 'gf-pixel';
default:
gfVersion = 'gf';
}
SONG.player3 = gfVersion; //Fix for the Chart Editor
}
gf = new Character(0, 0, gfVersion);
startCharacterPos(gf);
gf.scrollFactor.set(0.95, 0.95);
gfGroup.add(gf);
dad = new Character(0, 0, SONG.player2);
startCharacterPos(dad, true);
dadGroup.add(dad);
boyfriend = new Boyfriend(0, 0, SONG.player1);
startCharacterPos(boyfriend);
boyfriendGroup.add(boyfriend);
var camPos:FlxPoint = new FlxPoint(gf.getGraphicMidpoint().x, gf.getGraphicMidpoint().y);
camPos.x += gf.cameraPosition[0];
camPos.y += gf.cameraPosition[1];
if(dad.curCharacter.startsWith('gf')) {
dad.setPosition(GF_X, GF_Y);
gf.visible = false;
}
switch(curStage)
{
case 'limo':
resetFastCar();
insert(members.indexOf(gfGroup) - 1, fastCar);
case 'schoolEvil':
var evilTrail = new FlxTrail(dad, null, 4, 24, 0.3, 0.069); //nice
insert(members.indexOf(dadGroup) - 1, evilTrail);
}
var file:String = Paths.json(songName + '/dialogue'); //Checks for json/Psych Engine dialogue
if (OpenFlAssets.exists(file)) {
dialogueJson = DialogueBoxPsych.parseDialogue(file);
}
var file:String = Paths.txt(songName + '/' + songName + 'Dialogue'); //Checks for vanilla/Senpai dialogue
if (OpenFlAssets.exists(file)) {
dialogue = CoolUtil.coolTextFile(file);
}
var doof:DialogueBox = new DialogueBox(false, dialogue);
// doof.x += 70;
// doof.y = FlxG.height * 0.5;
doof.scrollFactor.set();
doof.finishThing = startCountdown;
doof.nextDialogueThing = startNextDialogue;
doof.skipDialogueThing = skipDialogue;
Conductor.songPosition = -5000;
strumLine = new FlxSprite(ClientPrefs.middleScroll ? STRUM_X_MIDDLESCROLL : STRUM_X, 50).makeGraphic(FlxG.width, 10);
if(ClientPrefs.downScroll) strumLine.y = FlxG.height - 150;
strumLine.scrollFactor.set();
timeTxt = new FlxText(STRUM_X + (FlxG.width / 2) - 248, 20, 400, "", 32);
timeTxt.setFormat(Paths.font("vcr.ttf"), 32, FlxColor.WHITE, CENTER, FlxTextBorderStyle.OUTLINE, FlxColor.BLACK);
timeTxt.scrollFactor.set();
timeTxt.alpha = 0;
timeTxt.borderSize = 2;
timeTxt.visible = !ClientPrefs.hideTime;
if(ClientPrefs.downScroll) timeTxt.y = FlxG.height - 45;
timeBarBG = new AttachedSprite('timeBar');
timeBarBG.x = timeTxt.x;
timeBarBG.y = timeTxt.y + (timeTxt.height / 4);
timeBarBG.scrollFactor.set();
timeBarBG.alpha = 0;
timeBarBG.visible = !ClientPrefs.hideTime;
timeBarBG.color = FlxColor.BLACK;
timeBarBG.xAdd = -4;
timeBarBG.yAdd = -4;
add(timeBarBG);
timeBar = new FlxBar(timeBarBG.x + 4, timeBarBG.y + 4, LEFT_TO_RIGHT, Std.int(timeBarBG.width - 8), Std.int(timeBarBG.height - 8), this,
'songPercent', 0, 1);
timeBar.scrollFactor.set();
timeBar.createFilledBar(0xFF000000, 0xFFFFFFFF);
timeBar.numDivisions = 800; //How much lag this causes?? Should i tone it down to idk, 400 or 200?
timeBar.alpha = 0;
timeBar.visible = !ClientPrefs.hideTime;
add(timeBar);
add(timeTxt);
timeBarBG.sprTracker = timeBar;
strumLineNotes = new FlxTypedGroup<StrumNote>();
add(strumLineNotes);
add(grpNoteSplashes);
var splash:NoteSplash = new NoteSplash(100, 100, 0);
grpNoteSplashes.add(splash);
splash.alpha = 0.0;
opponentStrums = new FlxTypedGroup<StrumNote>();
playerStrums = new FlxTypedGroup<StrumNote>();
// startCountdown();
generateSong(SONG.song);
#if LUA_ALLOWED
for (notetype in noteTypeMap.keys()) {
var luaToLoad:String = Paths.modFolders('custom_notetypes/' + notetype + '.lua');
if(FileSystem.exists(luaToLoad)) {
luaArray.push(new FunkinLua(luaToLoad));
}
}
for (event in eventPushedMap.keys()) {
var luaToLoad:String = Paths.modFolders('custom_events/' + event + '.lua');
if(FileSystem.exists(luaToLoad)) {
luaArray.push(new FunkinLua(luaToLoad));
}
}
#end
noteTypeMap.clear();
noteTypeMap = null;
eventPushedMap.clear();
eventPushedMap = null;
// After all characters being loaded, it makes then invisible 0.01s later so that the player won't freeze when you change characters
// add(strumLine);
camFollow = new FlxPoint();
camFollowPos = new FlxObject(0, 0, 1, 1);
snapCamFollowToPos(camPos.x, camPos.y);
if (prevCamFollow != null)
{
camFollow = prevCamFollow;
prevCamFollow = null;
}
if (prevCamFollowPos != null)
{
camFollowPos = prevCamFollowPos;
prevCamFollowPos = null;
}
add(camFollowPos);
FlxG.camera.follow(camFollowPos, LOCKON, 1);
// FlxG.camera.setScrollBounds(0, FlxG.width, 0, FlxG.height);
FlxG.camera.zoom = defaultCamZoom;
FlxG.camera.focusOn(camFollow);
FlxG.worldBounds.set(0, 0, FlxG.width, FlxG.height);
FlxG.fixedTimestep = false;
moveCameraSection(0);
healthBarBG = new AttachedSprite('healthBar');
healthBarBG.y = FlxG.height * 0.89;
healthBarBG.screenCenter(X);
healthBarBG.scrollFactor.set();
healthBarBG.visible = !ClientPrefs.hideHud;
healthBarBG.xAdd = -4;
healthBarBG.yAdd = -4;
add(healthBarBG);
if(ClientPrefs.downScroll) healthBarBG.y = 0.11 * FlxG.height;
healthBar = new FlxBar(healthBarBG.x + 4, healthBarBG.y + 4, RIGHT_TO_LEFT, Std.int(healthBarBG.width - 8), Std.int(healthBarBG.height - 8), this,
'health', 0, 2);
healthBar.scrollFactor.set();
// healthBar
healthBar.visible = !ClientPrefs.hideHud;
add(healthBar);
healthBarBG.sprTracker = healthBar;
iconP1 = new HealthIcon(boyfriend.healthIcon, true);
iconP1.y = healthBar.y - (iconP1.height / 2);
iconP1.visible = !ClientPrefs.hideHud;
add(iconP1);
iconP2 = new HealthIcon(dad.healthIcon, false);
iconP2.y = healthBar.y - (iconP2.height / 2);
iconP2.visible = !ClientPrefs.hideHud;
add(iconP2);
reloadHealthBarColors();
scoreTxt = new FlxText(0, healthBarBG.y + 36, FlxG.width, "", 20);
scoreTxt.setFormat(Paths.font("vcr.ttf"), 20, FlxColor.WHITE, CENTER, FlxTextBorderStyle.OUTLINE, FlxColor.BLACK);
scoreTxt.scrollFactor.set();
scoreTxt.borderSize = 1.25;
scoreTxt.visible = !ClientPrefs.hideHud;
add(scoreTxt);
botplayTxt = new FlxText(400, timeBarBG.y + 55, FlxG.width - 800, "BOTPLAY", 32);
botplayTxt.setFormat(Paths.font("vcr.ttf"), 32, FlxColor.WHITE, CENTER, FlxTextBorderStyle.OUTLINE, FlxColor.BLACK);
botplayTxt.scrollFactor.set();
botplayTxt.borderSize = 1.25;
botplayTxt.visible = cpuControlled;
add(botplayTxt);
if(ClientPrefs.downScroll) {
botplayTxt.y = timeBarBG.y - 78;
}
strumLineNotes.cameras = [camHUD];
grpNoteSplashes.cameras = [camHUD];
notes.cameras = [camHUD];
healthBar.cameras = [camHUD];
healthBarBG.cameras = [camHUD];
iconP1.cameras = [camHUD];
iconP2.cameras = [camHUD];
scoreTxt.cameras = [camHUD];
botplayTxt.cameras = [camHUD];
timeBar.cameras = [camHUD];
timeBarBG.cameras = [camHUD];
timeTxt.cameras = [camHUD];
doof.cameras = [camHUD];
// if (SONG.song == 'South')
// FlxG.camera.alpha = 0.7;
// UI_camera.zoom = 1;
// cameras = [FlxG.cameras.list[1]];
startingSong = true;
updateTime = true;
#if (MODS_ALLOWED && LUA_ALLOWED)
var doPush:Bool = false;
var luaFile:String = 'data/' + Paths.formatToSongPath(SONG.song) + '/script.lua';
if(FileSystem.exists(Paths.modFolders(luaFile))) {
luaFile = Paths.modFolders(luaFile);
doPush = true;
} else {
luaFile = Paths.getPreloadPath(luaFile);
if(FileSystem.exists(luaFile)) {
doPush = true;
}
}
if(doPush)
luaArray.push(new FunkinLua(luaFile));
#end
var daSong:String = Paths.formatToSongPath(curSong);
if (isStoryMode && !seenCutscene)
{
switch (daSong)
{
case "monster":
var whiteScreen:FlxSprite = new FlxSprite(0, 0).makeGraphic(Std.int(FlxG.width * 2), Std.int(FlxG.height * 2), FlxColor.WHITE);
add(whiteScreen);
whiteScreen.scrollFactor.set();
whiteScreen.blend = ADD;
camHUD.visible = false;
snapCamFollowToPos(dad.getMidpoint().x + 150, dad.getMidpoint().y - 100);
inCutscene = true;
FlxTween.tween(whiteScreen, {alpha: 0}, 1, {
startDelay: 0.1,
ease: FlxEase.linear,
onComplete: function(twn:FlxTween)
{
camHUD.visible = true;
remove(whiteScreen);
startCountdown();
}
});
FlxG.sound.play(Paths.soundRandom('thunder_', 1, 2));
gf.playAnim('scared', true);
boyfriend.playAnim('scared', true);
case "winter-horrorland":
var blackScreen:FlxSprite = new FlxSprite().makeGraphic(Std.int(FlxG.width * 2), Std.int(FlxG.height * 2), FlxColor.BLACK);
add(blackScreen);
blackScreen.scrollFactor.set();
camHUD.visible = false;
inCutscene = true;
FlxTween.tween(blackScreen, {alpha: 0}, 0.7, {
ease: FlxEase.linear,
onComplete: function(twn:FlxTween) {
remove(blackScreen);
}
});
FlxG.sound.play(Paths.sound('Lights_Turn_On'));
snapCamFollowToPos(400, -2050);
FlxG.camera.focusOn(camFollow);
FlxG.camera.zoom = 1.5;
new FlxTimer().start(0.8, function(tmr:FlxTimer)
{
camHUD.visible = true;
remove(blackScreen);
FlxTween.tween(FlxG.camera, {zoom: defaultCamZoom}, 2.5, {
ease: FlxEase.quadInOut,
onComplete: function(twn:FlxTween)
{
startCountdown();
}
});
});
case 'senpai' | 'roses' | 'thorns':
if(daSong == 'roses') FlxG.sound.play(Paths.sound('ANGRY'));
schoolIntro(doof);
default:
startCountdown();
}
seenCutscene = true;
} else {
startCountdown();
}
RecalculateRating();
//PRECACHING MISS SOUNDS BECAUSE I THINK THEY CAN LAG PEOPLE AND FUCK THEM UP IDK HOW HAXE WORKS
CoolUtil.precacheSound('missnote1');
CoolUtil.precacheSound('missnote2');
CoolUtil.precacheSound('missnote3');
#if desktop
// Updating Discord Rich Presence.
DiscordClient.changePresence(detailsText, SONG.song + " (" + storyDifficultyText + ")", iconP2.getCharacter());
#end
super.create();
}
public function addTextToDebug(text:String) {
#if LUA_ALLOWED
luaDebugGroup.forEachAlive(function(spr:DebugLuaText) {
spr.y += 20;
});
luaDebugGroup.add(new DebugLuaText(text, luaDebugGroup));
#end
}
public function reloadHealthBarColors() {
healthBar.createFilledBar(FlxColor.fromRGB(dad.healthColorArray[0], dad.healthColorArray[1], dad.healthColorArray[2]),
FlxColor.fromRGB(boyfriend.healthColorArray[0], boyfriend.healthColorArray[1], boyfriend.healthColorArray[2]));
healthBar.updateBar();
}
public function addCharacterToList(newCharacter:String, type:Int) {
switch(type) {
case 0:
if(!boyfriendMap.exists(newCharacter)) {
var newBoyfriend:Boyfriend = new Boyfriend(0, 0, newCharacter);
boyfriendMap.set(newCharacter, newBoyfriend);
boyfriendGroup.add(newBoyfriend);
startCharacterPos(newBoyfriend);
newBoyfriend.alpha = 0.00001;
newBoyfriend.alreadyLoaded = false;
}
case 1:
if(!dadMap.exists(newCharacter)) {
var newDad:Character = new Character(0, 0, newCharacter);
dadMap.set(newCharacter, newDad);
dadGroup.add(newDad);
startCharacterPos(newDad, true);
newDad.alpha = 0.00001;
newDad.alreadyLoaded = false;
}
case 2:
if(!gfMap.exists(newCharacter)) {
var newGf:Character = new Character(0, 0, newCharacter);
newGf.scrollFactor.set(0.95, 0.95);
gfMap.set(newCharacter, newGf);
gfGroup.add(newGf);
startCharacterPos(newGf);
newGf.alpha = 0.00001;
newGf.alreadyLoaded = false;
}
}
}
function startCharacterPos(char:Character, ?gfCheck:Bool = false) {
if(gfCheck && char.curCharacter.startsWith('gf')) { //IF DAD IS GIRLFRIEND, HE GOES TO HER POSITION
char.setPosition(GF_X, GF_Y);
char.scrollFactor.set(0.95, 0.95);
}
char.x += char.positionArray[0];
char.y += char.positionArray[1];
}
public function startVideo(name:String):Void {
#if VIDEOS_ALLOWED
var foundFile:Bool = false;
var fileName:String = #if MODS_ALLOWED Paths.modFolders('videos/' + name + '.' + Paths.VIDEO_EXT); #else ''; #end
#if sys
if(FileSystem.exists(fileName)) {
foundFile = true;
}
#end
if(!foundFile) {
fileName = Paths.video(name);
#if sys
if(FileSystem.exists(fileName)) {
#else
if(OpenFlAssets.exists(fileName)) {
#end
foundFile = true;
}
}
if(foundFile) {
inCutscene = true;
var bg = new FlxSprite(-FlxG.width, -FlxG.height).makeGraphic(FlxG.width * 3, FlxG.height * 3, FlxColor.BLACK);
bg.scrollFactor.set();
bg.cameras = [camHUD];
add(bg);
(new FlxVideo(fileName)).finishCallback = function() {
remove(bg);
if(endingSong) {
endSong();
} else {
startCountdown();
}
}
return;
} else {
FlxG.log.warn('Couldnt find video file: ' + fileName);
}
#end
if(endingSong) {
endSong();
} else {
startCountdown();
}
}
var dialogueCount:Int = 0;
//You don't have to add a song, just saying. You can just do "startDialogue(dialogueJson);" and it should work
public function startDialogue(dialogueFile:DialogueFile, ?song:String = null):Void
{
// TO DO: Make this more flexible, maybe?
if(dialogueFile.dialogue.length > 0) {
inCutscene = true;
CoolUtil.precacheSound('dialogue');
CoolUtil.precacheSound('dialogueClose');
var doof:DialogueBoxPsych = new DialogueBoxPsych(dialogueFile, song);
doof.scrollFactor.set();
if(endingSong) {
doof.finishThing = endSong;
} else {
doof.finishThing = startCountdown;
}
doof.nextDialogueThing = startNextDialogue;
doof.skipDialogueThing = skipDialogue;
doof.cameras = [camHUD];
add(doof);
} else {
FlxG.log.warn('Your dialogue file is badly formatted!');
if(endingSong) {
endSong();
} else {
startCountdown();
}
}
}
function schoolIntro(?dialogueBox:DialogueBox):Void
{
inCutscene = true;
var black:FlxSprite = new FlxSprite(-100, -100).makeGraphic(FlxG.width * 2, FlxG.height * 2, FlxColor.BLACK);
black.scrollFactor.set();
add(black);
var red:FlxSprite = new FlxSprite(-100, -100).makeGraphic(FlxG.width * 2, FlxG.height * 2, 0xFFff1b31);
red.scrollFactor.set();
var senpaiEvil:FlxSprite = new FlxSprite();
senpaiEvil.frames = Paths.getSparrowAtlas('weeb/senpaiCrazy');
senpaiEvil.animation.addByPrefix('idle', 'Senpai Pre Explosion', 24, false);
senpaiEvil.setGraphicSize(Std.int(senpaiEvil.width * 6));
senpaiEvil.scrollFactor.set();
senpaiEvil.updateHitbox();
senpaiEvil.screenCenter();
senpaiEvil.x += 300;
var songName:String = Paths.formatToSongPath(SONG.song);
if (songName == 'roses' || songName == 'thorns')
{
remove(black);
if (songName == 'thorns')
{
add(red);
camHUD.visible = false;
}
}
new FlxTimer().start(0.3, function(tmr:FlxTimer)
{
black.alpha -= 0.15;
if (black.alpha > 0)
{
tmr.reset(0.3);
}
else
{
if (dialogueBox != null)
{
if (Paths.formatToSongPath(SONG.song) == 'thorns')
{
add(senpaiEvil);
senpaiEvil.alpha = 0;
new FlxTimer().start(0.3, function(swagTimer:FlxTimer)
{
senpaiEvil.alpha += 0.15;
if (senpaiEvil.alpha < 1)
{
swagTimer.reset();
}
else
{
senpaiEvil.animation.play('idle');
FlxG.sound.play(Paths.sound('Senpai_Dies'), 1, false, null, true, function()
{
remove(senpaiEvil);
remove(red);
FlxG.camera.fade(FlxColor.WHITE, 0.01, true, function()
{
add(dialogueBox);
camHUD.visible = true;
}, true);
});
new FlxTimer().start(3.2, function(deadTime:FlxTimer)
{
FlxG.camera.fade(FlxColor.WHITE, 1.6, false);
});
}
});
}
else
{
add(dialogueBox);
}
}
else
startCountdown();
remove(black);
}
});
}
var startTimer:FlxTimer;
var finishTimer:FlxTimer = null;
// For being able to mess with the sprites on Lua
public var countDownSprites:Array<FlxSprite> = [];
public function startCountdown():Void
{
if(startedCountdown) {
callOnLuas('onStartCountdown', []);
return;
}
inCutscene = false;
var ret:Dynamic = callOnLuas('onStartCountdown', []);
if(ret != FunkinLua.Function_Stop) {
generateStaticArrows(0);
generateStaticArrows(1);
for (i in 0...playerStrums.length) {
setOnLuas('defaultPlayerStrumX' + i, playerStrums.members[i].x);
setOnLuas('defaultPlayerStrumY' + i, playerStrums.members[i].y);
}
for (i in 0...opponentStrums.length) {
setOnLuas('defaultOpponentStrumX' + i, opponentStrums.members[i].x);
setOnLuas('defaultOpponentStrumY' + i, opponentStrums.members[i].y);
if(ClientPrefs.middleScroll) opponentStrums.members[i].visible = false;
}
startedCountdown = true;
Conductor.songPosition = 0;
Conductor.songPosition -= Conductor.crochet * 5;
setOnLuas('startedCountdown', true);
var swagCounter:Int = 0;
startTimer = new FlxTimer().start(Conductor.crochet / 1000, function(tmr:FlxTimer)
{
if (tmr.loopsLeft % gfSpeed == 0 && !gf.stunned && gf.animation.curAnim.name != null && !gf.animation.curAnim.name.startsWith("sing"))
{
gf.dance();
}
if(tmr.loopsLeft % 2 == 0) {
if (boyfriend.animation.curAnim != null && !boyfriend.animation.curAnim.name.startsWith('sing'))
{
boyfriend.dance();
}
if (dad.animation.curAnim != null && !dad.animation.curAnim.name.startsWith('sing') && !dad.stunned)
{
dad.dance();
}
}
else if(dad.danceIdle && dad.animation.curAnim != null && !dad.stunned && !dad.curCharacter.startsWith('gf') && !dad.animation.curAnim.name.startsWith("sing"))
{
dad.dance();
}
var introAssets:Map<String, Array<String>> = new Map<String, Array<String>>();
introAssets.set('default', ['ready', 'set', 'go']);
introAssets.set('pixel', ['pixelUI/ready-pixel', 'pixelUI/set-pixel', 'pixelUI/date-pixel']);
var introAlts:Array<String> = introAssets.get('default');
var antialias:Bool = ClientPrefs.globalAntialiasing;
if(isPixelStage) {
introAlts = introAssets.get('pixel');
antialias = false;
}
// head bopping for bg characters on Mall
if(curStage == 'mall') {
if(!ClientPrefs.lowQuality)
upperBoppers.dance(true);
bottomBoppers.dance(true);
santa.dance(true);
}
switch (swagCounter)
{
case 0:
FlxG.sound.play(Paths.sound('intro3' + introSoundsSuffix), 0.6);
case 1:
var ready:FlxSprite = new FlxSprite().loadGraphic(Paths.image(introAlts[0]));
ready.scrollFactor.set();
ready.updateHitbox();
if (PlayState.isPixelStage)
ready.setGraphicSize(Std.int(ready.width * daPixelZoom));
ready.screenCenter();
ready.antialiasing = antialias;
add(ready);
countDownSprites.push(ready);
FlxTween.tween(ready, {y: ready.y += 100, alpha: 0}, Conductor.crochet / 1000, {
ease: FlxEase.cubeInOut,
onComplete: function(twn:FlxTween)
{
countDownSprites.remove(ready);
remove(ready);
ready.destroy();
}
});
FlxG.sound.play(Paths.sound('intro2' + introSoundsSuffix), 0.6);
case 2:
var set:FlxSprite = new FlxSprite().loadGraphic(Paths.image(introAlts[1]));
set.scrollFactor.set();
if (PlayState.isPixelStage)
set.setGraphicSize(Std.int(set.width * daPixelZoom));
set.screenCenter();
set.antialiasing = antialias;
add(set);
countDownSprites.push(set);
FlxTween.tween(set, {y: set.y += 100, alpha: 0}, Conductor.crochet / 1000, {
ease: FlxEase.cubeInOut,
onComplete: function(twn:FlxTween)
{
countDownSprites.remove(set);
remove(set);
set.destroy();
}
});
FlxG.sound.play(Paths.sound('intro1' + introSoundsSuffix), 0.6);
case 3:
var go:FlxSprite = new FlxSprite().loadGraphic(Paths.image(introAlts[2]));
go.scrollFactor.set();
if (PlayState.isPixelStage)
go.setGraphicSize(Std.int(go.width * daPixelZoom));
go.updateHitbox();
go.screenCenter();
go.antialiasing = antialias;
add(go);
countDownSprites.push(go);
FlxTween.tween(go, {y: go.y += 100, alpha: 0}, Conductor.crochet / 1000, {
ease: FlxEase.cubeInOut,
onComplete: function(twn:FlxTween)
{
countDownSprites.remove(go);
remove(go);
go.destroy();
}
});
FlxG.sound.play(Paths.sound('introGo' + introSoundsSuffix), 0.6);
case 4:
}
notes.forEachAlive(function(note:Note) {
note.copyAlpha = false;
note.alpha = 1 * note.multAlpha;
});
callOnLuas('onCountdownTick', [swagCounter]);
if (generatedMusic)
{
notes.sort(FlxSort.byY, ClientPrefs.downScroll ? FlxSort.ASCENDING : FlxSort.DESCENDING);
}
swagCounter += 1;
// generateSong('fresh');
}, 5);
}
}
function startNextDialogue() {
dialogueCount++;
callOnLuas('onNextDialogue', [dialogueCount]);
}
function skipDialogue() {
callOnLuas('onSkipDialogue', [dialogueCount]);
}
var previousFrameTime:Int = 0;
var lastReportedPlayheadPosition:Int = 0;
var songTime:Float = 0;
function startSong():Void
{
startingSong = false;
previousFrameTime = FlxG.game.ticks;
lastReportedPlayheadPosition = 0;
FlxG.sound.playMusic(Paths.inst(PlayState.SONG.song), 1, false);
FlxG.sound.music.onComplete = finishSong;
vocals.play();
if(paused) {
//trace('Oopsie doopsie! Paused sound');
FlxG.sound.music.pause();
vocals.pause();
}
// Song duration in a float, useful for the time left feature
songLength = FlxG.sound.music.length;
FlxTween.tween(timeBar, {alpha: 1}, 0.5, {ease: FlxEase.circOut});
FlxTween.tween(timeTxt, {alpha: 1}, 0.5, {ease: FlxEase.circOut});
#if desktop
// Updating Discord Rich Presence (with Time Left)
DiscordClient.changePresence(detailsText, SONG.song + " (" + storyDifficultyText + ")", iconP2.getCharacter(), true, songLength);
#end
setOnLuas('songLength', songLength);
callOnLuas('onSongStart', []);
}
var debugNum:Int = 0;
private var noteTypeMap:Map<String, Bool> = new Map<String, Bool>();
private var eventPushedMap:Map<String, Bool> = new Map<String, Bool>();
private function generateSong(dataPath:String):Void
{
// FlxG.log.add(ChartParser.parse());
var songData = SONG;
Conductor.changeBPM(songData.bpm);
curSong = songData.song;
if (SONG.needsVoices)
vocals = new FlxSound().loadEmbedded(Paths.voices(PlayState.SONG.song));
else
vocals = new FlxSound();
FlxG.sound.list.add(vocals);
FlxG.sound.list.add(new FlxSound().loadEmbedded(Paths.inst(PlayState.SONG.song)));
notes = new FlxTypedGroup<Note>();
add(notes);
var noteData:Array<SwagSection>;
// NEW SHIT
noteData = songData.notes;
var playerCounter:Int = 0;
var daBeats:Int = 0; // Not exactly representative of 'daBeats' lol, just how much it has looped
var songName:String = Paths.formatToSongPath(SONG.song);
var file:String = Paths.json(songName + '/events');
#if sys
if (FileSystem.exists(Paths.modsJson(songName + '/events')) || FileSystem.exists(file)) {
#else
if (OpenFlAssets.exists(file)) {
#end
var eventsData:Array<SwagSection> = Song.loadFromJson('events', songName).notes;
for (section in eventsData)
{
for (songNotes in section.sectionNotes)
{
if(songNotes[1] < 0) {
eventNotes.push([songNotes[0], songNotes[1], songNotes[2], songNotes[3], songNotes[4]]);
eventPushed(songNotes);
}
}
}
}
for (section in noteData)
{
for (songNotes in section.sectionNotes)
{
if(songNotes[1] > -1) { //Real notes
var daStrumTime:Float = songNotes[0];
var daNoteData:Int = Std.int(songNotes[1] % 4);
var gottaHitNote:Bool = section.mustHitSection;
if (songNotes[1] > 3)
{
gottaHitNote = !section.mustHitSection;
}
var oldNote:Note;
if (unspawnNotes.length > 0)
oldNote = unspawnNotes[Std.int(unspawnNotes.length - 1)];
else
oldNote = null;
var swagNote:Note = new Note(daStrumTime, daNoteData, oldNote);
swagNote.mustPress = gottaHitNote;
swagNote.sustainLength = songNotes[2];
swagNote.noteType = songNotes[3];
if(!Std.isOfType(songNotes[3], String)) swagNote.noteType = editors.ChartingState.noteTypeList[songNotes[3]]; //Backward compatibility + compatibility with Week 7 charts
swagNote.scrollFactor.set();
var susLength:Float = swagNote.sustainLength;
susLength = susLength / Conductor.stepCrochet;
unspawnNotes.push(swagNote);
var floorSus:Int = Math.floor(susLength);
if(floorSus > 0) {
for (susNote in 0...floorSus+1)
{
oldNote = unspawnNotes[Std.int(unspawnNotes.length - 1)];
var sustainNote:Note = new Note(daStrumTime + (Conductor.stepCrochet * susNote) + (Conductor.stepCrochet / FlxMath.roundDecimal(SONG.speed, 2)), daNoteData, oldNote, true);
sustainNote.mustPress = gottaHitNote;
sustainNote.noteType = swagNote.noteType;
sustainNote.scrollFactor.set();
unspawnNotes.push(sustainNote);
if (sustainNote.mustPress)
{
sustainNote.x += FlxG.width / 2; // general offset
}
}
}
if (swagNote.mustPress)
{
swagNote.x += FlxG.width / 2; // general offset
}
else {}
if(!noteTypeMap.exists(swagNote.noteType)) {
noteTypeMap.set(swagNote.noteType, true);
}
} else { //Event Notes
eventNotes.push([songNotes[0], songNotes[1], songNotes[2], songNotes[3], songNotes[4]]);
eventPushed(songNotes);
}
}
daBeats += 1;
}
// trace(unspawnNotes.length);
// playerCounter += 1;
unspawnNotes.sort(sortByShit);
if(eventNotes.length > 1) { //No need to sort if there's a single one or none at all
eventNotes.sort(sortByTime);
}
checkEventNote();
generatedMusic = true;
}
function eventPushed(event:Array<Dynamic>) {
switch(event[2]) {
case 'Change Character':
var charType:Int = 0;
switch(event[3].toLowerCase()) {
case 'gf' | 'girlfriend':
charType = 2;
case 'dad' | 'opponent':
charType = 1;
default:
charType = Std.parseInt(event[3]);
if(Math.isNaN(charType)) charType = 0;
}
var newCharacter:String = event[4];
addCharacterToList(newCharacter, charType);
}
if(!eventPushedMap.exists(event[2])) {
eventPushedMap.set(event[2], true);
}
}
function eventNoteEarlyTrigger(event:Array<Dynamic>):Float {
var returnedValue:Float = callOnLuas('eventEarlyTrigger', [event[2]]);
if(returnedValue != 0) {
return returnedValue;
}
switch(event[2]) {
case 'Kill Henchmen': //Better timing so that the kill sound matches the beat intended
return 280; //Plays 280ms before the actual position
}
return 0;
}
function sortByShit(Obj1:Note, Obj2:Note):Int
{
return FlxSort.byValues(FlxSort.ASCENDING, Obj1.strumTime, Obj2.strumTime);
}
function sortByTime(Obj1:Array<Dynamic>, Obj2:Array<Dynamic>):Int
{
var earlyTime1:Float = eventNoteEarlyTrigger(Obj1);
var earlyTime2:Float = eventNoteEarlyTrigger(Obj2);
return FlxSort.byValues(FlxSort.ASCENDING, Obj1[0] - earlyTime1, Obj2[0] - earlyTime2);
}
private function generateStaticArrows(player:Int):Void
{
for (i in 0...4)
{
// FlxG.log.add(i);
var babyArrow:StrumNote = new StrumNote(ClientPrefs.middleScroll ? STRUM_X_MIDDLESCROLL : STRUM_X, strumLine.y, i, player);
if (!isStoryMode)
{
babyArrow.y -= 10;
babyArrow.alpha = 0;
FlxTween.tween(babyArrow, {y: babyArrow.y + 10, alpha: 1}, 1, {ease: FlxEase.circOut, startDelay: 0.5 + (0.2 * i)});
}
if (player == 1)
{
playerStrums.add(babyArrow);
}
else
{
opponentStrums.add(babyArrow);
}
strumLineNotes.add(babyArrow);
babyArrow.postAddedToGroup();
}
}
override function openSubState(SubState:FlxSubState)
{
if (paused)
{
if (FlxG.sound.music != null)
{
FlxG.sound.music.pause();
vocals.pause();
}
if (!startTimer.finished)
startTimer.active = false;
if (finishTimer != null && !finishTimer.finished)
finishTimer.active = false;
if(blammedLightsBlackTween != null)
blammedLightsBlackTween.active = false;
if(phillyCityLightsEventTween != null)
phillyCityLightsEventTween.active = false;
if(carTimer != null) carTimer.active = false;
var chars:Array<Character> = [boyfriend, gf, dad];
for (i in 0...chars.length) {
if(chars[i].colorTween != null) {
chars[i].colorTween.active = false;
}
}
for (tween in modchartTweens) {
tween.active = false;
}
for (timer in modchartTimers) {
timer.active = false;
}
}
super.openSubState(SubState);
}
override function closeSubState()
{
if (paused)
{
if (FlxG.sound.music != null && !startingSong)
{
resyncVocals();
}
if (!startTimer.finished)
startTimer.active = true;
if (finishTimer != null && !finishTimer.finished)
finishTimer.active = true;
if(blammedLightsBlackTween != null)
blammedLightsBlackTween.active = true;
if(phillyCityLightsEventTween != null)
phillyCityLightsEventTween.active = true;
if(carTimer != null) carTimer.active = true;
var chars:Array<Character> = [boyfriend, gf, dad];
for (i in 0...chars.length) {
if(chars[i].colorTween != null) {
chars[i].colorTween.active = true;
}
}
for (tween in modchartTweens) {
tween.active = true;
}
for (timer in modchartTimers) {
timer.active = true;
}
paused = false;
callOnLuas('onResume', []);
#if desktop
if (startTimer.finished)
{
DiscordClient.changePresence(detailsText, SONG.song + " (" + storyDifficultyText + ")", iconP2.getCharacter(), true, songLength - Conductor.songPosition - ClientPrefs.noteOffset);
}
else
{
DiscordClient.changePresence(detailsText, SONG.song + " (" + storyDifficultyText + ")", iconP2.getCharacter());
}
#end
}
super.closeSubState();
}
override public function onFocus():Void
{
#if desktop
if (health > 0 && !paused)
{
if (Conductor.songPosition > 0.0)
{
DiscordClient.changePresence(detailsText, SONG.song + " (" + storyDifficultyText + ")", iconP2.getCharacter(), true, songLength - Conductor.songPosition - ClientPrefs.noteOffset);
}
else
{
DiscordClient.changePresence(detailsText, SONG.song + " (" + storyDifficultyText + ")", iconP2.getCharacter());
}
}
#end
super.onFocus();
}
override public function onFocusLost():Void
{
#if desktop
if (health > 0 && !paused)
{
DiscordClient.changePresence(detailsPausedText, SONG.song + " (" + storyDifficultyText + ")", iconP2.getCharacter());
}
#end
super.onFocusLost();
}
function resyncVocals():Void
{
if(finishTimer != null) return;
vocals.pause();
FlxG.sound.music.play();
Conductor.songPosition = FlxG.sound.music.time;
vocals.time = Conductor.songPosition;
vocals.play();
}
private var paused:Bool = false;
var startedCountdown:Bool = false;
var canPause:Bool = true;
var limoSpeed:Float = 0;
override public function update(elapsed:Float)
{
/*if (FlxG.keys.justPressed.NINE)
{
iconP1.swapOldIcon();
}*/
callOnLuas('onUpdate', [elapsed]);
switch (curStage)
{
case 'schoolEvil':
if(!ClientPrefs.lowQuality && bgGhouls.animation.curAnim.finished) {
bgGhouls.visible = false;
}
case 'philly':
if (trainMoving)
{
trainFrameTiming += elapsed;
if (trainFrameTiming >= 1 / 24)
{
updateTrainPos();
trainFrameTiming = 0;
}
}
phillyCityLights.members[curLight].alpha -= (Conductor.crochet / 1000) * FlxG.elapsed * 1.5;
case 'limo':
if(!ClientPrefs.lowQuality) {
grpLimoParticles.forEach(function(spr:BGSprite) {
if(spr.animation.curAnim.finished) {
spr.kill();
grpLimoParticles.remove(spr, true);
spr.destroy();
}
});
switch(limoKillingState) {
case 1:
limoMetalPole.x += 5000 * elapsed;
limoLight.x = limoMetalPole.x - 180;
limoCorpse.x = limoLight.x - 50;
limoCorpseTwo.x = limoLight.x + 35;
var dancers:Array<BackgroundDancer> = grpLimoDancers.members;
for (i in 0...dancers.length) {
if(dancers[i].x < FlxG.width * 1.5 && limoLight.x > (370 * i) + 130) {
switch(i) {
case 0 | 3:
if(i == 0) FlxG.sound.play(Paths.sound('dancerdeath'), 0.5);
var diffStr:String = i == 3 ? ' 2 ' : ' ';
var particle:BGSprite = new BGSprite('gore/noooooo', dancers[i].x + 200, dancers[i].y, 0.4, 0.4, ['hench leg spin' + diffStr + 'PINK'], false);
grpLimoParticles.add(particle);
var particle:BGSprite = new BGSprite('gore/noooooo', dancers[i].x + 160, dancers[i].y + 200, 0.4, 0.4, ['hench arm spin' + diffStr + 'PINK'], false);
grpLimoParticles.add(particle);
var particle:BGSprite = new BGSprite('gore/noooooo', dancers[i].x, dancers[i].y + 50, 0.4, 0.4, ['hench head spin' + diffStr + 'PINK'], false);
grpLimoParticles.add(particle);
var particle:BGSprite = new BGSprite('gore/stupidBlood', dancers[i].x - 110, dancers[i].y + 20, 0.4, 0.4, ['blood'], false);
particle.flipX = true;
particle.angle = -57.5;
grpLimoParticles.add(particle);
case 1:
limoCorpse.visible = true;
case 2:
limoCorpseTwo.visible = true;
} //Note: Nobody cares about the fifth dancer because he is mostly hidden offscreen :(
dancers[i].x += FlxG.width * 2;
}
}
if(limoMetalPole.x > FlxG.width * 2) {
resetLimoKill();
limoSpeed = 800;
limoKillingState = 2;
}
case 2:
limoSpeed -= 4000 * elapsed;
bgLimo.x -= limoSpeed * elapsed;
if(bgLimo.x > FlxG.width * 1.5) {
limoSpeed = 3000;
limoKillingState = 3;
}
case 3:
limoSpeed -= 2000 * elapsed;
if(limoSpeed < 1000) limoSpeed = 1000;
bgLimo.x -= limoSpeed * elapsed;
if(bgLimo.x < -275) {
limoKillingState = 4;
limoSpeed = 800;
}
case 4:
bgLimo.x = FlxMath.lerp(bgLimo.x, -150, CoolUtil.boundTo(elapsed * 9, 0, 1));
if(Math.round(bgLimo.x) == -150) {
bgLimo.x = -150;
limoKillingState = 0;
}
}
if(limoKillingState > 2) {
var dancers:Array<BackgroundDancer> = grpLimoDancers.members;
for (i in 0...dancers.length) {
dancers[i].x = (370 * i) + bgLimo.x + 280;
}
}
}
case 'mall':
if(heyTimer > 0) {
heyTimer -= elapsed;
if(heyTimer <= 0) {
bottomBoppers.dance(true);
heyTimer = 0;
}
}
}
if(!inCutscene) {
var lerpVal:Float = CoolUtil.boundTo(elapsed * 2.4 * cameraSpeed, 0, 1);
camFollowPos.setPosition(FlxMath.lerp(camFollowPos.x, camFollow.x, lerpVal), FlxMath.lerp(camFollowPos.y, camFollow.y, lerpVal));
if(!startingSong && !endingSong && boyfriend.animation.curAnim.name.startsWith('idle')) {
boyfriendIdleTime += elapsed;
if(boyfriendIdleTime >= 0.15) { // Kind of a mercy thing for making the achievement easier to get as it's apparently frustrating to some playerss
boyfriendIdled = true;
}
} else {
boyfriendIdleTime = 0;
}
}
super.update(elapsed);
if(ratingString == '?') {
scoreTxt.text = 'Score: ' + songScore + ' | Misses: ' + songMisses + ' | Rating: ' + ratingString;
} else {
scoreTxt.text = 'Score: ' + songScore + ' | Misses: ' + songMisses + ' | Rating: ' + ratingString + ' (' + Math.floor(ratingPercent * 100) + '%)';
}
if(cpuControlled) {
botplaySine += 180 * elapsed;
botplayTxt.alpha = 1 - Math.sin((Math.PI * botplaySine) / 180);
}
botplayTxt.visible = cpuControlled;
if (FlxG.keys.justPressed.ENTER && startedCountdown && canPause)
{
var ret:Dynamic = callOnLuas('onPause', []);
if(ret != FunkinLua.Function_Stop) {
persistentUpdate = false;
persistentDraw = true;
paused = true;
// 1 / 1000 chance for Gitaroo Man easter egg
if (FlxG.random.bool(0.1))
{
// gitaroo man easter egg
cancelFadeTween();
CustomFadeTransition.nextCamera = camOther;
MusicBeatState.switchState(new GitarooPause());
}
else {
if(FlxG.sound.music != null) {
FlxG.sound.music.pause();
vocals.pause();
}
PauseSubState.transCamera = camOther;
openSubState(new PauseSubState(boyfriend.getScreenPosition().x, boyfriend.getScreenPosition().y));
}
#if desktop
DiscordClient.changePresence(detailsPausedText, SONG.song + " (" + storyDifficultyText + ")", iconP2.getCharacter());
#end
}
}
if (FlxG.keys.justPressed.SEVEN && !endingSong && !inCutscene)
{
persistentUpdate = false;
paused = true;
cancelFadeTween();
CustomFadeTransition.nextCamera = camOther;
MusicBeatState.switchState(new ChartingState());
#if desktop
DiscordClient.changePresence("Chart Editor", null, null, true);
#end
}
// FlxG.watch.addQuick('VOL', vocals.amplitudeLeft);
// FlxG.watch.addQuick('VOLRight', vocals.amplitudeRight);
iconP1.setGraphicSize(Std.int(FlxMath.lerp(150, iconP1.width, CoolUtil.boundTo(1 - (elapsed * 30), 0, 1))));
iconP2.setGraphicSize(Std.int(FlxMath.lerp(150, iconP2.width, CoolUtil.boundTo(1 - (elapsed * 30), 0, 1))));
iconP1.updateHitbox();
iconP2.updateHitbox();
var iconOffset:Int = 26;
iconP1.x = healthBar.x + (healthBar.width * (FlxMath.remapToRange(healthBar.percent, 0, 100, 100, 0) * 0.01) - iconOffset);
iconP2.x = healthBar.x + (healthBar.width * (FlxMath.remapToRange(healthBar.percent, 0, 100, 100, 0) * 0.01)) - (iconP2.width - iconOffset);
if (health > 2)
health = 2;
if (healthBar.percent < 20)
iconP1.animation.curAnim.curFrame = 1;
else
iconP1.animation.curAnim.curFrame = 0;
if (healthBar.percent > 80)
iconP2.animation.curAnim.curFrame = 1;
else
iconP2.animation.curAnim.curFrame = 0;
if (FlxG.keys.justPressed.EIGHT && !endingSong && !inCutscene) {
persistentUpdate = false;
paused = true;
cancelFadeTween();
CustomFadeTransition.nextCamera = camOther;
MusicBeatState.switchState(new CharacterEditorState(SONG.player2));
}
if (startingSong)
{
if (startedCountdown)
{
Conductor.songPosition += FlxG.elapsed * 1000;
if (Conductor.songPosition >= 0)
startSong();
}
}
else
{
Conductor.songPosition += FlxG.elapsed * 1000;
if (!paused)
{
songTime += FlxG.game.ticks - previousFrameTime;
previousFrameTime = FlxG.game.ticks;
// Interpolation type beat
if (Conductor.lastSongPos != Conductor.songPosition)
{
songTime = (songTime + Conductor.songPosition) / 2;
Conductor.lastSongPos = Conductor.songPosition;
// Conductor.songPosition += FlxG.elapsed * 1000;
// trace('MISSED FRAME');
}
if(updateTime) {
var curTime:Float = Conductor.songPosition - ClientPrefs.noteOffset;
if(curTime < 0) curTime = 0;
songPercent = (curTime / songLength);
var secondsTotal:Int = Math.floor((songLength - curTime) / 1000);
if(secondsTotal < 0) secondsTotal = 0;
timeTxt.text = FlxStringUtil.formatTime(secondsTotal, false);
}
}
// Conductor.lastSongPos = FlxG.sound.music.time;
}
if (camZooming)
{
FlxG.camera.zoom = FlxMath.lerp(defaultCamZoom, FlxG.camera.zoom, CoolUtil.boundTo(1 - (elapsed * 3.125), 0, 1));
camHUD.zoom = FlxMath.lerp(1, camHUD.zoom, CoolUtil.boundTo(1 - (elapsed * 3.125), 0, 1));
}
FlxG.watch.addQuick("beatShit", curBeat);
FlxG.watch.addQuick("stepShit", curStep);
// RESET = Quick Game Over Screen
if (controls.RESET && !inCutscene && !endingSong)
{
health = 0;
trace("RESET = True");
}
doDeathCheck();
var roundedSpeed:Float = FlxMath.roundDecimal(SONG.speed, 2);
if (unspawnNotes[0] != null)
{
var time:Float = 1500;
if(roundedSpeed < 1) time /= roundedSpeed;
while (unspawnNotes.length > 0 && unspawnNotes[0].strumTime - Conductor.songPosition < time)
{
var dunceNote:Note = unspawnNotes[0];
notes.add(dunceNote);
var index:Int = unspawnNotes.indexOf(dunceNote);
unspawnNotes.splice(index, 1);
}
}
if (generatedMusic)
{
var fakeCrochet:Float = (60 / SONG.bpm) * 1000;
notes.forEachAlive(function(daNote:Note)
{
if(!daNote.mustPress && ClientPrefs.middleScroll)
{
daNote.active = true;
daNote.visible = false;
}
else if (daNote.y > FlxG.height)
{
daNote.active = false;
daNote.visible = false;
}
else
{
daNote.visible = true;
daNote.active = true;
}
// i am so fucking sorry for this if condition
var strumX:Float = 0;
var strumY:Float = 0;
var strumAngle:Float = 0;
var strumAlpha:Float = 0;
if(daNote.mustPress) {
strumX = playerStrums.members[daNote.noteData].x;
strumY = playerStrums.members[daNote.noteData].y;
strumAngle = playerStrums.members[daNote.noteData].angle;
strumAlpha = playerStrums.members[daNote.noteData].alpha;
} else {
strumX = opponentStrums.members[daNote.noteData].x;
strumY = opponentStrums.members[daNote.noteData].y;
strumAngle = opponentStrums.members[daNote.noteData].angle;
strumAlpha = opponentStrums.members[daNote.noteData].alpha;
}
strumX += daNote.offsetX;
strumY += daNote.offsetY;
strumAngle += daNote.offsetAngle;
strumAlpha *= daNote.multAlpha;
var center:Float = strumY + Note.swagWidth / 2;
if(daNote.copyX) {
daNote.x = strumX;
}
if(daNote.copyAngle) {
daNote.angle = strumAngle;
}
if(daNote.copyAlpha) {
daNote.alpha = strumAlpha;
}
if(daNote.copyY) {
if (ClientPrefs.downScroll) {
daNote.y = (strumY + 0.45 * (Conductor.songPosition - daNote.strumTime) * roundedSpeed);
if (daNote.isSustainNote) {
//Jesus fuck this took me so much mother fucking time AAAAAAAAAA
if (daNote.animation.curAnim.name.endsWith('end')) {
daNote.y += 10.5 * (fakeCrochet / 400) * 1.5 * roundedSpeed + (46 * (roundedSpeed - 1));
daNote.y -= 46 * (1 - (fakeCrochet / 600)) * roundedSpeed;
if(PlayState.isPixelStage) {
daNote.y += 8;
} else {
daNote.y -= 19;
}
}
daNote.y += (Note.swagWidth / 2) - (60.5 * (roundedSpeed - 1));
daNote.y += 27.5 * ((SONG.bpm / 100) - 1) * (roundedSpeed - 1);
if(daNote.mustPress || !daNote.ignoreNote)
{
if(daNote.y - daNote.offset.y * daNote.scale.y + daNote.height >= center
&& (!daNote.mustPress || (daNote.wasGoodHit || (daNote.prevNote.wasGoodHit && !daNote.canBeHit))))
{
var swagRect = new FlxRect(0, 0, daNote.frameWidth, daNote.frameHeight);
swagRect.height = (center - daNote.y) / daNote.scale.y;
swagRect.y = daNote.frameHeight - swagRect.height;
daNote.clipRect = swagRect;
}
}
}
} else {
daNote.y = (strumY - 0.45 * (Conductor.songPosition - daNote.strumTime) * roundedSpeed);
if(daNote.mustPress || !daNote.ignoreNote)
{
if (daNote.isSustainNote
&& daNote.y + daNote.offset.y * daNote.scale.y <= center
&& (!daNote.mustPress || (daNote.wasGoodHit || (daNote.prevNote.wasGoodHit && !daNote.canBeHit))))
{
var swagRect = new FlxRect(0, 0, daNote.width / daNote.scale.x, daNote.height / daNote.scale.y);
swagRect.y = (center - daNote.y) / daNote.scale.y;
swagRect.height -= swagRect.y;
daNote.clipRect = swagRect;
}
}
}
}
if (!daNote.mustPress && daNote.wasGoodHit && !daNote.hitByOpponent && !daNote.ignoreNote)
{
if (Paths.formatToSongPath(SONG.song) != 'tutorial')
camZooming = true;
if(daNote.noteType == 'Hey!' && dad.animOffsets.exists('hey')) {
dad.playAnim('hey', true);
dad.specialAnim = true;
dad.heyTimer = 0.6;
} else if(!daNote.noAnimation) {
var altAnim:String = "";
if (SONG.notes[Math.floor(curStep / 16)] != null)
{
if (SONG.notes[Math.floor(curStep / 16)].altAnim || daNote.noteType == 'Alt Animation') {
altAnim = '-alt';
}
}
var animToPlay:String = '';
switch (Math.abs(daNote.noteData))
{
case 0:
animToPlay = 'singLEFT';
case 1:
animToPlay = 'singDOWN';
case 2:
animToPlay = 'singUP';
case 3:
animToPlay = 'singRIGHT';
}
if(daNote.noteType == 'GF Sing') {
gf.playAnim(animToPlay + altAnim, true);
gf.holdTimer = 0;
} else {
dad.playAnim(animToPlay + altAnim, true);
dad.holdTimer = 0;
}
}
if (SONG.needsVoices)
vocals.volume = 1;
var time:Float = 0.15;
if(daNote.isSustainNote && !daNote.animation.curAnim.name.endsWith('end')) {
time += 0.15;
}
StrumPlayAnim(true, Std.int(Math.abs(daNote.noteData)) % 4, time);
daNote.hitByOpponent = true;
callOnLuas('opponentNoteHit', [notes.members.indexOf(daNote), Math.abs(daNote.noteData), daNote.noteType, daNote.isSustainNote]);
if (!daNote.isSustainNote)
{
daNote.kill();
notes.remove(daNote, true);
daNote.destroy();
}
}
if(daNote.mustPress && cpuControlled) {
if(daNote.isSustainNote) {
if(daNote.canBeHit) {
goodNoteHit(daNote);
}
} else if(daNote.strumTime <= Conductor.songPosition || (daNote.isSustainNote && daNote.canBeHit && daNote.mustPress)) {
goodNoteHit(daNote);
}
}
// WIP interpolation shit? Need to fix the pause issue
// daNote.y = (strumLine.y - (songTime - daNote.strumTime) * (0.45 * PlayState.SONG.speed));
var doKill:Bool = daNote.y < -daNote.height;
if(ClientPrefs.downScroll) doKill = daNote.y > FlxG.height;
if (doKill)
{
if (daNote.mustPress && !cpuControlled &&!daNote.ignoreNote && !endingSong && (daNote.tooLate || !daNote.wasGoodHit)) {
noteMiss(daNote);
}
daNote.active = false;
daNote.visible = false;
daNote.kill();
notes.remove(daNote, true);
daNote.destroy();
}
});
}
checkEventNote();
if (!inCutscene) {
if(!cpuControlled) {
keyShit();
} else if(boyfriend.holdTimer > Conductor.stepCrochet * 0.001 * boyfriend.singDuration && boyfriend.animation.curAnim.name.startsWith('sing') && !boyfriend.animation.curAnim.name.endsWith('miss')) {
boyfriend.dance();
}
}
#if debug
if(!endingSong && !startingSong) {
if (FlxG.keys.justPressed.ONE) {
KillNotes();
FlxG.sound.music.onComplete();
}
if(FlxG.keys.justPressed.TWO) { //Go 10 seconds into the future :O
FlxG.sound.music.pause();
vocals.pause();
Conductor.songPosition += 10000;
notes.forEachAlive(function(daNote:Note)
{
if(daNote.strumTime + 800 < Conductor.songPosition) {
daNote.active = false;
daNote.visible = false;
daNote.kill();
notes.remove(daNote, true);
daNote.destroy();
}
});
for (i in 0...unspawnNotes.length) {
var daNote:Note = unspawnNotes[0];
if(daNote.strumTime + 800 >= Conductor.songPosition) {
break;
}
daNote.active = false;
daNote.visible = false;
daNote.kill();
unspawnNotes.splice(unspawnNotes.indexOf(daNote), 1);
daNote.destroy();
}
FlxG.sound.music.time = Conductor.songPosition;
FlxG.sound.music.play();
vocals.time = Conductor.songPosition;
vocals.play();
}
}
setOnLuas('cameraX', camFollowPos.x);
setOnLuas('cameraY', camFollowPos.y);
setOnLuas('botPlay', PlayState.cpuControlled);
callOnLuas('onUpdatePost', [elapsed]);
#end
}
var isDead:Bool = false;
function doDeathCheck() {
if (health <= 0 && !practiceMode && !isDead)
{
var ret:Dynamic = callOnLuas('onGameOver', []);
if(ret != FunkinLua.Function_Stop) {
boyfriend.stunned = true;
deathCounter++;
persistentUpdate = false;
persistentDraw = false;
paused = true;
vocals.stop();
FlxG.sound.music.stop();
openSubState(new GameOverSubstate(boyfriend.getScreenPosition().x, boyfriend.getScreenPosition().y, camFollowPos.x, camFollowPos.y, this));
for (tween in modchartTweens) {
tween.active = true;
}
for (timer in modchartTimers) {
timer.active = true;
}
// MusicBeatState.switchState(new GameOverState(boyfriend.getScreenPosition().x, boyfriend.getScreenPosition().y));
#if desktop
// Game Over doesn't get his own variable because it's only used here
DiscordClient.changePresence("Game Over - " + detailsText, SONG.song + " (" + storyDifficultyText + ")", iconP2.getCharacter());
#end
isDead = true;
return true;
}
}
return false;
}
public function checkEventNote() {
while(eventNotes.length > 0) {
var early:Float = eventNoteEarlyTrigger(eventNotes[0]);
var leStrumTime:Float = eventNotes[0][0];
if(Conductor.songPosition < leStrumTime - early) {
break;
}
var value1:String = '';
if(eventNotes[0][3] != null)
value1 = eventNotes[0][3];
var value2:String = '';
if(eventNotes[0][4] != null)
value2 = eventNotes[0][4];
triggerEventNote(eventNotes[0][2], value1, value2);
eventNotes.shift();
}
}
public function getControl(key:String) {
var pressed:Bool = Reflect.getProperty(controls, key);
//trace('Control result: ' + pressed);
return pressed;
}
public function triggerEventNote(eventName:String, value1:String, value2:String) {
switch(eventName) {
case 'Hey!':
var value:Int = 2;
switch(value1.toLowerCase().trim()) {
case 'bf' | 'boyfriend' | '0':
value = 0;
case 'gf' | 'girlfriend' | '1':
value = 1;
}
var time:Float = Std.parseFloat(value2);
if(Math.isNaN(time) || time <= 0) time = 0.6;
if(value != 0) {
if(dad.curCharacter.startsWith('gf')) { //Tutorial GF is actually Dad! The GF is an imposter!! ding ding ding ding ding ding ding, dindinding, end my suffering
dad.playAnim('cheer', true);
dad.specialAnim = true;
dad.heyTimer = time;
} else {
gf.playAnim('cheer', true);
gf.specialAnim = true;
gf.heyTimer = time;
}
if(curStage == 'mall') {
bottomBoppers.animation.play('hey', true);
heyTimer = time;
}
}
if(value != 1) {
boyfriend.playAnim('hey', true);
boyfriend.specialAnim = true;
boyfriend.heyTimer = time;
}
case 'Set GF Speed':
var value:Int = Std.parseInt(value1);
if(Math.isNaN(value)) value = 1;
gfSpeed = value;
case 'Blammed Lights':
var lightId:Int = Std.parseInt(value1);
if(Math.isNaN(lightId)) lightId = 0;
if(lightId > 0 && curLightEvent != lightId) {
if(lightId > 5) lightId = FlxG.random.int(1, 5, [curLightEvent]);
var color:Int = 0xffffffff;
switch(lightId) {
case 1: //Blue
color = 0xff31a2fd;
case 2: //Green
color = 0xff31fd8c;
case 3: //Pink
color = 0xfff794f7;
case 4: //Red
color = 0xfff96d63;
case 5: //Orange
color = 0xfffba633;
}
curLightEvent = lightId;
if(blammedLightsBlack.alpha == 0) {
if(blammedLightsBlackTween != null) {
blammedLightsBlackTween.cancel();
}
blammedLightsBlackTween = FlxTween.tween(blammedLightsBlack, {alpha: 1}, 1, {ease: FlxEase.quadInOut,
onComplete: function(twn:FlxTween) {
blammedLightsBlackTween = null;
}
});
var chars:Array<Character> = [boyfriend, gf, dad];
for (i in 0...chars.length) {
if(chars[i].colorTween != null) {
chars[i].colorTween.cancel();
}
chars[i].colorTween = FlxTween.color(chars[i], 1, FlxColor.WHITE, color, {onComplete: function(twn:FlxTween) {
chars[i].colorTween = null;
}, ease: FlxEase.quadInOut});
}
} else {
if(blammedLightsBlackTween != null) {
blammedLightsBlackTween.cancel();
}
blammedLightsBlackTween = null;
blammedLightsBlack.alpha = 1;
var chars:Array<Character> = [boyfriend, gf, dad];
for (i in 0...chars.length) {
if(chars[i].colorTween != null) {
chars[i].colorTween.cancel();
}
chars[i].colorTween = null;
}
dad.color = color;
boyfriend.color = color;
gf.color = color;
}
if(curStage == 'philly') {
if(phillyCityLightsEvent != null) {
phillyCityLightsEvent.forEach(function(spr:BGSprite) {
spr.visible = false;
});
phillyCityLightsEvent.members[lightId - 1].visible = true;
phillyCityLightsEvent.members[lightId - 1].alpha = 1;
}
}
} else {
if(blammedLightsBlack.alpha != 0) {
if(blammedLightsBlackTween != null) {
blammedLightsBlackTween.cancel();
}
blammedLightsBlackTween = FlxTween.tween(blammedLightsBlack, {alpha: 0}, 1, {ease: FlxEase.quadInOut,
onComplete: function(twn:FlxTween) {
blammedLightsBlackTween = null;
}
});
}
if(curStage == 'philly') {
phillyCityLights.forEach(function(spr:BGSprite) {
spr.visible = false;
});
phillyCityLightsEvent.forEach(function(spr:BGSprite) {
spr.visible = false;
});
var memb:FlxSprite = phillyCityLightsEvent.members[curLightEvent - 1];
if(memb != null) {
memb.visible = true;
memb.alpha = 1;
if(phillyCityLightsEventTween != null)
phillyCityLightsEventTween.cancel();
phillyCityLightsEventTween = FlxTween.tween(memb, {alpha: 0}, 1, {onComplete: function(twn:FlxTween) {
phillyCityLightsEventTween = null;
}, ease: FlxEase.quadInOut});
}
}
var chars:Array<Character> = [boyfriend, gf, dad];
for (i in 0...chars.length) {
if(chars[i].colorTween != null) {
chars[i].colorTween.cancel();
}
chars[i].colorTween = FlxTween.color(chars[i], 1, chars[i].color, FlxColor.WHITE, {onComplete: function(twn:FlxTween) {
chars[i].colorTween = null;
}, ease: FlxEase.quadInOut});
}
curLight = 0;
curLightEvent = 0;
}
case 'Kill Henchmen':
killHenchmen();
case 'Add Camera Zoom':
if(ClientPrefs.camZooms && FlxG.camera.zoom < 1.35) {
var camZoom:Float = Std.parseFloat(value1);
var hudZoom:Float = Std.parseFloat(value2);
if(Math.isNaN(camZoom)) camZoom = 0.015;
if(Math.isNaN(hudZoom)) hudZoom = 0.03;
FlxG.camera.zoom += camZoom;
camHUD.zoom += hudZoom;
}
case 'Trigger BG Ghouls':
if(curStage == 'schoolEvil' && !ClientPrefs.lowQuality) {
bgGhouls.dance(true);
bgGhouls.visible = true;
}
case 'Play Animation':
//trace('Anim to play: ' + value1);
var char:Character = dad;
switch(value2.toLowerCase().trim()) {
case 'bf' | 'boyfriend':
char = boyfriend;
case 'gf' | 'girlfriend':
char = gf;
default:
var val2:Int = Std.parseInt(value2);
if(Math.isNaN(val2)) val2 = 0;
switch(val2) {
case 1: char = boyfriend;
case 2: char = gf;
}
}
char.playAnim(value1, true);
char.specialAnim = true;
case 'Camera Follow Pos':
var val1:Float = Std.parseFloat(value1);
var val2:Float = Std.parseFloat(value2);
if(Math.isNaN(val1)) val1 = 0;
if(Math.isNaN(val2)) val2 = 0;
isCameraOnForcedPos = false;
if(!Math.isNaN(Std.parseFloat(value1)) || !Math.isNaN(Std.parseFloat(value2))) {
camFollow.x = val1;
camFollow.y = val2;
isCameraOnForcedPos = true;
}
case 'Alt Idle Animation':
var char:Character = dad;
switch(value1.toLowerCase()) {
case 'gf' | 'girlfriend':
char = gf;
case 'boyfriend' | 'bf':
char = boyfriend;
default:
var val:Int = Std.parseInt(value1);
if(Math.isNaN(val)) val = 0;
switch(val) {
case 1: char = boyfriend;
case 2: char = gf;
}
}
char.idleSuffix = value2;
char.recalculateDanceIdle();
case 'Screen Shake':
var valuesArray:Array<String> = [value1, value2];
var targetsArray:Array<FlxCamera> = [camGame, camHUD];
for (i in 0...targetsArray.length) {
var split:Array<String> = valuesArray[i].split(',');
var duration:Float = Std.parseFloat(split[0].trim());
var intensity:Float = Std.parseFloat(split[1].trim());
if(Math.isNaN(duration)) duration = 0;
if(Math.isNaN(intensity)) intensity = 0;
if(duration > 0 && intensity != 0) {
targetsArray[i].shake(intensity, duration);
}
}
case 'Change Character':
var charType:Int = 0;
switch(value1) {
case 'gf' | 'girlfriend':
charType = 2;
case 'dad' | 'opponent':
charType = 1;
default:
charType = Std.parseInt(value1);
if(Math.isNaN(charType)) charType = 0;
}
switch(charType) {
case 0:
if(boyfriend.curCharacter != value2) {
if(!boyfriendMap.exists(value2)) {
addCharacterToList(value2, charType);
}
boyfriend.visible = false;
boyfriend = boyfriendMap.get(value2);
if(!boyfriend.alreadyLoaded) {
boyfriend.alpha = 1;
boyfriend.alreadyLoaded = true;
}
boyfriend.visible = true;
iconP1.changeIcon(boyfriend.healthIcon);
}
case 1:
if(dad.curCharacter != value2) {
if(!dadMap.exists(value2)) {
addCharacterToList(value2, charType);
}
var wasGf:Bool = dad.curCharacter.startsWith('gf');
dad.visible = false;
dad = dadMap.get(value2);
if(!dad.curCharacter.startsWith('gf')) {
if(wasGf) {
gf.visible = true;
}
} else {
gf.visible = false;
}
if(!dad.alreadyLoaded) {
dad.alpha = 1;
dad.alreadyLoaded = true;
}
dad.visible = true;
iconP2.changeIcon(dad.healthIcon);
}
case 2:
if(gf.curCharacter != value2) {
if(!gfMap.exists(value2)) {
addCharacterToList(value2, charType);
}
gf.visible = false;
gf = gfMap.get(value2);
if(!gf.alreadyLoaded) {
gf.alpha = 1;
gf.alreadyLoaded = true;
}
}
}
reloadHealthBarColors();
case 'BG Freaks Expression':
if(bgGirls != null) bgGirls.swapDanceType();
}
callOnLuas('onEvent', [eventName, value1, value2]);
}
function moveCameraSection(?id:Int = 0):Void {
if(SONG.notes[id] == null) return;
if (!SONG.notes[id].mustHitSection)
{
moveCamera(true);
callOnLuas('onMoveCamera', ['dad']);
}
else
{
moveCamera(false);
callOnLuas('onMoveCamera', ['boyfriend']);
}
}
var cameraTwn:FlxTween;
public function moveCamera(isDad:Bool) {
if(isDad) {
camFollow.set(dad.getMidpoint().x + 150, dad.getMidpoint().y - 100);
camFollow.x += dad.cameraPosition[0];
camFollow.y += dad.cameraPosition[1];
tweenCamIn();
} else {
camFollow.set(boyfriend.getMidpoint().x - 100, boyfriend.getMidpoint().y - 100);
switch (curStage)
{
case 'limo':
camFollow.x = boyfriend.getMidpoint().x - 300;
case 'mall':
camFollow.y = boyfriend.getMidpoint().y - 200;
case 'school' | 'schoolEvil':
camFollow.x = boyfriend.getMidpoint().x - 200;
camFollow.y = boyfriend.getMidpoint().y - 200;
}
camFollow.x -= boyfriend.cameraPosition[0];
camFollow.y += boyfriend.cameraPosition[1];
if (Paths.formatToSongPath(SONG.song) == 'tutorial' && cameraTwn == null && FlxG.camera.zoom != 1) {
cameraTwn = FlxTween.tween(FlxG.camera, {zoom: 1}, (Conductor.stepCrochet * 4 / 1000), {ease: FlxEase.elasticInOut, onComplete:
function (twn:FlxTween) {
cameraTwn = null;
}
});
}
}
}
function tweenCamIn() {
if (Paths.formatToSongPath(SONG.song) == 'tutorial' && cameraTwn == null && FlxG.camera.zoom != 1.3) {
cameraTwn = FlxTween.tween(FlxG.camera, {zoom: 1.3}, (Conductor.stepCrochet * 4 / 1000), {ease: FlxEase.elasticInOut, onComplete:
function (twn:FlxTween) {
cameraTwn = null;
}
});
}
}
function snapCamFollowToPos(x:Float, y:Float) {
camFollow.set(x, y);
camFollowPos.setPosition(x, y);
}
function finishSong():Void
{
var finishCallback:Void->Void = endSong; //In case you want to change it in a specific song.
updateTime = false;
FlxG.sound.music.volume = 0;
vocals.volume = 0;
vocals.pause();
if(ClientPrefs.noteOffset <= 0) {
finishCallback();
} else {
finishTimer = new FlxTimer().start(ClientPrefs.noteOffset / 1000, function(tmr:FlxTimer) {
finishCallback();
});
}
}
var transitioning = false;
public function endSong():Void
{
//Should kill you if you tried to cheat
if(!startingSong) {
notes.forEach(function(daNote:Note) {
if(daNote.strumTime < songLength - Conductor.safeZoneOffset) {
health -= 0.0475;
}
});
for (daNote in unspawnNotes) {
if(daNote.strumTime < songLength - Conductor.safeZoneOffset) {
health -= 0.0475;
}
}
if(doDeathCheck()) {
return;
}
}
timeBarBG.visible = false;
timeBar.visible = false;
timeTxt.visible = false;
canPause = false;
endingSong = true;
camZooming = false;
inCutscene = false;
updateTime = false;
deathCounter = 0;
seenCutscene = false;
#if ACHIEVEMENTS_ALLOWED
if(achievementObj != null) {
return;
} else {
var achieve:String = checkForAchievement(['week1_nomiss', 'week2_nomiss', 'week3_nomiss', 'week4_nomiss',
'week5_nomiss', 'week6_nomiss', 'week7_nomiss', 'ur_bad',
'ur_good', 'hype', 'two_keys', 'toastie', 'debugger']);
if(achieve != null) {
startAchievement(achieve);
return;
}
}
#end
#if LUA_ALLOWED
var ret:Dynamic = callOnLuas('onEndSong', []);
#else
var ret:Dynamic = FunkinLua.Function_Continue;
#end
if(ret != FunkinLua.Function_Stop && !transitioning) {
if (SONG.validScore)
{
#if !switch
var percent:Float = ratingPercent;
if(Math.isNaN(percent)) percent = 0;
Highscore.saveScore(SONG.song, songScore, storyDifficulty, percent);
#end
}
if (isStoryMode)
{
campaignScore += songScore;
campaignMisses += songMisses;
storyPlaylist.remove(storyPlaylist[0]);
if (storyPlaylist.length <= 0)
{
FlxG.sound.playMusic(Paths.music('freakyMenu'));
cancelFadeTween();
CustomFadeTransition.nextCamera = camOther;
if(FlxTransitionableState.skipNextTransIn) {
CustomFadeTransition.nextCamera = null;
}
MusicBeatState.switchState(new StoryMenuState());
// if ()
if(!usedPractice) {
StoryMenuState.weekCompleted.set(WeekData.weeksList[storyWeek], true);
if (SONG.validScore)
{
Highscore.saveWeekScore(WeekData.getWeekFileName(), campaignScore, storyDifficulty);
}
FlxG.save.data.weekCompleted = StoryMenuState.weekCompleted;
FlxG.save.flush();
}
usedPractice = false;
changedDifficulty = false;
cpuControlled = false;
}
else
{
var difficulty:String = '' + CoolUtil.difficultyStuff[storyDifficulty][1];
trace('LOADING NEXT SONG');
trace(Paths.formatToSongPath(PlayState.storyPlaylist[0]) + difficulty);
var winterHorrorlandNext = (Paths.formatToSongPath(SONG.song) == "eggnog");
if (winterHorrorlandNext)
{
var blackShit:FlxSprite = new FlxSprite(-FlxG.width * FlxG.camera.zoom,
-FlxG.height * FlxG.camera.zoom).makeGraphic(FlxG.width * 3, FlxG.height * 3, FlxColor.BLACK);
blackShit.scrollFactor.set();
add(blackShit);
camHUD.visible = false;
FlxG.sound.play(Paths.sound('Lights_Shut_off'));
}
FlxTransitionableState.skipNextTransIn = true;
FlxTransitionableState.skipNextTransOut = true;
prevCamFollow = camFollow;
prevCamFollowPos = camFollowPos;
PlayState.SONG = Song.loadFromJson(PlayState.storyPlaylist[0] + difficulty, PlayState.storyPlaylist[0]);
FlxG.sound.music.stop();
if(winterHorrorlandNext) {
new FlxTimer().start(1.5, function(tmr:FlxTimer) {
cancelFadeTween();
//resetSpriteCache = true;
LoadingState.loadAndSwitchState(new PlayState());
});
} else {
cancelFadeTween();
//resetSpriteCache = true;
LoadingState.loadAndSwitchState(new PlayState());
}
}
}
else
{
trace('WENT BACK TO FREEPLAY??');
cancelFadeTween();
CustomFadeTransition.nextCamera = camOther;
if(FlxTransitionableState.skipNextTransIn) {
CustomFadeTransition.nextCamera = null;
}
MusicBeatState.switchState(new FreeplayState());
FlxG.sound.playMusic(Paths.music('freakyMenu'));
usedPractice = false;
changedDifficulty = false;
cpuControlled = false;
}
transitioning = true;
}
}
#if ACHIEVEMENTS_ALLOWED
var achievementObj:AchievementObject = null;
function startAchievement(achieve:String) {
achievementObj = new AchievementObject(achieve, camOther);
achievementObj.onFinish = achievementEnd;
add(achievementObj);
trace('Giving achievement ' + achieve);
}
function achievementEnd():Void
{
achievementObj = null;
if(endingSong && !inCutscene) {
endSong();
}
}
#end
public function KillNotes() {
while(notes.length > 0) {
var daNote:Note = notes.members[0];
daNote.active = false;
daNote.visible = false;
daNote.kill();
notes.remove(daNote, true);
daNote.destroy();
}
unspawnNotes = [];
eventNotes = [];
}
private function popUpScore(note:Note = null):Void
{
var noteDiff:Float = Math.abs(note.strumTime - Conductor.songPosition + 8);
// boyfriend.playAnim('hey');
vocals.volume = 1;
var placement:String = Std.string(combo);
var coolText:FlxText = new FlxText(0, 0, 0, placement, 32);
coolText.screenCenter();
coolText.x = FlxG.width * 0.55;
//
var rating:FlxSprite = new FlxSprite();
var score:Int = 350;
var daRating:String = "sick";
if (noteDiff > Conductor.safeZoneOffset * 0.75)
{
daRating = 'shit';
score = 50;
}
else if (noteDiff > Conductor.safeZoneOffset * 0.5)
{
daRating = 'bad';
score = 100;
}
else if (noteDiff > Conductor.safeZoneOffset * 0.25)
{
daRating = 'good';
score = 200;
}
if(daRating == 'sick' && !note.noteSplashDisabled)
{
spawnNoteSplashOnNote(note);
}
if(!practiceMode && !cpuControlled) {
songScore += score;
songHits++;
RecalculateRating();
if(scoreTxtTween != null) {
scoreTxtTween.cancel();
}
scoreTxt.scale.x = 1.1;
scoreTxt.scale.y = 1.1;
scoreTxtTween = FlxTween.tween(scoreTxt.scale, {x: 1, y: 1}, 0.2, {
onComplete: function(twn:FlxTween) {
scoreTxtTween = null;
}
});
}
/* if (combo > 60)
daRating = 'sick';
else if (combo > 12)
daRating = 'good'
else if (combo > 4)
daRating = 'bad';
*/
var pixelShitPart1:String = "";
var pixelShitPart2:String = '';
if (PlayState.isPixelStage)
{
pixelShitPart1 = 'pixelUI/';
pixelShitPart2 = '-pixel';
}
rating.loadGraphic(Paths.image(pixelShitPart1 + daRating + pixelShitPart2));
rating.screenCenter();
rating.x = coolText.x - 40;
rating.y -= 60;
rating.acceleration.y = 550;
rating.velocity.y -= FlxG.random.int(140, 175);
rating.velocity.x -= FlxG.random.int(0, 10);
rating.visible = !ClientPrefs.hideHud;
var comboSpr:FlxSprite = new FlxSprite().loadGraphic(Paths.image(pixelShitPart1 + 'combo' + pixelShitPart2));
comboSpr.screenCenter();
comboSpr.x = coolText.x;
comboSpr.acceleration.y = 600;
comboSpr.velocity.y -= 150;
comboSpr.visible = !ClientPrefs.hideHud;
comboSpr.velocity.x += FlxG.random.int(1, 10);
add(rating);
if (!PlayState.isPixelStage)
{
rating.setGraphicSize(Std.int(rating.width * 0.7));
rating.antialiasing = ClientPrefs.globalAntialiasing;
comboSpr.setGraphicSize(Std.int(comboSpr.width * 0.7));
comboSpr.antialiasing = ClientPrefs.globalAntialiasing;
}
else
{
rating.setGraphicSize(Std.int(rating.width * daPixelZoom * 0.7));
comboSpr.setGraphicSize(Std.int(comboSpr.width * daPixelZoom * 0.7));
}
comboSpr.updateHitbox();
rating.updateHitbox();
var seperatedScore:Array<Int> = [];
if(combo >= 1000) {
seperatedScore.push(Math.floor(combo / 1000) % 10);
}
seperatedScore.push(Math.floor(combo / 100) % 10);
seperatedScore.push(Math.floor(combo / 10) % 10);
seperatedScore.push(combo % 10);
var daLoop:Int = 0;
for (i in seperatedScore)
{
var numScore:FlxSprite = new FlxSprite().loadGraphic(Paths.image(pixelShitPart1 + 'num' + Std.int(i) + pixelShitPart2));
numScore.screenCenter();
numScore.x = coolText.x + (43 * daLoop) - 90;
numScore.y += 80;
if (!PlayState.isPixelStage)
{
numScore.antialiasing = ClientPrefs.globalAntialiasing;
numScore.setGraphicSize(Std.int(numScore.width * 0.5));
}
else
{
numScore.setGraphicSize(Std.int(numScore.width * daPixelZoom));
}
numScore.updateHitbox();
numScore.acceleration.y = FlxG.random.int(200, 300);
numScore.velocity.y -= FlxG.random.int(140, 160);
numScore.velocity.x = FlxG.random.float(-5, 5);
numScore.visible = !ClientPrefs.hideHud;
if (combo >= 10 || combo == 0)
add(numScore);
FlxTween.tween(numScore, {alpha: 0}, 0.2, {
onComplete: function(tween:FlxTween)
{
numScore.destroy();
},
startDelay: Conductor.crochet * 0.002
});
daLoop++;
}
/*
trace(combo);
trace(seperatedScore);
*/
coolText.text = Std.string(seperatedScore);
// add(coolText);
FlxTween.tween(rating, {alpha: 0}, 0.2, {
startDelay: Conductor.crochet * 0.001
});
FlxTween.tween(comboSpr, {alpha: 0}, 0.2, {
onComplete: function(tween:FlxTween)
{
coolText.destroy();
comboSpr.destroy();
rating.destroy();
},
startDelay: Conductor.crochet * 0.001
});
}
private function keyShit():Void
{
// HOLDING
var up = controls.NOTE_UP;
var right = controls.NOTE_RIGHT;
var down = controls.NOTE_DOWN;
var left = controls.NOTE_LEFT;
var upP = controls.NOTE_UP_P;
var rightP = controls.NOTE_RIGHT_P;
var downP = controls.NOTE_DOWN_P;
var leftP = controls.NOTE_LEFT_P;
var upR = controls.NOTE_UP_R;
var rightR = controls.NOTE_RIGHT_R;
var downR = controls.NOTE_DOWN_R;
var leftR = controls.NOTE_LEFT_R;
var controlArray:Array<Bool> = [leftP, downP, upP, rightP];
var controlReleaseArray:Array<Bool> = [leftR, downR, upR, rightR];
var controlHoldArray:Array<Bool> = [left, down, up, right];
// FlxG.watch.addQuick('asdfa', upP);
if (!boyfriend.stunned && generatedMusic)
{
// rewritten inputs???
notes.forEachAlive(function(daNote:Note)
{
// hold note functions
if (daNote.isSustainNote && controlHoldArray[daNote.noteData] && daNote.canBeHit
&& daNote.mustPress && !daNote.tooLate && !daNote.wasGoodHit) {
goodNoteHit(daNote);
}
});
if ((controlHoldArray.contains(true) || controlArray.contains(true)) && !endingSong) {
var canMiss:Bool = !ClientPrefs.ghostTapping;
if (controlArray.contains(true)) {
for (i in 0...controlArray.length) {
// heavily based on my own code LOL if it aint broke dont fix it
var pressNotes:Array<Note> = [];
var notesDatas:Array<Int> = [];
var notesStopped:Bool = false;
var sortedNotesList:Array<Note> = [];
notes.forEachAlive(function(daNote:Note)
{
if (daNote.canBeHit && daNote.mustPress && !daNote.tooLate
&& !daNote.wasGoodHit && daNote.noteData == i) {
sortedNotesList.push(daNote);
notesDatas.push(daNote.noteData);
canMiss = true;
}
});
sortedNotesList.sort((a, b) -> Std.int(a.strumTime - b.strumTime));
if (sortedNotesList.length > 0) {
for (epicNote in sortedNotesList)
{
for (doubleNote in pressNotes) {
if (Math.abs(doubleNote.strumTime - epicNote.strumTime) < 10) {
doubleNote.kill();
notes.remove(doubleNote, true);
doubleNote.destroy();
} else
notesStopped = true;
}
// eee jack detection before was not super good
if (controlArray[epicNote.noteData] && !notesStopped) {
goodNoteHit(epicNote);
pressNotes.push(epicNote);
}
}
}
else if (canMiss)
ghostMiss(controlArray[i], i, true);
// I dunno what you need this for but here you go
// - Shubs
// Shubs, this is for the "Just the Two of Us" achievement lol
// - Shadow Mario
if (!keysPressed[i] && controlArray[i])
keysPressed[i] = true;
}
}
#if ACHIEVEMENTS_ALLOWED
var achieve:String = checkForAchievement(['oversinging']);
if (achieve != null) {
startAchievement(achieve);
}
#end
} else if (boyfriend.holdTimer > Conductor.stepCrochet * 0.001 * boyfriend.singDuration && boyfriend.animation.curAnim.name.startsWith('sing')
&& !boyfriend.animation.curAnim.name.endsWith('miss'))
boyfriend.dance();
}
playerStrums.forEach(function(spr:StrumNote)
{
if(controlArray[spr.ID] && spr.animation.curAnim.name != 'confirm') {
spr.playAnim('pressed');
spr.resetAnim = 0;
}
if(controlReleaseArray[spr.ID]) {
spr.playAnim('static');
spr.resetAnim = 0;
}
});
}
function ghostMiss(statement:Bool = false, direction:Int = 0, ?ghostMiss:Bool = false) {
if (statement) {
noteMissPress(direction, ghostMiss);
callOnLuas('noteMissPress', [direction]);
}
}
function noteMiss(daNote:Note):Void { //You didn't hit the key and let it go offscreen, also used by Hurt Notes
//Dupe note remove
notes.forEachAlive(function(note:Note) {
if (daNote != note && daNote.mustPress && daNote.noteData == note.noteData && daNote.isSustainNote == note.isSustainNote && Math.abs(daNote.strumTime - note.strumTime) < 10) {
note.kill();
notes.remove(note, true);
note.destroy();
}
});
health -= daNote.missHealth; //For testing purposes
//trace(daNote.missHealth);
songMisses++;
vocals.volume = 0;
RecalculateRating();
var animToPlay:String = '';
switch (Math.abs(daNote.noteData) % 4)
{
case 0:
animToPlay = 'singLEFTmiss';
case 1:
animToPlay = 'singDOWNmiss';
case 2:
animToPlay = 'singUPmiss';
case 3:
animToPlay = 'singRIGHTmiss';
}
if(daNote.noteType == 'GF Sing') {
gf.playAnim(animToPlay, true);
} else {
var daAlt = '';
if(daNote.noteType == 'Alt Animation') daAlt = '-alt';
boyfriend.playAnim(animToPlay + daAlt, true);
}
callOnLuas('noteMiss', [notes.members.indexOf(daNote), daNote.noteData, daNote.noteType, daNote.isSustainNote]);
}
function noteMissPress(direction:Int = 1, ?ghostMiss:Bool = false):Void //You pressed a key when there was no notes to press for this key
{
if (!boyfriend.stunned)
{
health -= 0.04;
if (combo > 5 && gf.animOffsets.exists('sad'))
{
gf.playAnim('sad');
}
combo = 0;
if(!practiceMode) songScore -= 10;
if(!endingSong) {
if(ghostMiss) ghostMisses++;
songMisses++;
}
RecalculateRating();
FlxG.sound.play(Paths.soundRandom('missnote', 1, 3), FlxG.random.float(0.1, 0.2));
// FlxG.sound.play(Paths.sound('missnote1'), 1, false);
// FlxG.log.add('played imss note');
/*boyfriend.stunned = true;
// get stunned for 1/60 of a second, makes you able to
new FlxTimer().start(1 / 60, function(tmr:FlxTimer)
{
boyfriend.stunned = false;
});*/
switch (direction)
{
case 0:
boyfriend.playAnim('singLEFTmiss', true);
case 1:
boyfriend.playAnim('singDOWNmiss', true);
case 2:
boyfriend.playAnim('singUPmiss', true);
case 3:
boyfriend.playAnim('singRIGHTmiss', true);
}
vocals.volume = 0;
}
}
function goodNoteHit(note:Note):Void
{
if (!note.wasGoodHit)
{
if(cpuControlled && (note.ignoreNote || note.hitCausesMiss)) return;
if(note.hitCausesMiss) {
noteMiss(note);
if(!note.noteSplashDisabled && !note.isSustainNote) {
spawnNoteSplashOnNote(note);
}
switch(note.noteType) {
case 'Hurt Note': //Hurt note
if(boyfriend.animation.getByName('hurt') != null) {
boyfriend.playAnim('hurt', true);
boyfriend.specialAnim = true;
}
}
note.wasGoodHit = true;
if (!note.isSustainNote)
{
note.kill();
notes.remove(note, true);
note.destroy();
}
return;
}
if (!note.isSustainNote)
{
popUpScore(note);
combo += 1;
if(combo > 9999) combo = 9999;
}
health += note.hitHealth;
if(!note.noAnimation) {
var daAlt = '';
if(note.noteType == 'Alt Animation') daAlt = '-alt';
var animToPlay:String = '';
switch (Std.int(Math.abs(note.noteData)))
{
case 0:
animToPlay = 'singLEFT';
case 1:
animToPlay = 'singDOWN';
case 2:
animToPlay = 'singUP';
case 3:
animToPlay = 'singRIGHT';
}
if(note.noteType == 'GF Sing') {
gf.playAnim(animToPlay + daAlt, true);
gf.holdTimer = 0;
} else {
boyfriend.playAnim(animToPlay + daAlt, true);
boyfriend.holdTimer = 0;
}
if(note.noteType == 'Hey!') {
if(boyfriend.animOffsets.exists('hey')) {
boyfriend.playAnim('hey', true);
boyfriend.specialAnim = true;
boyfriend.heyTimer = 0.6;
}
if(gf.animOffsets.exists('cheer')) {
gf.playAnim('cheer', true);
gf.specialAnim = true;
gf.heyTimer = 0.6;
}
}
}
if(cpuControlled) {
var time:Float = 0.15;
if(note.isSustainNote && !note.animation.curAnim.name.endsWith('end')) {
time += 0.15;
}
StrumPlayAnim(false, Std.int(Math.abs(note.noteData)) % 4, time);
} else {
playerStrums.forEach(function(spr:StrumNote)
{
if (Math.abs(note.noteData) == spr.ID)
{
spr.playAnim('confirm', true);
}
});
}
note.wasGoodHit = true;
vocals.volume = 1;
var isSus:Bool = note.isSustainNote; //GET OUT OF MY HEAD, GET OUT OF MY HEAD, GET OUT OF MY HEAD
var leData:Int = Math.round(Math.abs(note.noteData));
var leType:String = note.noteType;
callOnLuas('goodNoteHit', [notes.members.indexOf(note), leData, leType, isSus]);
if (!note.isSustainNote)
{
note.kill();
notes.remove(note, true);
note.destroy();
}
}
}
function spawnNoteSplashOnNote(note:Note) {
if(ClientPrefs.noteSplashes && note != null) {
var strum:StrumNote = playerStrums.members[note.noteData];
if(strum != null) {
spawnNoteSplash(strum.x, strum.y, note.noteData, note);
}
}
}
public function spawnNoteSplash(x:Float, y:Float, data:Int, ?note:Note = null) {
var skin:String = 'noteSplashes';
if(PlayState.SONG.splashSkin != null && PlayState.SONG.splashSkin.length > 0) skin = PlayState.SONG.splashSkin;
var hue:Float = ClientPrefs.arrowHSV[data % 4][0] / 360;
var sat:Float = ClientPrefs.arrowHSV[data % 4][1] / 100;
var brt:Float = ClientPrefs.arrowHSV[data % 4][2] / 100;
if(note != null) {
skin = note.noteSplashTexture;
hue = note.noteSplashHue;
sat = note.noteSplashSat;
brt = note.noteSplashBrt;
}
var splash:NoteSplash = grpNoteSplashes.recycle(NoteSplash);
splash.setupNoteSplash(x, y, data, skin, hue, sat, brt);
grpNoteSplashes.add(splash);
}
var fastCarCanDrive:Bool = true;
function resetFastCar():Void
{
fastCar.x = -12600;
fastCar.y = FlxG.random.int(140, 250);
fastCar.velocity.x = 0;
fastCarCanDrive = true;
}
var carTimer:FlxTimer;
function fastCarDrive()
{
//trace('Car drive');
FlxG.sound.play(Paths.soundRandom('carPass', 0, 1), 0.7);
fastCar.velocity.x = (FlxG.random.int(170, 220) / FlxG.elapsed) * 3;
fastCarCanDrive = false;
carTimer = new FlxTimer().start(2, function(tmr:FlxTimer)
{
resetFastCar();
carTimer = null;
});
}
var trainMoving:Bool = false;
var trainFrameTiming:Float = 0;
var trainCars:Int = 8;
var trainFinishing:Bool = false;
var trainCooldown:Int = 0;
function trainStart():Void
{
trainMoving = true;
if (!trainSound.playing)
trainSound.play(true);
}
var startedMoving:Bool = false;
function updateTrainPos():Void
{
if (trainSound.time >= 4700)
{
startedMoving = true;
gf.playAnim('hairBlow');
gf.specialAnim = true;
}
if (startedMoving)
{
phillyTrain.x -= 400;
if (phillyTrain.x < -2000 && !trainFinishing)
{
phillyTrain.x = -1150;
trainCars -= 1;
if (trainCars <= 0)
trainFinishing = true;
}
if (phillyTrain.x < -4000 && trainFinishing)
trainReset();
}
}
function trainReset():Void
{
gf.danced = false; //Sets head to the correct position once the animation ends
gf.playAnim('hairFall');
gf.specialAnim = true;
phillyTrain.x = FlxG.width + 200;
trainMoving = false;
// trainSound.stop();
// trainSound.time = 0;
trainCars = 8;
trainFinishing = false;
startedMoving = false;
}
function lightningStrikeShit():Void
{
FlxG.sound.play(Paths.soundRandom('thunder_', 1, 2));
if(!ClientPrefs.lowQuality) halloweenBG.animation.play('halloweem bg lightning strike');
lightningStrikeBeat = curBeat;
lightningOffset = FlxG.random.int(8, 24);
if(boyfriend.animOffsets.exists('scared')) {
boyfriend.playAnim('scared', true);
}
if(gf.animOffsets.exists('scared')) {
gf.playAnim('scared', true);
}
if(ClientPrefs.camZooms) {
FlxG.camera.zoom += 0.015;
camHUD.zoom += 0.03;
if(!camZooming) { //Just a way for preventing it to be permanently zoomed until Skid & Pump hits a note
FlxTween.tween(FlxG.camera, {zoom: defaultCamZoom}, 0.5);
FlxTween.tween(camHUD, {zoom: 1}, 0.5);
}
}
if(ClientPrefs.flashing) {
halloweenWhite.alpha = 0.4;
FlxTween.tween(halloweenWhite, {alpha: 0.5}, 0.075);
FlxTween.tween(halloweenWhite, {alpha: 0}, 0.25, {startDelay: 0.15});
}
}
function killHenchmen():Void
{
if(!ClientPrefs.lowQuality && ClientPrefs.violence && curStage == 'limo') {
if(limoKillingState < 1) {
limoMetalPole.x = -400;
limoMetalPole.visible = true;
limoLight.visible = true;
limoCorpse.visible = false;
limoCorpseTwo.visible = false;
limoKillingState = 1;
#if ACHIEVEMENTS_ALLOWED
Achievements.henchmenDeath++;
var achieve:String = checkForAchievement(['roadkill_enthusiast']);
if (achieve != null) {
startAchievement(achieve);
} else {
FlxG.save.data.henchmenDeath = Achievements.henchmenDeath;
FlxG.save.flush();
}
FlxG.log.add('Deaths: ' + Achievements.henchmenDeath);
#end
}
}
}
function resetLimoKill():Void
{
if(curStage == 'limo') {
limoMetalPole.x = -500;
limoMetalPole.visible = false;
limoLight.x = -500;
limoLight.visible = false;
limoCorpse.x = -500;
limoCorpse.visible = false;
limoCorpseTwo.x = -500;
limoCorpseTwo.visible = false;
}
}
private var preventLuaRemove:Bool = false;
override function destroy() {
preventLuaRemove = true;
for (i in 0...luaArray.length) {
luaArray[i].call('onDestroy', []);
luaArray[i].stop();
}
luaArray = [];
super.destroy();
}
public function cancelFadeTween() {
if(FlxG.sound.music.fadeTween != null) {
FlxG.sound.music.fadeTween.cancel();
}
FlxG.sound.music.fadeTween = null;
}
public function removeLua(lua:FunkinLua) {
if(luaArray != null && !preventLuaRemove) {
luaArray.remove(lua);
}
}
var lastStepHit:Int = -1;
override function stepHit()
{
super.stepHit();
if (FlxG.sound.music.time > Conductor.songPosition + 20 || FlxG.sound.music.time < Conductor.songPosition - 20)
{
resyncVocals();
}
if(curStep == lastStepHit) {
return;
}
lastStepHit = curStep;
setOnLuas('curStep', curStep);
callOnLuas('onStepHit', []);
}
var lightningStrikeBeat:Int = 0;
var lightningOffset:Int = 8;
var lastBeatHit:Int = -1;
override function beatHit()
{
super.beatHit();
if(lastBeatHit >= curBeat) {
trace('BEAT HIT: ' + curBeat + ', LAST HIT: ' + lastBeatHit);
return;
}
if (generatedMusic)
{
notes.sort(FlxSort.byY, ClientPrefs.downScroll ? FlxSort.ASCENDING : FlxSort.DESCENDING);
}
if (SONG.notes[Math.floor(curStep / 16)] != null)
{
if (SONG.notes[Math.floor(curStep / 16)].changeBPM)
{
Conductor.changeBPM(SONG.notes[Math.floor(curStep / 16)].bpm);
//FlxG.log.add('CHANGED BPM!');
setOnLuas('curBpm', Conductor.bpm);
setOnLuas('crochet', Conductor.crochet);
setOnLuas('stepCrochet', Conductor.stepCrochet);
}
setOnLuas('mustHitSection', SONG.notes[Math.floor(curStep / 16)].mustHitSection);
// else
// Conductor.changeBPM(SONG.bpm);
}
// FlxG.log.add('change bpm' + SONG.notes[Std.int(curStep / 16)].changeBPM);
if (generatedMusic && PlayState.SONG.notes[Std.int(curStep / 16)] != null && !endingSong && !isCameraOnForcedPos)
{
moveCameraSection(Std.int(curStep / 16));
}
if (camZooming && FlxG.camera.zoom < 1.35 && ClientPrefs.camZooms && curBeat % 4 == 0)
{
FlxG.camera.zoom += 0.015;
camHUD.zoom += 0.03;
}
iconP1.setGraphicSize(Std.int(iconP1.width + 30));
iconP2.setGraphicSize(Std.int(iconP2.width + 30));
iconP1.updateHitbox();
iconP2.updateHitbox();
if (curBeat % gfSpeed == 0 && !gf.stunned && gf.animation.curAnim.name != null && !gf.animation.curAnim.name.startsWith("sing"))
{
gf.dance();
}
if(curBeat % 2 == 0) {
if (boyfriend.animation.curAnim.name != null && !boyfriend.animation.curAnim.name.startsWith("sing"))
{
boyfriend.dance();
}
if (dad.animation.curAnim.name != null && !dad.animation.curAnim.name.startsWith("sing") && !dad.stunned)
{
dad.dance();
}
} else if(dad.danceIdle && dad.animation.curAnim.name != null && !dad.curCharacter.startsWith('gf') && !dad.animation.curAnim.name.startsWith("sing") && !dad.stunned) {
dad.dance();
}
switch (curStage)
{
case 'school':
if(!ClientPrefs.lowQuality) {
bgGirls.dance();
}
case 'mall':
if(!ClientPrefs.lowQuality) {
upperBoppers.dance(true);
}
if(heyTimer <= 0) bottomBoppers.dance(true);
santa.dance(true);
case 'limo':
if(!ClientPrefs.lowQuality) {
grpLimoDancers.forEach(function(dancer:BackgroundDancer)
{
dancer.dance();
});
}
if (FlxG.random.bool(10) && fastCarCanDrive)
fastCarDrive();
case "philly":
if (!trainMoving)
trainCooldown += 1;
if (curBeat % 4 == 0)
{
phillyCityLights.forEach(function(light:BGSprite)
{
light.visible = false;
});
curLight = FlxG.random.int(0, phillyCityLights.length - 1, [curLight]);
phillyCityLights.members[curLight].visible = true;
phillyCityLights.members[curLight].alpha = 1;
}
if (curBeat % 8 == 4 && FlxG.random.bool(30) && !trainMoving && trainCooldown > 8)
{
trainCooldown = FlxG.random.int(-4, 0);
trainStart();
}
}
if (curStage == 'spooky' && FlxG.random.bool(10) && curBeat > lightningStrikeBeat + lightningOffset)
{
lightningStrikeShit();
}
lastBeatHit = curBeat;
setOnLuas('curBeat', curBeat);
callOnLuas('onBeatHit', []);
}
public function callOnLuas(event:String, args:Array<Dynamic>):Dynamic {
var returnVal:Dynamic = FunkinLua.Function_Continue;
#if LUA_ALLOWED
for (i in 0...luaArray.length) {
var ret:Dynamic = luaArray[i].call(event, args);
if(ret != FunkinLua.Function_Continue) {
returnVal = ret;
}
}
#end
return returnVal;
}
public function setOnLuas(variable:String, arg:Dynamic) {
#if LUA_ALLOWED
for (i in 0...luaArray.length) {
luaArray[i].set(variable, arg);
}
#end
}
function StrumPlayAnim(isDad:Bool, id:Int, time:Float) {
var spr:StrumNote = null;
if(isDad) {
spr = strumLineNotes.members[id];
} else {
spr = playerStrums.members[id];
}
if(spr != null) {
spr.playAnim('confirm', true);
spr.resetAnim = time;
}
}
public var ratingString:String;
public var ratingPercent:Float;
public function RecalculateRating() {
setOnLuas('score', songScore);
setOnLuas('misses', songMisses);
setOnLuas('ghostMisses', songMisses);
setOnLuas('hits', songHits);
var ret:Dynamic = callOnLuas('onRecalculateRating', []);
if(ret != FunkinLua.Function_Stop) {
ratingPercent = songScore / ((songHits + songMisses - ghostMisses) * 350);
if(!Math.isNaN(ratingPercent) && ratingPercent < 0) ratingPercent = 0;
if(Math.isNaN(ratingPercent)) {
ratingString = '?';
} else if(ratingPercent >= 1) {
ratingPercent = 1;
ratingString = ratingStuff[ratingStuff.length-1][0]; //Uses last string
} else {
for (i in 0...ratingStuff.length-1) {
if(ratingPercent < ratingStuff[i][1]) {
ratingString = ratingStuff[i][0];
break;
}
}
}
setOnLuas('rating', ratingPercent);
setOnLuas('ratingName', ratingString);
}
}
#if ACHIEVEMENTS_ALLOWED
private function checkForAchievement(achievesToCheck:Array<String>):String {
for (i in 0...achievesToCheck.length) {
var achievementName:String = achievesToCheck[i];
if(!Achievements.isAchievementUnlocked(achievementName)) {
var unlock:Bool = false;
switch(achievementName)
{
case 'week1_nomiss' | 'week2_nomiss' | 'week3_nomiss' | 'week4_nomiss' | 'week5_nomiss' | 'week6_nomiss' | 'week7_nomiss':
if(isStoryMode && campaignMisses + songMisses < 1 && CoolUtil.difficultyString() == 'HARD' && storyPlaylist.length <= 1 && !changedDifficulty && !usedPractice)
{
var weekName:String = WeekData.getWeekFileName();
switch(weekName) //I know this is a lot of duplicated code, but it's easier readable and you can add weeks with different names than the achievement tag
{
case 'week1':
if(achievementName == 'week1_nomiss') unlock = true;
case 'week2':
if(achievementName == 'week2_nomiss') unlock = true;
case 'week3':
if(achievementName == 'week3_nomiss') unlock = true;
case 'week4':
if(achievementName == 'week4_nomiss') unlock = true;
case 'week5':
if(achievementName == 'week5_nomiss') unlock = true;
case 'week6':
if(achievementName == 'week6_nomiss') unlock = true;
case 'week7':
if(achievementName == 'week7_nomiss') unlock = true;
}
}
case 'ur_bad':
if(ratingPercent < 0.2 && !practiceMode && !cpuControlled) {
unlock = true;
}
case 'ur_good':
if(ratingPercent >= 1 && !usedPractice && !cpuControlled) {
unlock = true;
}
case 'roadkill_enthusiast':
if(Achievements.henchmenDeath >= 100) {
unlock = true;
}
case 'oversinging':
if(boyfriend.holdTimer >= 20 && !usedPractice) {
unlock = true;
}
case 'hype':
if(!boyfriendIdled && !usedPractice) {
unlock = true;
}
case 'two_keys':
if(!usedPractice) {
var howManyPresses:Int = 0;
for (j in 0...keysPressed.length) {
if(keysPressed[j]) howManyPresses++;
}
if(howManyPresses <= 2) {
unlock = true;
}
}
case 'toastie':
if(/*ClientPrefs.framerate <= 60 &&*/ ClientPrefs.lowQuality && !ClientPrefs.globalAntialiasing && !ClientPrefs.imagesPersist) {
unlock = true;
}
case 'debugger':
if(Paths.formatToSongPath(SONG.song) == 'test' && !usedPractice) {
unlock = true;
}
}
if(unlock) {
Achievements.unlockAchievement(achievementName);
return achievementName;
}
}
}
return null;
}
#end
var curLight:Int = 0;
var curLightEvent:Int = 0;
}