mirror of
https://github.com/FunkinCrew/Funkin.git
synced 2024-11-23 08:07:54 -05:00
Merge pull request #289 from FunkinCrew/feature/stage-solids
Stage solids
This commit is contained in:
commit
33982b140a
30 changed files with 482 additions and 629 deletions
2
assets
2
assets
|
@ -1 +1 @@
|
|||
Subproject commit d094640f727a670a348b3579d11af5ff6a2ada3a
|
||||
Subproject commit 9e385784b1d2f4332de0d696b1df655cfa269da0
|
|
@ -20,11 +20,11 @@ import openfl.display.BitmapData;
|
|||
import funkin.data.level.LevelRegistry;
|
||||
import funkin.data.notestyle.NoteStyleRegistry;
|
||||
import funkin.data.event.SongEventRegistry;
|
||||
import funkin.data.stage.StageRegistry;
|
||||
import funkin.play.cutscene.dialogue.ConversationDataParser;
|
||||
import funkin.play.cutscene.dialogue.DialogueBoxDataParser;
|
||||
import funkin.play.cutscene.dialogue.SpeakerDataParser;
|
||||
import funkin.data.song.SongRegistry;
|
||||
import funkin.play.stage.StageData.StageDataParser;
|
||||
import funkin.play.character.CharacterData.CharacterDataParser;
|
||||
import funkin.modding.module.ModuleHandler;
|
||||
import funkin.ui.title.TitleState;
|
||||
|
@ -217,8 +217,9 @@ class InitState extends FlxState
|
|||
ConversationDataParser.loadConversationCache();
|
||||
DialogueBoxDataParser.loadDialogueBoxCache();
|
||||
SpeakerDataParser.loadSpeakerCache();
|
||||
StageDataParser.loadStageCache();
|
||||
StageRegistry.instance.loadEntries();
|
||||
CharacterDataParser.loadCharacterCache();
|
||||
|
||||
ModuleHandler.buildModuleCallbacks();
|
||||
ModuleHandler.loadModuleCache();
|
||||
|
||||
|
|
0
source/funkin/data/character/TODO.md
Normal file
0
source/funkin/data/character/TODO.md
Normal file
0
source/funkin/data/conversation/TODO.md
Normal file
0
source/funkin/data/conversation/TODO.md
Normal file
0
source/funkin/data/dialogue/TODO.md
Normal file
0
source/funkin/data/dialogue/TODO.md
Normal file
|
@ -7,9 +7,9 @@ import funkin.ui.story.ScriptedLevel;
|
|||
class LevelRegistry extends BaseRegistry<Level, LevelData>
|
||||
{
|
||||
/**
|
||||
* The current version string for the stage data format.
|
||||
* The current version string for the level data format.
|
||||
* Handle breaking changes by incrementing this value
|
||||
* and adding migration to the `migrateStageData()` function.
|
||||
* and adding migration to the `migrateLevelData()` function.
|
||||
*/
|
||||
public static final LEVEL_DATA_VERSION:thx.semver.Version = "1.0.0";
|
||||
|
||||
|
|
|
@ -127,7 +127,7 @@ class SongRegistry extends BaseRegistry<Song, SongMetadata>
|
|||
variation = variation == null ? Constants.DEFAULT_VARIATION : variation;
|
||||
|
||||
var parser = new json2object.JsonParser<SongMetadata>();
|
||||
parser.ignoreUnknownVariables = false;
|
||||
parser.ignoreUnknownVariables = true;
|
||||
|
||||
switch (loadEntryMetadataFile(id, variation))
|
||||
{
|
||||
|
@ -150,7 +150,7 @@ class SongRegistry extends BaseRegistry<Song, SongMetadata>
|
|||
variation = variation == null ? Constants.DEFAULT_VARIATION : variation;
|
||||
|
||||
var parser = new json2object.JsonParser<SongMetadata>();
|
||||
parser.ignoreUnknownVariables = false;
|
||||
parser.ignoreUnknownVariables = true;
|
||||
parser.fromJson(contents, fileName);
|
||||
|
||||
if (parser.errors.length > 0)
|
||||
|
@ -210,7 +210,7 @@ class SongRegistry extends BaseRegistry<Song, SongMetadata>
|
|||
variation = variation == null ? Constants.DEFAULT_VARIATION : variation;
|
||||
|
||||
var parser = new json2object.JsonParser<SongMetadata_v2_1_0>();
|
||||
parser.ignoreUnknownVariables = false;
|
||||
parser.ignoreUnknownVariables = true;
|
||||
|
||||
switch (loadEntryMetadataFile(id, variation))
|
||||
{
|
||||
|
@ -232,7 +232,7 @@ class SongRegistry extends BaseRegistry<Song, SongMetadata>
|
|||
variation = variation == null ? Constants.DEFAULT_VARIATION : variation;
|
||||
|
||||
var parser = new json2object.JsonParser<SongMetadata_v2_0_0>();
|
||||
parser.ignoreUnknownVariables = false;
|
||||
parser.ignoreUnknownVariables = true;
|
||||
|
||||
switch (loadEntryMetadataFile(id, variation))
|
||||
{
|
||||
|
@ -252,7 +252,7 @@ class SongRegistry extends BaseRegistry<Song, SongMetadata>
|
|||
function parseEntryMetadataRaw_v2_1_0(contents:String, ?fileName:String = 'raw'):Null<SongMetadata>
|
||||
{
|
||||
var parser = new json2object.JsonParser<SongMetadata_v2_1_0>();
|
||||
parser.ignoreUnknownVariables = false;
|
||||
parser.ignoreUnknownVariables = true;
|
||||
parser.fromJson(contents, fileName);
|
||||
|
||||
if (parser.errors.length > 0)
|
||||
|
@ -266,7 +266,7 @@ class SongRegistry extends BaseRegistry<Song, SongMetadata>
|
|||
function parseEntryMetadataRaw_v2_0_0(contents:String, ?fileName:String = 'raw'):Null<SongMetadata>
|
||||
{
|
||||
var parser = new json2object.JsonParser<SongMetadata_v2_0_0>();
|
||||
parser.ignoreUnknownVariables = false;
|
||||
parser.ignoreUnknownVariables = true;
|
||||
parser.fromJson(contents, fileName);
|
||||
|
||||
if (parser.errors.length > 0)
|
||||
|
@ -347,7 +347,7 @@ class SongRegistry extends BaseRegistry<Song, SongMetadata>
|
|||
variation = variation == null ? Constants.DEFAULT_VARIATION : variation;
|
||||
|
||||
var parser = new json2object.JsonParser<SongChartData>();
|
||||
parser.ignoreUnknownVariables = false;
|
||||
parser.ignoreUnknownVariables = true;
|
||||
|
||||
switch (loadEntryChartFile(id, variation))
|
||||
{
|
||||
|
@ -370,7 +370,7 @@ class SongRegistry extends BaseRegistry<Song, SongMetadata>
|
|||
variation = variation == null ? Constants.DEFAULT_VARIATION : variation;
|
||||
|
||||
var parser = new json2object.JsonParser<SongChartData>();
|
||||
parser.ignoreUnknownVariables = false;
|
||||
parser.ignoreUnknownVariables = true;
|
||||
parser.fromJson(contents, fileName);
|
||||
|
||||
if (parser.errors.length > 0)
|
||||
|
|
0
source/funkin/data/speaker/TODO.md
Normal file
0
source/funkin/data/speaker/TODO.md
Normal file
199
source/funkin/data/stage/StageData.hx
Normal file
199
source/funkin/data/stage/StageData.hx
Normal file
|
@ -0,0 +1,199 @@
|
|||
package funkin.data.stage;
|
||||
|
||||
import funkin.data.animation.AnimationData;
|
||||
|
||||
@:nullSafety
|
||||
class StageData
|
||||
{
|
||||
/**
|
||||
* The sematic version number of the stage data JSON format.
|
||||
* Supports fancy comparisons like NPM does it's neat.
|
||||
*/
|
||||
@:default(funkin.data.stage.StageRegistry.STAGE_DATA_VERSION)
|
||||
public var version:String;
|
||||
|
||||
public var name:String = 'Unknown';
|
||||
public var props:Array<StageDataProp> = [];
|
||||
public var characters:StageDataCharacters;
|
||||
|
||||
@:default(1.0)
|
||||
@:optional
|
||||
public var cameraZoom:Null<Float>;
|
||||
|
||||
public function new()
|
||||
{
|
||||
this.version = StageRegistry.STAGE_DATA_VERSION;
|
||||
this.characters = makeDefaultCharacters();
|
||||
}
|
||||
|
||||
function makeDefaultCharacters():StageDataCharacters
|
||||
{
|
||||
return {
|
||||
bf:
|
||||
{
|
||||
zIndex: 0,
|
||||
position: [0, 0],
|
||||
cameraOffsets: [-100, -100]
|
||||
},
|
||||
dad:
|
||||
{
|
||||
zIndex: 0,
|
||||
position: [0, 0],
|
||||
cameraOffsets: [100, -100]
|
||||
},
|
||||
gf:
|
||||
{
|
||||
zIndex: 0,
|
||||
position: [0, 0],
|
||||
cameraOffsets: [0, 0]
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert this StageData into a JSON string.
|
||||
*/
|
||||
public function serialize(pretty:Bool = true):String
|
||||
{
|
||||
var writer = new json2object.JsonWriter<StageData>();
|
||||
return writer.write(this, pretty ? ' ' : null);
|
||||
}
|
||||
}
|
||||
|
||||
typedef StageDataCharacters =
|
||||
{
|
||||
var bf:StageDataCharacter;
|
||||
var dad:StageDataCharacter;
|
||||
var 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.
|
||||
*/
|
||||
@:optional
|
||||
var name:String;
|
||||
|
||||
/**
|
||||
* The asset used to display the prop.
|
||||
* NOTE: As of Stage data v1.0.1, you can also use a color here to create a rectangle, like "#ff0000".
|
||||
* In this case, the `scale` property will be used to determine the size of 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
|
||||
*/
|
||||
@:optional
|
||||
@:default(0)
|
||||
var zIndex:Int;
|
||||
|
||||
/**
|
||||
* If set to true, anti-aliasing will be forcibly disabled on the sprite.
|
||||
* This prevents blurry images on pixel-art levels.
|
||||
* @default false
|
||||
*/
|
||||
@:optional
|
||||
@:default(false)
|
||||
var isPixel: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.
|
||||
*/
|
||||
@:jcustomparse(funkin.data.DataParse.eitherFloatOrFloats)
|
||||
@:jcustomwrite(funkin.data.DataWrite.eitherFloatOrFloats)
|
||||
@:optional
|
||||
var scale:haxe.ds.Either<Float, Array<Float>>;
|
||||
|
||||
/**
|
||||
* The alpha of the prop, as a float.
|
||||
* @default 1.0
|
||||
*/
|
||||
@:optional
|
||||
@:default(1.0)
|
||||
var alpha: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
|
||||
*/
|
||||
@:default(0)
|
||||
@:optional
|
||||
var danceEvery:Int;
|
||||
|
||||
/**
|
||||
* How much the prop scrolls relative to the camera. Used to create a parallax effect.
|
||||
* Represented 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]
|
||||
*/
|
||||
@:optional
|
||||
@:default([0, 0])
|
||||
var scroll:Array<Float>;
|
||||
|
||||
/**
|
||||
* An optional array of animations which the prop can play.
|
||||
* @default Prop has no animations.
|
||||
*/
|
||||
@:optional
|
||||
@:default([])
|
||||
var animations:Array<AnimationData>;
|
||||
|
||||
/**
|
||||
* If animations are used, this is the name of the animation to play first.
|
||||
* @default Don't play an animation.
|
||||
*/
|
||||
@:optional
|
||||
var startingAnimation:Null<String>;
|
||||
|
||||
/**
|
||||
* The animation type to use.
|
||||
* Options: "sparrow", "packer"
|
||||
* @default "sparrow"
|
||||
*/
|
||||
@:default("sparrow")
|
||||
@:optional
|
||||
var animType:String;
|
||||
};
|
||||
|
||||
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
|
||||
*/
|
||||
@:optional
|
||||
@:default(0)
|
||||
var zIndex:Int;
|
||||
|
||||
/**
|
||||
* The position to render the character at.
|
||||
*/
|
||||
@:optional
|
||||
@:default([0, 0])
|
||||
var position:Array<Float>;
|
||||
|
||||
/**
|
||||
* The camera offsets to apply when focusing on the character on this stage.
|
||||
* @default [-100, -100] for BF, [100, -100] for DAD/OPPONENT, [0, 0] for GF
|
||||
*/
|
||||
@:optional
|
||||
var cameraOffsets:Array<Float>;
|
||||
};
|
103
source/funkin/data/stage/StageRegistry.hx
Normal file
103
source/funkin/data/stage/StageRegistry.hx
Normal file
|
@ -0,0 +1,103 @@
|
|||
package funkin.data.stage;
|
||||
|
||||
import funkin.data.stage.StageData;
|
||||
import funkin.play.stage.Stage;
|
||||
import funkin.play.stage.ScriptedStage;
|
||||
|
||||
class StageRegistry extends BaseRegistry<Stage, StageData>
|
||||
{
|
||||
/**
|
||||
* 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:thx.semver.Version = "1.0.1";
|
||||
|
||||
public static final STAGE_DATA_VERSION_RULE:thx.semver.VersionRule = "1.0.x";
|
||||
|
||||
public static final instance:StageRegistry = new StageRegistry();
|
||||
|
||||
public function new()
|
||||
{
|
||||
super('STAGE', 'stages', STAGE_DATA_VERSION_RULE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read, parse, and validate the JSON data and produce the corresponding data object.
|
||||
*/
|
||||
public function parseEntryData(id:String):Null<StageData>
|
||||
{
|
||||
// JsonParser does not take type parameters,
|
||||
// otherwise this function would be in BaseRegistry.
|
||||
var parser = new json2object.JsonParser<StageData>();
|
||||
parser.ignoreUnknownVariables = false;
|
||||
|
||||
switch (loadEntryFile(id))
|
||||
{
|
||||
case {fileName: fileName, contents: contents}:
|
||||
parser.fromJson(contents, fileName);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
if (parser.errors.length > 0)
|
||||
{
|
||||
printErrors(parser.errors, id);
|
||||
return null;
|
||||
}
|
||||
return parser.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse and validate the JSON data and produce the corresponding data object.
|
||||
*
|
||||
* NOTE: Must be implemented on the implementation class.
|
||||
* @param contents The JSON as a string.
|
||||
* @param fileName An optional file name for error reporting.
|
||||
*/
|
||||
public function parseEntryDataRaw(contents:String, ?fileName:String):Null<StageData>
|
||||
{
|
||||
var parser = new json2object.JsonParser<StageData>();
|
||||
parser.ignoreUnknownVariables = false;
|
||||
parser.fromJson(contents, fileName);
|
||||
|
||||
if (parser.errors.length > 0)
|
||||
{
|
||||
printErrors(parser.errors, fileName);
|
||||
return null;
|
||||
}
|
||||
return parser.value;
|
||||
}
|
||||
|
||||
function createScriptedEntry(clsName:String):Stage
|
||||
{
|
||||
return ScriptedStage.init(clsName, "unknown");
|
||||
}
|
||||
|
||||
function getScriptedClassNames():Array<String>
|
||||
{
|
||||
return ScriptedStage.listScriptClasses();
|
||||
}
|
||||
|
||||
/**
|
||||
* A list of all the stages from the base game, in order.
|
||||
* TODO: Should this be hardcoded?
|
||||
*/
|
||||
public function listBaseGameStageIds():Array<String>
|
||||
{
|
||||
return [
|
||||
"mainStage", "spookyMansion", "phillyTrain", "limoRide", "mallXmas", "mallEvil", "school", "schoolEvil", "tankmanBattlefield", "phillyStreets",
|
||||
"phillyBlazin",
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* A list of all installed story weeks that are not from the base game.
|
||||
*/
|
||||
public function listModdedStageIds():Array<String>
|
||||
{
|
||||
return listEntryIds().filter(function(id:String):Bool {
|
||||
return listBaseGameStageIds().indexOf(id) == -1;
|
||||
});
|
||||
}
|
||||
}
|
53
source/funkin/graphics/FunkinSprite.hx
Normal file
53
source/funkin/graphics/FunkinSprite.hx
Normal file
|
@ -0,0 +1,53 @@
|
|||
package funkin.graphics;
|
||||
|
||||
import flixel.FlxSprite;
|
||||
import flixel.util.FlxColor;
|
||||
import flixel.graphics.FlxGraphic;
|
||||
|
||||
/**
|
||||
* An FlxSprite with additional functionality.
|
||||
*/
|
||||
class FunkinSprite extends FlxSprite
|
||||
{
|
||||
/**
|
||||
* @param x Starting X position
|
||||
* @param y Starting Y position
|
||||
*/
|
||||
public function new(?x:Float = 0, ?y:Float = 0)
|
||||
{
|
||||
super(x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Acts similarly to `makeGraphic`, but with improved memory usage,
|
||||
* at the expense of not being able to paint onto the sprite.
|
||||
*
|
||||
* @param width The target width of the sprite.
|
||||
* @param height The target height of the sprite.
|
||||
* @param color The color to fill the sprite with.
|
||||
*/
|
||||
public function makeSolidColor(width:Int, height:Int, color:FlxColor = FlxColor.WHITE):FunkinSprite
|
||||
{
|
||||
var graphic:FlxGraphic = FlxG.bitmap.create(2, 2, color, false, 'solid#${color.toHexString(true, false)}');
|
||||
frames = graphic.imageFrame;
|
||||
scale.set(width / 2, height / 2);
|
||||
updateHitbox();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure scale is applied when cloning a sprite.
|
||||
* The default `clone()` method acts kinda weird TBH.
|
||||
* @return A clone of this sprite.
|
||||
*/
|
||||
public override function clone():FunkinSprite
|
||||
{
|
||||
var result = new FunkinSprite(this.x, this.y);
|
||||
result.frames = this.frames;
|
||||
result.scale.set(this.scale.x, this.scale.y);
|
||||
result.updateHitbox();
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -4,11 +4,12 @@ import funkin.util.macro.ClassMacro;
|
|||
import funkin.modding.module.ModuleHandler;
|
||||
import funkin.play.character.CharacterData.CharacterDataParser;
|
||||
import funkin.data.song.SongData;
|
||||
import funkin.play.stage.StageData;
|
||||
import funkin.data.stage.StageData;
|
||||
import polymod.Polymod;
|
||||
import polymod.backends.PolymodAssets.PolymodAssetType;
|
||||
import polymod.format.ParseRules.TextFileFormat;
|
||||
import funkin.data.event.SongEventRegistry;
|
||||
import funkin.data.stage.StageRegistry;
|
||||
import funkin.util.FileUtil;
|
||||
import funkin.data.level.LevelRegistry;
|
||||
import funkin.data.notestyle.NoteStyleRegistry;
|
||||
|
@ -275,7 +276,7 @@ class PolymodHandler
|
|||
ConversationDataParser.loadConversationCache();
|
||||
DialogueBoxDataParser.loadDialogueBoxCache();
|
||||
SpeakerDataParser.loadSpeakerCache();
|
||||
StageDataParser.loadStageCache();
|
||||
StageRegistry.instance.loadEntries();
|
||||
CharacterDataParser.loadCharacterCache();
|
||||
ModuleHandler.loadModuleCache();
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import flixel.sound.FlxSound;
|
|||
import funkin.ui.story.StoryMenuState;
|
||||
import flixel.util.FlxColor;
|
||||
import flixel.util.FlxTimer;
|
||||
import funkin.graphics.FunkinSprite;
|
||||
import funkin.ui.MusicBeatSubState;
|
||||
import funkin.modding.events.ScriptEvent;
|
||||
import funkin.modding.events.ScriptEventDispatcher;
|
||||
|
@ -94,7 +95,7 @@ class GameOverSubState extends MusicBeatSubState
|
|||
//
|
||||
|
||||
// Add a black background to the screen.
|
||||
var bg = new FlxSprite().makeGraphic(FlxG.width * 2, FlxG.height * 2, FlxColor.BLACK);
|
||||
var bg = new FunkinSprite().makeSolidColor(FlxG.width * 2, FlxG.height * 2, FlxColor.BLACK);
|
||||
// We make this transparent so that we can see the stage underneath during debugging,
|
||||
// but it's normally opaque.
|
||||
bg.alpha = transparent ? 0.25 : 1.0;
|
||||
|
|
|
@ -50,11 +50,11 @@ import funkin.play.notes.SustainTrail;
|
|||
import funkin.play.scoring.Scoring;
|
||||
import funkin.play.song.Song;
|
||||
import funkin.data.song.SongRegistry;
|
||||
import funkin.data.stage.StageRegistry;
|
||||
import funkin.data.song.SongData.SongEventData;
|
||||
import funkin.data.song.SongData.SongNoteData;
|
||||
import funkin.data.song.SongData.SongCharacterData;
|
||||
import funkin.play.stage.Stage;
|
||||
import funkin.play.stage.StageData.StageDataParser;
|
||||
import funkin.ui.transition.LoadingState;
|
||||
import funkin.play.components.PopUpStuff;
|
||||
import funkin.ui.options.PreferencesMenu;
|
||||
|
@ -1353,7 +1353,7 @@ class PlayState extends MusicBeatSubState
|
|||
*/
|
||||
function loadStage(id:String):Void
|
||||
{
|
||||
currentStage = StageDataParser.fetchStage(id);
|
||||
currentStage = StageRegistry.instance.fetchEntry(id);
|
||||
|
||||
if (currentStage != null)
|
||||
{
|
||||
|
|
|
@ -9,6 +9,7 @@ import flixel.math.FlxMath;
|
|||
import flixel.math.FlxPoint.FlxCallbackPoint;
|
||||
import flixel.math.FlxPoint;
|
||||
import flixel.math.FlxRect;
|
||||
import funkin.graphics.FunkinSprite;
|
||||
import flixel.system.FlxAssets.FlxGraphicAsset;
|
||||
import flixel.util.FlxColor;
|
||||
import flixel.util.FlxDestroyUtil;
|
||||
|
@ -621,7 +622,7 @@ class AnimateAtlasCharacter extends BaseCharacter
|
|||
* This functionality isn't supported in SpriteGroup
|
||||
* @return this sprite group
|
||||
*/
|
||||
public override function loadGraphicFromSprite(Sprite:FlxSprite):FlxSprite
|
||||
public override function loadGraphicFromSprite(Sprite:FlxSprite):FunkinSprite
|
||||
{
|
||||
#if FLX_DEBUG
|
||||
throw "This function is not supported in FlxSpriteGroup";
|
||||
|
|
|
@ -5,13 +5,16 @@ import flixel.group.FlxSpriteGroup;
|
|||
import flixel.math.FlxPoint;
|
||||
import flixel.system.FlxAssets.FlxShader;
|
||||
import flixel.util.FlxSort;
|
||||
import flixel.util.FlxColor;
|
||||
import funkin.modding.IScriptedClass;
|
||||
import funkin.modding.events.ScriptEvent;
|
||||
import funkin.modding.events.ScriptEventType;
|
||||
import funkin.modding.events.ScriptEventDispatcher;
|
||||
import funkin.play.character.BaseCharacter;
|
||||
import funkin.play.stage.StageData.StageDataCharacter;
|
||||
import funkin.play.stage.StageData.StageDataParser;
|
||||
import funkin.data.IRegistryEntry;
|
||||
import funkin.data.stage.StageData;
|
||||
import funkin.data.stage.StageData.StageDataCharacter;
|
||||
import funkin.data.stage.StageRegistry;
|
||||
import funkin.play.stage.StageProp;
|
||||
import funkin.util.SortUtil;
|
||||
import funkin.util.assets.FlxAnimationUtil;
|
||||
|
@ -23,14 +26,25 @@ typedef StagePropGroup = FlxTypedSpriteGroup<StageProp>;
|
|||
*
|
||||
* A Stage is comprised of one or more props, each of which is a FlxSprite.
|
||||
*/
|
||||
class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass
|
||||
class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass implements IRegistryEntry<StageData>
|
||||
{
|
||||
public final stageId:String;
|
||||
public final stageName:String;
|
||||
public final id:String;
|
||||
|
||||
final _data:StageData;
|
||||
public final _data:StageData;
|
||||
|
||||
public var camZoom:Float = 1.0;
|
||||
public var stageName(get, never):String;
|
||||
|
||||
function get_stageName():String
|
||||
{
|
||||
return _data?.name ?? 'Unknown';
|
||||
}
|
||||
|
||||
public var camZoom(get, never):Float;
|
||||
|
||||
function get_camZoom():Float
|
||||
{
|
||||
return _data?.cameraZoom ?? 1.0;
|
||||
}
|
||||
|
||||
var namedProps:Map<String, StageProp> = new Map<String, StageProp>();
|
||||
var characters:Map<String, BaseCharacter> = new Map<String, BaseCharacter>();
|
||||
|
@ -41,21 +55,18 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass
|
|||
* 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
|
||||
* @param id
|
||||
*/
|
||||
public function new(stageId:String)
|
||||
public function new(id:String)
|
||||
{
|
||||
super();
|
||||
|
||||
this.stageId = stageId;
|
||||
_data = StageDataParser.parseStageData(this.stageId);
|
||||
this.id = id;
|
||||
_data = _fetchData(id);
|
||||
|
||||
if (_data == null)
|
||||
{
|
||||
throw 'Could not find stage data for stageId: $stageId';
|
||||
}
|
||||
else
|
||||
{
|
||||
this.stageName = _data.name;
|
||||
throw 'Could not find stage data for stage id: $id';
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -129,9 +140,7 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass
|
|||
*/
|
||||
function buildStage():Void
|
||||
{
|
||||
trace('Building stage for display: ${this.stageId}');
|
||||
|
||||
this.camZoom = _data.cameraZoom;
|
||||
trace('Building stage for display: ${this.id}');
|
||||
|
||||
this.debugIconGroup = new FlxSpriteGroup();
|
||||
|
||||
|
@ -139,6 +148,7 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass
|
|||
{
|
||||
trace(' Placing prop: ${dataProp.name} (${dataProp.assetPath})');
|
||||
|
||||
var isSolidColor = dataProp.assetPath.startsWith('#');
|
||||
var isAnimated = dataProp.animations.length > 0;
|
||||
|
||||
var propSprite:StageProp;
|
||||
|
@ -162,6 +172,22 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass
|
|||
propSprite.frames = Paths.getSparrowAtlas(dataProp.assetPath);
|
||||
}
|
||||
}
|
||||
else if (isSolidColor)
|
||||
{
|
||||
var width:Int = 1;
|
||||
var height:Int = 1;
|
||||
switch (dataProp.scale)
|
||||
{
|
||||
case Left(value):
|
||||
width = Std.int(value);
|
||||
height = Std.int(value);
|
||||
|
||||
case Right(values):
|
||||
width = Std.int(values[0]);
|
||||
height = Std.int(values[1]);
|
||||
}
|
||||
propSprite.makeSolidColor(width, height, FlxColor.fromString(dataProp.assetPath));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Initalize static sprite.
|
||||
|
@ -177,13 +203,16 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass
|
|||
continue;
|
||||
}
|
||||
|
||||
switch (dataProp.scale)
|
||||
if (!isSolidColor)
|
||||
{
|
||||
case Left(value):
|
||||
propSprite.scale.set(value);
|
||||
switch (dataProp.scale)
|
||||
{
|
||||
case Left(value):
|
||||
propSprite.scale.set(value);
|
||||
|
||||
case Right(values):
|
||||
propSprite.scale.set(values[0], values[1]);
|
||||
case Right(values):
|
||||
propSprite.scale.set(values[0], values[1]);
|
||||
}
|
||||
}
|
||||
propSprite.updateHitbox();
|
||||
|
||||
|
@ -195,15 +224,8 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass
|
|||
// If pixel, disable antialiasing.
|
||||
propSprite.antialiasing = !dataProp.isPixel;
|
||||
|
||||
switch (dataProp.scroll)
|
||||
{
|
||||
case Left(value):
|
||||
propSprite.scrollFactor.x = value;
|
||||
propSprite.scrollFactor.y = value;
|
||||
case Right(values):
|
||||
propSprite.scrollFactor.x = values[0];
|
||||
propSprite.scrollFactor.y = values[1];
|
||||
}
|
||||
propSprite.scrollFactor.x = dataProp.scroll[0];
|
||||
propSprite.scrollFactor.y = dataProp.scroll[1];
|
||||
|
||||
propSprite.zIndex = dataProp.zIndex;
|
||||
|
||||
|
@ -731,6 +753,11 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass
|
|||
return Sprite;
|
||||
}
|
||||
|
||||
static function _fetchData(id:String):Null<StageData>
|
||||
{
|
||||
return StageRegistry.instance.parseEntryDataWithMigration(id, StageRegistry.instance.fetchEntryVersion(id));
|
||||
}
|
||||
|
||||
public function onScriptEvent(event:ScriptEvent) {}
|
||||
|
||||
public function onPause(event:PauseScriptEvent) {}
|
||||
|
|
|
@ -1,548 +0,0 @@
|
|||
package funkin.play.stage;
|
||||
|
||||
import funkin.data.animation.AnimationData;
|
||||
import funkin.play.stage.ScriptedStage;
|
||||
import funkin.play.stage.Stage;
|
||||
import funkin.util.VersionUtil;
|
||||
import funkin.util.assets.DataAssets;
|
||||
import haxe.Json;
|
||||
import openfl.Assets;
|
||||
|
||||
/**
|
||||
* 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.0";
|
||||
|
||||
/**
|
||||
* The current version rule check for the stage data format.
|
||||
*/
|
||||
public static final STAGE_DATA_VERSION_RULE:String = "1.0.x";
|
||||
|
||||
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("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)
|
||||
{
|
||||
trace(' An error occurred while loading stage data: ${stageId}');
|
||||
// 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('Successfully fetch stage: ${stageId}');
|
||||
var stage:Stage = stageCache.get(stageId);
|
||||
stage.revive();
|
||||
return stage;
|
||||
}
|
||||
else
|
||||
{
|
||||
trace('Failed to fetch stage, not found in cache: ${stageId}');
|
||||
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);
|
||||
}
|
||||
|
||||
public static function listStageIds():Array<String>
|
||||
{
|
||||
return stageCache.keys().array();
|
||||
}
|
||||
|
||||
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):Null<StageData>
|
||||
{
|
||||
// If you update the stage data format in a breaking way,
|
||||
// handle migration here by checking the `version` value.
|
||||
|
||||
try
|
||||
{
|
||||
var parser = new json2object.JsonParser<StageData>();
|
||||
parser.ignoreUnknownVariables = false;
|
||||
parser.fromJson(rawJson, '$stageId.json');
|
||||
|
||||
if (parser.errors.length > 0)
|
||||
{
|
||||
trace('[STAGE] Failed to parse stage data');
|
||||
|
||||
for (error in parser.errors)
|
||||
funkin.data.DataError.printError(error);
|
||||
|
||||
return null;
|
||||
}
|
||||
return parser.value;
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
trace(' Error parsing data for stage: ${stageId}');
|
||||
trace(' ${e}');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static final DEFAULT_ANIMTYPE:String = "sparrow";
|
||||
static final DEFAULT_CAMERAZOOM:Float = 1.0;
|
||||
static final DEFAULT_DANCEEVERY:Int = 0;
|
||||
static final DEFAULT_ISPIXEL:Bool = false;
|
||||
static final DEFAULT_NAME:String = "Untitled Stage";
|
||||
static final DEFAULT_OFFSETS:Array<Float> = [0, 0];
|
||||
static final DEFAULT_CAMERA_OFFSETS_BF:Array<Float> = [-100, -100];
|
||||
static final DEFAULT_CAMERA_OFFSETS_DAD:Array<Float> = [150, -100];
|
||||
static final DEFAULT_POSITION:Array<Float> = [0, 0];
|
||||
static final DEFAULT_SCALE:Float = 1.0;
|
||||
static final DEFAULT_ALPHA:Float = 1.0;
|
||||
static final DEFAULT_SCROLL:Array<Float> = [0, 0];
|
||||
static final DEFAULT_ZINDEX:Int = 0;
|
||||
|
||||
static final DEFAULT_CHARACTER_DATA:StageDataCharacter =
|
||||
{
|
||||
zIndex: DEFAULT_ZINDEX,
|
||||
position: DEFAULT_POSITION,
|
||||
cameraOffsets: DEFAULT_OFFSETS,
|
||||
}
|
||||
|
||||
/**
|
||||
* 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('ERROR: Could not parse stage data for "${id}".');
|
||||
return null;
|
||||
}
|
||||
|
||||
if (input.version == null)
|
||||
{
|
||||
trace('ERROR: Could not load stage data for "$id": missing version');
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!VersionUtil.validateVersionStr(input.version, STAGE_DATA_VERSION_RULE))
|
||||
{
|
||||
trace('ERROR: Could not load stage data for "$id": bad version (got ${input.version}, expected ${STAGE_DATA_VERSION_RULE})');
|
||||
return null;
|
||||
}
|
||||
|
||||
if (input.name == null)
|
||||
{
|
||||
trace('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 = [];
|
||||
}
|
||||
|
||||
for (inputProp in input.props)
|
||||
{
|
||||
// It's fine for inputProp.name to be null
|
||||
|
||||
if (inputProp.assetPath == null)
|
||||
{
|
||||
trace('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.animType == null)
|
||||
{
|
||||
inputProp.animType = DEFAULT_ANIMTYPE;
|
||||
}
|
||||
|
||||
switch (inputProp.scale)
|
||||
{
|
||||
case null:
|
||||
inputProp.scale = Right([DEFAULT_SCALE, DEFAULT_SCALE]);
|
||||
case Left(value):
|
||||
inputProp.scale = Right([value, value]);
|
||||
case Right(_):
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
switch (inputProp.scroll)
|
||||
{
|
||||
case null:
|
||||
inputProp.scroll = Right(DEFAULT_SCROLL);
|
||||
case Left(value):
|
||||
inputProp.scroll = Right([value, value]);
|
||||
case Right(_):
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
if (inputProp.alpha == null)
|
||||
{
|
||||
inputProp.alpha = DEFAULT_ALPHA;
|
||||
}
|
||||
|
||||
if (inputProp.animations == null)
|
||||
{
|
||||
inputProp.animations = [];
|
||||
}
|
||||
|
||||
if (inputProp.animations.length == 0 && inputProp.startingAnimation != null)
|
||||
{
|
||||
trace('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('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.offsets == null)
|
||||
{
|
||||
inputAnimation.offsets = DEFAULT_OFFSETS;
|
||||
}
|
||||
|
||||
if (inputAnimation.looped == null)
|
||||
{
|
||||
inputAnimation.looped = true;
|
||||
}
|
||||
|
||||
if (inputAnimation.flipX == null)
|
||||
{
|
||||
inputAnimation.flipX = false;
|
||||
}
|
||||
|
||||
if (inputAnimation.flipY == null)
|
||||
{
|
||||
inputAnimation.flipY = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (input.characters == null)
|
||||
{
|
||||
trace('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.position == null || inputCharacter.position.length != 2)
|
||||
{
|
||||
inputCharacter.position = [0, 0];
|
||||
}
|
||||
}
|
||||
|
||||
// All good!
|
||||
return input;
|
||||
}
|
||||
}
|
||||
|
||||
class StageData
|
||||
{
|
||||
/**
|
||||
* The sematic version number of the stage data JSON format.
|
||||
* Supports fancy comparisons like NPM does it's neat.
|
||||
*/
|
||||
public var version:String;
|
||||
|
||||
public var name:String;
|
||||
public var cameraZoom:Null<Float>;
|
||||
public var props:Array<StageDataProp>;
|
||||
public var characters:StageDataCharacters;
|
||||
|
||||
public function new()
|
||||
{
|
||||
this.version = StageDataParser.STAGE_DATA_VERSION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert this StageData into a JSON string.
|
||||
*/
|
||||
public function serialize(pretty:Bool = true):String
|
||||
{
|
||||
var writer = new json2object.JsonWriter<StageData>();
|
||||
return writer.write(this, pretty ? ' ' : null);
|
||||
}
|
||||
}
|
||||
|
||||
typedef StageDataCharacters =
|
||||
{
|
||||
var bf:StageDataCharacter;
|
||||
var dad:StageDataCharacter;
|
||||
var 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.
|
||||
*/
|
||||
@:optional
|
||||
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
|
||||
*/
|
||||
@:optional
|
||||
@:default(0)
|
||||
var zIndex:Int;
|
||||
|
||||
/**
|
||||
* If set to true, anti-aliasing will be forcibly disabled on the sprite.
|
||||
* This prevents blurry images on pixel-art levels.
|
||||
* @default false
|
||||
*/
|
||||
@:optional
|
||||
@:default(false)
|
||||
var isPixel: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.
|
||||
*/
|
||||
@:jcustomparse(funkin.data.DataParse.eitherFloatOrFloats)
|
||||
@:jcustomwrite(funkin.data.DataWrite.eitherFloatOrFloats)
|
||||
@:optional
|
||||
var scale:haxe.ds.Either<Float, Array<Float>>;
|
||||
|
||||
/**
|
||||
* The alpha of the prop, as a float.
|
||||
* @default 1.0
|
||||
*/
|
||||
@:optional
|
||||
@:default(1.0)
|
||||
var alpha: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
|
||||
*/
|
||||
@:default(0)
|
||||
@:optional
|
||||
var danceEvery: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]
|
||||
*/
|
||||
@:jcustomparse(funkin.data.DataParse.eitherFloatOrFloats)
|
||||
@:jcustomwrite(funkin.data.DataWrite.eitherFloatOrFloats)
|
||||
@:optional
|
||||
var scroll:haxe.ds.Either<Float, Array<Float>>;
|
||||
|
||||
/**
|
||||
* An optional array of animations which the prop can play.
|
||||
* @default Prop has no animations.
|
||||
*/
|
||||
@:optional
|
||||
var animations:Array<AnimationData>;
|
||||
|
||||
/**
|
||||
* If animations are used, this is the name of the animation to play first.
|
||||
* @default Don't play an animation.
|
||||
*/
|
||||
@:optional
|
||||
var startingAnimation:Null<String>;
|
||||
|
||||
/**
|
||||
* The animation type to use.
|
||||
* Options: "sparrow", "packer"
|
||||
* @default "sparrow"
|
||||
*/
|
||||
@:default("sparrow")
|
||||
@:optional
|
||||
var animType:String;
|
||||
};
|
||||
|
||||
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
|
||||
*/
|
||||
var zIndex:Int;
|
||||
|
||||
/**
|
||||
* The position to render the character at.
|
||||
*/
|
||||
var position:Array<Float>;
|
||||
|
||||
/**
|
||||
* The camera offsets to apply when focusing on the character on this stage.
|
||||
* @default [-100, -100] for BF, [100, -100] for DAD/OPPONENT, [0, 0] for GF
|
||||
*/
|
||||
var cameraOffsets:Array<Float>;
|
||||
};
|
|
@ -1,10 +1,10 @@
|
|||
package funkin.play.stage;
|
||||
|
||||
import funkin.modding.events.ScriptEvent;
|
||||
import flixel.FlxSprite;
|
||||
import funkin.graphics.FunkinSprite;
|
||||
import funkin.modding.IScriptedClass.IStateStageProp;
|
||||
|
||||
class StageProp extends FlxSprite implements IStateStageProp
|
||||
class StageProp extends FunkinSprite implements IStateStageProp
|
||||
{
|
||||
/**
|
||||
* An internal name for this prop.
|
||||
|
|
|
@ -12,6 +12,7 @@ import flixel.FlxCamera;
|
|||
import flixel.FlxSprite;
|
||||
import flixel.FlxSubState;
|
||||
import flixel.group.FlxSpriteGroup;
|
||||
import funkin.graphics.FunkinSprite;
|
||||
import flixel.input.keyboard.FlxKey;
|
||||
import flixel.math.FlxMath;
|
||||
import flixel.math.FlxPoint;
|
||||
|
@ -56,7 +57,7 @@ import funkin.data.song.SongData.SongNoteData;
|
|||
import funkin.data.song.SongData.SongCharacterData;
|
||||
import funkin.data.song.SongDataUtils;
|
||||
import funkin.ui.debug.charting.commands.ChartEditorCommand;
|
||||
import funkin.play.stage.StageData;
|
||||
import funkin.data.stage.StageData;
|
||||
import funkin.save.Save;
|
||||
import funkin.ui.debug.charting.commands.AddEventsCommand;
|
||||
import funkin.ui.debug.charting.commands.AddNotesCommand;
|
||||
|
@ -2230,7 +2231,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
var playheadWidth:Int = GRID_SIZE * (STRUMLINE_SIZE * 2 + 1) + (PLAYHEAD_SCROLL_AREA_WIDTH * 2);
|
||||
var playheadBaseYPos:Float = GRID_INITIAL_Y_POS;
|
||||
gridPlayhead.setPosition(GRID_X_POS, playheadBaseYPos);
|
||||
var playheadSprite:FlxSprite = new FlxSprite().makeGraphic(playheadWidth, PLAYHEAD_HEIGHT, PLAYHEAD_COLOR);
|
||||
var playheadSprite:FunkinSprite = new FunkinSprite().makeSolidColor(playheadWidth, PLAYHEAD_HEIGHT, PLAYHEAD_COLOR);
|
||||
playheadSprite.x = -PLAYHEAD_SCROLL_AREA_WIDTH;
|
||||
playheadSprite.y = 0;
|
||||
gridPlayhead.add(playheadSprite);
|
||||
|
|
|
@ -13,7 +13,7 @@ import funkin.play.character.BaseCharacter;
|
|||
import funkin.play.character.CharacterData;
|
||||
import funkin.play.character.CharacterData.CharacterDataParser;
|
||||
import funkin.play.song.Song;
|
||||
import funkin.play.stage.StageData;
|
||||
import funkin.data.stage.StageData;
|
||||
import funkin.ui.debug.charting.dialogs.ChartEditorAboutDialog;
|
||||
import funkin.ui.debug.charting.dialogs.ChartEditorBaseDialog.DialogDropTarget;
|
||||
import funkin.ui.debug.charting.dialogs.ChartEditorCharacterIconSelectorMenu;
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package funkin.ui.debug.charting.handlers;
|
||||
|
||||
import funkin.play.stage.StageData.StageDataParser;
|
||||
import funkin.play.stage.StageData;
|
||||
import funkin.data.stage.StageData;
|
||||
import funkin.play.character.CharacterData;
|
||||
import funkin.play.character.CharacterData.CharacterDataParser;
|
||||
import haxe.ui.components.HorizontalSlider;
|
||||
|
@ -16,8 +15,7 @@ import funkin.play.character.CharacterData;
|
|||
import funkin.play.character.CharacterData.CharacterDataParser;
|
||||
import funkin.play.event.SongEvent;
|
||||
import funkin.play.song.SongSerializer;
|
||||
import funkin.play.stage.StageData;
|
||||
import funkin.play.stage.StageData.StageDataParser;
|
||||
import funkin.data.stage.StageData;
|
||||
import haxe.ui.RuntimeComponentBuilder;
|
||||
import funkin.ui.debug.charting.util.ChartEditorDropdowns;
|
||||
import funkin.ui.haxeui.components.CharacterPlayer;
|
||||
|
|
|
@ -2,7 +2,7 @@ package funkin.ui.debug.charting.toolboxes;
|
|||
|
||||
import funkin.play.character.BaseCharacter.CharacterType;
|
||||
import funkin.play.character.CharacterData;
|
||||
import funkin.play.stage.StageData;
|
||||
import funkin.data.stage.StageData;
|
||||
import funkin.play.event.SongEvent;
|
||||
import funkin.data.event.SongEventSchema;
|
||||
import funkin.ui.debug.charting.commands.ChangeStartingBPMCommand;
|
||||
|
|
|
@ -2,7 +2,8 @@ package funkin.ui.debug.charting.toolboxes;
|
|||
|
||||
import funkin.play.character.BaseCharacter.CharacterType;
|
||||
import funkin.play.character.CharacterData;
|
||||
import funkin.play.stage.StageData;
|
||||
import funkin.data.stage.StageData;
|
||||
import funkin.data.stage.StageRegistry;
|
||||
import funkin.ui.debug.charting.commands.ChangeStartingBPMCommand;
|
||||
import funkin.ui.debug.charting.util.ChartEditorDropdowns;
|
||||
import haxe.ui.components.Button;
|
||||
|
@ -13,6 +14,7 @@ import haxe.ui.components.Label;
|
|||
import haxe.ui.components.NumberStepper;
|
||||
import haxe.ui.components.Slider;
|
||||
import haxe.ui.components.TextField;
|
||||
import funkin.play.stage.Stage;
|
||||
import haxe.ui.containers.Box;
|
||||
import haxe.ui.containers.Frame;
|
||||
import haxe.ui.events.UIEvent;
|
||||
|
@ -199,11 +201,11 @@ class ChartEditorMetadataToolbox extends ChartEditorBaseToolbox
|
|||
inputTimeSignature.value = {id: currentTimeSignature, text: currentTimeSignature};
|
||||
|
||||
var stageId:String = chartEditorState.currentSongMetadata.playData.stage;
|
||||
var stageData:Null<StageData> = StageDataParser.parseStageData(stageId);
|
||||
var stage:Null<Stage> = StageRegistry.instance.fetchEntry(stageId);
|
||||
if (inputStage != null)
|
||||
{
|
||||
inputStage.value = (stageData != null) ?
|
||||
{id: stageId, text: stageData.name} :
|
||||
inputStage.value = (stage != null) ?
|
||||
{id: stage.id, text: stage.stageName} :
|
||||
{id: "mainStage", text: "Main Stage"};
|
||||
}
|
||||
|
||||
|
|
|
@ -2,10 +2,11 @@ package funkin.ui.debug.charting.util;
|
|||
|
||||
import funkin.data.notestyle.NoteStyleRegistry;
|
||||
import funkin.play.notes.notestyle.NoteStyle;
|
||||
import funkin.play.stage.StageData;
|
||||
import funkin.play.stage.StageData.StageDataParser;
|
||||
import funkin.data.stage.StageData;
|
||||
import funkin.data.stage.StageRegistry;
|
||||
import funkin.play.character.CharacterData;
|
||||
import haxe.ui.components.DropDown;
|
||||
import funkin.play.stage.Stage;
|
||||
import funkin.play.character.BaseCharacter.CharacterType;
|
||||
import funkin.play.character.CharacterData.CharacterDataParser;
|
||||
|
||||
|
@ -60,16 +61,16 @@ class ChartEditorDropdowns
|
|||
{
|
||||
dropDown.dataSource.clear();
|
||||
|
||||
var stageIds:Array<String> = StageDataParser.listStageIds();
|
||||
var stageIds:Array<String> = StageRegistry.instance.listEntryIds();
|
||||
|
||||
var returnValue:DropDownEntry = {id: "mainStage", text: "Main Stage"};
|
||||
|
||||
for (stageId in stageIds)
|
||||
{
|
||||
var stage:Null<StageData> = StageDataParser.parseStageData(stageId);
|
||||
var stage:Null<Stage> = StageRegistry.instance.fetchEntry(stageId);
|
||||
if (stage == null) continue;
|
||||
|
||||
var value = {id: stageId, text: stage.name};
|
||||
var value = {id: stage.id, text: stage.stageName};
|
||||
if (startingStageId == stageId) returnValue = value;
|
||||
|
||||
dropDown.dataSource.add(value);
|
||||
|
|
|
@ -5,15 +5,17 @@ import flixel.input.mouse.FlxMouseEvent;
|
|||
import flixel.math.FlxPoint;
|
||||
import funkin.play.character.BaseCharacter;
|
||||
import funkin.play.PlayState;
|
||||
import funkin.play.stage.StageData;
|
||||
import funkin.data.stage.StageData;
|
||||
import funkin.play.stage.StageProp;
|
||||
import funkin.graphics.shaders.StrokeShader;
|
||||
import funkin.ui.haxeui.HaxeUISubState;
|
||||
import funkin.ui.debug.stage.StageEditorCommand;
|
||||
import funkin.util.SerializerUtil;
|
||||
import funkin.data.stage.StageRegistry;
|
||||
import funkin.util.MouseUtil;
|
||||
import haxe.ui.containers.ListView;
|
||||
import haxe.ui.core.Component;
|
||||
import funkin.graphics.FunkinSprite;
|
||||
import haxe.ui.events.UIEvent;
|
||||
import haxe.ui.RuntimeComponentBuilder;
|
||||
import openfl.events.Event;
|
||||
|
@ -354,7 +356,13 @@ class StageOffsetSubState extends HaxeUISubState
|
|||
|
||||
function prepStageStuff():String
|
||||
{
|
||||
var stageLol:StageData = StageDataParser.parseStageData(PlayState.instance.currentStageId);
|
||||
var stageLol:StageData = StageRegistry.instance.fetchEntry(PlayState.instance.currentStageId)?._data;
|
||||
|
||||
if (stageLol == null)
|
||||
{
|
||||
FlxG.log.error("Stage not found in registry!");
|
||||
return "";
|
||||
}
|
||||
|
||||
for (prop in stageLol.props)
|
||||
{
|
||||
|
@ -378,6 +386,6 @@ class StageOffsetSubState extends HaxeUISubState
|
|||
stageLol.characters.gf.position[0] = Std.int(GF_FEET_SNIIIIIIIIIIIIIFFFF.x);
|
||||
stageLol.characters.gf.position[1] = Std.int(GF_FEET_SNIIIIIIIIIIIIIFFFF.y);
|
||||
|
||||
return SerializerUtil.toJSON(stageLol);
|
||||
return stageLol.serialize();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import flixel.group.FlxGroup;
|
|||
import flixel.input.actions.FlxActionInput;
|
||||
import flixel.input.gamepad.FlxGamepadInputID;
|
||||
import flixel.input.keyboard.FlxKey;
|
||||
import funkin.graphics.FunkinSprite;
|
||||
import funkin.input.Controls;
|
||||
import funkin.ui.AtlasText;
|
||||
import funkin.ui.MenuList;
|
||||
|
@ -61,8 +62,8 @@ class ControlsMenu extends funkin.ui.options.OptionsState.Page
|
|||
|
||||
if (FlxG.gamepads.numActiveGamepads > 0)
|
||||
{
|
||||
var devicesBg:FlxSprite = new FlxSprite();
|
||||
devicesBg.makeGraphic(FlxG.width, 100, 0xFFFAFD6D);
|
||||
var devicesBg:FunkinSprite = new FunkinSprite();
|
||||
devicesBg.makeSolidColor(FlxG.width, 100, 0xFFFAFD6D);
|
||||
add(devicesBg);
|
||||
deviceList = new TextMenuList(Horizontal, None);
|
||||
add(deviceList);
|
||||
|
|
|
@ -10,6 +10,7 @@ import flixel.group.FlxGroup.FlxTypedGroup;
|
|||
import flixel.text.FlxText;
|
||||
import flixel.addons.transition.FlxTransitionableState;
|
||||
import flixel.tweens.FlxEase;
|
||||
import funkin.graphics.FunkinSprite;
|
||||
import funkin.ui.MusicBeatState;
|
||||
import flixel.tweens.FlxTween;
|
||||
import flixel.util.FlxColor;
|
||||
|
@ -153,7 +154,7 @@ class StoryMenuState extends MusicBeatState
|
|||
|
||||
updateBackground();
|
||||
|
||||
var black:FlxSprite = new FlxSprite(levelBackground.x, 0).makeGraphic(FlxG.width, Std.int(400 + levelBackground.y), FlxColor.BLACK);
|
||||
var black:FunkinSprite = new FunkinSprite(levelBackground.x, 0).makeSolidColor(FlxG.width, Std.int(400 + levelBackground.y), FlxColor.BLACK);
|
||||
black.zIndex = levelBackground.zIndex - 1;
|
||||
add(black);
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ class OutdatedSubState extends MusicBeatState
|
|||
override function create()
|
||||
{
|
||||
super.create();
|
||||
var bg:FlxSprite = new FlxSprite().makeGraphic(FlxG.width, FlxG.height, FlxColor.BLACK);
|
||||
var bg:FunkinSprite = new FunkinSprite().makeSolidColor(FlxG.width, FlxG.height, FlxColor.BLACK);
|
||||
add(bg);
|
||||
var ver = "v" + Application.current.meta.get('version');
|
||||
var txt:FlxText = new FlxText(0, 0, FlxG.width,
|
||||
|
|
|
@ -13,6 +13,7 @@ import funkin.audio.visualize.SpectogramSprite;
|
|||
import funkin.graphics.shaders.ColorSwap;
|
||||
import funkin.graphics.shaders.LeftMaskShader;
|
||||
import funkin.data.song.SongRegistry;
|
||||
import funkin.graphics.FunkinSprite;
|
||||
import funkin.ui.MusicBeatState;
|
||||
import funkin.data.song.SongData.SongMusicData;
|
||||
import funkin.graphics.shaders.TitleOutline;
|
||||
|
@ -118,7 +119,8 @@ class TitleState extends MusicBeatState
|
|||
|
||||
persistentUpdate = true;
|
||||
|
||||
var bg:FlxSprite = new FlxSprite().makeGraphic(FlxG.width, FlxG.height, FlxColor.BLACK);
|
||||
var bg:FunkinSprite = new FunkinSprite().makeSolidColor(FlxG.width, FlxG.height, FlxColor.BLACK);
|
||||
bg.screenCenter();
|
||||
add(bg);
|
||||
|
||||
logoBl = new FlxSprite(-150, -100);
|
||||
|
|
|
@ -13,6 +13,7 @@ import funkin.play.song.Song.SongDifficulty;
|
|||
import funkin.ui.mainmenu.MainMenuState;
|
||||
import funkin.ui.MusicBeatState;
|
||||
import haxe.io.Path;
|
||||
import funkin.graphics.FunkinSprite;
|
||||
import lime.app.Future;
|
||||
import lime.app.Promise;
|
||||
import lime.utils.AssetLibrary;
|
||||
|
@ -42,7 +43,7 @@ class LoadingState extends MusicBeatState
|
|||
|
||||
override function create():Void
|
||||
{
|
||||
var bg:FlxSprite = new FlxSprite().makeGraphic(FlxG.width, FlxG.height, 0xFFcaff4d);
|
||||
var bg:FlxSprite = new FunkinSprite().makeSolidColor(FlxG.width, FlxG.height, 0xFFcaff4d);
|
||||
add(bg);
|
||||
|
||||
funkay = new FlxSprite();
|
||||
|
@ -53,7 +54,7 @@ class LoadingState extends MusicBeatState
|
|||
funkay.scrollFactor.set();
|
||||
funkay.screenCenter();
|
||||
|
||||
loadBar = new FlxSprite(0, FlxG.height - 20).makeGraphic(FlxG.width, 10, 0xFFff16d2);
|
||||
loadBar = new FunkinSprite(0, FlxG.height - 20).makeSolidColor(FlxG.width, 10, 0xFFff16d2);
|
||||
loadBar.screenCenter(X);
|
||||
add(loadBar);
|
||||
|
||||
|
|
Loading…
Reference in a new issue