Merge branch 'feature/scripted-stages'

This commit is contained in:
Cameron Taylor 2022-03-18 22:04:28 -04:00
commit 2d673e037a
130 changed files with 2397 additions and 1826 deletions

16
.vscode/launch.json vendored
View file

@ -2,18 +2,16 @@
"version": "0.2.0",
"configurations": [
{
"name": "HTML5 Debug",
"type": "chrome",
"request": "launch",
"url": "http://127.0.0.1:3001",
"sourceMaps": true,
"webRoot": "${workspaceFolder}",
"preLaunchTask": "debug: html"
},
{
// Launch in native/CPP
"name": "Lime",
"type": "lime",
"request": "launch"
},
{
// Evaluate macros with debugging enabled
"name": "Haxe Eval",
"type": "haxe-eval",
"request": "launch"
}
]
}

View file

@ -2,7 +2,7 @@
<project>
<!-- _________________________ Application Settings _________________________ -->
<app title="Friday Night Funkin'" file="Funkin" packageName="com.funkin.fnf" package="com.funkin.fnf" main="Main" version="0.2.8" company="ninjamuffin99" />
<app title="Friday Night Funkin'" file="Funkin" packageName="com.funkin.fnf" package="com.funkin.fnf" main="Main" version="0.3.0" company="ninjamuffin99" />
<!--Switch Export with Unique ApplicationID and Icon-->
<set name="APP_ID" value="0x0100f6c013bbc000" />
@ -109,6 +109,7 @@
<assets path="assets/fonts" embed='true' />
<!-- _______________________________ Libraries ______________________________ -->
<haxelib name="openfl" />
<haxelib name="flixel" />
<haxedev set='webgl' />
@ -120,7 +121,7 @@
<haxelib name="flixel-ui" />
<!--haxelib name="newgrounds" unless="switch"/> -->
<haxelib name="faxe" if='switch' />
<haxelib name="polymod" if="desktop"/>
<haxelib name="polymod" />
<haxelib name="firetongue" />
<!-- <haxelib name="colyseus"/> -->
@ -138,7 +139,8 @@
<!-- https://github.com/ninjamuffin99/Flixel-Animate-Atlas-Player -->
<!--<haxelib name="discord_rpc" if="cpp"/> --> <!-- foesn't work with neko -->
<!--<haxelib name="discord_rpc" if="cpp"/> -->
<!-- foesn't work with neko -->
<!-- <haxelib name="hxcpp-debug-server" if="desktop"/> -->
<!-- <haxelib name="markdown" /> -->
@ -179,12 +181,23 @@
<!--Enable this for Nape release builds for a serious peformance improvement-->
<haxedef name="NAPE_RELEASE_BUILD" unless="debug" />
<!-- _________________________________ Custom _______________________________ -->
<!-- Disable trace() calls in release builds to bump up performance. -->
<haxeflag name="--no-traces" unless="debug" />
<haxeflag name="--dce full" if="release" />
<!-- HScript relies heavily on Reflection, which means we can't use DCE. -->
<haxeflag name="-dce no" />
<haxeflag name="--macro" value="include('funkin')" />
<!-- Necessary to provide stack traces for HScript. -->
<haxedef name="hscriptPos" />
<haxedef name="HXCPP_CHECK_POINTER" />
<haxedef name="HXCPP_STACK_LINE" />
<!-- _________________________________ Custom _______________________________ -->
<!-- This macro allows addition of new functionality to existing Flixel. -->
<haxeflag name="--macro" value="addMetadata('@:build(funkin.util.macro.FlxMacro.buildFlxBasic())', 'flixel.FlxBasic')" />
<!--Place custom nodes like icons here (higher priority to override the HaxeFlixel icon)-->
@ -193,12 +206,6 @@
<icon path="art/icon64.png" size='64' />
<icon path="art/iconOG.png" />
<!--
aww yeah hackerman time, run a macro on the class rather than edit it
took me a while to get this working but turns out the classname comes SECOND
-->
<haxeflag name="--macro" value="addMetadata('@:build(util.macro.FlxMacro.buildFlxBasic())', 'flixel.FlxBasic')" />
<!-- <haxedef name="SKIP_TO_PLAYSTATE" if="debug" /> -->
<haxedef name="CAN_OPEN_LINKS" unless="switch" />

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

View file

@ -0,0 +1,8 @@
{
"title": "Intro Mod",
"description": "An introductory mod.",
"author": "MasterEric",
"api_version": "0.1.0",
"mod_version": "1.0.0",
"license": "Apache-2.0"
}

View file

Before

Width:  |  Height:  |  Size: 2.3 MiB

After

Width:  |  Height:  |  Size: 2.3 MiB

View file

@ -1,64 +0,0 @@
package;
import flixel.FlxSprite;
class BGSprite extends FlxSprite
{
/**
Cool lil utility thing just so that it can easy do antialiasing and scrollfactor bullshit
*/
public var idleAnim:String;
/**
* NOTE: loadOldWay param is just for current backward compatibility! Will be moved later!
*/
public function new(image:String, x:Float = 0, y:Float = 0, parX:Float = 1, parY:Float = 1, ?daAnimations:Array<String>, ?loopingAnim:Bool = false,
?loadOldWay:Bool = true)
{
super(x, y);
if (loadOldWay)
{
if (daAnimations != null)
{
setupSparrow(image, daAnimations, loopingAnim);
}
else
{
justLoadImage(image);
}
}
scrollFactor.set(parX, parY);
antialiasing = true;
}
public function setupSparrow(image:String, daAnimations:Array<String>, ?loopingAnim:Bool = false)
{
frames = Paths.getSparrowAtlas(image);
for (anims in daAnimations)
{
var daLoop:Bool = loopingAnim;
if (loopingAnim == null)
daLoop = false;
animation.addByPrefix(anims, anims, 24, daLoop);
animation.play(anims);
if (idleAnim == null)
idleAnim = anims;
}
}
public function justLoadImage(image:String)
{
loadGraphic(Paths.image(image));
active = false;
}
public function dance():Void
{
if (idleAnim != null)
animation.play(idleAnim);
}
}

View file

@ -1,31 +0,0 @@
package;
import flixel.FlxSprite;
import flixel.graphics.frames.FlxAtlasFrames;
class BackgroundDancer extends FlxSprite
{
public function new(x:Float, y:Float)
{
super(x, y);
frames = Paths.getSparrowAtlas("limo/limoDancer");
animation.addByIndices('danceLeft', 'bg dancer sketch PINK', [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], "", 24, false);
animation.addByIndices('danceRight', 'bg dancer sketch PINK', [15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29], "", 24, false);
animation.play('danceLeft');
animation.finish();
antialiasing = true;
}
var danceDir:Bool = false;
public function dance():Void
{
danceDir = !danceDir;
if (danceDir)
animation.play('danceRight', true);
else
animation.play('danceLeft', true);
}
}

View file

@ -1,41 +0,0 @@
package;
import flixel.FlxSprite;
import flixel.graphics.frames.FlxAtlasFrames;
class BackgroundGirls extends FlxSprite
{
public function new(x:Float, y:Float)
{
super(x, y);
// BG fangirls dissuaded
frames = Paths.getSparrowAtlas('weeb/bgFreaks');
animation.addByIndices('danceLeft', 'BG girls group', CoolUtil.numberArray(14), "", 24, false);
animation.addByIndices('danceRight', 'BG girls group', CoolUtil.numberArray(30, 15), "", 24, false);
animation.play('danceLeft');
animation.finish();
}
var danceDir:Bool = false;
public function getScared():Void
{
animation.addByIndices('danceLeft', 'BG fangirls dissuaded', CoolUtil.numberArray(14), "", 24, false);
animation.addByIndices('danceRight', 'BG fangirls dissuaded', CoolUtil.numberArray(30, 15), "", 24, false);
dance();
animation.finish();
}
public function dance():Void
{
danceDir = !danceDir;
if (danceDir)
animation.play('danceRight', true);
else
animation.play('danceLeft', true);
}
}

View file

