From e0ab7794ec4d071ec9b64744ab8ad7d4e3569248 Mon Sep 17 00:00:00 2001 From: lemz1 Date: Fri, 1 Nov 2024 21:32:57 +0100 Subject: [PATCH] migration is finished --- source/funkin/data/character/CharacterData.hx | 35 ++++- .../data/character/CharacterRegistry.hx | 72 ++++++++-- .../migrator/CharacterDataMigrator.hx | 18 ++- .../migrator/CharacterData_v1_0_0.hx | 133 +++--------------- .../play/character/MultiSparrowCharacter.hx | 11 +- 5 files changed, 135 insertions(+), 134 deletions(-) diff --git a/source/funkin/data/character/CharacterData.hx b/source/funkin/data/character/CharacterData.hx index e83d26634c..d657fc6ccf 100644 --- a/source/funkin/data/character/CharacterData.hx +++ b/source/funkin/data/character/CharacterData.hx @@ -52,6 +52,8 @@ typedef CharacterData = * The type of rendering system to use for the character. * @default sparrow */ + @:optional + @:default('sparrow') var renderType:CharacterRenderType; /** @@ -66,19 +68,25 @@ typedef CharacterData = * Pro tip: On pixel-art levels, save the sprites small and set this value to 6 or so to save memory. * @default 1 */ + @:optional + @:default(1.0) var scale:Null; /** * Optional data about the health icon for the character. */ + @:optional var healthIcon:Null; + @:optional var death:Null; /** * The global offset to the character's position, in pixels. * @default [0, 0] */ + @:optional + @:default([0, 0]) var offsets:Null>; /** @@ -86,12 +94,16 @@ typedef CharacterData = * Default value focuses on the character directly. * @default [0, 0] */ + @:optional + @:default([0, 0]) var cameraOffsets:Array; /** * Setting this to true disables anti-aliasing for the character. * @default false */ + @:optional + @:default(false) var isPixel:Null; /** @@ -111,8 +123,10 @@ typedef CharacterData = * * Examples: * - Daddy Dearest uses a value of `1.525`. - * @default 1.0 + * @default 8.0 */ + @:optional + @:default(8.0) var singTime:Null; /** @@ -124,6 +138,8 @@ typedef CharacterData = * If animations are used, this is the name of the animation to play first. * @default idle */ + @:optional + @:default('idle') var startingAnimation:Null; /** @@ -132,6 +148,8 @@ typedef CharacterData = * * @default false */ + @:optional + @:default(false) var flipX:Null; }; @@ -144,29 +162,38 @@ typedef HealthIconData = * The ID to use for the health icon. * @default The character's ID */ + @:optional var id:Null; /** * The scale of the health icon. */ + @:optional + @:default(1.0) var scale:Null; /** * Whether to flip the health icon horizontally. * @default false */ + @:optional + @:default(false) var flipX:Null; /** * Multiply scale by 6 and disable antialiasing * @default false */ + @:optional + @:default(false) var isPixel:Null; /** * The offset of the health icon, in pixels. * @default [0, 25] */ + @:optional + @:default([0, 25]) var offsets:Null>; } @@ -177,6 +204,8 @@ typedef DeathData = * Default value focuses on the character's graphic midpoint. * @default [0, 0] */ + @:optional + @:default([0, 0]) var ?cameraOffsets:Array; /** @@ -184,11 +213,15 @@ typedef DeathData = * Value is a multiplier of the default camera zoom for the stage. * @default 1.0 */ + @:optional + @:default(1.0) var ?cameraZoom:Float; /** * Impose a delay between when the character reaches `0` health and when the death animation plays. * @default 0.0 */ + @:optional + @:default(0.0) var ?preTransitionDelay:Float; } diff --git a/source/funkin/data/character/CharacterRegistry.hx b/source/funkin/data/character/CharacterRegistry.hx index 00065ef520..4aa0a907c5 100644 --- a/source/funkin/data/character/CharacterRegistry.hx +++ b/source/funkin/data/character/CharacterRegistry.hx @@ -2,6 +2,7 @@ package funkin.data.character; import funkin.data.animation.AnimationData; import funkin.data.character.CharacterData; +import funkin.data.character.migrator.CharacterData_v1_0_0; import funkin.modding.events.ScriptEvent; import funkin.modding.events.ScriptEventDispatcher; import funkin.play.character.AnimateAtlasCharacter; @@ -19,6 +20,8 @@ import funkin.util.VersionUtil; import haxe.Json; import flixel.graphics.frames.FlxFrame; +using funkin.data.character.migrator.CharacterDataMigrator; + /** * NOTE: This doesn't act the same as the other registries. * It doesn't implement `BaseRegistry` and fetching produces a new instance rather than reusing them. @@ -388,24 +391,24 @@ class CharacterRegistry { var rawJson:JsonFile = loadCharacterFile(charId); - var charData:CharacterData = buildCharacterData(rawJson, charId); + var version = parseVersion(rawJson); - if (charData == null) + if (version == null) { - // trace('[CHARACTER] Could not load character data for "$charId", check above for potential errors'); return null; } - if (CHARACTER_DATA_VERSION_RULE == null || VersionUtil.validateVersionStr(charData.version, CHARACTER_DATA_VERSION_RULE)) + + if (CHARACTER_DATA_VERSION_RULE == null || VersionUtil.validateVersionStr(version, CHARACTER_DATA_VERSION_RULE)) + { + return buildCharacterData(rawJson, charId); + } + else if (VersionUtil.validateVersion(version, "1.0.x")) { - return charData; + return buildCharacterData_v1_0_0(rawJson, charId); } - // else if (VersionUtil.validateVersion(charData.version, "1.0.x")) - // { - // return migrateCharacterData_v1_0_0(charData, charId); - // } else { - trace('[CHARACTER] Could not load character data for "$charId": bad version (got ${charData.version}, expected ${CHARACTER_DATA_VERSION_RULE})'); + trace('[CHARACTER] Could not load character data for "$charId": bad version (got ${version}, expected ${CHARACTER_DATA_VERSION_RULE})'); return null; } } @@ -426,6 +429,28 @@ class CharacterRegistry }; } + static function parseVersion(rawJson:JsonFile):Null + { + var parser = new json2object.JsonParser(); + parser.ignoreUnknownVariables = true; + + switch (rawJson) + { + case {fileName: fileName, contents: contents}: + parser.fromJson(contents, fileName); + default: + return null; + } + + if (parser.errors.length > 0) + { + trace(parser.errors, "NO VERSION"); + return null; + } + + return parser.value.version; + } + static function buildCharacterData(rawJson:JsonFile, charId:String):Null { var parser = new json2object.JsonParser(); @@ -448,6 +473,28 @@ class CharacterRegistry return parser.value; } + static function buildCharacterData_v1_0_0(rawJson:JsonFile, charId:String):Null + { + var parser = new json2object.JsonParser(); + parser.ignoreUnknownVariables = false; + + switch (rawJson) + { + case {fileName: fileName, contents: contents}: + parser.fromJson(contents, fileName); + default: + return null; + } + + if (parser.errors.length > 0) + { + trace(parser.errors, charId); + return null; + } + + return parser.value.migrate(); + } + /** * The default time the character should sing for, in steps. * Values that are too low will cause the character to stop singing between notes. @@ -640,3 +687,8 @@ class CharacterRegistry return input; } } + +typedef JsonVersionGet = +{ + var version:String; +} diff --git a/source/funkin/data/character/migrator/CharacterDataMigrator.hx b/source/funkin/data/character/migrator/CharacterDataMigrator.hx index 324ef14d02..ea941096ce 100644 --- a/source/funkin/data/character/migrator/CharacterDataMigrator.hx +++ b/source/funkin/data/character/migrator/CharacterDataMigrator.hx @@ -3,21 +3,31 @@ package funkin.data.character.migrator; import funkin.data.character.CharacterData; import funkin.data.character.CharacterRegistry; import funkin.data.animation.AnimationData; +import funkin.data.character.migrator.CharacterData_v1_0_0; class CharacterDataMigrator { - public static inline function migrate(input:CharacterData_v1_0_0.CharacterData_v1_0_0):CharacterData + public static overload extern inline function migrate(input:CharacterData_v1_0_0):CharacterData { return migrate_CharacterData_v1_0_0(input); } - public static function migrate_CharacterData_v1_0_0(input:CharacterData_v1_0_0.CharacterData_v1_0_0):CharacterData + public static function migrate_CharacterData_v1_0_0(input:CharacterData_v1_0_0):CharacterData { + var assetPaths:Array = [input.assetPath]; + for (animation in input.animations) + { + if (animation.assetPath != null) + { + assetPaths.pushUnique(animation.assetPath); + } + } + return { version: CharacterRegistry.CHARACTER_DATA_VERSION, name: input.name, renderType: input.renderType, - assetPaths: [input.assetPath], + assetPaths: assetPaths, scale: input.scale, healthIcon: input.healthIcon, death: input.death, @@ -32,7 +42,7 @@ class CharacterDataMigrator }; } - static function migrate_AnimationData_v1_0_0(input:Array):Array + static function migrate_AnimationData_v1_0_0(input:Array):Array { var animations:Array = []; for (animation in input) diff --git a/source/funkin/data/character/migrator/CharacterData_v1_0_0.hx b/source/funkin/data/character/migrator/CharacterData_v1_0_0.hx index 0f1317bf51..1707ec239d 100644 --- a/source/funkin/data/character/migrator/CharacterData_v1_0_0.hx +++ b/source/funkin/data/character/migrator/CharacterData_v1_0_0.hx @@ -2,187 +2,90 @@ package funkin.data.character.migrator; import funkin.data.character.CharacterData; -/** - * The JSON data schema used to define a character. - */ typedef CharacterData_v1_0_0 = { - /** - * The sematic version number of the character data JSON format. - */ var version:String; - /** - * The readable name of the character. - */ var name:String; - /** - * The type of rendering system to use for the character. - * @default sparrow - */ + @:optional + @:default('sparrow') var renderType:CharacterRenderType; - /** - * Behavior varies by render type: - * - SPARROW: Path to retrieve both the spritesheet and the XML data from. - * - PACKER: Path to retrieve both the spritsheet and the TXT data from. - */ var assetPath:String; - /** - * The scale of the graphic as a float. - * Pro tip: On pixel-art levels, save the sprites small and set this value to 6 or so to save memory. - * @default 1 - */ + @:optional + @:default(1.0) var scale:Null; - /** - * Optional data about the health icon for the character. - */ + @:optional var healthIcon:Null; + @:optional var death:Null; - /** - * The global offset to the character's position, in pixels. - * @default [0, 0] - */ + @:optional + @:default([0, 0]) var offsets:Null>; - /** - * The amount to offset the camera by while focusing on this character. - * Default value focuses on the character directly. - * @default [0, 0] - */ + @:optional + @:default([0, 0]) var cameraOffsets:Array; - /** - * Setting this to true disables anti-aliasing for the character. - * @default false - */ + @:optional + @:default(false) var isPixel:Null; - /** - * The frequency at which the character will play its idle animation, in beats. - * Increasing this number will make the character dance less often. - * Supports up to `0.25` precision. - * @default `1.0` on characters - */ @:optional @:default(1.0) var danceEvery:Null; - /** - * The minimum duration that a character will play a note animation for, in beats. - * If this number is too low, you may see the character start playing the idle animation between notes. - * If this number is too high, you may see the the character play the sing animation for too long after the notes are gone. - * - * Examples: - * - Daddy Dearest uses a value of `1.525`. - * @default 1.0 - */ + @:optional + @:default(8.0) var singTime:Null; - /** - * An optional array of animations which the character can play. - */ var animations:Array; - /** - * If animations are used, this is the name of the animation to play first. - * @default idle - */ + @:optional + @:default('idle') var startingAnimation:Null; - /** - * Whether or not the whole ass sprite is flipped by default. - * Useful for characters that could also be played (Pico) - * - * @default false - */ + @:optional + @:default(false) var flipX:Null; }; -/** - * A data structure representing an animation in a spritesheet. - * This is a generic data structure used by characters, stage props, and more! - * BE CAREFUL when changing it. - */ typedef AnimationData_v1_0_0 = { - /** - * The prefix for the frames of the animation as defined by the XML file. - * This will may or may not differ from the `name` of the animation, - * depending on how your animator organized their FLA or whatever. - * - * NOTE: For Sparrow animations, this is not optional, but for Packer animations it is. - */ @:optional var prefix:String; - /** - * Optionally specify an asset path to use for this specific animation. - * ONLY for use by MultiSparrow characters. - * @default The assetPath of the parent sprite - */ @:optional var assetPath:Null; - /** - * Offset the character's position by this amount when playing this animation. - * @default [0, 0] - */ @:default([0, 0]) @:optional var offsets:Null>; - /** - * Whether the animation should loop when it finishes. - * @default false - */ @:default(false) @:optional var looped:Bool; - /** - * Whether the animation's sprites should be flipped horizontally. - * @default false - */ @:default(false) @:optional var flipX:Null; - /** - * Whether the animation's sprites should be flipped vertically. - * @default false - */ @:default(false) @:optional var flipY:Null; - /** - * The frame rate of the animation. - * @default 24 - */ @:default(24) @:optional var frameRate:Null; - /** - * 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) - */ @:default([]) @:optional var frameIndices:Null>; - /** - * The name for the animation. - * This should match the animation name queried by the game; - * for example, characters need animations with names `idle`, `singDOWN`, `singUPmiss`, etc. - */ var name:String; } diff --git a/source/funkin/play/character/MultiSparrowCharacter.hx b/source/funkin/play/character/MultiSparrowCharacter.hx index 84d2ee20f5..f7cd507c53 100644 --- a/source/funkin/play/character/MultiSparrowCharacter.hx +++ b/source/funkin/play/character/MultiSparrowCharacter.hx @@ -53,8 +53,6 @@ class MultiSparrowCharacter extends BaseCharacter function buildSpritesheet():Void { - var assetList = []; - var texture:FlxAtlasFrames = Paths.getSparrowAtlas(_data.assetPaths[0]); if (texture == null) @@ -69,19 +67,24 @@ class MultiSparrowCharacter extends BaseCharacter texture.parent.destroyOnNoUse = false; } - for (asset in assetList) + for (i => asset in _data.assetPaths) { + if (i == 0) + { + continue; + } + var subTexture:FlxAtlasFrames = Paths.getSparrowAtlas(asset); // If we don't do this, the unused textures will be removed as soon as they're loaded. if (subTexture == null) { trace('Multi-Sparrow atlas could not load subtexture: ${asset}'); + continue; } else { trace('Concatenating multi-sparrow atlas: ${asset}'); - subTexture.parent.destroyOnNoUse = false; } texture.addAtlas(subTexture);