@ -1,5 +1,7 @@
package;
import funkin.InitState;
import funkin.MemoryCounter;
import flixel.FlxGame;
import flixel.FlxState;
import openfl.Lib;
@ -42,9 +44,9 @@ class Main extends Sprite
// A design similar to that of Minecraft resource packs would be intuitive.
// 3. The interface should save (to the save file) and output an ordered array of mod IDs.
// 4. Replace the call to PolymodHandler.loadAllMods() with a call to PolymodHandler.loadModsById(ids:Array<String>).
modding.PolymodHandler.loadAllMods();
funkin.modding.PolymodHandler.loadAllMods();
i18n.FireTongueHandler.init();
funkin.i18n.FireTongueHandler.init();
if (stage != null)
{
@ -98,9 +100,11 @@ class Main extends Sprite
#if debug
fpsCounter = new FPS(10, 3, 0xFFFFFF);
addChild(fpsCounter);
#if !html5
memoryCounter = new MemoryCounter(10, 13, 0xFFFFFF);
addChild(memoryCounter);
#end
#end
/*
video = new Video();

View file

@ -1,89 +0,0 @@
package;
import flixel.FlxSprite;
import haxe.display.Display.Package;
class TankmenBG extends FlxSprite
{
public static var animationNotes:Array<Dynamic> = [];
public var strumTime:Float = 0;
public var goingRight:Bool = false;
public var tankSpeed:Float = 0.7;
public var endingOffset:Float;
public function new(x:Float, y:Float, isGoingRight:Bool)
{
super(x, y);
// makeGraphic(200, 200);
frames = Paths.getSparrowAtlas('tankmanKilled1');
antialiasing = true;
animation.addByPrefix('run', 'tankman running', 24, true);
animation.addByPrefix('shot', 'John Shot ' + FlxG.random.int(1, 2), 24, false);
animation.play('run');
animation.curAnim.curFrame = FlxG.random.int(0, animation.curAnim.numFrames - 1);
updateHitbox();
setGraphicSize(Std.int(width * 0.8));
updateHitbox();
}
public function resetShit(x:Float, y:Float, isGoingRight:Bool)
{
setPosition(x, y);
goingRight = isGoingRight;
endingOffset = FlxG.random.float(50, 200);
tankSpeed = FlxG.random.float(0.6, 1);
if (goingRight)
flipX = true;
}
override function update(elapsed:Float)
{
super.update(elapsed);
if (x >= FlxG.width * 1.2 || x <= FlxG.width * -0.5)
visible = false;
else
visible = true;
if (animation.curAnim.name == 'run')
{
var endDirection:Float = (FlxG.width * 0.74) + endingOffset;
if (goingRight)
{
endDirection = (FlxG.width * 0.02) - endingOffset;
x = (endDirection + (Conductor.songPosition - strumTime) * tankSpeed);
}
else
{
x = (endDirection - (Conductor.songPosition - strumTime) * tankSpeed);
}
}
if (Conductor.songPosition > strumTime)
{
// kill();
animation.play('shot');
if (goingRight)
{
offset.y = 200;
offset.x = 300;
}
}
if (animation.curAnim.name == 'shot' && animation.curAnim.curFrame >= animation.curAnim.frames.length - 1)
{
kill();
}
}
}

View file

@ -1,4 +1,4 @@
package;
package funkin;
import flixel.FlxSprite;
import flixel.graphics.frames.FlxAtlasFrames;

View file

@ -1,4 +1,4 @@
package;
package funkin;
import flixel.FlxSprite;
import flixel.graphics.frames.FlxAtlasFrames;

View file

@ -1,4 +1,4 @@
package;
package funkin;
import flixel.FlxSubState;

View file

@ -1,11 +1,14 @@
package;
package funkin;
import Section.SwagSection;
import funkin.Note.NoteData;
import funkin.SongLoad.SwagSong;
import funkin.Section.SwagSection;
import flixel.FlxSprite;
import flixel.animation.FlxBaseAnimation;
import flixel.graphics.frames.FlxAtlasFrames;
import flixel.util.FlxSort;
import haxe.io.Path;
import funkin.play.PlayState;
using StringTools;
@ -19,7 +22,7 @@ class Character extends FlxSprite
public var holdTimer:Float = 0;
public var animationNotes:Array<Dynamic> = [];
public var animationNotes:Array<NoteData> = [];
public function new(x:Float, y:Float, ?character:String = "bf", ?isPlayer:Bool = false)
{
@ -267,6 +270,8 @@ class Character extends FlxSprite
quickAnimAdd('singLEFTmiss', 'BF NOTE LEFT MISS');
quickAnimAdd('singRIGHTmiss', 'BF NOTE RIGHT MISS');
quickAnimAdd('singDOWNmiss', 'BF NOTE DOWN MISS');
quickAnimAdd('preAttack', 'bf pre attack');
quickAnimAdd('attack', 'boyfriend attack');
quickAnimAdd('hey', 'BF HEY');
quickAnimAdd('firstDeath', "BF dies");
@ -582,27 +587,25 @@ class Character extends FlxSprite
public function loadMappedAnims()
{
var swagshit = SongLoad.loadFromJson('picospeaker', 'stress');
var swagshit:SwagSong = SongLoad.loadFromJson('stress', 'stress');
var notes = swagshit.extraNotes.get('picospeaker');
var notes:Array<SwagSection> = swagshit.noteMap.get('picospeaker');
for (section in notes)
{
for (idk in section.sectionNotes)
for (noteData in section.sectionNotes)
{
animationNotes.push(idk);
animationNotes.push(noteData);
}
}
TankmenBG.animationNotes = animationNotes;
trace(animationNotes);
animationNotes.sort(sortAnims);
}
function sortAnims(val1:Array<Dynamic>, val2:Array<Dynamic>):Int
function sortAnims(val1:NoteData, val2:NoteData):Int
{
return FlxSort.byValues(FlxSort.ASCENDING, val1[0], val2[0]);
return FlxSort.byValues(FlxSort.ASCENDING, val1.strumTime, val2.strumTime);
}
function quickAnimAdd(name:String, prefix:String)
@ -657,13 +660,13 @@ class Character extends FlxSprite
// for pico??
if (animationNotes.length > 0)
{
if (Conductor.songPosition > animationNotes[0][0])
if (Conductor.songPosition > animationNotes[0].strumTime)
{
trace('played shoot anim' + animationNotes[0][1]);
trace('played shoot anim' + animationNotes[0].noteData);
var shootAnim:Int = 1;
if (animationNotes[0][1] >= 2)
if ((cast animationNotes[0].noteData) >= 2)
shootAnim = 3;
shootAnim += FlxG.random.int(0, 1);
@ -689,6 +692,8 @@ class Character extends FlxSprite
*/
public function dance()
{
if (animation == null)
return;
if (!debugMode)
{
switch (curCharacter)
@ -723,6 +728,8 @@ class Character extends FlxSprite
public function playAnim(AnimName:String, Force:Bool = false, Reversed:Bool = false, Frame:Int = 0):Void
{
if (animation == null)
return;
animation.play(AnimName, Force, Reversed, Frame);
var daOffset = animOffsets.get(AnimName);

View file

@ -1,4 +1,4 @@
package;
package funkin;
import flixel.FlxSprite;
import flixel.group.FlxGroup.FlxTypedGroup;

View file

@ -1,6 +1,6 @@
package;
package funkin;
import SongLoad.SwagSong;
import funkin.SongLoad.SwagSong;
/**
* ...

View file

@ -1,4 +1,4 @@
package;
package funkin;
import flixel.FlxObject;
import flixel.input.FlxInput;

View file

@ -1,4 +1,4 @@
package;
package funkin;
import flixel.FlxSprite;
import flixel.FlxState;
@ -15,7 +15,8 @@ import haxe.format.JsonParser;
import lime.math.Rectangle;
import lime.utils.Assets;
import openfl.filters.ShaderFilter;
import shaderslmfao.ScreenWipeShader;
import funkin.play.PlayState;
import funkin.shaderslmfao.ScreenWipeShader;
using StringTools;

View file

@ -1,4 +1,4 @@
package;
package funkin;
import flixel.FlxSprite;
import flixel.FlxState;

View file

@ -1,4 +1,4 @@
package;
package funkin;
import flixel.FlxSprite;
import flixel.group.FlxGroup.FlxTypedGroup;

View file

@ -1,4 +1,4 @@
package;
package funkin;
import flixel.FlxSprite;
import flixel.addons.text.FlxTypeText;
@ -8,6 +8,7 @@ import flixel.input.FlxKeyManager;
import flixel.text.FlxText;
import flixel.util.FlxColor;
import flixel.util.FlxTimer;
import funkin.play.PlayState;
using StringTools;

View file

@ -1,4 +1,4 @@
package;
package funkin;
import Sys.sleep;

View file

@ -1,4 +1,4 @@
package;
package funkin;
import flixel.FlxCamera;
import flixel.FlxSprite;

View file

@ -1,4 +1,4 @@
package;
package funkin;
import flixel.FlxBasic;
import flixel.FlxSprite;

View file

@ -1,6 +1,6 @@
package;
package funkin;
import Controls.Control;
import funkin.Controls.Control;
import flash.text.TextField;
import flixel.FlxCamera;
import flixel.FlxGame;
@ -21,15 +21,16 @@ import flixel.tweens.FlxTween;
import flixel.util.FlxColor;
import flixel.util.FlxSpriteUtil;
import flixel.util.FlxTimer;
import freeplayStuff.BGScrollingText;
import freeplayStuff.DJBoyfriend;
import freeplayStuff.FreeplayScore;
import freeplayStuff.SongMenuItem;
import funkin.freeplayStuff.BGScrollingText;
import funkin.freeplayStuff.DJBoyfriend;
import funkin.freeplayStuff.FreeplayScore;
import funkin.freeplayStuff.SongMenuItem;
import lime.app.Future;
import lime.utils.Assets;
import shaderslmfao.AngleMask;
import shaderslmfao.PureColor;
import shaderslmfao.StrokeShader;
import funkin.shaderslmfao.AngleMask;
import funkin.shaderslmfao.PureColor;
import funkin.shaderslmfao.StrokeShader;
import funkin.play.PlayState;
using StringTools;
@ -78,6 +79,7 @@ class FreeplayState extends MusicBeatSubstate
#if debug
isDebug = true;
addSong('Test', 1, 'bf-pixel');
addSong('Pyro', 4, 'bf');
#end
var initSonglist = CoolUtil.coolTextFile(Paths.txt('freeplaySonglist'));
@ -517,10 +519,7 @@ class FreeplayState extends MusicBeatSubstate
if (controls.BACK)
{
FlxG.sound.play(Paths.sound('cancelMenu'));
close();
// FlxG.switchState(new MainMenuState());
FlxG.switchState(new MainMenuState());
}
if (accepted)

View file

@ -1,4 +1,4 @@
package;
package funkin;
import flixel.FlxObject;
import flixel.FlxSubState;
@ -6,8 +6,9 @@ import flixel.math.FlxPoint;
import flixel.system.FlxSound;
import flixel.util.FlxColor;
import flixel.util.FlxTimer;
import haxe.display.Display.Package;
import ui.PreferencesMenu;
import haxe.display.Display;
import funkin.ui.PreferencesMenu;
import funkin.play.PlayState;
class GameOverSubstate extends MusicBeatSubstate
{
@ -24,7 +25,7 @@ class GameOverSubstate extends MusicBeatSubstate
gameOverMusic = new FlxSound();
FlxG.sound.list.add(gameOverMusic);
var daStage = PlayState.curStage;
var daStage = PlayState.curStageId;
var daBf:String = '';
switch (daStage)
{

View file

@ -1,7 +1,8 @@
package;
package funkin;
import flixel.FlxSprite;
import flixel.graphics.frames.FlxAtlasFrames;
import funkin.play.PlayState;
class GitarooPause extends MusicBeatState
{

View file

@ -1,7 +1,8 @@
package;
package funkin;
import flixel.FlxSprite;
import openfl.utils.Assets;
import funkin.play.PlayState;
using StringTools;

View file

@ -1,4 +1,4 @@
package;
package funkin;
class Highscore
{

View file

@ -1,7 +1,7 @@
package;
package funkin;
#if !(macro)
import charting.ChartingState;
import funkin.play.stage.StageData;
import funkin.charting.ChartingState;
import flixel.addons.transition.FlxTransitionSprite.GraphicTransTileDiamond;
import flixel.addons.transition.FlxTransitionableState;
import flixel.addons.transition.TransitionData;
@ -10,9 +10,11 @@ import flixel.math.FlxPoint;
import flixel.math.FlxRect;
import flixel.util.FlxColor;
import openfl.display.BitmapData;
import play.PicoFight;
import ui.PreferencesMenu;
import ui.stageBuildShit.StageBuilderState;
import funkin.play.PicoFight;
import funkin.ui.PreferencesMenu;
import funkin.ui.stageBuildShit.StageBuilderState;
import funkin.util.macro.MacroUtil;
import funkin.play.PlayState;
using StringTools;
@ -29,12 +31,17 @@ import sys.io.File;
import sys.thread.Thread;
#end
/**
* Initializes the game state using custom defines.
* Only used in Debug builds.
*/
class InitState extends FlxTransitionableState
{
override public function create():Void
{
trace('This is a debug build, loading InitState...');
#if android
FlxG.android.preventDefaultKeys = [FlxAndroidKey.BACK];
FlxG.android.preventDefaultKeys = [flixel.input.android.FlxAndroidKey.BACK];
#end
#if newgrounds
NGio.init();
@ -113,6 +120,8 @@ class InitState extends FlxTransitionableState
// FlxTransitionableState.skipNextTransOut = true;
FlxTransitionableState.skipNextTransIn = true;
StageDataParser.loadStageCache();
#if song
var song = getSong();
@ -191,21 +200,12 @@ class InitState extends FlxTransitionableState
LoadingState.loadAndSwitchState(new PlayState());
}
}
#end
function getWeek()
return Std.parseInt(getDefine("week"));
return Std.parseInt(MacroUtil.getDefine("week"));
function getSong()
return getDefine("song");
return MacroUtil.getDefine("song");
function getDif()
return Std.parseInt(getDefine("dif", "1"));
macro function getDefine(key:String, defaultValue:String = null):haxe.macro.Expr
{
var value = haxe.macro.Context.definedValue(key);
if (value == null)
value = defaultValue;
return macro $v{value};
}
return Std.parseInt(MacroUtil.getDefine("dif", "1"));

View file

@ -1,6 +1,6 @@
package;
package funkin;
import Controls;
import funkin.Controls;
import flixel.input.gamepad.FlxGamepad;
import flixel.input.gamepad.FlxGamepadInputID;
import flixel.input.keyboard.FlxKey;

View file

@ -1,4 +1,4 @@
package;
package funkin;
import flixel.FlxSprite;
import flixel.FlxState;

View file

@ -1,4 +1,4 @@
package;
package funkin;
import flixel.FlxSprite;
import flixel.FlxState;
@ -12,6 +12,7 @@ import lime.utils.AssetLibrary;
import lime.utils.AssetManifest;
import lime.utils.Assets as LimeAssets;
import openfl.utils.Assets;
import funkin.play.PlayState;
class LoadingState extends MusicBeatState
{
@ -186,8 +187,15 @@ class LoadingState extends MusicBeatState
}
static function getNextState(target:FlxState, stopMusic = false):FlxState
{
if (PlayState.storyWeek == 0)
{
Paths.setCurrentLevel('tutorial');
}
else
{
Paths.setCurrentLevel("week" + PlayState.storyWeek);
}
#if NO_PRELOAD_ALL
var loaded = isSoundLoaded(getSongPath())
&& (!PlayState.SONG.needsVoices || isSoundLoaded(getVocalPath()))

View file

@ -1,6 +1,6 @@
package;
package funkin;
import NGio;
import funkin.NGio;
import flixel.FlxObject;
import flixel.FlxSprite;
import flixel.FlxState;
@ -17,12 +17,12 @@ import flixel.util.FlxColor;
import flixel.util.FlxTimer;
import lime.app.Application;
import openfl.filters.ShaderFilter;
import shaderslmfao.ScreenWipeShader;
import ui.AtlasMenuList;
import ui.MenuList;
import ui.OptionsState;
import ui.PreferencesMenu;
import ui.Prompt;
import funkin.shaderslmfao.ScreenWipeShader;
import funkin.ui.AtlasMenuList;
import funkin.ui.MenuList;
import funkin.ui.OptionsState;
import funkin.ui.PreferencesMenu;
import funkin.ui.Prompt;
using StringTools;
@ -31,7 +31,7 @@ import Discord.DiscordClient;
#end
#if newgrounds
import io.newgrounds.NG;
import ui.NgPrompt;
import funkin.ui.NgPrompt;
#end
class MainMenuState extends MusicBeatState

View file

@ -1,3 +1,5 @@
package funkin;
import openfl.text.TextFormat;
import openfl.system.System;
import openfl.text.TextField;

View file

@ -1,4 +1,4 @@
package;
package funkin;
import flixel.FlxSprite;
import flixel.graphics.frames.FlxAtlasFrames;

View file

@ -1,4 +1,4 @@
package;
package funkin;
import flixel.FlxSprite;
import flixel.graphics.frames.FlxAtlasFrames;

View file

@ -1,6 +1,6 @@
package;
package funkin;
import Conductor.BPMChangeEvent;
import funkin.Conductor.BPMChangeEvent;
import flixel.FlxGame;
import flixel.addons.transition.FlxTransitionableState;
import flixel.addons.ui.FlxUIState;

View file

@ -1,6 +1,6 @@
package;
package funkin;
import Conductor.BPMChangeEvent;
import funkin.Conductor.BPMChangeEvent;
import flixel.FlxSubState;
class MusicBeatSubstate extends FlxSubState

View file

@ -1,4 +1,4 @@
package;
package funkin;
#if newgrounds
import flixel.util.FlxSignal;

View file

@ -1,19 +1,13 @@
package;
package funkin;
import flixel.FlxSprite;
import flixel.graphics.frames.FlxAtlasFrames;
import flixel.math.FlxMath;
import flixel.util.FlxColor;
import flixel.util.FlxTimer;
import shaderslmfao.ColorSwap;
import ui.PreferencesMenu;
import funkin.shaderslmfao.ColorSwap;
import funkin.ui.PreferencesMenu;
import funkin.play.PlayState;
using StringTools;
#if polymod
import polymod.format.ParseRules.TargetSignatureElement;
#end
class Note extends FlxSprite
{
public var data = new NoteData();
@ -46,25 +40,37 @@ class Note extends FlxSprite
/** the lowercase name of the note, for anim control, i.e. left right up down */
public var dirName(get, never):String;
inline function get_dirName() return data.dirName;
inline function get_dirName()
return data.dirName;
/** the uppercase name of the note, for anim control, i.e. left right up down */
public var dirNameUpper(get, never):String;
inline function get_dirNameUpper() return data.dirNameUpper;
inline function get_dirNameUpper()
return data.dirNameUpper;
/** the lowercase name of the note's color, for anim control, i.e. purple blue green red */
public var colorName(get, never):String;
inline function get_colorName() return data.colorName;
inline function get_colorName()
return data.colorName;
/** the lowercase name of the note's color, for anim control, i.e. purple blue green red */
public var colorNameUpper(get, never):String;
inline function get_colorNameUpper() return data.colorNameUpper;
inline function get_colorNameUpper()
return data.colorNameUpper;
public var highStakes(get, never):Bool;
inline function get_highStakes() return data.highStakes;
inline function get_highStakes()
return data.highStakes;
public var lowStakes(get, never):Bool;
inline function get_lowStakes() return data.lowStakes;
inline function get_lowStakes()
return data.lowStakes;
public static var swagWidth:Float = 160 * 0.7;
public static var PURP_NOTE:Int = 0;
@ -103,8 +109,9 @@ class Note extends FlxSprite
data.noteData = noteData;
var daStage:String = PlayState.curStage;
var daStage:String = PlayState.curStageId;
// TODO: Make this logic more generic
switch (daStage)
{
case 'school' | 'schoolEvil':
@ -187,7 +194,7 @@ class Note extends FlxSprite
x -= width / 2;
if (PlayState.curStage.startsWith('school'))
if (PlayState.curStageId.startsWith('school'))
x += 30;
if (prevNote.isSustainNote)
@ -276,68 +283,97 @@ typedef RawNoteData =
var noteData:NoteType;
var sustainLength:Float;
var altNote:Bool;
var noteKind:NoteKind;
}
@:forward
abstract NoteData(RawNoteData)
{
public function new (strumTime = 0.0, noteData:NoteType = 0, sustainLength = 0.0, altNote = false)
public function new(strumTime = 0.0, noteData:NoteType = 0, sustainLength = 0.0, altNote = false, noteKind = NORMAL)
{
this =
{ strumTime : strumTime
, noteData : noteData
, sustainLength : sustainLength
, altNote : altNote
this = {
strumTime: strumTime,
noteData: noteData,
sustainLength: sustainLength,
altNote: altNote,
noteKind: noteKind
}
}
public var note(get, never):NoteType;
inline function get_note() return this.noteData.value;
inline function get_note()
return this.noteData.value;
public var int(get, never):Int;
inline function get_int() return this.noteData.int;
inline function get_int()
return this.noteData.int;
public var dir(get, never):NoteDir;
inline function get_dir() return this.noteData.value;
inline function get_dir()
return this.noteData.value;
public var dirName(get, never):String;
inline function get_dirName() return dir.name;
inline function get_dirName()
return dir.name;
public var dirNameUpper(get, never):String;
inline function get_dirNameUpper() return dir.nameUpper;
inline function get_dirNameUpper()
return dir.nameUpper;
public var color(get, never):NoteColor;
inline function get_color() return this.noteData.value;
inline function get_color()
return this.noteData.value;
public var colorName(get, never):String;
inline function get_colorName() return color.name;
inline function get_colorName()
return color.name;
public var colorNameUpper(get, never):String;
inline function get_colorNameUpper() return color.nameUpper;
inline function get_colorNameUpper()
return color.nameUpper;
public var highStakes(get, never):Bool;
inline function get_highStakes() return this.noteData.highStakes;
inline function get_highStakes()
return this.noteData.highStakes;
public var lowStakes(get, never):Bool;
inline function get_lowStakes() return this.noteData.lowStakes;
inline function get_lowStakes()
return this.noteData.lowStakes;
}
enum abstract NoteType(Int) from Int to Int
{
// public var raw(get, never):Int;
// inline function get_raw() return this;
public var int(get, never):Int;
inline function get_int() return this < 0 ? -this : this % 4;
inline function get_int()
return this < 0 ? -this : this % 4;
public var value(get, never):NoteType;
inline function get_value() return int;
inline function get_value()
return int;
public var highStakes(get, never):Bool;
inline function get_highStakes() return this > 3;
inline function get_highStakes()
return this > 3;
public var lowStakes(get, never):Bool;
inline function get_lowStakes() return this < 0;
inline function get_lowStakes()
return this < 0;
}
@:forward
@ -347,11 +383,13 @@ enum abstract NoteDir(NoteType) from Int to Int from NoteType
var DOWN = 1;
var UP = 2;
var RIGHT = 3;
var value(get, never):NoteDir;
inline function get_value() return this.value;
inline function get_value()
return this.value;
public var name(get, never):String;
function get_name()
{
return switch (value)
@ -364,6 +402,7 @@ enum abstract NoteDir(NoteType) from Int to Int from NoteType
}
public var nameUpper(get, never):String;
function get_nameUpper()
{
return switch (value)
@ -383,11 +422,13 @@ enum abstract NoteColor(NoteType) from Int to Int from NoteType
var BLUE = 1;
var GREEN = 2;
var RED = 3;
var value(get, never):NoteColor;
inline function get_value() return this.value;
inline function get_value()
return this.value;
public var name(get, never):String;
function get_name()
{
return switch (value)
@ -400,6 +441,7 @@ enum abstract NoteColor(NoteType) from Int to Int from NoteType
}
public var nameUpper(get, never):String;
function get_nameUpper()
{
return switch (value)
@ -411,3 +453,13 @@ enum abstract NoteColor(NoteType) from Int to Int from NoteType
}
}
}
enum abstract NoteKind(String) from String to String
{
var NORMAL = "normal";
var PYRO_LIGHT = "pyro_light";
var PYRO_KICK = "pyro_kick";
var PYRO_TOSS = "pyro_toss";
var PYRO_COCK = "pyro_cock"; // lol
var PYRO_SHOOT = "pyro_shoot";
}

View file

@ -1,4 +1,4 @@
package;
package funkin;
import flixel.FlxSprite;
import haxe.io.Path;
@ -34,7 +34,8 @@ class NoteSplash extends FlxSprite
animation.play('note' + noteData + '-' + FlxG.random.int(0, 1), true);
animation.curAnim.frameRate = 24 + FlxG.random.int(-2, 2);
animation.finishCallback = function(name){
animation.finishCallback = function(name)
{
kill();
};
updateHitbox();

View file

@ -1,4 +1,4 @@
package;
package funkin;
class Options
{

View file

@ -1,4 +1,4 @@
package;
package funkin;
import flixel.FlxSprite;
import flixel.FlxSubState;
@ -6,6 +6,7 @@ import flixel.text.FlxText;
import flixel.util.FlxColor;
import lime.app.Application;
#if newgrounds
class OutdatedSubState extends MusicBeatState
{
public static var leftState:Bool = false;
@ -42,3 +43,4 @@ class OutdatedSubState extends MusicBeatState
super.update(elapsed);
}
}
#end

View file

@ -1,4 +1,4 @@
package;
package funkin;
import flixel.graphics.frames.FlxAtlasFrames;
import openfl.utils.AssetType;

View file

@ -1,6 +1,6 @@
package;
package funkin;
import Controls.Control;
import funkin.Controls.Control;
import flixel.FlxSprite;
import flixel.FlxSubState;
import flixel.addons.transition.FlxTransitionableState;
@ -11,6 +11,7 @@ import flixel.text.FlxText;
import flixel.tweens.FlxEase;
import flixel.tweens.FlxTween;
import flixel.util.FlxColor;
import funkin.play.PlayState;
class PauseSubState extends MusicBeatSubstate
{

View file

@ -1,6 +1,6 @@
package;
package funkin;
import Controls;
import funkin.Controls;
import flixel.FlxCamera;
import flixel.input.actions.FlxActionInput;
import flixel.input.gamepad.FlxGamepad;

View file

@ -1,6 +1,6 @@
package;
package funkin;
import Note.NoteData;
import funkin.Note.NoteData;
typedef SwagSection =
{

View file

@ -1,7 +1,7 @@
package;
package funkin;
import Note.NoteData;
import Section.SwagSection;
import funkin.Note.NoteData;
import funkin.Section.SwagSection;
import haxe.Json;
import haxe.format.JsonParser;
import lime.utils.Assets;
@ -48,7 +48,7 @@ class SongLoad
public static function loadFromJson(jsonInput:String, ?folder:String):SwagSong
{
var rawJson = Assets.getText(Paths.json(folder.toLowerCase() + '/' + jsonInput.toLowerCase())).trim();
var rawJson = Assets.getText(Paths.json('songs/${folder.toLowerCase()}/${jsonInput.toLowerCase()}')).trim();
while (!rawJson.endsWith("}"))
{
@ -174,6 +174,8 @@ class SongLoad
for (sectionIndex => section in noteStuff)
{
if (section == null || section.sectionNotes == null)
continue;
for (noteIndex => noteDataArray in section.sectionNotes)
{
var arrayDipshit:Array<Dynamic> = cast noteDataArray; // crackhead
@ -188,6 +190,10 @@ class SongLoad
noteStuff[sectionIndex].sectionNotes[noteIndex].noteData = arrayDipshit[1];
noteStuff[sectionIndex].sectionNotes[noteIndex].sustainLength = arrayDipshit[2];
noteStuff[sectionIndex].sectionNotes[noteIndex].altNote = arrayDipshit[3];
if (arrayDipshit.length >= 5)
{
noteStuff[sectionIndex].sectionNotes[noteIndex].noteKind = arrayDipshit[4];
}
}
else if (noteDataArray != null)
{

View file

@ -1,4 +1,4 @@
package;
package funkin;
#if discord_rpc
import Discord.DiscordClient;
@ -14,6 +14,7 @@ import flixel.tweens.FlxTween;
import flixel.util.FlxColor;
import flixel.util.FlxTimer;
import lime.net.curl.CURLCode;
import funkin.play.PlayState;
using StringTools;
@ -21,7 +22,7 @@ class StoryMenuState extends MusicBeatState
{
var scoreText:FlxText;
var weekData:Array<Dynamic> = [
var weekData:Array<Array<String>> = [
['Tutorial'],
['Bopeebo', 'Fresh', 'Dadbattle'],
['Spookeez', 'South', "Monster"],
@ -430,11 +431,10 @@ class StoryMenuState extends MusicBeatState
// grpWeekCharacters.members[0].updateHitbox();
}
var stringThing:Array<String> = weekData[curWeek];
for (i in stringThing)
var trackNames:Array<String> = weekData[curWeek];
for (i in trackNames)
{
txtTracklist.text += "\n" + i;
txtTracklist.text += '\n${i}';
}
txtTracklist.text = txtTracklist.text.toUpperCase();

View file

@ -1,4 +1,4 @@
package;
package funkin;
import flixel.FlxCamera;
import flixel.FlxSprite;

View file

@ -1,4 +1,4 @@
package;
package funkin;
import flixel.FlxSprite;
import flixel.system.FlxSound;

View file

@ -1,6 +1,6 @@
package;
package funkin;
import audiovis.SpectogramSprite;
import funkin.audiovis.SpectogramSprite;
import flixel.FlxObject;
import flixel.FlxSprite;
import flixel.FlxState;
@ -29,10 +29,10 @@ import openfl.events.NetStatusEvent;
import openfl.media.Video;
import openfl.net.NetConnection;
import openfl.net.NetStream;
import shaderslmfao.BuildingShaders;
import shaderslmfao.ColorSwap;
import shaderslmfao.TitleOutline;
import ui.PreferencesMenu;
import funkin.shaderslmfao.BuildingShaders;
import funkin.shaderslmfao.ColorSwap;
import funkin.shaderslmfao.TitleOutline;
import funkin.ui.PreferencesMenu;
using StringTools;
@ -351,17 +351,6 @@ class TitleState extends MusicBeatState
FlxG.switchState(new CutsceneAnimTestState());
#end
/*
if (FlxG.keys.justPressed.R)
{
#if polymod
polymod.Polymod.init({modRoot: "mods", dirs: ['introMod']});
trace('reinitialized');
#end
}
*/
if (FlxG.sound.music != null)
Conductor.songPosition = FlxG.sound.music.time;
if (FlxG.keys.justPressed.F)

View file

@ -1,4 +1,4 @@
package;
package funkin;
import openfl.display.Sprite;
import openfl.events.AsyncErrorEvent;

View file

@ -1,3 +1,5 @@
package funkin;
import flixel.group.FlxGroup.FlxTypedGroup;
import flixel.system.FlxSound;

View file

@ -1,4 +1,4 @@
package animate;
package funkin.animate;
import flixel.FlxSprite;
import flixel.FlxState;

View file

@ -1,7 +1,5 @@
package animate;
package funkin.animate;
// import animate.FlxSymbol.Parsed;
// import animate.FlxSymbol.Timeline;
import flixel.FlxCamera;
import flixel.FlxSprite;
import flixel.group.FlxGroup.FlxTypedGroup;

View file

@ -1,10 +1,8 @@
package animate;
package funkin.animate;
// import animateAtlasPlayer.assets.AssetManager;
// import animateAtlasPlayer.core.Animation;
import animate.ParseAnimate.AnimJson;
import animate.ParseAnimate.Sprite;
import animate.ParseAnimate.Spritemap;
import funkin.animate.ParseAnimate.AnimJson;
import funkin.animate.ParseAnimate.Sprite;
import funkin.animate.ParseAnimate.Spritemap;
import flixel.FlxCamera;
import flixel.FlxSprite;
import flixel.graphics.FlxGraphic;

View file

@ -1,12 +1,12 @@
package animate;
package funkin.animate;
import animate.ParseAnimate.AnimJson;
import animate.ParseAnimate.Animation;
import animate.ParseAnimate.Frame;
import animate.ParseAnimate.Sprite;
import animate.ParseAnimate.Spritemap;
import animate.ParseAnimate.SymbolDictionary;
import animate.ParseAnimate.Timeline;
import funkin.animate.ParseAnimate.AnimJson;
import funkin.animate.ParseAnimate.Animation;
import funkin.animate.ParseAnimate.Frame;
import funkin.animate.ParseAnimate.Sprite;
import funkin.animate.ParseAnimate.Spritemap;
import funkin.animate.ParseAnimate.SymbolDictionary;
import funkin.animate.ParseAnimate.Timeline;
import flixel.FlxCamera;
import flixel.FlxSprite;
import flixel.graphics.frames.FlxFrame.FlxFrameAngle;

View file

@ -1,10 +1,9 @@
package animate;
package funkin.animate;
import haxe.format.JsonParser;
import openfl.Assets;
import openfl.geom.Matrix3D;
import openfl.geom.Matrix;
import sys.io.File;
/**
* Generally designed / written in a way that can be easily taken out of FNF and used elsewhere

View file

@ -1,6 +1,6 @@
package animate;
package funkin.animate;
import animate.FlxSymbol.Frame;
import funkin.animate.ParseAnimate.Frame;
import flixel.FlxSprite;
import flixel.input.mouse.FlxMouseEventManager;
import flixel.util.FlxColor;

View file

@ -1,13 +1,13 @@
package audiovis;
package funkin.audiovis;
import audiovis.dsp.FFT;
import funkin.audiovis.dsp.FFT;
import flixel.FlxSprite;
import flixel.addons.plugin.taskManager.FlxTask;
import flixel.graphics.frames.FlxAtlasFrames;
import flixel.group.FlxSpriteGroup.FlxTypedSpriteGroup;
import flixel.math.FlxMath;
import flixel.system.FlxSound;
import ui.PreferencesMenu.CheckboxThingie;
import funkin.ui.PreferencesMenu.CheckboxThingie;
using Lambda;

View file

@ -1,12 +1,12 @@
package audiovis;
package funkin.audiovis;
import audiovis.VisShit.CurAudioInfo;
import funkin.audiovis.VisShit.CurAudioInfo;
import flixel.math.FlxMath;
import flixel.math.FlxPoint;
import flixel.system.FlxSound;
import flixel.util.FlxColor;
import lime.utils.Int16Array;
import rendering.MeshRender;
import funkin.rendering.MeshRender;
class PolygonSpectogram extends MeshRender
{

View file

@ -1,8 +1,8 @@
package audiovis;
package funkin.audiovis;
import audiovis.PolygonSpectogram.VISTYPE;
import audiovis.VisShit.CurAudioInfo;
import audiovis.dsp.FFT;
import funkin.audiovis.PolygonSpectogram.VISTYPE;
import funkin.audiovis.VisShit.CurAudioInfo;
import funkin.audiovis.dsp.FFT;
import flixel.FlxSprite;
import flixel.group.FlxGroup;
import flixel.group.FlxSpriteGroup.FlxTypedSpriteGroup;

View file

@ -1,6 +1,6 @@
package audiovis;
package funkin.audiovis;
import audiovis.dsp.FFT;
import funkin.audiovis.dsp.FFT;
import flixel.math.FlxMath;
import flixel.system.FlxSound;
import lime.utils.Int16Array;

View file

@ -1,4 +1,4 @@
package audiovis.dsp;
package funkin.audiovis.dsp;
/**
Complex number representation.

View file

@ -1,9 +1,9 @@
package audiovis.dsp;
package funkin.audiovis.dsp;
import audiovis.dsp.Complex;
import funkin.audiovis.dsp.Complex;
using audiovis.dsp.OffsetArray;
using audiovis.dsp.Signal;
using funkin.audiovis.dsp.OffsetArray;
using funkin.audiovis.dsp.Signal;
// these are only used for testing, down in FFT.main()

View file

@ -1,4 +1,4 @@
package audiovis.dsp;
package funkin.audiovis.dsp;
/**
A view into an Array with an indexing offset.

View file

@ -1,4 +1,4 @@
package audiovis.dsp;
package funkin.audiovis.dsp;
using Lambda;

View file

@ -1,12 +1,12 @@
package charting;
package funkin.charting;
import Conductor.BPMChangeEvent;
import Note.NoteData;
import Section.SwagSection;
import SongLoad.SwagSong;
import audiovis.ABotVis;
import audiovis.PolygonSpectogram;
import audiovis.SpectogramSprite;
import funkin.Conductor.BPMChangeEvent;
import funkin.Note.NoteData;
import funkin.Section.SwagSection;
import funkin.SongLoad.SwagSong;
import funkin.audiovis.ABotVis;
import funkin.audiovis.PolygonSpectogram;
import funkin.audiovis.SpectogramSprite;
import flixel.FlxSprite;
import flixel.addons.display.FlxGridOverlay;
import flixel.addons.transition.FlxTransitionableState;
@ -29,7 +29,8 @@ import lime.utils.Assets;
import openfl.events.Event;
import openfl.events.IOErrorEvent;
import openfl.net.FileReference;
import rendering.MeshRender;
import funkin.rendering.MeshRender;
import funkin.play.PlayState;
using Lambda;
using StringTools;

View file

@ -1,4 +1,4 @@
package freeplayStuff;
package funkin.freeplayStuff;
import flixel.FlxObject;
import flixel.group.FlxGroup.FlxTypedGroup;

View file

@ -1,4 +1,4 @@
package freeplayStuff;
package funkin.freeplayStuff;
import flixel.FlxSprite;
import flixel.util.FlxSignal;

View file

@ -1,4 +1,4 @@
package freeplayStuff;
package funkin.freeplayStuff;
import flixel.FlxSprite;
import flixel.group.FlxSpriteGroup.FlxTypedSpriteGroup;
@ -11,6 +11,8 @@ class FreeplayScore extends FlxTypedSpriteGroup<ScoreNum>
function set_scoreShit(val):Int
{
if (group == null || group.members == null)
return val;
var loopNum:Int = group.members.length - 1;
var dumbNumb = Std.parseInt(Std.string(val));
var prevNum:ScoreNum;

View file

@ -1,4 +1,4 @@
package freeplayStuff;
package funkin.freeplayStuff;
import flixel.FlxSprite;
import flixel.graphics.frames.FlxAtlasFrames;

View file

@ -1,4 +1,4 @@
package i18n;
package funkin.i18n;
import firetongue.FireTongue;

View file

@ -0,0 +1,3 @@
# i18n
This package contains functions used for internationalization (i18n).

View file

@ -1,6 +1,6 @@
#if !macro
// Only import these when we aren't in a macro.
import Paths;
import funkin.Paths;
import flixel.FlxG; // This one in particular causes a compile error if you're using macros.
#end

View file

@ -1,4 +1,4 @@
package modding;
package funkin.modding;
import polymod.hscript.HScriptable;

View file

@ -0,0 +1,71 @@
package funkin.modding;
import polymod.Polymod;
class PolymodErrorHandler
{
/**
* Show a popup with the given text.
* This displays a system popup, it WILL interrupt the game.
* Make sure to only use this when it's important, like when there's a script error.
*
* @param name The name at the top of the popup.
* @param desc The body text of the popup.
*/
public static function showAlert(name:String, desc:String):Void
{
lime.app.Application.current.window.alert(desc, name);
}
public static function onPolymodError(error:PolymodError):Void
{
// Perform an action based on the error code.
switch (error.code)
{
case MOD_LOAD_PREPARE:
logInfo('[POLYMOD]: ${error.message}');
case MOD_LOAD_DONE:
logInfo('[POLYMOD]: ${error.message}');
case MISSING_ICON:
logWarn('[POLYMOD]: A mod is missing an icon. Please add one.');
case SCRIPT_PARSE_ERROR:
// A syntax error when parsing a script.
logError('[POLYMOD]: ${error.message}');
showAlert('Polymod Script Parsing Error', error.message);
case SCRIPT_EXCEPTION:
// A runtime error when running a script.
logError('[POLYMOD]: ${error.message}');
showAlert('Polymod Script Execution Error', error.message);
case SCRIPT_CLASS_NOT_FOUND:
// A scripted class tried to reference an unknown superclass.
logError('[POLYMOD]: ${error.message}');
showAlert('Polymod Script Parsing Error', error.message);
default:
// Log the message based on its severity.
switch (error.severity)
{
case NOTICE:
logInfo('[POLYMOD]: ${error.message}');
case WARNING:
logWarn('[POLYMOD]: ${error.message}');
case ERROR:
logError('[POLYMOD]: ${error.message}');
}
}
}
static function logInfo(message:String):Void
{
trace('[INFO ] ${message}');
}
static function logError(message:String):Void
{
trace('[ERROR] ${message}');
}
static function logWarn(message:String):Void
{
trace('[WARN ] ${message}');
}
}

View file

@ -1,13 +1,11 @@
package modding;
package funkin.modding;
#if polymod
import polymod.Polymod.ModMetadata;
import polymod.Polymod;
import polymod.backends.OpenFLBackend;
import polymod.backends.PolymodAssets.PolymodAssetType;
import polymod.format.ParseRules.LinesParseFormat;
import polymod.format.ParseRules.TextFileFormat;
#end
class PolymodHandler
{
@ -29,12 +27,8 @@ class PolymodHandler
*/
public static function loadAllMods()
{
#if polymod
trace("Initializing Polymod (using all mods)...");
loadModsById(getAllModIds());
#else
trace("Polymod not initialized; not supported on this platform.");
#end
}
/**
@ -43,17 +37,12 @@ class PolymodHandler
public static function loadNoMods()
{
// We still need to configure the debug print calls etc.
#if polymod
trace("Initializing Polymod (using no mods)...");
loadModsById([]);
#else
trace("Polymod not initialized; not supported on this platform.");
#end
}
public static function loadModsById(ids:Array<String>)
{
#if polymod
if (ids.length == 0)
{
trace('You attempted to load zero mods.');
@ -72,7 +61,7 @@ class PolymodHandler
// The current version of our API.
apiVersion: API_VERSION,
// Call this function any time an error occurs.
errorCallback: onPolymodError,
errorCallback: PolymodErrorHandler.onPolymodError,
// Enforce semantic version patterns for each mod.
// modVersions: null,
// A map telling Polymod what the asset type is for unfamiliar file extensions.
@ -85,6 +74,9 @@ class PolymodHandler
// Parsing rules for various data formats.
parseRules: buildParseRules(),
// Parse hxc files and register the scripted classes in them.
useScriptedClasses: true,
});
if (loadedModList == null)
@ -104,35 +96,38 @@ class PolymodHandler
}
for (mod in loadedModList)
{
trace(' * ${mod.title} v${mod.modVersion} [${mod.id}]');
}
#if debug
var fileList = Polymod.listModFiles("IMAGE");
var fileList = Polymod.listModFiles(PolymodAssetType.IMAGE);
trace('[POLYMOD] Installed mods have replaced ${fileList.length} images.');
for (item in fileList)
trace(' * $item');
fileList = Polymod.listModFiles("TEXT");
fileList = Polymod.listModFiles(PolymodAssetType.TEXT);
trace('[POLYMOD] Installed mods have replaced ${fileList.length} text files.');
for (item in fileList)
trace(' * $item');
fileList = Polymod.listModFiles("MUSIC");
fileList = Polymod.listModFiles(PolymodAssetType.AUDIO_MUSIC);
trace('[POLYMOD] Installed mods have replaced ${fileList.length} music files.');
for (item in fileList)
trace(' * $item');
fileList = Polymod.listModFiles("SOUND");
fileList = Polymod.listModFiles(PolymodAssetType.AUDIO_SOUND);
trace('[POLYMOD] Installed mods have replaced ${fileList.length} sound files.');
for (item in fileList)
trace(' * $item');
#end
#else
trace("[POLYMOD] Mods are not supported on this platform.");
fileList = Polymod.listModFiles(PolymodAssetType.AUDIO_GENERIC);
trace('[POLYMOD] Installed mods have replaced ${fileList.length} generic audio files.');
for (item in fileList)
trace(' * $item');
#end
}
#if polymod
static function buildParseRules():polymod.format.ParseRules
{
var output = polymod.format.ParseRules.getDefault();
@ -140,6 +135,7 @@ class PolymodHandler
output.addType("txt", TextFileFormat.LINES);
// Ensure script files have merge support.
output.addType("hscript", TextFileFormat.PLAINTEXT);
output.addType("hxs", TextFileFormat.PLAINTEXT);
// You can specify the format of a specific file, with file extension.
// output.addFile("data/introText.txt", TextFileFormat.LINES)
@ -150,48 +146,18 @@ class PolymodHandler
{
return {
assetLibraryPaths: [
"songs" => "./songs", "shared" => "./", "tutorial" => "./tutorial", "scripts" => "./scripts", "week1" => "./week1", "week2" => "./week2",
"week3" => "./week3", "week4" => "./week4", "week5" => "./week5", "week6" => "./week6", "week7" => "./week7", "week8" => "./week8",
"songs" => "songs", "shared" => "", "tutorial" => "tutorial", "scripts" => "scripts", "week1" => "week1", "week2" => "week2",
"week3" => "week3", "week4" => "week4", "week5" => "week5", "week6" => "week6", "week7" => "week7", "week8" => "week8",
]
}
}
static function onPolymodError(error:PolymodError):Void
public static function getAllMods():Array<ModMetadata>
{
// Perform an action based on the error code.
switch (error.code)
{
case MOD_LOAD_PREPARE:
trace('[POLYMOD] ${error.message}');
case MOD_LOAD_DONE:
trace('[POLYMOD] ${error.message}');
case MISSING_ICON:
trace('[POLYMOD] A mod is missing an icon. Please add one.');
default:
// Log the message based on its severity.
switch (error.severity)
{
case NOTICE:
trace('[POLYMOD] ${error.message}');
case WARNING:
trace('[POLYMOD] ${error.message}');
case ERROR:
trace('[POLYMOD] ${error.message}');
}
}
}
#end
public static function getAllMods()
{
#if polymod
trace('Scanning the mods folder...');
var modMetadata = Polymod.scan(MOD_FOLDER);
trace('Found ${modMetadata.length} mods when scanning.');
return modMetadata;
#else
return new Array<Dynamic>();
#end
}
public static function getAllModIds():Array<String>

View file

@ -0,0 +1,5 @@
# modding.base
This package is used to allow modders to create scripted classes which extend these base classes.
For example, one script can extend FlxSprite and another can call `ScriptedFlxSprite.init('ClassName')`.
Most of these scripted class stubs are not used by the game itself, so this package has been explicitly marked to be ignored by DCE.

View file

@ -0,0 +1,10 @@
package funkin.modding.base;
import flixel.FlxSprite;
import funkin.modding.IHook;
@:hscriptClass
class ScriptedFlxSprite extends FlxSprite implements IHook
{
// No body needed for this class, it's magic ;)
}

View file

@ -0,0 +1,10 @@
package funkin.modding.base;
import flixel.group.FlxSpriteGroup;
import funkin.modding.IHook;
@:hscriptClass
class ScriptedFlxSpriteGroup extends FlxSpriteGroup implements IHook
{
// No body needed for this class, it's magic ;)
}

View file

@ -1,4 +1,4 @@
package play;
package funkin.play;
import flixel.FlxSprite;

View file

@ -1,7 +1,7 @@
package play;
package funkin.play;
import Note.NoteData;
import audiovis.PolygonSpectogram;
import funkin.Note.NoteData;
import funkin.audiovis.PolygonSpectogram;
import flixel.FlxObject;
import flixel.FlxSprite;
import flixel.addons.effects.FlxTrail;

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,9 @@
package funkin.play.character;
enum CharacterType
{
BF;
GF;
DAD;
OTHER;
}

View file

@ -0,0 +1,119 @@
package funkin.play.stage;
import flixel.FlxSprite;
/**
* A Bopper is a stage prop which plays a dance animation.
* Y'know, a thingie that bops. A bopper.
*/
class Bopper extends FlxSprite
{
/**
* The bopper plays the dance animation once every `danceEvery` beats.
*/
public var danceEvery:Int = 1;
/**
* Whether the bopper should dance left and right.
* - If true, alternate playing `danceLeft` and `danceRight`.
* - If false, play `idle` every time.
*
* You can manually set this value, or you can leave it as `null` to determine it automatically.
*/
public var shouldAlternate:Null<Bool> = null;
/**
* Set this value to define an additional horizontal offset to this sprite's position.
*/
public var xOffset:Float = 0;
override function set_x(value:Float):Float
{
this.x = value + this.xOffset;
return value;
}
public var idleSuffix(default, set):String = "";
function set_idleSuffix(value:String):String
{
this.idleSuffix = value;
this.dance();
return value;
}
/**
* Set this value to define an additional vertical offset to this sprite's position.
*/
public var yOffset:Float = 0;
override function set_y(value:Float):Float
{
this.y = value + this.yOffset;
return value;
}
/**
* Whether to play `danceRight` next iteration.
* Only used when `shouldAlternate` is true.
*/
var hasDanced:Bool = false;
public function new(danceEvery:Int = 1)
{
super();
this.danceEvery = danceEvery;
}
function update_shouldAlternate():Void
{
if (this.animation.getByName('danceLeft') != null)
{
this.shouldAlternate = true;
}
}
/**
* Called once every beat of the song.
*/
public function onBeatHit(curBeat:Int):Void
{
if (curBeat % danceEvery == 0)
{
dance();
}
}
/**
* Called every `danceEvery` beats of the song.
*/
public function dance():Void
{
if (this.animation == null)
{
return;
}
if (shouldAlternate == null)
{
update_shouldAlternate();
}
if (shouldAlternate)
{
if (hasDanced)
{
this.animation.play('danceRight$idleSuffix');
}
else
{
this.animation.play('danceLeft$idleSuffix');
}
hasDanced = !hasDanced;
}
else
{
this.animation.play('idle$idleSuffix');
}
}
}

View file

@ -0,0 +1,10 @@
package funkin.play.stage;
import funkin.modding.IHook;
@:hscriptClass
@:keep
class ScriptedBopper extends Bopper implements IHook
{
// No body needed for this class, it's magic ;)
}

View file

@ -0,0 +1,9 @@
package funkin.play.stage;
import funkin.modding.IHook;
@:hscriptClass
class ScriptedStage extends Stage implements IHook
{
// No body needed for this class, it's magic ;)
}

View file

@ -0,0 +1,402 @@
package funkin.play.stage;
import flixel.FlxSprite;
import flixel.group.FlxSpriteGroup;
import flixel.math.FlxPoint;
import flixel.util.FlxSort;
import funkin.modding.IHook;
import funkin.play.character.Character.CharacterType;
import funkin.play.stage.StageData.StageDataParser;
import funkin.util.SortUtil;
/**
* A Stage is a group of objects rendered in the PlayState.
*
* A Stage is comprised of one or more props, each of which is a FlxSprite.
*/
class Stage extends FlxSpriteGroup implements IHook
{
public final stageId:String;
public final stageName:String;
final _data:StageData;
public var camZoom:Float = 1.0;
var namedProps:Map<String, FlxSprite> = new Map<String, FlxSprite>();
var characters:Map<String, Character> = new Map<String, Character>();
var boppers:Array<Bopper> = new Array<Bopper>();
/**
* The Stage elements get initialized at the beginning of the game.
* They're used to cache the data needed to build the stage,
* then accessed and fleshed out when the stage needs to be built.
*
* @param stageId
*/
public function new(stageId:String)
{
super();
this.stageId = stageId;
_data = StageDataParser.parseStageData(this.stageId);
if (_data == null)
{
throw 'Could not find stage data for stageId: $stageId';
}
else
{
this.stageName = _data.name;
}
}
/**
* The default stage construction routine. Called when the stage is going to be played in.
* Instantiates each prop and adds it to the stage, while setting its parameters.
*/
public function buildStage()
{
trace('Building stage for display: ${this.stageId}');
this.camZoom = _data.cameraZoom;
// this.scrollFactor = new FlxPoint(1, 1);
for (dataProp in _data.props)
{
trace(' Placing prop: ${dataProp.name} (${dataProp.assetPath})');
var isAnimated = dataProp.animations.length > 0;
var propSprite:FlxSprite;
if (dataProp.danceEvery != 0)
{
propSprite = new Bopper(dataProp.danceEvery);
}
else
{
propSprite = new FlxSprite();
}
if (isAnimated)
{
// Initalize sprite frames.
switch (dataProp.animType)
{
case "packer":
propSprite.frames = Paths.getPackerAtlas(dataProp.assetPath);
default: // "sparrow"
propSprite.frames = Paths.getSparrowAtlas(dataProp.assetPath);
}
}
else
{
// Initalize static sprite.
propSprite.loadGraphic(Paths.image(dataProp.assetPath));
// Disables calls to update() for a performance boost.
propSprite.active = false;
}
if (propSprite.frames == null || propSprite.frames.numFrames == 0)
{
trace(' ERROR: Could not build texture for prop.');
continue;
}
if (Std.isOfType(dataProp.scale, Array))
{
propSprite.scale.set(dataProp.scale[0], dataProp.scale[1]);
}
else
{
propSprite.scale.set(dataProp.scale);
}
propSprite.updateHitbox();
propSprite.x = dataProp.position[0];
propSprite.y = dataProp.position[1];
// If pixel, disable antialiasing.
propSprite.antialiasing = !dataProp.isPixel;
propSprite.scrollFactor.x = dataProp.scroll[0];
propSprite.scrollFactor.y = dataProp.scroll[1];
propSprite.zIndex = dataProp.zIndex;
switch (dataProp.animType)
{
case "packer":
for (propAnim in dataProp.animations)
{
propSprite.animation.add(propAnim.name, propAnim.frameIndices);
}
default: // "sparrow"
for (propAnim in dataProp.animations)
{
if (propAnim.frameIndices.length == 0)
{
propSprite.animation.addByPrefix(propAnim.name, propAnim.prefix, propAnim.frameRate, propAnim.loop, propAnim.flipX,
propAnim.flipY);
}
else
{
propSprite.animation.addByIndices(propAnim.name, propAnim.prefix, propAnim.frameIndices, "", propAnim.frameRate, propAnim.loop,
propAnim.flipX, propAnim.flipY);
}
}
}
if (dataProp.startingAnimation != null)
{
propSprite.animation.play(dataProp.startingAnimation);
}
if (Std.isOfType(propSprite, Bopper))
{
addBopper(cast propSprite, dataProp.name);
}
else
{
addProp(propSprite, dataProp.name);
}
trace(' Prop placed.');
}
this.refresh();
}
/**
* Add a sprite to the stage.
* @param prop The sprite to add.
* @param name (Optional) A unique name for the sprite.
* You can call `getNamedProp(name)` to retrieve it later.
*/
public function addProp(prop:FlxSprite, ?name:String = null)
{
if (name != null)
{
namedProps.set(name, prop);
}
this.add(prop);
}
/**
* Add a sprite to the stage which animates to the beat of the song.
*/
public function addBopper(bopper:Bopper, ?name:String = null)
{
boppers.push(bopper);
this.addProp(bopper, name);
}
/**
* Refreshes the stage, by redoing the render order of all props.
* It does this based on the `zIndex` of each prop.
*/
public function refresh()
{
sort(SortUtil.byZIndex, FlxSort.ASCENDING);
trace('Stage sorted by z-index');
}
/**
* Resets the stage and it's props (needs to be overridden with your own logic!)
*/
public function resetStage()
{
// Override me in your script to reset stage shit however you please!
// also note: maybe add some default behaviour to reset stage stuff?
}
/**
* A function that should get called every frame.
*/
public function onUpdate(elapsed:Float):Void
{
// Override me in your scripted stage to perform custom behavior!
}
/**
* A function that gets called when the player hits a note.
*/
public function onNoteHit(note:Note):Void
{
// Override me in your scripted stage to perform custom behavior!
}
/**
* A function that gets called when the player hits a note.
*/
public function onNoteMiss(note:Note):Void
{
// Override me in your scripted stage to perform custom behavior!
}
/**
* Adjusts the position and other properties of the soon-to-be child of this sprite group.
* Private helper to avoid duplicate code in `add()` and `insert()`.
*
* @param Sprite The sprite or sprite group that is about to be added or inserted into the group.
*/
override function preAdd(Sprite:FlxSprite):Void
{
var sprite:FlxSprite = cast Sprite;
sprite.x += x;
sprite.y += y;
sprite.alpha *= alpha;
// Don't override scroll factors.
// sprite.scrollFactor.copyFrom(scrollFactor);
sprite.cameras = _cameras; // _cameras instead of cameras because get_cameras() will not return null
if (clipRect != null)
clipRectTransform(sprite, clipRect);
}
/**
* A function that gets called once per step in the song.
* @param curStep The current step number.
*/
public function onStepHit(curStep:Int):Void
{
// Override me in your scripted stage to perform custom behavior!
}
/**
* A function that gets called once per beat in the song (once every four steps).
* @param curStep The current beat number.
*/
public function onBeatHit(curBeat:Int):Void
{
// Override me in your scripted stage to perform custom behavior!
// Make sure to call super.onBeatHit(curBeat) if you want to keep the boppers dancing.
for (bopper in boppers)
{
bopper.onBeatHit(curBeat);
}
}
/**
* Used by the PlayState to add a character to the stage.
*/
public function addCharacter(character:Character, charType:CharacterType)
{
// Apply position and z-index.
switch (charType)
{
case BF:
this.characters.set("bf", character);
character.zIndex = _data.characters.bf.zIndex;
character.x = _data.characters.bf.position[0];
character.y = _data.characters.bf.position[1];
case GF:
this.characters.set("gf", character);
character.zIndex = _data.characters.gf.zIndex;
character.x = _data.characters.gf.position[0];
character.y = _data.characters.gf.position[1];
case DAD:
this.characters.set("dad", character);
character.zIndex = _data.characters.dad.zIndex;
character.x = _data.characters.dad.position[0];
character.y = _data.characters.dad.position[1];
default:
this.characters.set(character.curCharacter, character);
}
// Add the character to the scene.
this.add(character);
}
/**
* Retrieves a given character from the stage.
*/
public function getCharacter(id:String):Character
{
return this.characters.get(id);
}
public function getBoyfriend():Character
{
return getCharacter('bf');
}
public function getGirlfriend():Character
{
return getCharacter('gf');
}
public function getDad():Character
{
return getCharacter('dad');
}
/**
* Retrieve a specific prop by the name assigned in the JSON file.
* @param name The name of the prop to retrieve.
* @return The corresponding FlxSprite.
*/
public function getNamedProp(name:String):FlxSprite
{
return this.namedProps.get(name);
}
/**
* Retrieve a list of all the asset paths required to load the stage.
* Override this in a scripted class to ensure that all necessary assets are loaded!
*
* @return An array of file names.
*/
public function fetchAssetPaths():Array<String>
{
var result:Array<String> = [];
for (dataProp in _data.props)
{
result.push(Paths.image(dataProp.assetPath));
}
return result;
}
/**
* Perform cleanup for when you are leaving the level.
*/
public override function kill()
{
super.kill();
for (prop in this.namedProps)
{
prop.destroy();
}
namedProps.clear();
for (char in this.characters)
{
char.destroy();
}
characters.clear();
for (bopper in boppers)
{
bopper.destroy();
}
boppers = [];
for (sprite in this.group)
{
sprite.destroy();
}
group.clear();
}
/**
* Perform cleanup for when you are destroying the stage
* and removing all its data from cache.
*
* Call this ONLY when you are performing a hard cache clear.
*/
public override function destroy()
{
super.destroy();
}
}

View file

@ -0,0 +1,509 @@
package funkin.play.stage;
import openfl.Assets;
import funkin.util.assets.DataAssets;
import haxe.Json;
import flixel.util.typeLimit.OneOfTwo;
using StringTools;
/**
* Contains utilities for loading and parsing stage data.
*/
class StageDataParser
{
/**
* The current version string for the stage data format.
* Handle breaking changes by incrementing this value
* and adding migration to the `migrateStageData()` function.
*/
public static final STAGE_DATA_VERSION:String = "1.0";
static final stageCache:Map<String, Stage> = new Map<String, Stage>();
static final DEFAULT_STAGE_ID = 'UNKNOWN';
/**
* Parses and preloads the game's stage data and scripts when the game starts.
*
* If you want to force stages to be reloaded, you can just call this function again.
*/
public static function loadStageCache():Void
{
// Clear any stages that are cached if there were any.
clearStageCache();
trace("[STAGEDATA] Loading stage cache...");
//
// SCRIPTED STAGES
//
var scriptedStageClassNames:Array<String> = ScriptedStage.listScriptClasses();
trace(' Instantiating ${scriptedStageClassNames.length} scripted stages...');
for (stageCls in scriptedStageClassNames)
{
var stage:Stage = ScriptedStage.init(stageCls, DEFAULT_STAGE_ID);
if (stage != null)
{
trace(' Loaded scripted stage: ${stage.stageName}');
// Disable the rendering logic for stage until it's loaded.
// Note that kill() =/= destroy()
stage.kill();
// Then store it.
stageCache.set(stage.stageId, stage);
}
else
{
trace(' Failed to instantiate scripted stage class: ${stageCls}');
}
}
//
// UNSCRIPTED STAGES
//
var stageIdList:Array<String> = DataAssets.listDataFilesInPath('stages/');
var unscriptedStageIds:Array<String> = stageIdList.filter(function(stageId:String):Bool
{
return !stageCache.exists(stageId);
});
trace(' Instantiating ${unscriptedStageIds.length} non-scripted stages...');
for (stageId in unscriptedStageIds)
{
var stage:Stage;
try
{
stage = new Stage(stageId);
if (stage != null)
{
trace(' Loaded stage data: ${stage.stageName}');
stageCache.set(stageId, stage);
}
}
catch (e)
{
// Assume error was already logged.
continue;
}
}
trace(' Successfully loaded ${Lambda.count(stageCache)} stages.');
}
public static function fetchStage(stageId:String):Null<Stage>
{
if (stageCache.exists(stageId))
{
trace('[STAGEDATA] Successfully fetch stage: ${stageId}');
var stage:Stage = stageCache.get(stageId);
stage.revive();
return stage;
}
else
{
trace('[STAGEDATA] Failed to fetch stage, not found in cache: ${stageId}');
return null;
}
}
static function clearStageCache():Void
{
if (stageCache != null)
{
for (stage in stageCache)
{
stage.destroy();
}
stageCache.clear();
}
}
/**
* Load a stage's JSON file, parse its data, and return it.
*
* @param stageId The stage to load.
* @return The stage data, or null if validation failed.
*/
public static function parseStageData(stageId:String):Null<StageData>
{
var rawJson:String = loadStageFile(stageId);
var stageData:StageData = migrateStageData(rawJson, stageId);
return validateStageData(stageId, stageData);
}
static function loadStageFile(stagePath:String):String
{
var stageFilePath:String = Paths.json('stages/${stagePath}');
var rawJson = Assets.getText(stageFilePath).trim();
while (!rawJson.endsWith("}"))
{
rawJson = rawJson.substr(0, rawJson.length - 1);
}
return rawJson;
}
static function migrateStageData(rawJson:String, stageId:String)
{
// If you update the stage data format in a breaking way,
// handle migration here by checking the `version` value.
try
{
var stageData:StageData = cast Json.parse(rawJson);
return stageData;
}
catch (e)
{
trace(' Error parsing data for stage: ${stageId}');
trace(' ${e}');
return null;
}
}
static final DEFAULT_NAME:String = "Untitled Stage";
static final DEFAULT_CAMERAZOOM:Float = 1.0;
static final DEFAULT_ZINDEX:Int = 0;
static final DEFAULT_DANCEEVERY:Int = 0;
static final DEFAULT_SCALE:Float = 1.0;
static final DEFAULT_ISPIXEL:Bool = false;
static final DEFAULT_POSITION:Array<Float> = [0, 0];
static final DEFAULT_SCROLL:Array<Float> = [0, 0];
static final DEFAULT_FRAMEINDICES:Array<Int> = [];
static final DEFAULT_ANIMTYPE:String = "sparrow";
static final DEFAULT_CHARACTER_DATA:StageDataCharacter = {
zIndex: DEFAULT_ZINDEX,
position: DEFAULT_POSITION,
}
/**
* Set unspecified parameters to their defaults.
* If the parameter is mandatory, print an error message.
* @param id
* @param input
* @return The validated stage data
*/
static function validateStageData(id:String, input:StageData):Null<StageData>
{
if (input == null)
{
trace('[STAGEDATA] ERROR: Could not parse stage data for "${id}".');
return null;
}
if (input.version != STAGE_DATA_VERSION)
{
trace('[STAGEDATA] ERROR: Could not load stage data for "$id": missing version');
return null;
}
if (input.name == null)
{
trace('[STAGEDATA] WARN: Stage data for "$id" missing name');
input.name = DEFAULT_NAME;
}
if (input.cameraZoom == null)
{
input.cameraZoom = DEFAULT_CAMERAZOOM;
}
if (input.props == null || input.props.length == 0)
{
trace('[STAGEDATA] ERROR: Could not load stage data for "$id": missing props');
return null;
}
for (inputProp in input.props)
{
// It's fine for inputProp.name to be null
if (inputProp.assetPath == null)
{
trace('[STAGEDATA] ERROR: Could not load stage data for "$id": missing assetPath for prop "${inputProp.name}"');
return null;
}
if (inputProp.position == null)
{
inputProp.position = DEFAULT_POSITION;
}
if (inputProp.zIndex == null)
{
inputProp.zIndex = DEFAULT_ZINDEX;
}
if (inputProp.isPixel == null)
{
inputProp.isPixel = DEFAULT_ISPIXEL;
}
if (inputProp.danceEvery == null)
{
inputProp.danceEvery = DEFAULT_DANCEEVERY;
}
if (inputProp.scale == null)
{
inputProp.scale = DEFAULT_SCALE;
}
if (inputProp.animType == null)
{
inputProp.animType = DEFAULT_ANIMTYPE;
}
if (Std.isOfType(inputProp.scale, Float))
{
inputProp.scale = [inputProp.scale, inputProp.scale];
}
if (inputProp.scroll == null)
{
inputProp.scroll = DEFAULT_SCROLL;
}
if (Std.isOfType(inputProp.scroll, Float))
{
inputProp.scroll = [inputProp.scroll, inputProp.scroll];
}
if (inputProp.animations == null)
{
inputProp.animations = [];
}
if (inputProp.animations.length == 0 && inputProp.startingAnimation != null)
{
trace('[STAGEDATA] ERROR: Could not load stage data for "$id": missing animations for prop "${inputProp.name}"');
return null;
}
for (inputAnimation in inputProp.animations)
{
if (inputAnimation.name == null)
{
trace('[STAGEDATA] ERROR: Could not load stage data for "$id": missing animation name for prop "${inputProp.name}"');
return null;
}
if (inputAnimation.frameRate == null)
{
inputAnimation.frameRate = 24;
}
if (inputAnimation.frameIndices == null)
{
inputAnimation.frameIndices = DEFAULT_FRAMEINDICES;
}
if (inputAnimation.loop == null)
{
inputAnimation.loop = true;
}
if (inputAnimation.flipX == null)
{
inputAnimation.flipX = false;
}
if (inputAnimation.flipY == null)
{
inputAnimation.flipY = false;
}
}
}
if (input.characters == null)
{
trace('[STAGEDATA] ERROR: Could not load stage data for "$id": missing characters');
return null;
}
if (input.characters.bf == null)
{
input.characters.bf = DEFAULT_CHARACTER_DATA;
}
if (input.characters.dad == null)
{
input.characters.dad = DEFAULT_CHARACTER_DATA;
}
if (input.characters.gf == null)
{
input.characters.gf = DEFAULT_CHARACTER_DATA;
}
for (inputCharacter in [input.characters.bf, input.characters.dad, input.characters.gf])
{
if (inputCharacter.zIndex == null)
{
inputCharacter.zIndex = 0;
}
if (inputCharacter.position == null || inputCharacter.position.length != 2)
{
inputCharacter.position = [0, 0];
}
}
// All good!
return input;
}
}
typedef StageData =
{
// Uses semantic versioning.
var version:String;
var name:String;
var cameraZoom:Null<Float>;
var props:Array<StageDataProp>;
var characters:
{
bf:StageDataCharacter,
dad:StageDataCharacter,
gf:StageDataCharacter,
};
};
typedef StageDataProp =
{
/**
* The name of the prop for later lookup by scripts.
* Optional; if unspecified, the prop can't be referenced by scripts.
*/
var name:String;
/**
* The asset used to display the prop.
*/
var assetPath:String;
/**
* The position of the prop as an [x, y] array of two floats.
*/
var position:Array<Float>;
/**
* A number determining the stack order of the prop, relative to other props and the characters in the stage.
* Props with lower numbers render below those with higher numbers.
* This is just like CSS, it isn't hard.
* @default 0
*/
var zIndex:Null<Int>;
/**
* If set to true, anti-aliasing will be forcibly disabled on the sprite.
* This prevents blurry images on pixel-art levels.
* @default false
*/
var isPixel:Null<Bool>;
/**
* Either the scale of the prop as a float, or the [w, h] scale as an array of two floats.
* Pro tip: On pixel-art levels, save the sprite small and set this value to 6 or so to save memory.
* @default 1
*/
var scale:OneOfTwo<Float, Array<Float>>;
/**
* If not zero, this prop will play an animation every X beats of the song.
* This requires animations to be defined. If `danceLeft` and `danceRight` are defined,
* they will alternated between, otherwise the `idle` animation will be used.
*
* @default 0
*/
var danceEvery:Null<Int>;
/**
* How much the prop scrolls relative to the camera. Used to create a parallax effect.
* Represented as a float or as an [x, y] array of two floats.
* [1, 1] means the prop moves 1:1 with the camera.
* [0.5, 0.5] means the prop half as much as the camera.
* [0, 0] means the prop is not moved.
* @default [0, 0]
*/
var scroll:OneOfTwo<Float, Array<Float>>;
/**
* An optional array of animations which the prop can play.
* @default Prop has no animations.
*/
var animations:Array<StageDataPropAnimation>;
/**
* If animations are used, this is the name of the animation to play first.
* @default Don't play an animation.
*/
var startingAnimation:String;
/**
* The animation type to use.
* Options: "sparrow", "packer"
* @default "sparrow"
*/
var animType:String;
};
typedef StageDataPropAnimation =
{
/**
* The name of the animation.
*/
var name:String;
/**
* The common beginning of image names in atlas for this animation's frames.
* For example, if the frames are named "test0001.png", "test0002.png", etc., use "test".
*/
var prefix:String;
/**
* If you want this animation to use only certain frames of an animation with a given prefix,
* select them here.
* @example [0, 1, 2, 3] (use only the first four frames)
* @default [] (all frames)
*/
var frameIndices:Array<Int>;
/**
* The speed of the animation in frames per second.
* @default 24
*/
var frameRate:Null<Int>;
/**
* Whether the animation should loop.
* @default false
*/
var loop:Null<Bool>;
/**
* Whether to flip the sprite horizontally while animating.
* @default false
*/
var flipX:Null<Bool>;
/**
* Whether to flip the sprite vertically while animating.
* @default false
*/
var flipY:Null<Bool>;
};
typedef StageDataCharacter =
{
/**
* A number determining the stack order of the character, relative to props and other characters in the stage.
* Again, just like CSS.
* @default 0
*/
zIndex:Null<Int>,
/**
* The position to render the character at.
*/ position:Array<Float>
};

View file

@ -1,4 +1,4 @@
package rendering;
package funkin.rendering;
import flixel.FlxStrip;
import flixel.util.FlxColor;

View file

@ -1,4 +1,4 @@
package shaderslmfao;
package funkin.shaderslmfao;
import flixel.system.FlxAssets.FlxShader;

View file

@ -1,4 +1,4 @@
package;
package funkin.shaderslmfao;
import flixel.util.FlxColor;
import openfl.display.ShaderParameter;

View file

@ -1,4 +1,4 @@
package shaderslmfao;
package funkin.shaderslmfao;
import flixel.system.FlxAssets.FlxShader;

View file

@ -1,4 +1,4 @@
package shaderslmfao;
package funkin.shaderslmfao;
import flixel.system.FlxAssets.FlxShader;
import flixel.util.FlxColor;

View file

@ -1,4 +1,4 @@
package;
package funkin.shaderslmfao;
import flixel.system.FlxAssets.FlxShader;

Some files were not shown because too many files have changed in this diff Show more