Replace FlxAnimate with flixel-animate
Co-Authored-By: Hyper_ <40342021+NotHyper-474@users.noreply.github.com> Co-Authored-By: MaybeMaru <97055307+MaybeMaru@users.noreply.github.com> Co-Authored-By: Trayfellow <48515908+trayfellow@users.noreply.github.com>
This commit is contained in:
committed by
Hundrec
co-authored by
Hyper_
MaybeMaru
Trayfellow
parent
fe6f5eb821
commit
fcfde61b24
+1
-1
Submodule assets updated: d5de6a05fa...91af5e79b2
@@ -56,11 +56,11 @@
|
||||
"url": "https://github.com/FunkinCrew/flixel-addons"
|
||||
},
|
||||
{
|
||||
"name": "flxanimate",
|
||||
"name": "flixel-animate",
|
||||
"type": "git",
|
||||
"dir": null,
|
||||
"ref": "49214278b9124823582cdcecd94f4a1de9a4b36b",
|
||||
"url": "https://github.com/FunkinCrew/flxanimate"
|
||||
"ref": "9aacdf7afe5ffdcd086a2898ebb7abb966b95a3d",
|
||||
"url": "https://github.com/MaybeMaru/flixel-animate"
|
||||
},
|
||||
{
|
||||
"name": "format",
|
||||
|
||||
+1
-1
@@ -1304,7 +1304,7 @@ class Project extends HXProject
|
||||
}
|
||||
|
||||
addHaxelib('polymod'); // Modding framework
|
||||
addHaxelib('flxanimate'); // Texture atlas rendering
|
||||
addHaxelib('flixel-animate'); // Texture atlas rendering
|
||||
|
||||
addHaxelib('json2object'); // JSON parsing
|
||||
addHaxelib('jsonpath'); // JSON parsing
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
package funkin;
|
||||
|
||||
import openfl.utils.Future;
|
||||
import funkin.util.macro.ConsoleMacro;
|
||||
|
||||
/**
|
||||
* A wrapper around `openfl.utils.Assets` which disallows access to the harmful functions.
|
||||
* Later we'll add Funkin-specific caching to this.
|
||||
*/
|
||||
@:nullSafety
|
||||
class Assets
|
||||
class Assets implements ConsoleClass
|
||||
{
|
||||
/**
|
||||
* The assets cache.
|
||||
|
||||
@@ -111,6 +111,16 @@ class FunkinMemory
|
||||
|
||||
///// TEXTURES /////
|
||||
|
||||
/**
|
||||
* Determine whether the texture with the given key is cached.
|
||||
* @param key The key of the texture to check.
|
||||
* @return Whether the texture is cached.
|
||||
*/
|
||||
public static function isTextureCached(key:String):Bool
|
||||
{
|
||||
return FlxG.bitmap.get(key) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures a texture with the given key is cached.
|
||||
* @param key The key of the texture to cache.
|
||||
@@ -233,6 +243,7 @@ class FunkinMemory
|
||||
if (graphic != null)
|
||||
{
|
||||
FlxG.bitmap.remove(graphic);
|
||||
graphic.persist = false;
|
||||
graphic.destroy();
|
||||
previousCachedTextures.remove(graphicKey);
|
||||
Assets.cache.clear(graphicKey);
|
||||
@@ -250,7 +261,7 @@ class FunkinMemory
|
||||
{
|
||||
var obj:Null<FlxGraphic> = FlxG.bitmap.get(key);
|
||||
|
||||
if (obj == null || obj.persist || permanentCachedTextures.exists(key) || key.contains("fonts"))
|
||||
if (obj == null || (obj.persist && permanentCachedTextures.exists(key)) || key.contains("fonts"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -262,6 +273,7 @@ class FunkinMemory
|
||||
if (key.contains(purgeEntry))
|
||||
{
|
||||
FlxG.bitmap.removeKey(key);
|
||||
obj.persist = false;
|
||||
obj.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,13 +2,14 @@ package funkin;
|
||||
|
||||
import flixel.graphics.frames.FlxAtlasFrames;
|
||||
import openfl.utils.AssetType;
|
||||
import funkin.util.macro.ConsoleMacro;
|
||||
import haxe.io.Path;
|
||||
|
||||
/**
|
||||
* A core class which handles determining asset paths.
|
||||
*/
|
||||
@:nullSafety
|
||||
class Paths
|
||||
class Paths implements ConsoleClass
|
||||
{
|
||||
static var currentLevel:Null<String> = null;
|
||||
|
||||
|
||||
@@ -83,7 +83,7 @@ typedef UnnamedAnimationData =
|
||||
|
||||
/**
|
||||
* Optionally specify an asset path to use for this specific animation.
|
||||
* ONLY for use by MultiSparrow characters.
|
||||
* ONLY for use by MultiSparrow and MultiAnimateAtlas characters.
|
||||
* @default The assetPath of the parent sprite
|
||||
*/
|
||||
@:optional
|
||||
@@ -138,4 +138,13 @@ typedef UnnamedAnimationData =
|
||||
@:default([])
|
||||
@:optional
|
||||
var frameIndices:Null<Array<Int>>;
|
||||
|
||||
/**
|
||||
* The type of animation to use.
|
||||
* Only available for texture atlases.
|
||||
* Options: "framelabel", "symbol"
|
||||
*/
|
||||
@:default("framelabel")
|
||||
@:optional
|
||||
var animType:String;
|
||||
}
|
||||
|
||||
@@ -6,12 +6,14 @@ import funkin.modding.events.ScriptEventDispatcher;
|
||||
import funkin.play.character.ScriptedCharacter.ScriptedAnimateAtlasCharacter;
|
||||
import funkin.play.character.ScriptedCharacter.ScriptedBaseCharacter;
|
||||
import funkin.play.character.ScriptedCharacter.ScriptedMultiSparrowCharacter;
|
||||
import funkin.play.character.ScriptedCharacter.ScriptedMultiAnimateAtlasCharacter;
|
||||
import funkin.play.character.ScriptedCharacter.ScriptedPackerCharacter;
|
||||
import funkin.play.character.ScriptedCharacter.ScriptedSparrowCharacter;
|
||||
import funkin.play.character.AnimateAtlasCharacter;
|
||||
import funkin.play.character.BaseCharacter;
|
||||
import funkin.play.character.SparrowCharacter;
|
||||
import funkin.play.character.MultiSparrowCharacter;
|
||||
import funkin.play.character.MultiAnimateAtlasCharacter;
|
||||
import funkin.play.character.PackerCharacter;
|
||||
import funkin.util.assets.DataAssets;
|
||||
import funkin.util.VersionUtil;
|
||||
@@ -160,6 +162,25 @@ class CharacterDataParser
|
||||
}
|
||||
}
|
||||
|
||||
var scriptedCharClassNames5:Array<String> = ScriptedMultiAnimateAtlasCharacter.listScriptClasses();
|
||||
if (scriptedCharClassNames5.length > 0)
|
||||
{
|
||||
trace(' Instantiating ${scriptedCharClassNames5.length} (Multi-Animate Atlas) scripted characters...');
|
||||
for (charCls in scriptedCharClassNames5)
|
||||
{
|
||||
try
|
||||
{
|
||||
var character:MultiAnimateAtlasCharacter = ScriptedMultiAnimateAtlasCharacter.init(charCls, DEFAULT_CHAR_ID);
|
||||
characterScriptedClass.set(character.characterId, charCls);
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
trace(' FAILED to instantiate scripted Multi-Animate Atlas character: ${charCls}');
|
||||
trace(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Only instantiate the ones not populated above.
|
||||
// ScriptedBaseCharacter.listScriptClasses() will pick up scripts extending the other classes.
|
||||
var scriptedCharClassNames:Array<String> = ScriptedBaseCharacter.listScriptClasses();
|
||||
@@ -167,7 +188,8 @@ class CharacterDataParser
|
||||
return !(scriptedCharClassNames1.contains(charCls)
|
||||
|| scriptedCharClassNames2.contains(charCls)
|
||||
|| scriptedCharClassNames3.contains(charCls)
|
||||
|| scriptedCharClassNames4.contains(charCls));
|
||||
|| scriptedCharClassNames4.contains(charCls)
|
||||
|| scriptedCharClassNames5.contains(charCls));
|
||||
});
|
||||
|
||||
if (scriptedCharClassNames.length > 0)
|
||||
@@ -226,6 +248,8 @@ class CharacterDataParser
|
||||
char = ScriptedSparrowCharacter.init(charScriptClass, charId);
|
||||
case CharacterRenderType.Packer:
|
||||
char = ScriptedPackerCharacter.init(charScriptClass, charId);
|
||||
case CharacterRenderType.MultiAnimateAtlas:
|
||||
char = ScriptedMultiAnimateAtlasCharacter.init(charScriptClass, charId);
|
||||
default:
|
||||
// We're going to assume that the script class does the rendering.
|
||||
char = ScriptedBaseCharacter.init(charScriptClass, charId, CharacterRenderType.Custom);
|
||||
@@ -243,6 +267,8 @@ class CharacterDataParser
|
||||
char = new SparrowCharacter(charId);
|
||||
case CharacterRenderType.Packer:
|
||||
char = new PackerCharacter(charId);
|
||||
case CharacterRenderType.MultiAnimateAtlas:
|
||||
char = new MultiAnimateAtlasCharacter(charId);
|
||||
default:
|
||||
trace(' WARNING '.bold().bg_yellow() + ' Creating character with undefined renderType ${charData.renderType}');
|
||||
char = new BaseCharacter(charId, CharacterRenderType.Custom);
|
||||
@@ -431,6 +457,15 @@ class CharacterDataParser
|
||||
public static final DEFAULT_SCALE:Float = 1;
|
||||
public static final DEFAULT_SCROLL:Array<Float> = [0, 0];
|
||||
public static final DEFAULT_STARTINGANIM:String = 'idle';
|
||||
public static final DEFAULT_APPLYSTAGEMATRIX:Bool = false;
|
||||
public static final DEFAULT_ANIMTYPE:String = "framelabel";
|
||||
public static final DEFAULT_ATLASSETTINGS:funkin.data.stage.StageData.TextureAtlasData =
|
||||
{
|
||||
swfMode: true,
|
||||
cacheOnLoad: false,
|
||||
filterQuality: 1,
|
||||
applyStageMatrix: false
|
||||
};
|
||||
|
||||
/**
|
||||
* Set unspecified parameters to their defaults.
|
||||
@@ -559,6 +594,16 @@ class CharacterDataParser
|
||||
input.flipX = DEFAULT_FLIPX;
|
||||
}
|
||||
|
||||
if (input.applyStageMatrix == null)
|
||||
{
|
||||
input.applyStageMatrix = DEFAULT_APPLYSTAGEMATRIX;
|
||||
}
|
||||
|
||||
if (input.atlasSettings == null)
|
||||
{
|
||||
input.atlasSettings = DEFAULT_ATLASSETTINGS;
|
||||
}
|
||||
|
||||
if (input.animations.length == 0 && input.startingAnimation != null)
|
||||
{
|
||||
return null;
|
||||
@@ -596,6 +641,11 @@ class CharacterDataParser
|
||||
{
|
||||
inputAnimation.flipY = DEFAULT_FLIPY;
|
||||
}
|
||||
|
||||
if (inputAnimation.animType == null)
|
||||
{
|
||||
inputAnimation.animType = DEFAULT_ANIMTYPE;
|
||||
}
|
||||
}
|
||||
|
||||
// All good!
|
||||
@@ -624,10 +674,15 @@ enum abstract CharacterRenderType(String) from String to String
|
||||
public var MultiSparrow = 'multisparrow';
|
||||
|
||||
/**
|
||||
* Renders the character using a spritesheet of symbols and JSON data.
|
||||
* Renders the character using a single spritesheet of symbols and JSON data.
|
||||
*/
|
||||
public var AnimateAtlas = 'animateatlas';
|
||||
|
||||
/**
|
||||
* Renders the character using multiple spritesheets of symbols and JSON data.
|
||||
*/
|
||||
public var MultiAnimateAtlas = 'multianimateatlas';
|
||||
|
||||
/**
|
||||
* Renders the character using a custom method.
|
||||
*/
|
||||
@@ -674,6 +729,9 @@ typedef CharacterData =
|
||||
*/
|
||||
var healthIcon:Null<HealthIconData>;
|
||||
|
||||
/**
|
||||
* Optional data about the death animation for the character.
|
||||
*/
|
||||
var death:Null<DeathData>;
|
||||
|
||||
/**
|
||||
@@ -734,6 +792,23 @@ typedef CharacterData =
|
||||
* @default false
|
||||
*/
|
||||
var flipX:Null<Bool>;
|
||||
|
||||
/**
|
||||
* NOTE: This only applies to animate atlas characters.
|
||||
*
|
||||
* Whether to apply the stage matrix, if it was exported from a symbol instance.
|
||||
* Also positions the Texture Atlas as it displays in Animate.
|
||||
* Turning this on is only recommended if you prepositioned the character in Animate.
|
||||
* For other cases, it should be turned off to act similarly to a normal FlxSprite.
|
||||
*/
|
||||
var applyStageMatrix:Null<Bool>;
|
||||
|
||||
/**
|
||||
* Various settings for the prop.
|
||||
* Only available for texture atlases.
|
||||
*/
|
||||
@:optional
|
||||
var atlasSettings:funkin.data.stage.StageData.TextureAtlasData;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -103,6 +103,10 @@ class PlayerFreeplayDJData
|
||||
var assetPath:String;
|
||||
var animations:Array<AnimationData>;
|
||||
|
||||
@:optional
|
||||
@:default(false)
|
||||
var applyStageMatrix:Bool;
|
||||
|
||||
@:optional
|
||||
@:default("BOYFRIEND")
|
||||
var text1:String;
|
||||
@@ -158,11 +162,16 @@ class PlayerFreeplayDJData
|
||||
}
|
||||
|
||||
public inline function getAssetPath():String
|
||||
return assetPath; // return Paths.animateAtlas(assetPath);
|
||||
return assetPath; // return assetPath;
|
||||
|
||||
public inline function getAnimationsList():Array<AnimationData>
|
||||
return animations;
|
||||
|
||||
public function useApplyStageMatrix():Bool
|
||||
{
|
||||
return applyStageMatrix;
|
||||
}
|
||||
|
||||
public function getFreeplayDJText(index:Int):String
|
||||
{
|
||||
switch (index)
|
||||
@@ -343,6 +352,10 @@ typedef PlayerResultsAnimationData =
|
||||
*/
|
||||
var renderType:String;
|
||||
|
||||
@:optional
|
||||
@:default(false)
|
||||
var applyStageMatrix:Bool;
|
||||
|
||||
@:optional
|
||||
var assetPath:Null<String>;
|
||||
|
||||
|
||||
@@ -196,7 +196,7 @@ typedef StageDataProp =
|
||||
|
||||
/**
|
||||
* The animation type to use.
|
||||
* Options: "sparrow", "packer"
|
||||
* Options: "sparrow", "packer", "animateatlas"
|
||||
* @default "sparrow"
|
||||
*/
|
||||
@:default("sparrow")
|
||||
@@ -228,6 +228,52 @@ typedef StageDataProp =
|
||||
@:default("#FFFFFF")
|
||||
@:optional
|
||||
var color:String;
|
||||
|
||||
/**
|
||||
* Various settings for the prop.
|
||||
* Only available for texture atlases.
|
||||
*/
|
||||
@:optional
|
||||
var atlasSettings:TextureAtlasData;
|
||||
};
|
||||
|
||||
typedef TextureAtlasData =
|
||||
{
|
||||
/**
|
||||
* If true, the texture atlas will behave as if it was exported as an SWF file.
|
||||
* Notably, this allows MovieClip symbols to play.
|
||||
*/
|
||||
@:optional
|
||||
var swfMode:Bool;
|
||||
|
||||
/**
|
||||
* If true, filters and masks will be cached when the atlas is loaded, instead of during runtime.
|
||||
*/
|
||||
@:optional
|
||||
var cacheOnLoad:Bool;
|
||||
|
||||
/**
|
||||
* The filter quality.
|
||||
* Available values are: HIGH, MEDIUM, LOW, and RUDY.
|
||||
*
|
||||
* If you're making an atlas sprite in HScript, you pass an Int instead:
|
||||
*
|
||||
* HIGH - 0
|
||||
* MEDIUM - 1
|
||||
* LOW - 2
|
||||
* RUDY - 3
|
||||
*/
|
||||
@:optional
|
||||
var filterQuality:Int;
|
||||
|
||||
/**
|
||||
* Whether to apply the stage matrix, if it was exported from a symbol instance.
|
||||
* Also positions the Texture Atlas as it displays in Animate.
|
||||
* Turning this on is only recommended if you prepositioned the character in Animate.
|
||||
* For other cases, it should be turned off to act similarly to a normal FlxSprite.
|
||||
*/
|
||||
@:optional
|
||||
var applyStageMatrix:Bool;
|
||||
};
|
||||
|
||||
typedef StageDataCharacter =
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package funkin.graphics;
|
||||
|
||||
import funkin.graphics.FunkinSprite;
|
||||
import animate.FlxAnimateController;
|
||||
|
||||
class FunkinAnimationController extends FlxAnimateController
|
||||
{
|
||||
/**
|
||||
* The sprite that this animation controller is attached to.
|
||||
*/
|
||||
var _parentSprite:FunkinSprite;
|
||||
|
||||
public function new(sprite:FunkinSprite)
|
||||
{
|
||||
super(sprite);
|
||||
_parentSprite = sprite;
|
||||
}
|
||||
|
||||
/**
|
||||
* We override `FlxAnimationController`'s `play` method to account for texture atlases.
|
||||
*/
|
||||
public override function play(animName:String, force = false, reversed = false, frame = 0):Void
|
||||
{
|
||||
if (animName == null || animName == '') animName = _parentSprite.getDefaultSymbol();
|
||||
|
||||
if (!_parentSprite.hasAnimation(animName))
|
||||
{
|
||||
// Skip if the animation doesn't exist
|
||||
trace('Animation ${animName} does not exist!');
|
||||
return;
|
||||
}
|
||||
|
||||
super.play(animName, force, reversed, frame);
|
||||
}
|
||||
}
|
||||
@@ -9,20 +9,97 @@ import funkin.graphics.framebuffer.FixedBitmapData;
|
||||
import openfl.display.BitmapData;
|
||||
import flixel.math.FlxRect;
|
||||
import flixel.math.FlxPoint;
|
||||
import flixel.graphics.frames.FlxFrame.FlxFrameAngle;
|
||||
import flixel.math.FlxMatrix;
|
||||
import flixel.graphics.frames.FlxFrame;
|
||||
import flixel.FlxCamera;
|
||||
import openfl.system.System;
|
||||
import flixel.system.FlxAssets.FlxGraphicAsset;
|
||||
import funkin.FunkinMemory;
|
||||
import animate.internal.SymbolItem;
|
||||
import animate.internal.elements.Element;
|
||||
import animate.internal.elements.AtlasInstance;
|
||||
import animate.internal.elements.SymbolInstance;
|
||||
import animate.FlxAnimate;
|
||||
import animate.FlxAnimateFrames;
|
||||
|
||||
using StringTools;
|
||||
|
||||
typedef AtlasSpriteSettings =
|
||||
{
|
||||
/**
|
||||
* If true, the texture atlas will behave as if it was exported as an SWF file.
|
||||
* Notably, this allows MovieClip symbols to play.
|
||||
*/
|
||||
@:optional
|
||||
var swfMode:Bool;
|
||||
|
||||
/**
|
||||
* If true, filters and masks will be cached when the atlas is loaded, instead of during runtime.
|
||||
*/
|
||||
@:optional
|
||||
var cacheOnLoad:Bool;
|
||||
|
||||
/**
|
||||
* The filter quality.
|
||||
* Available values are: HIGH, MEDIUM, LOW, and RUDY.
|
||||
*
|
||||
* If you're making an atlas sprite in HScript, you pass an Int instead:
|
||||
*
|
||||
* HIGH - 0
|
||||
* MEDIUM - 1
|
||||
* LOW - 2
|
||||
* RUDY - 3
|
||||
*/
|
||||
@:optional
|
||||
var filterQuality:FilterQuality;
|
||||
|
||||
/**
|
||||
* Optional, an array of spritemaps for the atlas to load.
|
||||
*/
|
||||
@:optional
|
||||
var spritemaps:Array<SpritemapInput>;
|
||||
|
||||
/**
|
||||
* Optional, string of the metadata.json contents.
|
||||
*/
|
||||
@:optional
|
||||
var metadataJson:String;
|
||||
|
||||
/**
|
||||
* Optional, force the cache to use a specific key to index the texture atlas.
|
||||
*/
|
||||
@:optional
|
||||
var cacheKey:String;
|
||||
|
||||
/**
|
||||
* If true, the texture atlas will use a new slot in the cache.
|
||||
*/
|
||||
@:optional
|
||||
var uniqueInCache:Bool;
|
||||
|
||||
/**
|
||||
* Optional callback for when a symbol is created.
|
||||
*/
|
||||
@:optional
|
||||
var onSymbolCreate:animate.internal.SymbolItem->Void;
|
||||
|
||||
/**
|
||||
* Whether to apply the stage matrix, if it was exported from a symbol instance.
|
||||
* Also positions the Texture Atlas as it displays in Animate.
|
||||
* Turning this on is only recommended if you prepositioned the character in Animate.
|
||||
* For other cases, it should be turned off to act similarly to a normal FlxSprite.
|
||||
*/
|
||||
@:optional
|
||||
var applyStageMatrix:Bool;
|
||||
}
|
||||
|
||||
/**
|
||||
* An FlxSprite with additional functionality.
|
||||
* - A more efficient method for creating solid color sprites.
|
||||
* - TODO: Better cache handling for textures.
|
||||
*/
|
||||
@:nullSafety
|
||||
class FunkinSprite extends FlxSprite
|
||||
class FunkinSprite extends FlxAnimate
|
||||
{
|
||||
/**
|
||||
* @param x Starting X position
|
||||
@@ -33,6 +110,16 @@ class FunkinSprite extends FlxSprite
|
||||
super(x, y);
|
||||
}
|
||||
|
||||
override function initVars():Void
|
||||
{
|
||||
super.initVars();
|
||||
|
||||
var newController:FunkinAnimationController = new FunkinAnimationController(this);
|
||||
|
||||
animation = newController;
|
||||
anim = newController;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new FunkinSprite with a static texture.
|
||||
* @param x The starting X position.
|
||||
@@ -75,6 +162,20 @@ class FunkinSprite extends FlxSprite
|
||||
return sprite;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new FunkinSprite with an Adobe Animate texture atlas.
|
||||
* @param x The starting X position.
|
||||
* @param y The starting Y position.
|
||||
* @param key The key of the texture to load.
|
||||
* @return The new FunkinSprite.
|
||||
*/
|
||||
public static function createTextureAtlas(x:Float = 0.0, y:Float = 0.0, key:String, ?assetLibrary:Null<String>, ?settings:AtlasSpriteSettings):FunkinSprite
|
||||
{
|
||||
var sprite:FunkinSprite = new FunkinSprite(x, y);
|
||||
sprite.loadTextureAtlas(key, assetLibrary ?? "", settings);
|
||||
return sprite;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a static image as the sprite's texture.
|
||||
* @param key The key of the texture to load.
|
||||
@@ -83,7 +184,17 @@ class FunkinSprite extends FlxSprite
|
||||
public function loadTexture(key:String):FunkinSprite
|
||||
{
|
||||
var graphicKey:String = Paths.image(key);
|
||||
if (!isTextureCached(graphicKey)) FlxG.log.warn('Texture not cached, may experience stuttering! $graphicKey');
|
||||
|
||||
if (!Assets.exists(graphicKey, IMAGE))
|
||||
{
|
||||
FlxG.log.error('Texture not found, check your path! $graphicKey');
|
||||
return this;
|
||||
}
|
||||
|
||||
if (!FunkinMemory.isTextureCached(graphicKey))
|
||||
{
|
||||
FlxG.log.warn('Texture not cached, may experience stuttering! $graphicKey');
|
||||
}
|
||||
|
||||
loadGraphic(graphicKey);
|
||||
|
||||
@@ -163,6 +274,63 @@ class FunkinSprite extends FlxSprite
|
||||
return loadBitmapData(inputBitmap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads an Adobe Animate texture atlas as the sprite's texture.
|
||||
* @param key The key of the texture to load.
|
||||
* @param settings Additional settings for loading the atlas.
|
||||
* @return This sprite, for chaining.
|
||||
*/
|
||||
public function loadTextureAtlas(key:Null<String>, ?assetLibrary:Null<String>, ?settings:AtlasSpriteSettings):FunkinSprite
|
||||
{
|
||||
if (key == null)
|
||||
{
|
||||
throw 'Null path specified for loadTextureAtlas()!';
|
||||
}
|
||||
|
||||
var validatedSettings:AtlasSpriteSettings =
|
||||
{
|
||||
swfMode: settings?.swfMode ?? false,
|
||||
cacheOnLoad: settings?.cacheOnLoad ?? false,
|
||||
filterQuality: settings?.filterQuality ?? MEDIUM,
|
||||
spritemaps: settings?.spritemaps ?? null,
|
||||
metadataJson: settings?.metadataJson ?? null,
|
||||
cacheKey: settings?.cacheKey ?? null,
|
||||
uniqueInCache: settings?.uniqueInCache ?? false,
|
||||
onSymbolCreate: settings?.onSymbolCreate ?? null,
|
||||
applyStageMatrix: settings?.applyStageMatrix ?? false
|
||||
};
|
||||
|
||||
var assetLibrary:String = assetLibrary ?? "";
|
||||
var graphicKey:String = "";
|
||||
|
||||
if (assetLibrary != "")
|
||||
{
|
||||
graphicKey = Paths.animateAtlas(key, assetLibrary);
|
||||
}
|
||||
else
|
||||
{
|
||||
graphicKey = Paths.animateAtlas(key);
|
||||
}
|
||||
|
||||
// Validate asset path.
|
||||
if (!Assets.exists('${graphicKey}/Animation.json'))
|
||||
{
|
||||
throw 'No Animation.json file exists at the specified path (${graphicKey})';
|
||||
}
|
||||
|
||||
this.applyStageMatrix = validatedSettings.applyStageMatrix ?? false;
|
||||
|
||||
frames = FlxAnimateFrames.fromAnimate(graphicKey, validatedSettings.spritemaps, validatedSettings.metadataJson, validatedSettings.cacheKey,
|
||||
validatedSettings.uniqueInCache, {
|
||||
swfMode: validatedSettings.swfMode,
|
||||
cacheOnLoad: validatedSettings.cacheOnLoad,
|
||||
filterQuality: validatedSettings.filterQuality,
|
||||
onSymbolCreate: validatedSettings.onSymbolCreate
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load an animated texture (Sparrow atlas spritesheet) as the sprite's texture.
|
||||
* @param key The key of the texture to load.
|
||||
@@ -171,7 +339,7 @@ class FunkinSprite extends FlxSprite
|
||||
public function loadSparrow(key:String):FunkinSprite
|
||||
{
|
||||
var graphicKey:String = Paths.image(key);
|
||||
if (!isTextureCached(graphicKey)) FlxG.log.warn('Texture not cached, may experience stuttering! $graphicKey');
|
||||
if (!FunkinMemory.isTextureCached(graphicKey)) FlxG.log.warn('Texture not cached, may experience stuttering! $graphicKey');
|
||||
|
||||
this.frames = Paths.getSparrowAtlas(key);
|
||||
|
||||
@@ -186,73 +354,13 @@ class FunkinSprite extends FlxSprite
|
||||
public function loadPacker(key:String):FunkinSprite
|
||||
{
|
||||
var graphicKey:String = Paths.image(key);
|
||||
if (!isTextureCached(graphicKey)) FlxG.log.warn('Texture not cached, may experience stuttering! $graphicKey');
|
||||
if (!FunkinMemory.isTextureCached(graphicKey)) FlxG.log.warn('Texture not cached, may experience stuttering! $graphicKey');
|
||||
|
||||
this.frames = Paths.getPackerAtlas(key);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the texture with the given key is cached.
|
||||
* @param key The key of the texture to check.
|
||||
* @return Whether the texture is cached.
|
||||
*/
|
||||
public static function isTextureCached(key:String):Bool
|
||||
{
|
||||
return FlxG.bitmap.get(key) != null;
|
||||
}
|
||||
|
||||
@:deprecated("Use FunkinMemory.cacheTexture() instead")
|
||||
public static function cacheTexture(key:String):Void
|
||||
{
|
||||
FunkinMemory.cacheTexture(Paths.image(key));
|
||||
}
|
||||
|
||||
@:deprecated("Use FunkinMemory.permanentCacheTexture() instead")
|
||||
public static function permanentCacheTexture(key:String):Void
|
||||
{
|
||||
@:privateAccess FunkinMemory.permanentCacheTexture(Paths.image(key));
|
||||
}
|
||||
|
||||
@:deprecated("Use FunkinMemory.cacheTexture() instead")
|
||||
public static function cacheSparrow(key:String):Void
|
||||
{
|
||||
FunkinMemory.cacheTexture(Paths.image(key));
|
||||
}
|
||||
|
||||
@:deprecated("Use FunkinMemory.cacheTexture() instead")
|
||||
public static function cachePacker(key:String):Void
|
||||
{
|
||||
FunkinMemory.cacheTexture(Paths.image(key));
|
||||
}
|
||||
|
||||
@:deprecated("Use FunkinMemory.preparePurgeTextureCache() instead")
|
||||
public static function preparePurgeCache():Void
|
||||
{
|
||||
FunkinMemory.preparePurgeTextureCache();
|
||||
}
|
||||
|
||||
@:deprecated("Use FunkinMemory.purgeCache() instead")
|
||||
public static function purgeCache():Void
|
||||
{
|
||||
FunkinMemory.purgeCache();
|
||||
}
|
||||
|
||||
static function isGraphicCached(graphic:FlxGraphic):Bool
|
||||
{
|
||||
var result = null;
|
||||
if (graphic == null) return false;
|
||||
result = FlxG.bitmap.get(graphic.key);
|
||||
if (result == null) return false;
|
||||
if (result != graphic)
|
||||
{
|
||||
FlxG.log.warn('Cached graphic does not match original: ${graphic.key}');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param id The animation ID to check.
|
||||
* @return Whether the animation is dynamic (has multiple frames). `false` for static, one-frame animations.
|
||||
@@ -266,6 +374,67 @@ class FunkinSprite extends FlxSprite
|
||||
return animData.numFrames > 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not this sprite has an animation with the given ID.
|
||||
* @param id The ID of the animation to check.
|
||||
*/
|
||||
public function hasAnimation(id:String):Bool
|
||||
{
|
||||
var animationList:Array<String> = this.animation.getNameList();
|
||||
if (animationList.contains(id))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (this.isAnimate && !animationList.contains(id))
|
||||
{
|
||||
return addAnimationIfMissing(id);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an animation if it doesn't exist.
|
||||
* @param id The animation ID to check.
|
||||
*/
|
||||
function addAnimationIfMissing(id:String):Bool
|
||||
{
|
||||
@:privateAccess
|
||||
var symbols:Array<String> = this.library.dictionary.keys().array();
|
||||
var frameLabels:Array<String> = listAnimations();
|
||||
|
||||
if (frameLabels.contains(id))
|
||||
{
|
||||
// Animation exists as a frame label but wasn't added, so we add it
|
||||
anim.addByFrameLabel(id, id, this.library.frameRate, false);
|
||||
return true;
|
||||
}
|
||||
else if (symbols.contains(id))
|
||||
{
|
||||
// Animation exists as a symbol but wasn't added, so we add it
|
||||
anim.addBySymbol(id, id, this.library.frameRate, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current animation ID.
|
||||
*/
|
||||
public function getCurrentAnimation():String
|
||||
{
|
||||
return this.animation.curAnim?.name ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not the current animation is finished.
|
||||
*/
|
||||
public function isAnimationFinished():Bool
|
||||
{
|
||||
return this.animation?.finished ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Acts similarly to `makeGraphic`, but with improved memory usage,
|
||||
* at the expense of not being able to paint onto the resulting sprite.
|
||||
@@ -286,6 +455,201 @@ class FunkinSprite extends FlxSprite
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A list of all the animations this sprite has available.
|
||||
*/
|
||||
public function listAnimations():Array<String>
|
||||
{
|
||||
var frameLabels:Array<String> = getFrameLabelList();
|
||||
var animationList:Array<String> = this.animation.getNameList();
|
||||
|
||||
return frameLabels.concat(animationList);
|
||||
}
|
||||
|
||||
/**
|
||||
* TEXTURE ATLAS-EXCLUSIVE FUNCTIONS
|
||||
* These functions only work if the sprite's texture is an Adobe Animate texture atlas.
|
||||
* Calling these functions on non-texture atlases will do nothing.
|
||||
*/
|
||||
/**
|
||||
* Gets a list of frame labels from the default timeline.
|
||||
*/
|
||||
public function getFrameLabelList():Array<String>
|
||||
{
|
||||
if (!this.isAnimate)
|
||||
{
|
||||
trace('WARNING: getFrameLabelList() only works texture atlases!');
|
||||
return [];
|
||||
}
|
||||
|
||||
var foundLabels:Array<String> = [];
|
||||
var mainTimeline:Null<animate.internal.Timeline> = this.library.timeline;
|
||||
|
||||
for (layer in mainTimeline.layers)
|
||||
{
|
||||
@:nullSafety(Off)
|
||||
for (frame in layer.frames)
|
||||
{
|
||||
if (frame.name.rtrim() != '')
|
||||
{
|
||||
foundLabels.push(frame.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return foundLabels;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a frame label by its name.
|
||||
* @param name The name of the frame label to retrieve.
|
||||
* @return The frame label, or null if it doesn't exist.
|
||||
*/
|
||||
public function getFrameLabel(name:String):Null<animate.internal.Frame>
|
||||
{
|
||||
if (!this.isAnimate)
|
||||
{
|
||||
trace('WARNING: getFrameLabel() only works texture atlases!');
|
||||
return null;
|
||||
}
|
||||
|
||||
for (layer in this.timeline.layers)
|
||||
{
|
||||
@:nullSafety(Off)
|
||||
for (frame in layer.frames)
|
||||
{
|
||||
if (frame.name == name)
|
||||
{
|
||||
return frame;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default symbol in the atlas.
|
||||
*/
|
||||
public function getDefaultSymbol():String
|
||||
{
|
||||
if (!this.isAnimate)
|
||||
{
|
||||
trace('WARNING: getDefaultSymbol() only works texture atlases!');
|
||||
return '';
|
||||
}
|
||||
|
||||
return library.timeline.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the graphic of a symbol in the atlas.
|
||||
* @param symbol The symbol to replace.
|
||||
* @param graphic The new graphic to use.
|
||||
* @param adjustScale Whether to adjust the scale of new frame to match the old one.
|
||||
*/
|
||||
public function replaceSymbolGraphic(symbol:String, ?graphic:Null<FlxGraphicAsset>, ?adjustScale:Bool = true):Void
|
||||
{
|
||||
if (!this.isAnimate)
|
||||
{
|
||||
trace('WARNING: replaceSymbolGraphic() only works texture atlases!');
|
||||
return;
|
||||
}
|
||||
|
||||
var elements:Array<Element> = getSymbolElements(symbol);
|
||||
|
||||
for (element in elements)
|
||||
{
|
||||
var atlasInstance:AtlasInstance = element.toAtlasInstance();
|
||||
var frame:Null<FlxFrame> = graphic != null ? FlxG.bitmap.add(graphic).imageFrame.frame : null;
|
||||
|
||||
atlasInstance.replaceFrame(frame, adjustScale);
|
||||
element = atlasInstance;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first element of a symbol in the atlas.
|
||||
* @param symbol The symbol to get elements from.
|
||||
* @return The first element of the symbol. WARNING: Can be null.
|
||||
*/
|
||||
public function getFirstElement(symbol:String):Null<Element>
|
||||
{
|
||||
if (!this.isAnimate)
|
||||
{
|
||||
trace('WARNING: getFirstElement() only works texture atlases!');
|
||||
return null;
|
||||
}
|
||||
|
||||
var symbolElements:Array<Element> = getSymbolElements(symbol);
|
||||
return symbolElements.length > 0 ? symbolElements[0] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the elements of a symbol in the atlas.
|
||||
* @param symbol The symbol to get elements from.
|
||||
*/
|
||||
public function getSymbolElements(symbol:String):Array<Element>
|
||||
{
|
||||
if (!this.isAnimate)
|
||||
{
|
||||
trace('WARNING: getSymbolElements() only works texture atlases!');
|
||||
return [];
|
||||
}
|
||||
|
||||
var symbolInstance:Null<SymbolItem> = this.library.getSymbol(symbol);
|
||||
|
||||
if (symbolInstance == null)
|
||||
{
|
||||
throw 'Symbol not found in atlas: ${symbol}';
|
||||
return [];
|
||||
}
|
||||
|
||||
var elements:Array<Element> = symbolInstance.timeline.getElementsAtIndex(0);
|
||||
|
||||
if (elements?.length == 0)
|
||||
{
|
||||
trace('WARNING: No Atlas Elements found for "$symbol" symbol.');
|
||||
}
|
||||
|
||||
return elements ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Scales an element by a certain multiplier.
|
||||
* @param element The element to scale.
|
||||
* @param scale The scale multiplier.
|
||||
* @param positionOffset The offset to apply to `tx` and `ty` after scaling.
|
||||
* (Or in other words, the position of the element.)
|
||||
*/
|
||||
public function scaleElement(element:Element, scale:Float, positionOffset:Float = 0, scaleEverything:Bool = false):Void
|
||||
{
|
||||
if (!this.isAnimate)
|
||||
{
|
||||
trace('WARNING: scaleElement() only works texture atlases!');
|
||||
return;
|
||||
}
|
||||
|
||||
var elementMatrix:FlxMatrix = element.matrix;
|
||||
|
||||
if (scaleEverything)
|
||||
{
|
||||
elementMatrix.scale(scale, scale);
|
||||
return;
|
||||
}
|
||||
|
||||
var symbolInstance:SymbolInstance = element.parentFrame.convertToSymbol(0, 1);
|
||||
var transformPoint:FlxPoint = symbolInstance.transformationPoint;
|
||||
|
||||
elementMatrix.a += scale;
|
||||
elementMatrix.d += scale;
|
||||
|
||||
elementMatrix.tx -= transformPoint.x * scale;
|
||||
elementMatrix.ty -= transformPoint.y * scale;
|
||||
|
||||
elementMatrix.tx -= positionOffset;
|
||||
elementMatrix.ty -= positionOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure scale is applied when cloning a sprite.R
|
||||
* The default `clone()` method acts kinda weird TBH.
|
||||
@@ -325,77 +689,10 @@ class FunkinSprite extends FlxSprite
|
||||
return _rect;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the screen position of this object.
|
||||
*
|
||||
* @param result Optional arg for the returning point
|
||||
* @param camera The desired "screen" coordinate space. If `null`, `FlxG.camera` is used.
|
||||
* @return The screen position of this object.
|
||||
*/
|
||||
public override function getScreenPosition(?result:FlxPoint, ?camera:FlxCamera):FlxPoint
|
||||
override function preparePixelPerfectMatrix(matrix:FlxMatrix)
|
||||
{
|
||||
if (result == null) result = FlxPoint.get();
|
||||
|
||||
if (camera == null) camera = FlxG.camera;
|
||||
|
||||
result.set(x, y);
|
||||
if (pixelPerfectPosition)
|
||||
{
|
||||
_rect.width = _rect.width / this.scale.x;
|
||||
_rect.height = _rect.height / this.scale.y;
|
||||
_rect.x = _rect.x / this.scale.x;
|
||||
_rect.y = _rect.y / this.scale.y;
|
||||
_rect.round();
|
||||
_rect.x = _rect.x * this.scale.x;
|
||||
_rect.y = _rect.y * this.scale.y;
|
||||
_rect.width = _rect.width * this.scale.x;
|
||||
_rect.height = _rect.height * this.scale.y;
|
||||
}
|
||||
|
||||
return result.subtract(camera.scroll.x * scrollFactor.x, camera.scroll.y * scrollFactor.y);
|
||||
}
|
||||
|
||||
override function drawSimple(camera:FlxCamera):Void
|
||||
{
|
||||
getScreenPosition(_point, camera).subtractPoint(offset);
|
||||
if (isPixelPerfectRender(camera))
|
||||
{
|
||||
_point.x = _point.x / this.scale.x;
|
||||
_point.y = _point.y / this.scale.y;
|
||||
_point.round();
|
||||
|
||||
_point.x = _point.x * this.scale.x;
|
||||
_point.y = _point.y * this.scale.y;
|
||||
}
|
||||
|
||||
_point.copyToFlash(_flashPoint);
|
||||
camera.copyPixels(_frame, framePixels, _flashRect, _flashPoint, colorTransform, blend, antialiasing);
|
||||
}
|
||||
|
||||
override function drawComplex(camera:FlxCamera):Void
|
||||
{
|
||||
_frame.prepareMatrix(_matrix, FlxFrameAngle.ANGLE_0, checkFlipX(), checkFlipY());
|
||||
_matrix.translate(-origin.x, -origin.y);
|
||||
_matrix.scale(scale.x, scale.y);
|
||||
|
||||
if (bakedRotationAngle <= 0)
|
||||
{
|
||||
updateTrig();
|
||||
|
||||
if (angle != 0) _matrix.rotateWithTrig(_cosAngle, _sinAngle);
|
||||
}
|
||||
|
||||
getScreenPosition(_point, camera).subtractPoint(offset);
|
||||
_point.add(origin.x, origin.y);
|
||||
_matrix.translate(_point.x, _point.y);
|
||||
|
||||
if (isPixelPerfectRender(camera))
|
||||
{
|
||||
_matrix.tx = Math.round(_matrix.tx / this.scale.x) * this.scale.x;
|
||||
_matrix.ty = Math.round(_matrix.ty / this.scale.y) * this.scale.y;
|
||||
}
|
||||
|
||||
camera.drawPixels(_frame, framePixels, _matrix, colorTransform, blend, antialiasing, shader);
|
||||
matrix.tx = Math.round(matrix.tx / this.scale.x) * this.scale.x;
|
||||
matrix.ty = Math.round(matrix.ty / this.scale.y) * this.scale.y;
|
||||
}
|
||||
|
||||
public override function destroy():Void
|
||||
|
||||
@@ -1,414 +0,0 @@
|
||||
package funkin.graphics.adobeanimate;
|
||||
|
||||
import flixel.util.FlxSignal.FlxTypedSignal;
|
||||
import flxanimate.FlxAnimate;
|
||||
import flxanimate.FlxAnimate.Settings;
|
||||
import flixel.graphics.frames.FlxFrame;
|
||||
import flixel.system.FlxAssets.FlxGraphicAsset;
|
||||
import flixel.math.FlxPoint;
|
||||
import flxanimate.animate.FlxKeyFrame;
|
||||
|
||||
/**
|
||||
* A sprite which provides convenience functions for rendering a texture atlas with animations.
|
||||
*/
|
||||
@:nullSafety
|
||||
class FlxAtlasSprite extends FlxAnimate
|
||||
{
|
||||
static final SETTINGS:Settings =
|
||||
{
|
||||
// ?ButtonSettings:Map<String, flxanimate.animate.FlxAnim.ButtonSettings>,
|
||||
FrameRate: 24.0,
|
||||
Reversed: false,
|
||||
// ?OnComplete:Void -> Void,
|
||||
ShowPivot: false,
|
||||
Antialiasing: true,
|
||||
ScrollFactor: null,
|
||||
// Offset: new FlxPoint(0, 0), // This is just FlxSprite.offset
|
||||
};
|
||||
|
||||
/**
|
||||
* Signal dispatched when an animation advances to the next frame.
|
||||
*/
|
||||
public var onAnimationFrame:FlxTypedSignal<String->Int->Void> = new FlxTypedSignal();
|
||||
|
||||
/**
|
||||
* Signal dispatched when a non-looping animation finishes playing.
|
||||
*/
|
||||
public var onAnimationComplete:FlxTypedSignal<String->Void> = new FlxTypedSignal();
|
||||
|
||||
/**
|
||||
* Signal dispatched when a looping animation finishes playing.
|
||||
*/
|
||||
public var onAnimationLoop:FlxTypedSignal<String->Void> = new FlxTypedSignal();
|
||||
|
||||
var currentAnimation:String = '';
|
||||
|
||||
var canPlayOtherAnims:Bool = true;
|
||||
|
||||
@:nullSafety(Off) // null safety HATES new classes atm, it'll be fixed in haxe 4.0.0?
|
||||
public function new(x:Float, y:Float, ?path:String, ?settings:Settings, ?pathSafety:Bool = true)
|
||||
{
|
||||
if (settings == null) settings = SETTINGS;
|
||||
|
||||
if (path == null && pathSafety) throw 'Null path specified for FlxAtlasSprite!';
|
||||
|
||||
// Validate asset path.
|
||||
if (!Assets.exists('${path}/Animation.json')
|
||||
&& pathSafety) throw 'FlxAtlasSprite does not have an Animation.json file at the specified path (${path})';
|
||||
|
||||
super(x, y, path, settings);
|
||||
|
||||
if (this.anim.stageInstance == null && pathSafety) throw 'FlxAtlasSprite not initialized properly. Are you sure the path (${path}) exists?';
|
||||
|
||||
onAnimationComplete.add(cleanupAnimation);
|
||||
|
||||
// This defaults the sprite to play the first animation in the atlas,
|
||||
// then pauses it. This ensures symbols are intialized properly.
|
||||
if (this.anim.curInstance != null)
|
||||
{
|
||||
this.anim.play('');
|
||||
this.anim.pause();
|
||||
}
|
||||
|
||||
this.anim.onComplete.add(_onAnimationComplete);
|
||||
this.anim.onFrame.add(_onAnimationFrame);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A list of all the animations this sprite has available.
|
||||
*/
|
||||
public function listAnimations():Array<String>
|
||||
{
|
||||
var mainSymbol = this.anim.symbolDictionary[this.anim.stageInstance.symbol.name];
|
||||
if (mainSymbol == null)
|
||||
{
|
||||
FlxG.log.error('FlxAtlasSprite does not have its main symbol!');
|
||||
return [];
|
||||
}
|
||||
return mainSymbol.getFrameLabels().map(keyFrame -> keyFrame.name).filterNull();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param id A string ID of the animation.
|
||||
* @return Whether the animation was found on this sprite.
|
||||
*/
|
||||
public function hasAnimation(id:String):Bool
|
||||
{
|
||||
return getLabelIndex(id) != -1 || anim.symbolDictionary.exists(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The current animation being played.
|
||||
*/
|
||||
public function getCurrentAnimation():String
|
||||
{
|
||||
return this.currentAnimation;
|
||||
}
|
||||
|
||||
var _completeAnim:Bool = false;
|
||||
|
||||
var fr:Null<FlxKeyFrame> = null;
|
||||
|
||||
var looping:Bool = false;
|
||||
|
||||
public var ignoreExclusionPref:Array<String> = [];
|
||||
|
||||
/**
|
||||
* Plays an animation.
|
||||
* @param id A string ID of the animation to play.
|
||||
* @param restart Whether to restart the animation if it is already playing.
|
||||
* @param ignoreOther Whether to ignore all other animation inputs, until this one is done playing
|
||||
* @param loop Whether to loop the animation
|
||||
* @param startFrame The frame to start the animation on
|
||||
* NOTE: `loop` and `ignoreOther` are not compatible with each other!
|
||||
*/
|
||||
public function playAnimation(id:String, restart:Bool = false, ignoreOther:Bool = false, loop:Bool = false, startFrame:Int = 0):Void
|
||||
{
|
||||
// Skip if not allowed to play animations.
|
||||
if ((!canPlayOtherAnims))
|
||||
{
|
||||
if (this.currentAnimation == id && restart) {}
|
||||
else if (ignoreExclusionPref != null && ignoreExclusionPref.length > 0)
|
||||
{
|
||||
var detected:Bool = false;
|
||||
for (entry in ignoreExclusionPref)
|
||||
{
|
||||
if (StringTools.startsWith(id, entry))
|
||||
{
|
||||
detected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!detected) return;
|
||||
}
|
||||
else
|
||||
return;
|
||||
}
|
||||
|
||||
if (anim == null) return;
|
||||
|
||||
if (id == null || id == '') id = this.currentAnimation;
|
||||
|
||||
if (this.currentAnimation == id && !restart)
|
||||
{
|
||||
if (!anim.isPlaying)
|
||||
{
|
||||
if (fr != null) anim.curFrame = fr.index + startFrame;
|
||||
else
|
||||
anim.curFrame = startFrame;
|
||||
|
||||
// Resume animation if it's paused.
|
||||
anim.resume();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
else if (!hasAnimation(id))
|
||||
{
|
||||
// Skip if the animation doesn't exist
|
||||
trace('Animation ' + id + ' not found');
|
||||
return;
|
||||
}
|
||||
|
||||
this.currentAnimation = id;
|
||||
anim.onComplete.removeAll();
|
||||
anim.onComplete.add(function() {
|
||||
_onAnimationComplete();
|
||||
});
|
||||
|
||||
looping = loop;
|
||||
|
||||
// Prevent other animations from playing if `ignoreOther` is true.
|
||||
if (ignoreOther) canPlayOtherAnims = false;
|
||||
|
||||
// Move to the first frame of the animation.
|
||||
// goToFrameLabel(id);
|
||||
// trace('Playing animation $id');
|
||||
if ((id == null || id == "") || this.anim.symbolDictionary.exists(id) || (this.anim.getByName(id) != null))
|
||||
{
|
||||
this.anim.play(id, restart, false, startFrame);
|
||||
|
||||
this.currentAnimation = anim.curSymbol.name;
|
||||
|
||||
fr = null;
|
||||
}
|
||||
var frameLabelNames = getFrameLabelNames();
|
||||
// Only call goToFrameLabel if there is a frame label with that name. This prevents annoying warnings!
|
||||
if (frameLabelNames != null && frameLabelNames.indexOf(id) != -1)
|
||||
{
|
||||
goToFrameLabel(id);
|
||||
fr = anim.getFrameLabel(id);
|
||||
anim.curFrame += startFrame;
|
||||
// Resume animation if it's paused.
|
||||
anim.resume();
|
||||
}
|
||||
}
|
||||
|
||||
override public function update(elapsed:Float):Void
|
||||
{
|
||||
super.update(elapsed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the animation has finished playing.
|
||||
* @return Whether the animation has finished playing.
|
||||
*/
|
||||
public function isAnimationFinished():Bool
|
||||
{
|
||||
return isLoopComplete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the animation has reached the last frame.
|
||||
* Can be true even if animation is configured to loop.
|
||||
* @return Whether the animation has reached the last frame.
|
||||
*/
|
||||
public function isLoopComplete():Bool
|
||||
{
|
||||
if (this.anim == null) return false;
|
||||
if (!this.anim.isPlaying) return false;
|
||||
|
||||
if (fr != null)
|
||||
{
|
||||
var curFrame = anim.curFrame;
|
||||
|
||||
var startFrame = fr.index;
|
||||
var endFrame = (fr.index + fr.duration);
|
||||
|
||||
return (anim.reversed) ? (curFrame < startFrame) : (curFrame >= endFrame);
|
||||
}
|
||||
|
||||
return (anim.reversed && anim.curFrame == 0 || !(anim.reversed) && (anim.curFrame) >= (anim.length - 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the current animation.
|
||||
*/
|
||||
public function stopAnimation():Void
|
||||
{
|
||||
if (this.currentAnimation == null) return;
|
||||
|
||||
this.anim.removeAllCallbacksFrom(getNextFrameLabel(this.currentAnimation));
|
||||
|
||||
goToFrameIndex(0);
|
||||
}
|
||||
|
||||
function addFrameCallback(label:String, callback:Void->Void):Void
|
||||
{
|
||||
var frameLabel = this.anim.getFrameLabel(label);
|
||||
frameLabel.add(callback);
|
||||
}
|
||||
|
||||
function goToFrameLabel(label:String):Void
|
||||
{
|
||||
this.anim.goToFrameLabel(label);
|
||||
}
|
||||
|
||||
function getFrameLabelNames(?layer:haxe.extern.EitherType<Int, String>):Null<Array<String>>
|
||||
{
|
||||
var labels = this.anim.getFrameLabels(layer);
|
||||
var array = [];
|
||||
for (label in labels)
|
||||
{
|
||||
array.push(label.name);
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
function getNextFrameLabel(label:String):String
|
||||
{
|
||||
return listAnimations()[(getLabelIndex(label) + 1) % listAnimations().length];
|
||||
}
|
||||
|
||||
function getLabelIndex(label:String):Int
|
||||
{
|
||||
return listAnimations().indexOf(label);
|
||||
}
|
||||
|
||||
function goToFrameIndex(index:Int):Void
|
||||
{
|
||||
this.anim.curFrame = index;
|
||||
}
|
||||
|
||||
public function cleanupAnimation(_:String):Void
|
||||
{
|
||||
canPlayOtherAnims = true;
|
||||
// this.currentAnimation = null;
|
||||
this.anim.pause();
|
||||
}
|
||||
|
||||
function _onAnimationFrame(frame:Int):Void
|
||||
{
|
||||
if (currentAnimation != null)
|
||||
{
|
||||
onAnimationFrame.dispatch(currentAnimation, frame);
|
||||
|
||||
if (isLoopComplete())
|
||||
{
|
||||
anim.pause();
|
||||
|
||||
if (looping)
|
||||
{
|
||||
anim.curFrame = (fr != null) ? fr.index : 0;
|
||||
anim.resume();
|
||||
_onAnimationLoop();
|
||||
}
|
||||
else if (fr != null && anim.curFrame != anim.length - 1)
|
||||
{
|
||||
anim.curFrame--;
|
||||
_onAnimationComplete();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function _onAnimationComplete():Void
|
||||
{
|
||||
if (currentAnimation != null)
|
||||
{
|
||||
onAnimationComplete.dispatch(currentAnimation);
|
||||
}
|
||||
else
|
||||
{
|
||||
onAnimationComplete.dispatch('');
|
||||
}
|
||||
}
|
||||
|
||||
function _onAnimationLoop():Void
|
||||
{
|
||||
if (currentAnimation != null)
|
||||
{
|
||||
onAnimationLoop.dispatch(currentAnimation);
|
||||
}
|
||||
else
|
||||
{
|
||||
onAnimationLoop.dispatch('');
|
||||
}
|
||||
}
|
||||
|
||||
var prevFrames:Map<Int, FlxFrame> = [];
|
||||
|
||||
public function replaceFrameGraphic(index:Int, ?graphic:FlxGraphicAsset):Void
|
||||
{
|
||||
var cond = false;
|
||||
|
||||
if (graphic == null) cond = true;
|
||||
else
|
||||
{
|
||||
if ((graphic is String)) cond = !Assets.exists(graphic)
|
||||
else
|
||||
cond = false;
|
||||
}
|
||||
if (cond)
|
||||
{
|
||||
var prevFrame:Null<FlxFrame> = prevFrames.get(index);
|
||||
if (prevFrame == null) return;
|
||||
|
||||
prevFrame.copyTo(frames.getByIndex(index));
|
||||
return;
|
||||
}
|
||||
|
||||
var prevFrame:FlxFrame = prevFrames.get(index) ?? frames.getByIndex(index).copyTo();
|
||||
prevFrames.set(index, prevFrame);
|
||||
|
||||
@:nullSafety(Off) // TODO: Remove this once flixel.system.frontEnds.BitmapFrontEnd has been null safed
|
||||
var frame = FlxG.bitmap.add(graphic).imageFrame.frame;
|
||||
frame.copyTo(frames.getByIndex(index));
|
||||
|
||||
// Additional sizing fix.
|
||||
@:privateAccess
|
||||
if (true)
|
||||
{
|
||||
var frame = frames.getByIndex(index);
|
||||
frame.tileMatrix.a = prevFrame.frame.width / frame.frame.width;
|
||||
frame.tileMatrix.d = prevFrame.frame.height / frame.frame.height;
|
||||
}
|
||||
}
|
||||
|
||||
public function getBasePosition():Null<FlxPoint>
|
||||
{
|
||||
// var stagePos = new FlxPoint(anim.stageInstance.matrix.tx, anim.stageInstance.matrix.ty);
|
||||
var instancePos = new FlxPoint(anim.curInstance.matrix.tx, anim.curInstance.matrix.ty);
|
||||
var firstElement = anim.curSymbol.timeline?.get(0)?.get(0)?.get(0);
|
||||
if (firstElement == null) return instancePos;
|
||||
var firstElementPos = new FlxPoint(firstElement.matrix.tx, firstElement.matrix.ty);
|
||||
|
||||
return instancePos + firstElementPos;
|
||||
}
|
||||
|
||||
public function getPivotPosition():Null<FlxPoint>
|
||||
{
|
||||
return anim.curInstance.symbol.transformationPoint;
|
||||
}
|
||||
|
||||
public override function destroy():Void
|
||||
{
|
||||
for (prevFrameId in prevFrames.keys())
|
||||
{
|
||||
replaceFrameGraphic(prevFrameId, null);
|
||||
}
|
||||
|
||||
super.destroy();
|
||||
}
|
||||
}
|
||||
@@ -274,6 +274,11 @@ class PolymodHandler
|
||||
Polymod.addImportAlias('funkin.play.character.CharacterDataParser', funkin.data.character.CharacterData.CharacterDataParser);
|
||||
Polymod.addImportAlias('funkin.play.character.CharacterData.CharacterDataParser', funkin.data.character.CharacterData.CharacterDataParser);
|
||||
|
||||
// `FlxAtlasSprite` was merged into `FunkinSprite` and then removed.
|
||||
// We add the import alias here so mods don't error out as much.
|
||||
Polymod.addImportAlias('funkin.graphics.adobeanimate.FlxAtlasSprite', funkin.graphics.FunkinSprite);
|
||||
Polymod.addImportAlias('funkin.modding.base.ScriptedFlxAtlasSprite', funkin.graphics.ScriptedFunkinSprite);
|
||||
|
||||
// `funkin.util.FileUtil` has unrestricted access to the file system.
|
||||
Polymod.addImportAlias('funkin.util.FileUtil', funkin.util.FileUtilSandboxed);
|
||||
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
package funkin.modding.base;
|
||||
|
||||
/**
|
||||
* A script that can be tied to an FlxAtlasSprite
|
||||
* Create a scripted class that extends FlxAtlasSprite to use this.
|
||||
*/
|
||||
@:hscriptClass
|
||||
class ScriptedFlxAtlasSprite extends funkin.graphics.adobeanimate.FlxAtlasSprite implements HScriptedClass {}
|
||||
@@ -20,11 +20,9 @@ import funkin.audio.FunkinSound;
|
||||
import funkin.data.freeplay.player.PlayerData.PlayerResultsAnimationData;
|
||||
import funkin.data.freeplay.player.PlayerRegistry;
|
||||
import funkin.data.song.SongRegistry;
|
||||
import funkin.graphics.adobeanimate.FlxAtlasSprite;
|
||||
import funkin.graphics.FunkinCamera;
|
||||
import funkin.graphics.FunkinSprite;
|
||||
import funkin.graphics.shaders.LeftMaskShader;
|
||||
import funkin.modding.base.ScriptedFlxAtlasSprite;
|
||||
import funkin.play.components.ClearPercentCounter;
|
||||
import funkin.play.components.TallyCounter;
|
||||
import funkin.play.scoring.Scoring;
|
||||
@@ -78,7 +76,7 @@ class ResultState extends MusicBeatSubState
|
||||
|
||||
var characterAtlasAnimations:Array<
|
||||
{
|
||||
sprite:FlxAtlasSprite,
|
||||
sprite:FunkinSprite,
|
||||
delay:Float,
|
||||
forceLoop:Bool,
|
||||
startFrameLabel:String,
|
||||
@@ -193,7 +191,7 @@ class ResultState extends MusicBeatSubState
|
||||
add(soundSystem);
|
||||
|
||||
// Fetch playable character data. Default to BF on the results screen if we can't find it.
|
||||
playerCharacterId = PlayerRegistry.instance.getCharacterOwnerId(params.characterId);
|
||||
playerCharacterId = PlayerRegistry.instance.getCharacterOwnerId(params.characterId) ?? params.characterId;
|
||||
playerCharacter = PlayerRegistry.instance.fetchEntry(playerCharacterId ?? 'bf');
|
||||
|
||||
trace('Got playable character: ${playerCharacter?.getName()}');
|
||||
@@ -222,17 +220,22 @@ class ResultState extends MusicBeatSubState
|
||||
{
|
||||
case 'animateatlas':
|
||||
@:nullSafety(Off)
|
||||
var animation:FlxAtlasSprite = null;
|
||||
var animation:FunkinSprite = null;
|
||||
|
||||
var xPos = offsets[0] + (FullScreenScaleMode.gameCutoutSize.x / 2);
|
||||
var yPos = offsets[1];
|
||||
|
||||
if (animData.scriptClass != null) animation = ScriptedFlxAtlasSprite.init(animData.scriptClass, xPos, yPos);
|
||||
if (animData.scriptClass != null) animation = ScriptedFunkinSprite.init(animData.scriptClass, xPos, yPos);
|
||||
else
|
||||
animation = new FlxAtlasSprite(xPos, yPos, Paths.animateAtlas(animPath, animLibrary));
|
||||
animation = FunkinSprite.createTextureAtlas(xPos, yPos, animPath, animLibrary);
|
||||
|
||||
if (animation == null) continue;
|
||||
|
||||
if (animData?.applyStageMatrix ?? false)
|
||||
{
|
||||
animation.applyStageMatrix = true;
|
||||
}
|
||||
|
||||
animation.zIndex = animData.zIndex ?? 500;
|
||||
|
||||
animation.scale.set(animData.scale ?? 1.0, animData.scale ?? 1.0);
|
||||
@@ -240,7 +243,7 @@ class ResultState extends MusicBeatSubState
|
||||
if (!(animData.looped ?? true))
|
||||
{
|
||||
// Animation is not looped.
|
||||
animation.onAnimationComplete.add((_name:String) -> {
|
||||
animation.anim.onFinish.add((_name:String) -> {
|
||||
if (animation != null)
|
||||
{
|
||||
animation.anim.pause();
|
||||
@@ -249,23 +252,24 @@ class ResultState extends MusicBeatSubState
|
||||
}
|
||||
else if (animData.loopFrameLabel != null)
|
||||
{
|
||||
animation.onAnimationComplete.add((_name:String) -> {
|
||||
animation.anim.onFinish.add((_name:String) -> {
|
||||
if (animation != null)
|
||||
{
|
||||
animation.playAnimation(animData.loopFrameLabel ?? '', true, false, true); // unpauses this anim, since it's on PlayOnce!
|
||||
animation.anim.play(animData.loopFrameLabel ?? '', true); // unpauses this anim, since it's on PlayOnce!
|
||||
animation.anim.curAnim.looped = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (animData.loopFrame != null)
|
||||
{
|
||||
animation.onAnimationComplete.add((_name:String) -> {
|
||||
animation.anim.onFinish.add((_name:String) -> {
|
||||
if (animation != null)
|
||||
{
|
||||
animation.anim.curFrame = animData.loopFrame ?? 0;
|
||||
animation.anim.play(); // unpauses this anim, since it's on PlayOnce!
|
||||
animation.anim.play("", true, false, animData.loopFrame ?? 0); // unpauses this anim, since it's on PlayOnce!
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Hide until ready to play.
|
||||
animation.visible = false;
|
||||
// Queue to play.
|
||||
@@ -660,7 +664,7 @@ class ResultState extends MusicBeatSubState
|
||||
new FlxTimer().start(atlas.delay, _ -> {
|
||||
if (atlas.sprite == null) return;
|
||||
atlas.sprite.visible = true;
|
||||
atlas.sprite.playAnimation(atlas.startFrameLabel);
|
||||
atlas.sprite.anim.play(atlas.startFrameLabel);
|
||||
if (atlas.sound != "")
|
||||
{
|
||||
var sndPath:String = Paths.stripLibrary(atlas.sound);
|
||||
|
||||
@@ -1,35 +1,11 @@
|
||||
package funkin.play.character;
|
||||
|
||||
import flixel.animation.FlxAnimationController;
|
||||
import flixel.FlxCamera;
|
||||
import flixel.FlxSprite;
|
||||
import flixel.graphics.frames.FlxFrame;
|
||||
import flixel.graphics.frames.FlxFramesCollection;
|
||||
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;
|
||||
import funkin.graphics.adobeanimate.FlxAtlasSprite;
|
||||
import funkin.util.assets.FlxAnimationUtil;
|
||||
import funkin.modding.events.ScriptEvent;
|
||||
import funkin.data.animation.AnimationData;
|
||||
import funkin.data.character.CharacterData.CharacterRenderType;
|
||||
import flixel.util.FlxDirectionFlags;
|
||||
import openfl.display.BitmapData;
|
||||
import openfl.display.BlendMode;
|
||||
|
||||
/**
|
||||
* Individual animation data for an AnimateAtlasCharacter.
|
||||
*/
|
||||
typedef AnimateAtlasAnimation =
|
||||
{
|
||||
name:String,
|
||||
prefix:String,
|
||||
offsets:Null<Array<Float>>,
|
||||
looped:Bool,
|
||||
}
|
||||
import flixel.math.FlxPoint;
|
||||
|
||||
/**
|
||||
* An AnimateAtlasCharacter is a Character which is rendered by
|
||||
@@ -40,39 +16,13 @@ typedef AnimateAtlasAnimation =
|
||||
*/
|
||||
class AnimateAtlasCharacter extends BaseCharacter
|
||||
{
|
||||
// BaseCharacter extends FlxSprite but we can't make it also extend FlxAtlasSprite UGH
|
||||
// I basically copied the code from FlxSpriteGroup to make the FlxAtlasSprite a "child" of this class
|
||||
var mainSprite:FlxAtlasSprite;
|
||||
|
||||
var _skipTransformChildren:Bool = false;
|
||||
|
||||
var animations:Map<String, AnimateAtlasAnimation> = new Map<String, AnimateAtlasAnimation>();
|
||||
var currentAnimName:Null<String> = null;
|
||||
var animFinished:Bool = false;
|
||||
var originalSizes(default, never):FlxPoint = new FlxPoint(0, 0);
|
||||
|
||||
public function new(id:String)
|
||||
{
|
||||
super(id, CharacterRenderType.AnimateAtlas);
|
||||
}
|
||||
|
||||
override function initVars():Void
|
||||
{
|
||||
// this.flixelType = SPRITEGROUP;
|
||||
|
||||
// TODO: Make `animation` a stub that redirects calls to `mainSprite`?
|
||||
animation = new FlxAnimationController(this);
|
||||
|
||||
offset = new FlxCallbackPoint(offsetCallback);
|
||||
origin = new FlxCallbackPoint(originCallback);
|
||||
scale = new FlxCallbackPoint(scaleCallback);
|
||||
scrollFactor = new FlxCallbackPoint(scrollFactorCallback);
|
||||
|
||||
scale.set(1, 1);
|
||||
scrollFactor.set(1, 1);
|
||||
|
||||
initMotionVars();
|
||||
}
|
||||
|
||||
override function onCreate(event:ScriptEvent):Void
|
||||
{
|
||||
// Display a custom scope for debugging purposes.
|
||||
@@ -83,756 +33,71 @@ class AnimateAtlasCharacter extends BaseCharacter
|
||||
try
|
||||
{
|
||||
trace('Loading assets for Animate Atlas character "${characterId}"', flixel.util.FlxColor.fromString("#89CFF0"));
|
||||
var atlasSprite:FlxAtlasSprite = loadAtlasSprite();
|
||||
setSprite(atlasSprite);
|
||||
|
||||
loadAtlas();
|
||||
loadAnimations();
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
throw "Exception thrown while building FlxAtlasSprite: " + e;
|
||||
throw "Exception thrown while building sprite: " + e;
|
||||
}
|
||||
|
||||
trace('[ATLASCHAR] Successfully loaded texture atlas for ${characterId} with ${_data.animations.length} animations.');
|
||||
super.onCreate(event);
|
||||
|
||||
originalSizes.set(this.width, this.height);
|
||||
}
|
||||
|
||||
public override function playAnimation(name:String, restart:Bool = false, ignoreOther:Bool = false, reverse:Bool = false):Void
|
||||
{
|
||||
var correctName = correctAnimationName(name);
|
||||
if (correctName == null)
|
||||
{
|
||||
trace('$characterName Could not find Atlas animation: ' + name);
|
||||
return;
|
||||
}
|
||||
|
||||
var animData = getAnimationData(correctName);
|
||||
currentAnimName = correctName;
|
||||
var prefix:String = animData.prefix;
|
||||
if (prefix == null) prefix = correctName;
|
||||
var loop:Bool = animData.looped;
|
||||
|
||||
this.mainSprite.playAnimation(prefix, restart, ignoreOther, loop);
|
||||
}
|
||||
|
||||
public override function hasAnimation(name:String):Bool
|
||||
{
|
||||
return getAnimationData(name) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the animation has finished playing.
|
||||
* Never true if animation is configured to loop.
|
||||
*/
|
||||
public override function isAnimationFinished():Bool
|
||||
{
|
||||
return mainSprite?.isAnimationFinished() ?? false;
|
||||
}
|
||||
|
||||
function loadAtlasSprite():FlxAtlasSprite
|
||||
function loadAtlas()
|
||||
{
|
||||
trace('[ATLASCHAR] Loading sprite atlas for ${characterId}.');
|
||||
var assetLibrary:String = Paths.getLibrary(_data.assetPath);
|
||||
var assetPath:String = Paths.stripLibrary(_data.assetPath);
|
||||
|
||||
var animLibrary:String = Paths.getLibrary(_data.assetPath);
|
||||
var animPath:String = Paths.stripLibrary(_data.assetPath);
|
||||
var assetPath:String = Paths.animateAtlas(animPath, animLibrary);
|
||||
loadTextureAtlas(assetPath, assetLibrary, cast _data.atlasSettings);
|
||||
|
||||
var sprite:FlxAtlasSprite = new FlxAtlasSprite(0, 0, assetPath);
|
||||
|
||||
// sprite.onAnimationComplete.removeAll();
|
||||
sprite.onAnimationComplete.add(this.onAnimationFinished);
|
||||
|
||||
return sprite;
|
||||
}
|
||||
|
||||
override function onAnimationFinished(prefix:String):Void
|
||||
{
|
||||
super.onAnimationFinished(prefix);
|
||||
|
||||
if (!getCurrentAnimation().endsWith(Constants.ANIMATION_HOLD_SUFFIX)
|
||||
&& hasAnimation(getCurrentAnimation() + Constants.ANIMATION_HOLD_SUFFIX))
|
||||
if (_data.isPixel)
|
||||
{
|
||||
playAnimation(getCurrentAnimation() + Constants.ANIMATION_HOLD_SUFFIX);
|
||||
}
|
||||
|
||||
if (getAnimationData() != null && getAnimationData().looped)
|
||||
{
|
||||
if (StringTools.endsWith(prefix, "-hold")) trace(prefix);
|
||||
playAnimation(prefix, true, false);
|
||||
this.isPixel = true;
|
||||
this.antialiasing = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Make the game hold on the last frame.
|
||||
this.mainSprite.cleanupAnimation(prefix);
|
||||
// currentAnimName = null;
|
||||
|
||||
// Fallback to idle!
|
||||
// playAnimation('idle', true, false);
|
||||
this.isPixel = false;
|
||||
this.antialiasing = true;
|
||||
}
|
||||
|
||||
this.setScale(_data.scale);
|
||||
}
|
||||
|
||||
function setSprite(sprite:FlxAtlasSprite):Void
|
||||
function loadAnimations()
|
||||
{
|
||||
trace('[ATLASCHAR] Applying sprite properties to ${characterId}');
|
||||
trace('[ATLASCHAR] Loading ${_data.animations.length} animations for ${characterId}');
|
||||
|
||||
this.mainSprite = sprite;
|
||||
FlxAnimationUtil.addTextureAtlasAnimations(this, _data.animations);
|
||||
|
||||
mainSprite.ignoreExclusionPref = ["sing"];
|
||||
|
||||
// This forces the atlas to recalcuate its width and height
|
||||
this.mainSprite.alpha = 0.0001;
|
||||
this.mainSprite.draw();
|
||||
this.mainSprite.alpha = 1.0;
|
||||
|
||||
var feetPos:FlxPoint = feetPosition;
|
||||
this.updateHitbox();
|
||||
|
||||
sprite.x = this.x;
|
||||
sprite.y = this.y;
|
||||
sprite.alpha *= alpha;
|
||||
sprite.flipX = flipX;
|
||||
sprite.flipY = flipY;
|
||||
sprite.scrollFactor.copyFrom(scrollFactor);
|
||||
sprite.cameras = _cameras; // _cameras instead of cameras because get_cameras() will not return null
|
||||
|
||||
if (clipRect != null) clipRectTransform(sprite, clipRect);
|
||||
}
|
||||
|
||||
function loadAnimations():Void
|
||||
{
|
||||
trace('[ATLASCHAR] Attempting to load ${_data.animations.length} animations for ${characterId}');
|
||||
|
||||
var animData:Array<AnimateAtlasAnimation> = cast _data.animations;
|
||||
|
||||
for (anim in animData)
|
||||
for (anim in _data.animations)
|
||||
{
|
||||
// Validate the animation before adding.
|
||||
var prefix = anim.prefix;
|
||||
if (!this.mainSprite.hasAnimation(prefix))
|
||||
if (anim.offsets == null)
|
||||
{
|
||||
FlxG.log.warn('[ATLASCHAR] Animation ${prefix} not found in Animate Atlas ${_data.assetPath}');
|
||||
trace('[ATLASCHAR] Animation ${prefix} not found in Animate Atlas ${_data.assetPath}');
|
||||
continue;
|
||||
setAnimationOffsets(anim.name, 0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
setAnimationOffsets(anim.name, anim.offsets[0], anim.offsets[1]);
|
||||
}
|
||||
animations.set(anim.name, anim);
|
||||
trace('[ATLASCHAR] - Successfully loaded animation ${anim.name} to ${characterId}');
|
||||
}
|
||||
|
||||
trace('[ATLASCHAR] Loaded ${animations.size()} animations for ${characterId}');
|
||||
}
|
||||
|
||||
public override function getCurrentAnimation():String
|
||||
{
|
||||
// return this.mainSprite.getCurrentAnimation();
|
||||
return currentAnimName;
|
||||
}
|
||||
|
||||
function getAnimationData(name:String = null):AnimateAtlasAnimation
|
||||
{
|
||||
if (name == null) name = getCurrentAnimation();
|
||||
return animations.get(name);
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
// Code copied from FlxSpriteGroup
|
||||
//
|
||||
//
|
||||
|
||||
/**
|
||||
* Handy function that allows you to quickly transform one property of sprites in this group at a time.
|
||||
*
|
||||
* @param callback Function to transform the sprites. Example:
|
||||
* `function(sprite, v:Dynamic) { s.acceleration.x = v; s.makeGraphic(10,10,0xFF000000); }`
|
||||
* @param value Value which will passed to lambda function.
|
||||
*/
|
||||
@:generic
|
||||
public function transformChildren<V>(callback:FlxAtlasSprite->V->Void, value:V):Void
|
||||
{
|
||||
if (_skipTransformChildren || this.mainSprite == null) return;
|
||||
|
||||
callback(this.mainSprite, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls `kill()` on the group's members and then on the group itself.
|
||||
* You can revive this group later via `revive()` after this.
|
||||
*/
|
||||
public override function kill():Void
|
||||
{
|
||||
_skipTransformChildren = true;
|
||||
super.kill();
|
||||
_skipTransformChildren = false;
|
||||
if (this.mainSprite != null)
|
||||
{
|
||||
this.mainSprite.kill();
|
||||
this.mainSprite = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Revives the group.
|
||||
*/
|
||||
public override function revive():Void
|
||||
{
|
||||
_skipTransformChildren = true;
|
||||
super.revive(); // calls set_exists and set_alive
|
||||
_skipTransformChildren = false;
|
||||
this.mainSprite.revive();
|
||||
}
|
||||
|
||||
/**
|
||||
* **WARNING:** A destroyed `FlxBasic` can't be used anymore.
|
||||
* It may even cause crashes if it is still part of a group or state.
|
||||
* You may want to use `kill()` instead if you want to disable the object temporarily only and `revive()` it later.
|
||||
*
|
||||
* This function is usually not called manually (Flixel calls it automatically during state switches for all `add()`ed objects).
|
||||
*
|
||||
* Override this function to `null` out variables manually or call `destroy()` on class members if necessary.
|
||||
* Don't forget to call `super.destroy()`!
|
||||
*/
|
||||
public override function destroy():Void
|
||||
{
|
||||
// normally don't have to destroy FlxPoints, but these are FlxCallbackPoints!
|
||||
offset = FlxDestroyUtil.destroy(offset);
|
||||
origin = FlxDestroyUtil.destroy(origin);
|
||||
scale = FlxDestroyUtil.destroy(scale);
|
||||
scrollFactor = FlxDestroyUtil.destroy(scrollFactor);
|
||||
|
||||
this.mainSprite = FlxDestroyUtil.destroy(this.mainSprite);
|
||||
|
||||
super.destroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check and see if any sprite in this group is currently on screen.
|
||||
*
|
||||
* @param Camera Specify which game camera you want. If `null`, it will just grab the first global camera.
|
||||
* @return Whether the object is on screen or not.
|
||||
*/
|
||||
public override function isOnScreen(?camera:FlxCamera):Bool
|
||||
{
|
||||
if (this.mainSprite != null && this.mainSprite.exists && this.mainSprite.visible && this.mainSprite.isOnScreen(camera)) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if a point in 2D world space overlaps any `FlxSprite` object from this group.
|
||||
*
|
||||
* @param Point The point in world space you want to check.
|
||||
* @param InScreenSpace Whether to take scroll factors into account when checking for overlap.
|
||||
* @param Camera Specify which game camera you want. If `null`, it will just grab the first global camera.
|
||||
* @return Whether or not the point overlaps this group.
|
||||
*/
|
||||
public override function overlapsPoint(point:FlxPoint, inScreenSpace:Bool = false, camera:FlxCamera = null):Bool
|
||||
{
|
||||
var result:Bool = false;
|
||||
result = this.mainSprite.overlapsPoint(point, inScreenSpace, camera);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if a point in 2D world space overlaps any of FlxSprite object's current displayed pixels.
|
||||
* This check is ALWAYS made in screen space, and always takes scroll factors into account.
|
||||
*
|
||||
* @param Point The point in world space you want to check.
|
||||
* @param Mask Used in the pixel hit test to determine what counts as solid.
|
||||
* @param Camera Specify which game camera you want. If `null`, it will just grab the first global camera.
|
||||
* @return Whether or not the point overlaps this object.
|
||||
*/
|
||||
public override function pixelsOverlapPoint(point:FlxPoint, Mask:Int = 0xFF, Camera:FlxCamera = null):Bool
|
||||
{
|
||||
var result:Bool = false;
|
||||
if (this.mainSprite != null && this.mainSprite.exists && this.mainSprite.visible)
|
||||
{
|
||||
result = this.mainSprite.pixelsOverlapPoint(point, Mask, Camera);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public override function update(elapsed:Float):Void
|
||||
{
|
||||
this.mainSprite.update(elapsed);
|
||||
|
||||
if (moves) updateMotion(elapsed);
|
||||
}
|
||||
|
||||
public override function draw():Void
|
||||
{
|
||||
this.mainSprite.draw();
|
||||
|
||||
#if FLX_DEBUG
|
||||
if (FlxG.debugger.drawDebug) drawDebug();
|
||||
#end
|
||||
}
|
||||
|
||||
inline function xTransform(sprite:FlxSprite, x:Float):Void
|
||||
sprite.x += x; // addition
|
||||
|
||||
inline function yTransform(sprite:FlxSprite, y:Float):Void
|
||||
sprite.y += y; // addition
|
||||
|
||||
inline function angleTransform(sprite:FlxSprite, angle:Float):Void
|
||||
sprite.angle += angle; // addition
|
||||
|
||||
inline function alphaTransform(sprite:FlxSprite, alpha:Float):Void
|
||||
{
|
||||
if (sprite.alpha != 0 || alpha == 0)
|
||||
{
|
||||
sprite.alpha *= alpha; // multiplication
|
||||
}
|
||||
else
|
||||
{
|
||||
sprite.alpha = 1 / alpha; // direct set to avoid stuck sprites
|
||||
}
|
||||
}
|
||||
|
||||
inline function directAlphaTransform(sprite:FlxSprite, alpha:Float):Void
|
||||
sprite.alpha = alpha; // direct set
|
||||
|
||||
inline function facingTransform(sprite:FlxSprite, facing:FlxDirectionFlags):Void
|
||||
sprite.facing = facing;
|
||||
|
||||
inline function flipXTransform(sprite:FlxSprite, flipX:Bool):Void
|
||||
sprite.flipX = flipX;
|
||||
|
||||
inline function flipYTransform(sprite:FlxSprite, flipY:Bool):Void
|
||||
sprite.flipY = flipY;
|
||||
|
||||
inline function movesTransform(sprite:FlxSprite, moves:Bool):Void
|
||||
sprite.moves = moves;
|
||||
|
||||
inline function pixelPerfectTransform(sprite:FlxSprite, pixelPerfect:Bool):Void
|
||||
sprite.pixelPerfectRender = pixelPerfect;
|
||||
|
||||
inline function gColorTransform(sprite:FlxSprite, color:Int):Void
|
||||
sprite.color = color;
|
||||
|
||||
inline function blendTransform(sprite:FlxSprite, blend:BlendMode):Void
|
||||
sprite.blend = blend;
|
||||
|
||||
inline function immovableTransform(sprite:FlxSprite, immovable:Bool):Void
|
||||
sprite.immovable = immovable;
|
||||
|
||||
inline function visibleTransform(sprite:FlxSprite, visible:Bool):Void
|
||||
sprite.visible = visible;
|
||||
|
||||
inline function activeTransform(sprite:FlxSprite, active:Bool):Void
|
||||
sprite.active = active;
|
||||
|
||||
inline function solidTransform(sprite:FlxSprite, solid:Bool):Void
|
||||
sprite.solid = solid;
|
||||
|
||||
inline function aliveTransform(sprite:FlxSprite, alive:Bool):Void
|
||||
sprite.alive = alive;
|
||||
|
||||
inline function existsTransform(sprite:FlxSprite, exists:Bool):Void
|
||||
sprite.exists = exists;
|
||||
|
||||
inline function cameraTransform(sprite:FlxSprite, camera:FlxCamera):Void
|
||||
sprite.camera = camera;
|
||||
|
||||
inline function camerasTransform(sprite:FlxSprite, cameras:Array<FlxCamera>):Void
|
||||
sprite.cameras = cameras;
|
||||
|
||||
inline function offsetTransform(sprite:FlxSprite, offset:FlxPoint):Void
|
||||
sprite.offset.copyFrom(offset);
|
||||
|
||||
inline function originTransform(sprite:FlxSprite, origin:FlxPoint):Void
|
||||
sprite.origin.copyFrom(origin);
|
||||
|
||||
inline function scaleTransform(sprite:FlxSprite, scale:FlxPoint):Void
|
||||
sprite.scale.copyFrom(scale);
|
||||
|
||||
inline function scrollFactorTransform(sprite:FlxSprite, scrollFactor:FlxPoint):Void
|
||||
sprite.scrollFactor.copyFrom(scrollFactor);
|
||||
|
||||
inline function clipRectTransform(sprite:FlxSprite, clipRect:FlxRect):Void
|
||||
{
|
||||
if (clipRect == null)
|
||||
{
|
||||
sprite.clipRect = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
sprite.clipRect = FlxRect.get(clipRect.x - sprite.x + x, clipRect.y - sprite.y + y, clipRect.width, clipRect.height);
|
||||
}
|
||||
}
|
||||
|
||||
var resS:FlxPoint = new FlxPoint();
|
||||
|
||||
/**
|
||||
* Reset the character so it can be used at the start of the level.
|
||||
* Call this when restarting the level.
|
||||
*/
|
||||
override public function resetCharacter(resetCamera:Bool = true):Void
|
||||
{
|
||||
trace("RESETTING ATLAS " + characterName);
|
||||
|
||||
// Reset the animation offsets. This will modify x and y to be the absolute position of the character.
|
||||
// this.animOffsets = [0, 0];
|
||||
|
||||
// Now we can set the x and y to be their original values without having to account for animOffsets.
|
||||
this.resetPosition();
|
||||
mainSprite.setPosition(originalPosition.x, originalPosition.y);
|
||||
|
||||
// Then reapply animOffsets...
|
||||
// applyAnimationOffsets(getCurrentAnimation());
|
||||
|
||||
// Make sure we are playing the idle animation
|
||||
// ...then update the hitbox so that this.width and this.height are correct.
|
||||
|
||||
mainSprite.scale.set(1, 1);
|
||||
mainSprite.alpha = 0.0001;
|
||||
mainSprite.width = 0;
|
||||
mainSprite.height = 0;
|
||||
this.dance(true); // Force to avoid the old animation playing with the wrong offset at the start of the song.
|
||||
|
||||
mainSprite.draw(); // refresh frame
|
||||
|
||||
if (resS.x == 0)
|
||||
{
|
||||
resS.x = mainSprite.width; // clunky bizz
|
||||
resS.y = mainSprite.height;
|
||||
}
|
||||
|
||||
mainSprite.alpha = alpha;
|
||||
|
||||
mainSprite.width = resS.x;
|
||||
mainSprite.height = resS.y;
|
||||
frameWidth = 0;
|
||||
frameHeight = 0;
|
||||
|
||||
scaleCallback(scale);
|
||||
this.updateHitbox();
|
||||
|
||||
// Reset the camera focus point while we're at it.
|
||||
if (resetCamera) this.resetCameraFocusPoint();
|
||||
}
|
||||
|
||||
inline function offsetCallback(offset:FlxPoint):Void
|
||||
transformChildren(offsetTransform, offset);
|
||||
|
||||
inline function originCallback(origin:FlxPoint):Void
|
||||
transformChildren(originTransform, origin);
|
||||
|
||||
inline function scaleCallback(scale:FlxPoint):Void
|
||||
transformChildren(scaleTransform, scale);
|
||||
|
||||
inline function scrollFactorCallback(scrollFactor:FlxPoint):Void
|
||||
transformChildren(scrollFactorTransform, scrollFactor);
|
||||
|
||||
override function set_camera(value:FlxCamera):FlxCamera
|
||||
{
|
||||
if (camera != value) transformChildren(cameraTransform, value);
|
||||
return super.set_camera(value);
|
||||
}
|
||||
|
||||
override function set_cameras(value:Array<FlxCamera>):Array<FlxCamera>
|
||||
{
|
||||
if (cameras != value) transformChildren(camerasTransform, value);
|
||||
return super.set_cameras(value);
|
||||
}
|
||||
|
||||
override function set_exists(value:Bool):Bool
|
||||
{
|
||||
if (exists != value) transformChildren(existsTransform, value);
|
||||
return super.set_exists(value);
|
||||
}
|
||||
|
||||
override function set_visible(value:Bool):Bool
|
||||
{
|
||||
if (exists && visible != value) transformChildren(visibleTransform, value);
|
||||
return super.set_visible(value);
|
||||
}
|
||||
|
||||
override function set_active(value:Bool):Bool
|
||||
{
|
||||
if (exists && active != value) transformChildren(activeTransform, value);
|
||||
return super.set_active(value);
|
||||
}
|
||||
|
||||
override function set_alive(value:Bool):Bool
|
||||
{
|
||||
if (alive != value) transformChildren(aliveTransform, value);
|
||||
return super.set_alive(value);
|
||||
}
|
||||
|
||||
override function set_x(value:Float):Float
|
||||
{
|
||||
if (!exists || x == value) return x; // early return (no need to transform)
|
||||
|
||||
transformChildren(xTransform, value - x); // offset
|
||||
return x = value;
|
||||
}
|
||||
|
||||
override function set_y(value:Float):Float
|
||||
{
|
||||
if (exists && y != value) transformChildren(yTransform, value - y); // offset
|
||||
return y = value;
|
||||
}
|
||||
|
||||
override function set_angle(value:Float):Float
|
||||
{
|
||||
if (exists && angle != value) transformChildren(angleTransform, value - angle); // offset
|
||||
return angle = value;
|
||||
}
|
||||
|
||||
override function set_alpha(value:Float):Float
|
||||
{
|
||||
value = value.clamp(0, 1);
|
||||
|
||||
if (exists && alpha != value)
|
||||
{
|
||||
transformChildren(directAlphaTransform, value);
|
||||
}
|
||||
return alpha = value;
|
||||
}
|
||||
|
||||
override function set_facing(value:FlxDirectionFlags):FlxDirectionFlags
|
||||
{
|
||||
if (exists && facing != value) transformChildren(facingTransform, value);
|
||||
return facing = value;
|
||||
}
|
||||
|
||||
override function set_flipX(value:Bool):Bool
|
||||
{
|
||||
if (exists && flipX != value) transformChildren(flipXTransform, value);
|
||||
return flipX = value;
|
||||
}
|
||||
|
||||
override function set_flipY(value:Bool):Bool
|
||||
{
|
||||
if (exists && flipY != value) transformChildren(flipYTransform, value);
|
||||
return flipY = value;
|
||||
}
|
||||
|
||||
override function set_moves(value:Bool):Bool
|
||||
{
|
||||
if (exists && moves != value) transformChildren(movesTransform, value);
|
||||
return moves = value;
|
||||
}
|
||||
|
||||
override function set_immovable(value:Bool):Bool
|
||||
{
|
||||
if (exists && immovable != value) transformChildren(immovableTransform, value);
|
||||
return immovable = value;
|
||||
}
|
||||
|
||||
override function set_solid(value:Bool):Bool
|
||||
{
|
||||
if (exists && solid != value) transformChildren(solidTransform, value);
|
||||
return super.set_solid(value);
|
||||
}
|
||||
|
||||
override function set_color(value:Int):Int
|
||||
{
|
||||
if (exists && color != value) transformChildren(gColorTransform, value);
|
||||
return color = value;
|
||||
}
|
||||
|
||||
override function set_blend(value:BlendMode):BlendMode
|
||||
{
|
||||
if (exists && blend != value) transformChildren(blendTransform, value);
|
||||
return blend = value;
|
||||
}
|
||||
|
||||
override function set_clipRect(rect:FlxRect):FlxRect
|
||||
{
|
||||
if (exists) transformChildren(clipRectTransform, rect);
|
||||
return super.set_clipRect(rect);
|
||||
}
|
||||
|
||||
override function set_pixelPerfectRender(value:Bool):Bool
|
||||
{
|
||||
if (exists && pixelPerfectRender != value) transformChildren(pixelPerfectTransform, value);
|
||||
return super.set_pixelPerfectRender(value);
|
||||
}
|
||||
|
||||
override function set_width(value:Float):Float
|
||||
{
|
||||
return value;
|
||||
var animNames = this.anim.getNameList();
|
||||
trace('[ATLASCHAR] Successfully loaded ${animNames.length} animations for ${characterId}');
|
||||
}
|
||||
|
||||
override function get_width():Float
|
||||
{
|
||||
if (this.mainSprite == null) return 0;
|
||||
|
||||
return this.mainSprite.width;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the left-most position of the left-most member.
|
||||
* If there are no members, x is returned.
|
||||
*
|
||||
* @since 5.0.0
|
||||
* @return the left-most position of the left-most member
|
||||
*/
|
||||
public function findMinX():Float
|
||||
{
|
||||
return this.mainSprite == null ? x : findMinXHelper();
|
||||
}
|
||||
|
||||
function findMinXHelper():Float
|
||||
{
|
||||
return this.mainSprite.x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the right-most position of the right-most member.
|
||||
* If there are no members, x is returned.
|
||||
*
|
||||
* @since 5.0.0
|
||||
* @return the right-most position of the right-most member
|
||||
*/
|
||||
public function findMaxX():Float
|
||||
{
|
||||
return this.mainSprite == null ? x : findMaxXHelper();
|
||||
}
|
||||
|
||||
function findMaxXHelper():Float
|
||||
{
|
||||
return this.mainSprite.x + this.mainSprite.width;
|
||||
}
|
||||
|
||||
/**
|
||||
* This functionality isn't supported in SpriteGroup
|
||||
*/
|
||||
override function set_height(value:Float):Float
|
||||
{
|
||||
return value;
|
||||
return originalSizes.x;
|
||||
}
|
||||
|
||||
override function get_height():Float
|
||||
{
|
||||
if (this.mainSprite == null) return 0;
|
||||
|
||||
return this.mainSprite.height;
|
||||
return originalSizes.y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the top-most position of the top-most member.
|
||||
* If there are no members, y is returned.
|
||||
*
|
||||
* @since 5.0.0
|
||||
* @return the top-most position of the top-most member
|
||||
*/
|
||||
public function findMinY():Float
|
||||
{
|
||||
return this.mainSprite == null ? y : findMinYHelper();
|
||||
}
|
||||
|
||||
function findMinYHelper():Float
|
||||
{
|
||||
return this.mainSprite.y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the top-most position of the top-most member.
|
||||
* If there are no members, y is returned.
|
||||
*
|
||||
* @since 5.0.0
|
||||
* @return the bottom-most position of the bottom-most member
|
||||
*/
|
||||
public function findMaxY():Float
|
||||
{
|
||||
return this.mainSprite == null ? y : findMaxYHelper();
|
||||
}
|
||||
|
||||
function findMaxYHelper():Float
|
||||
{
|
||||
return this.mainSprite.y + this.mainSprite.height;
|
||||
}
|
||||
|
||||
/**
|
||||
* This functionality isn't supported in SpriteGroup
|
||||
* @return this sprite group
|
||||
*/
|
||||
public override function loadGraphicFromSprite(Sprite:FlxSprite):FunkinSprite
|
||||
{
|
||||
#if FLX_DEBUG
|
||||
throw "This function is not supported in FlxSpriteGroup";
|
||||
#end
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* This functionality isn't supported in SpriteGroup
|
||||
* @return this sprite group
|
||||
*/
|
||||
public override function loadGraphic(Graphic:FlxGraphicAsset, Animated:Bool = false, Width:Int = 0, Height:Int = 0, Unique:Bool = false,
|
||||
?Key:String):FlxSprite
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* This functionality isn't supported in SpriteGroup
|
||||
* @return this sprite group
|
||||
*/
|
||||
public override function loadRotatedGraphic(Graphic:FlxGraphicAsset, Rotations:Int = 16, Frame:Int = -1, AntiAliasing:Bool = false, AutoBuffer:Bool = false,
|
||||
?Key:String):FlxSprite
|
||||
{
|
||||
#if FLX_DEBUG
|
||||
throw "This function is not supported in FlxSpriteGroup";
|
||||
#end
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* This functionality isn't supported in SpriteGroup
|
||||
* @return this sprite group
|
||||
*/
|
||||
public override function makeGraphic(Width:Int, Height:Int, Color:Int = FlxColor.WHITE, Unique:Bool = false, ?Key:String):FlxSprite
|
||||
{
|
||||
#if FLX_DEBUG
|
||||
throw "This function is not supported in FlxSpriteGroup";
|
||||
#end
|
||||
return this;
|
||||
}
|
||||
|
||||
override function set_pixels(value:BitmapData):BitmapData
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
override function set_frame(value:FlxFrame):FlxFrame
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
override function get_pixels():BitmapData
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal function to update the current animation frame.
|
||||
*
|
||||
* @param RunOnCpp Whether the frame should also be recalculated if we're on a non-flash target
|
||||
*/
|
||||
override inline function calcFrame(RunOnCpp:Bool = false):Void
|
||||
{
|
||||
// Nothing to do here
|
||||
}
|
||||
|
||||
/**
|
||||
* This functionality isn't supported in SpriteGroup
|
||||
*/
|
||||
override inline function resetHelpers():Void {}
|
||||
|
||||
/**
|
||||
* This functionality isn't supported in SpriteGroup
|
||||
*/
|
||||
public override inline function stamp(Brush:FlxSprite, X:Int = 0, Y:Int = 0):Void {}
|
||||
|
||||
override function set_frames(Frames:FlxFramesCollection):FlxFramesCollection
|
||||
{
|
||||
return Frames;
|
||||
}
|
||||
|
||||
/**
|
||||
* This functionality isn't supported in SpriteGroup
|
||||
*/
|
||||
override inline function updateColorTransform():Void {}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
package funkin.play.character;
|
||||
|
||||
import funkin.graphics.FunkinSprite;
|
||||
import funkin.util.assets.FlxAnimationUtil;
|
||||
import animate.FlxAnimateFrames;
|
||||
import funkin.modding.events.ScriptEvent;
|
||||
import funkin.data.animation.AnimationData;
|
||||
import funkin.data.character.CharacterData.CharacterRenderType;
|
||||
import flixel.math.FlxPoint;
|
||||
|
||||
/**
|
||||
* This render type is the most complex, and is used by characters which use
|
||||
* multiple Adobe Animate texture atlases. This render type concatenates multiple
|
||||
* texture atlases into a single sprite.
|
||||
*
|
||||
* BaseCharacter has game logic, MultiAnimateAtlasCharacter has only rendering logic.
|
||||
* KEEP THEM SEPARATE!
|
||||
*/
|
||||
class MultiAnimateAtlasCharacter extends BaseCharacter
|
||||
{
|
||||
var originalSizes(default, never):FlxPoint = new FlxPoint(0, 0);
|
||||
|
||||
public function new(id:String)
|
||||
{
|
||||
super(id, CharacterRenderType.MultiAnimateAtlas);
|
||||
}
|
||||
|
||||
override function onCreate(event:ScriptEvent):Void
|
||||
{
|
||||
// Display a custom scope for debugging purposes.
|
||||
#if FEATURE_DEBUG_TRACY
|
||||
cpp.vm.tracy.TracyProfiler.zoneScoped('MultiAnimateAtlasCharacter.create(${this.characterId})');
|
||||
#end
|
||||
|
||||
try
|
||||
{
|
||||
trace('Loading assets for Multi-Animate Atlas character "${characterId}"', flixel.util.FlxColor.fromString("#89CFF0"));
|
||||
loadAtlases();
|
||||
loadAnimations();
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
throw "Exception thrown while building sprite: " + e;
|
||||
}
|
||||
|
||||
trace('[MULTIATLASCHAR] Successfully loaded texture atlases for ${characterId} with ${_data.animations.length} animations.');
|
||||
super.onCreate(event);
|
||||
|
||||
originalSizes.set(this.width, this.height);
|
||||
}
|
||||
|
||||
function loadAtlases()
|
||||
{
|
||||
trace('[MULTIATLASCHAR] Loading sprite atlases for ${characterId}.');
|
||||
|
||||
var assetList:Array<String> = [];
|
||||
for (anim in _data.animations)
|
||||
{
|
||||
if (anim.assetPath != null && !assetList.contains(anim.assetPath))
|
||||
{
|
||||
assetList.push(anim.assetPath);
|
||||
}
|
||||
}
|
||||
|
||||
var baseAssetLibrary:String = Paths.getLibrary(_data.assetPath);
|
||||
var baseAssetPath:String = Paths.stripLibrary(_data.assetPath);
|
||||
|
||||
loadTextureAtlas(baseAssetPath, baseAssetLibrary, cast _data.atlasSettings);
|
||||
|
||||
for (asset in assetList)
|
||||
{
|
||||
var subAssetLibrary:String = Paths.getLibrary(asset);
|
||||
var subAssetPath:String = Paths.stripLibrary(asset);
|
||||
|
||||
var clone:FunkinSprite = FunkinSprite.createTextureAtlas(0, 0, subAssetPath, subAssetLibrary, cast _data.atlasSettings);
|
||||
var subTexture:FlxAnimateFrames = clone.library;
|
||||
|
||||
trace('Concatenating texture atlas: ${asset}');
|
||||
subTexture.parent.destroyOnNoUse = false;
|
||||
|
||||
this.library.addAtlas(subTexture);
|
||||
}
|
||||
|
||||
if (_data.isPixel)
|
||||
{
|
||||
this.isPixel = true;
|
||||
this.antialiasing = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.isPixel = false;
|
||||
this.antialiasing = true;
|
||||
}
|
||||
|
||||
this.setScale(_data.scale);
|
||||
}
|
||||
|
||||
function loadAnimations()
|
||||
{
|
||||
trace('[MULTIATLASCHAR] Loading ${_data.animations.length} animations for ${characterId}');
|
||||
|
||||
FlxAnimationUtil.addTextureAtlasAnimations(this, _data.animations);
|
||||
|
||||
for (anim in _data.animations)
|
||||
{
|
||||
if (anim.offsets == null)
|
||||
{
|
||||
setAnimationOffsets(anim.name, 0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
setAnimationOffsets(anim.name, anim.offsets[0], anim.offsets[1]);
|
||||
}
|
||||
}
|
||||
|
||||
var animNames = this.anim.getNameList();
|
||||
trace('[MULTIATLASCHAR] Successfully loaded ${animNames.length} animations for ${characterId}');
|
||||
}
|
||||
|
||||
override function get_width():Float
|
||||
{
|
||||
return originalSizes.x;
|
||||
}
|
||||
|
||||
override function get_height():Float
|
||||
{
|
||||
return originalSizes.y;
|
||||
}
|
||||
}
|
||||
@@ -126,9 +126,4 @@ class MultiSparrowCharacter extends BaseCharacter
|
||||
var animNames = this.animation.getNameList();
|
||||
trace('[MULTISPARROWCHAR] Successfully loaded ${animNames.length} animations for ${characterId}');
|
||||
}
|
||||
|
||||
public override function playAnimation(name:String, restart:Bool = false, ignoreOther:Bool = false, reverse:Bool = false):Void
|
||||
{
|
||||
super.playAnimation(name, restart, ignoreOther, reverse);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,14 @@ class ScriptedSparrowCharacter extends SparrowCharacter implements polymod.hscri
|
||||
@:hscriptClass
|
||||
class ScriptedMultiSparrowCharacter extends MultiSparrowCharacter implements polymod.hscript.HScriptedClass {}
|
||||
|
||||
/**
|
||||
* A script that can be tied to a MultiAnimateAtlasCharacter, which persists across states.
|
||||
* Create a scripted class that extends MultiAnimateAtlasCharacter,
|
||||
* then call `super('charId')` in the constructor to use this.
|
||||
*/
|
||||
@:hscriptClass
|
||||
class ScriptedMultiAnimateAtlasCharacter extends MultiAnimateAtlasCharacter implements polymod.hscript.HScriptedClass {}
|
||||
|
||||
/**
|
||||
* A script that can be tied to a PackerCharacter, which persists across states.
|
||||
* Create a scripted class that extends PackerCharacter,
|
||||
|
||||
@@ -49,8 +49,6 @@ class SparrowCharacter extends BaseCharacter
|
||||
{
|
||||
this.isPixel = true;
|
||||
this.antialiasing = false;
|
||||
// pixelPerfectRender = true;
|
||||
// pixelPerfectPosition = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -438,35 +438,6 @@ class HealthIcon extends FunkinSprite
|
||||
this.antialiasing = !isPixel;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Name of the current animation being played by this health icon.
|
||||
*/
|
||||
public function getCurrentAnimation():String
|
||||
{
|
||||
if (this.animation == null || this.animation.curAnim == null) return "";
|
||||
return this.animation.curAnim.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param id The name of the animation to check for.
|
||||
* @return Whether this sprite posesses the given animation.
|
||||
* Only true if the animation was successfully loaded from the XML.
|
||||
*/
|
||||
public function hasAnimation(id:String):Bool
|
||||
{
|
||||
if (this.animation == null) return false;
|
||||
|
||||
return this.animation.getByName(id) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Whether the current animation is in the finished state.
|
||||
*/
|
||||
public function isAnimationFinished():Bool
|
||||
{
|
||||
return this.animation.finished;
|
||||
}
|
||||
|
||||
/**
|
||||
* Plays the animation with the given name.
|
||||
* @param name The name of the animation to play.
|
||||
|
||||
@@ -201,12 +201,6 @@ class Speaker extends FlxSprite implements IDialogueScriptedClass implements IRe
|
||||
applyAnimationOffsets(correctName);
|
||||
}
|
||||
|
||||
public function getCurrentAnimation():String
|
||||
{
|
||||
if (this.animation == null || this.animation.curAnim == null) return "";
|
||||
return this.animation.curAnim.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that a given animation exists before playing it.
|
||||
* Will gracefully check for name, then name with stripped suffixes, then 'idle', then fail to play.
|
||||
|
||||
@@ -175,22 +175,6 @@ class StrumlineNote extends FunkinSprite
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the animation that is currently playing.
|
||||
* If no animation is playing (usually this means the sprite is BROKEN!),
|
||||
* returns an empty string to prevent NPEs.
|
||||
*/
|
||||
public function getCurrentAnimation():String
|
||||
{
|
||||
if (this.animation == null || this.animation.curAnim == null) return "";
|
||||
return this.animation.curAnim.name;
|
||||
}
|
||||
|
||||
public function isAnimationFinished():Bool
|
||||
{
|
||||
return this.animation.finished;
|
||||
}
|
||||
|
||||
static final DEFAULT_OFFSET:Int = 13;
|
||||
|
||||
/**
|
||||
|
||||
@@ -115,7 +115,7 @@ class NoteStyle implements IRegistryEntry<NoteStyleData>
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!FunkinSprite.isTextureCached(Paths.image(noteAssetPath)))
|
||||
if (!FunkinMemory.isTextureCached(Paths.image(noteAssetPath)))
|
||||
{
|
||||
FlxG.log.warn('Note texture is not cached: ${noteAssetPath}');
|
||||
}
|
||||
@@ -896,7 +896,7 @@ class NoteStyle implements IRegistryEntry<NoteStyleData>
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!FunkinSprite.isTextureCached(Paths.image(splashAssetPath)))
|
||||
if (!FunkinMemory.isTextureCached(Paths.image(splashAssetPath)))
|
||||
{
|
||||
FlxG.log.warn('Note Splash texture not cached: ${splashAssetPath}');
|
||||
}
|
||||
@@ -1046,7 +1046,7 @@ class NoteStyle implements IRegistryEntry<NoteStyleData>
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!FunkinSprite.isTextureCached(Paths.image(holdCoverAssetPath)))
|
||||
if (!FunkinMemory.isTextureCached(Paths.image(holdCoverAssetPath)))
|
||||
{
|
||||
FlxG.log.warn('Hold Note Cover texture not cached: ${holdCoverAssetPath}');
|
||||
}
|
||||
|
||||
@@ -203,13 +203,6 @@ class Bopper extends StageProp implements IPlayStateScriptedClass
|
||||
}
|
||||
}
|
||||
|
||||
public function hasAnimation(id:String):Bool
|
||||
{
|
||||
if (this.animation == null) return false;
|
||||
|
||||
return this.animation.getByName(id) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that a given animation exists before playing it.
|
||||
* Will gracefully check for name, then name with stripped suffixes, then fail to play.
|
||||
@@ -327,27 +320,11 @@ class Bopper extends StageProp implements IPlayStateScriptedClass
|
||||
this.animOffsets = offsets;
|
||||
}
|
||||
|
||||
public function isAnimationFinished():Bool
|
||||
{
|
||||
return this.animation?.finished ?? false;
|
||||
}
|
||||
|
||||
public function setAnimationOffsets(name:String, xOffset:Float, yOffset:Float):Void
|
||||
{
|
||||
animationOffsets.set(name, [xOffset, yOffset]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the animation that is currently playing.
|
||||
* If no animation is playing (usually this means the character is BROKEN!),
|
||||
* returns an empty string to prevent NPEs.
|
||||
*/
|
||||
public function getCurrentAnimation():String
|
||||
{
|
||||
if (this.animation == null || this.animation.curAnim == null) return "";
|
||||
return this.animation.curAnim.name;
|
||||
}
|
||||
|
||||
// override getScreenPosition (used by FlxSprite's draw method) to account for animation offsets.
|
||||
override function getScreenPosition(?result:FlxPoint, ?camera:FlxCamera):FlxPoint
|
||||
{
|
||||
|
||||
@@ -183,6 +183,8 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass implements
|
||||
{
|
||||
case 'packer':
|
||||
propSprite.loadPacker(dataProp.assetPath);
|
||||
case 'animateatlas':
|
||||
propSprite.loadTextureAtlas(dataProp.assetPath, _data.directory, cast dataProp.atlasSettings);
|
||||
default: // 'sparrow'
|
||||
propSprite.loadSparrow(dataProp.assetPath);
|
||||
}
|
||||
@@ -268,6 +270,8 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass implements
|
||||
cast(propSprite, Bopper).setAnimationOffsets(propAnim.name, propAnim.offsets[0], propAnim.offsets[1]);
|
||||
}
|
||||
}
|
||||
case 'animateatlas':
|
||||
FlxAnimationUtil.addTextureAtlasAnimations(propSprite, dataProp.animations);
|
||||
default: // 'sparrow'
|
||||
FlxAnimationUtil.addAtlasAnimations(propSprite, dataProp.animations);
|
||||
if (Std.isOfType(propSprite, Bopper))
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
package funkin.ui.charSelect;
|
||||
|
||||
import flixel.FlxSprite;
|
||||
|
||||
@:nullSafety
|
||||
class CharIcon extends FlxSprite
|
||||
{
|
||||
public var locked:Bool = false;
|
||||
|
||||
public function new(x:Float, y:Float, locked:Bool = false)
|
||||
{
|
||||
super(x, y);
|
||||
|
||||
this.locked = locked;
|
||||
|
||||
makeGraphic(128, 128);
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
package funkin.ui.charSelect;
|
||||
|
||||
import openfl.display.BitmapData;
|
||||
import openfl.filters.DropShadowFilter;
|
||||
import openfl.filters.ConvolutionFilter;
|
||||
import funkin.graphics.shaders.StrokeShader;
|
||||
|
||||
@:nullSafety
|
||||
class CharIconCharacter extends CharIcon
|
||||
{
|
||||
// public var dropShadowFilter:DropShadowFilter;
|
||||
|
||||
var matrixFilter:Array<Float> = [
|
||||
1, 1, 1,
|
||||
1, 1, 1,
|
||||
1, 1, 1
|
||||
];
|
||||
|
||||
var divisor:Int = 1;
|
||||
var bias:Int = 0;
|
||||
// var convolutionFilter:ConvolutionFilter;
|
||||
|
||||
// public var noDropShadow:BitmapData;
|
||||
// public var withDropShadow:BitmapData;
|
||||
|
||||
// var strokeShader:StrokeShader;
|
||||
|
||||
public function new(path:String)
|
||||
{
|
||||
super(0, 0, false);
|
||||
|
||||
loadGraphic(Paths.image('freeplay/icons/' + path + 'pixel'));
|
||||
setGraphicSize(128, 128);
|
||||
updateHitbox();
|
||||
antialiasing = false;
|
||||
|
||||
// strokeShader = new StrokeShader();
|
||||
// shader = strokeShader;
|
||||
|
||||
// noDropShadow = pixels.clone();
|
||||
|
||||
// dropShadowFilter = new DropShadowFilter(5, 45, 0, 1, 0, 0);
|
||||
// convolutionFilter = new ConvolutionFilter(3, 3, matrixFilter, divisor, bias);
|
||||
// pixels.applyFilter(pixels, pixels.rect, new openfl.geom.Point(0, 0), dropShadowFilter);
|
||||
// pixels.applyFilter(pixels, pixels.rect, new openfl.geom.Point(0, 0), convolutionFilter);
|
||||
// withDropShadow = pixels.clone();
|
||||
|
||||
// pixels = noDropShadow.clone();
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
package funkin.ui.charSelect;
|
||||
|
||||
@:nullSafety
|
||||
class CharIconLocked extends CharIcon {}
|
||||
@@ -0,0 +1,45 @@
|
||||
package funkin.ui.charSelect;
|
||||
|
||||
import animate.FlxAnimateFrames;
|
||||
import flixel.FlxG;
|
||||
|
||||
/**
|
||||
* Utility class for handling the atlases loaded by CharSelect & co. in an efficient way.
|
||||
* TODO: Maybe this should be a general utility class instead?
|
||||
*/
|
||||
class CharSelectAtlasHandler
|
||||
{
|
||||
static final framesCache:Map<String, FlxAnimateFrames> = [];
|
||||
|
||||
public static function loadAtlas(path:String, ?settings:FlxAnimateSettings):Null<FlxAnimateFrames>
|
||||
{
|
||||
if (framesCache.exists(path)) return framesCache.get(path);
|
||||
|
||||
var result:FlxAnimateFrames = FlxAnimateFrames.fromAnimate(Paths.animateAtlas(path),
|
||||
{
|
||||
swfMode: settings?.swfMode ?? true,
|
||||
filterQuality: settings?.filterQuality ?? MEDIUM,
|
||||
cacheOnLoad: settings?.cacheOnLoad ?? false
|
||||
});
|
||||
|
||||
if (result == null)
|
||||
{
|
||||
FlxG.log.error('Failed to load atlas at path $path!');
|
||||
return null;
|
||||
}
|
||||
|
||||
result.parent.destroyOnNoUse = false;
|
||||
framesCache.set(path, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static function clearAtlasCache():Void
|
||||
{
|
||||
for (frames in framesCache.iterator())
|
||||
{
|
||||
// NOTE: Doing this already calls checkUseCount!
|
||||
frames.parent.destroyOnNoUse = true;
|
||||
}
|
||||
framesCache.clear();
|
||||
}
|
||||
}
|
||||
@@ -1,67 +1,34 @@
|
||||
package funkin.ui.charSelect;
|
||||
|
||||
import funkin.graphics.adobeanimate.FlxAtlasSprite;
|
||||
import flixel.math.FlxMath;
|
||||
import funkin.util.FramesJSFLParser;
|
||||
import funkin.util.FramesJSFLParser.FramesJSFLInfo;
|
||||
import funkin.util.FramesJSFLParser.FramesJSFLFrame;
|
||||
import funkin.graphics.FunkinSprite;
|
||||
import funkin.modding.IScriptedClass.IBPMSyncedScriptedClass;
|
||||
import funkin.modding.events.ScriptEvent;
|
||||
import funkin.vis.dsp.SpectralAnalyzer;
|
||||
import funkin.data.freeplay.player.PlayerRegistry;
|
||||
import funkin.ui.FullScreenScaleMode;
|
||||
import flixel.math.FlxPoint;
|
||||
|
||||
@:nullSafety
|
||||
class CharSelectGF extends FlxAtlasSprite implements IBPMSyncedScriptedClass
|
||||
class CharSelectGF extends FunkinSprite implements IBPMSyncedScriptedClass
|
||||
{
|
||||
var fadeTimer:Float = 0;
|
||||
var fadingStatus:FadeStatus = OFF;
|
||||
var fadeAnimIndex:Int = 0;
|
||||
|
||||
var animInInfo:Null<FramesJSFLInfo>;
|
||||
var animOutInfo:Null<FramesJSFLInfo>;
|
||||
|
||||
var intendedYPos:Float = 0;
|
||||
var intendedAlpha:Float = 0;
|
||||
var list:Array<Null<String>> = [];
|
||||
|
||||
var analyzer:Null<SpectralAnalyzer>;
|
||||
var analyzerLevelsCache:Array<Bar> = new Array<Bar>();
|
||||
|
||||
var currentGFPath:Null<String>;
|
||||
var currentGFPath:String = "";
|
||||
var enableVisualizer:Bool = false;
|
||||
|
||||
public function new()
|
||||
{
|
||||
super(0, 0, Paths.animateAtlas("charSelect/gfChill"));
|
||||
var danceEvery:Int = 2;
|
||||
|
||||
list = anim.curSymbol.getFrameLabelNames();
|
||||
public function new(x:Float, y:Float)
|
||||
{
|
||||
super(x, y);
|
||||
this.applyStageMatrix = true;
|
||||
|
||||
switchGF(Constants.DEFAULT_CHARACTER);
|
||||
}
|
||||
|
||||
override public function update(elapsed:Float):Void
|
||||
{
|
||||
super.update(elapsed);
|
||||
|
||||
switch (fadingStatus)
|
||||
{
|
||||
case OFF:
|
||||
// do nothing if it's off!
|
||||
// or maybe force position to be 0,0?
|
||||
// maybe reset timers?
|
||||
resetFadeAnimParams();
|
||||
case FADE_OUT:
|
||||
if (animOutInfo != null) doFade(animOutInfo);
|
||||
case FADE_IN:
|
||||
if (animInInfo != null) doFade(animInInfo);
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
public function onStepHit(event:SongTimeScriptEvent):Void {}
|
||||
|
||||
var danceEvery:Int = 2;
|
||||
|
||||
public function onBeatHit(event:SongTimeScriptEvent):Void
|
||||
{
|
||||
// TODO: There's a minor visual bug where there's a little stutter.
|
||||
@@ -72,7 +39,7 @@ class CharSelectGF extends FlxAtlasSprite implements IBPMSyncedScriptedClass
|
||||
if (getCurrentAnimation() == "idle" && (event.beat % danceEvery == 0))
|
||||
{
|
||||
trace('GF beat hit');
|
||||
playAnimation("idle", true, false, false);
|
||||
anim.play("idle", true);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -87,9 +54,8 @@ class CharSelectGF extends FlxAtlasSprite implements IBPMSyncedScriptedClass
|
||||
if (enableVisualizer && analyzer != null)
|
||||
{
|
||||
analyzerLevelsCache = analyzer.getLevels(analyzerLevelsCache);
|
||||
var frame = anim.curSymbol.timeline.get("VIZ_bars").get(anim.curFrame);
|
||||
if (frame == null) return;
|
||||
var elements = frame.getList();
|
||||
var frame:Null<animate.internal.Frame> = this.timeline.getLayer("VIZ_bars")?.getFrameAtIndex(anim.curAnim.curFrame) ?? null;
|
||||
var elements:Array<animate.internal.elements.Element> = frame?.elements ?? [];
|
||||
var len:Int = cast Math.min(elements.length, 7);
|
||||
|
||||
for (i in 0...len)
|
||||
@@ -107,73 +73,31 @@ class CharSelectGF extends FlxAtlasSprite implements IBPMSyncedScriptedClass
|
||||
|
||||
animFrame = Std.int(Math.abs(animFrame - 12)); // shitty dumbass flip, cuz dave got da shit backwards lol!
|
||||
|
||||
elements[i].symbol.firstFrame = animFrame;
|
||||
var convertedSymbol = elements[i].toSymbolInstance();
|
||||
convertedSymbol.firstFrame = animFrame;
|
||||
|
||||
elements[i] = convertedSymbol;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param animInfo Should not be confused with animInInfo!
|
||||
* This is merely a local var for the function!
|
||||
*/
|
||||
function doFade(animInfo:Null<FramesJSFLInfo>):Void
|
||||
{
|
||||
if (animInfo == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
fadeTimer += FlxG.elapsed;
|
||||
if (fadeTimer >= 1 / 24)
|
||||
{
|
||||
fadeTimer -= FlxG.elapsed;
|
||||
// only inc the index for the first frame, used for reference of where to "start"
|
||||
if (fadeAnimIndex == 0)
|
||||
{
|
||||
fadeAnimIndex++;
|
||||
return;
|
||||
}
|
||||
|
||||
var curFrame:FramesJSFLFrame = animInfo.frames[fadeAnimIndex];
|
||||
var prevFrame:FramesJSFLFrame = animInfo.frames[fadeAnimIndex - 1];
|
||||
|
||||
var xDiff:Float = curFrame.x - prevFrame.x;
|
||||
var yDiff:Float = curFrame.y - prevFrame.y;
|
||||
var alphaDiff:Float = curFrame.alpha - prevFrame.alpha;
|
||||
alphaDiff /= 100; // flash exports alpha as a whole number
|
||||
|
||||
alpha += alphaDiff;
|
||||
alpha = alpha.clamp(0, 1);
|
||||
x += xDiff;
|
||||
y += yDiff;
|
||||
|
||||
fadeAnimIndex++;
|
||||
}
|
||||
|
||||
if (fadeAnimIndex >= animInfo.frames.length) fadingStatus = OFF;
|
||||
}
|
||||
|
||||
function resetFadeAnimParams()
|
||||
{
|
||||
fadeTimer = 0;
|
||||
fadeAnimIndex = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* For switching between "GFs" such as gf, nene, etc
|
||||
* @param bf Which BF we are selecting, so that we know the accompyaning GF
|
||||
*/
|
||||
public function switchGF(bf:String):Void
|
||||
{
|
||||
var previousGFPath = currentGFPath;
|
||||
var previousGFPath:String = currentGFPath;
|
||||
|
||||
var bfObj = PlayerRegistry.instance.fetchEntry(bf);
|
||||
var gfData = bfObj?.getCharSelectData()?.gf;
|
||||
if (gfData != null && gfData.assetPath != null) currentGFPath = Paths.animateAtlas(gfData.assetPath);
|
||||
var assetPath:Null<String> = gfData?.assetPath ?? "";
|
||||
|
||||
currentGFPath = assetPath;
|
||||
|
||||
// We don't need to update any anims if we didn't change GF
|
||||
trace('currentGFPath(${currentGFPath})');
|
||||
if (currentGFPath == null)
|
||||
if (currentGFPath == "")
|
||||
{
|
||||
this.visible = false;
|
||||
return;
|
||||
@@ -181,20 +105,24 @@ class CharSelectGF extends FlxAtlasSprite implements IBPMSyncedScriptedClass
|
||||
else if (previousGFPath != currentGFPath)
|
||||
{
|
||||
this.visible = true;
|
||||
loadAtlas(currentGFPath);
|
||||
|
||||
var path:String = currentGFPath;
|
||||
var texture:Null<animate.FlxAnimateFrames> = CharSelectAtlasHandler.loadAtlas(path, {swfMode: true});
|
||||
if (texture != null)
|
||||
{
|
||||
frames = texture;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.visible = false;
|
||||
currentGFPath = "";
|
||||
return;
|
||||
}
|
||||
|
||||
enableVisualizer = gfData?.visualizer ?? false;
|
||||
|
||||
var animInfoPath = Paths.file('images/${gfData?.animInfoPath}');
|
||||
|
||||
animInInfo = FramesJSFLParser.parse(animInfoPath + '/In.txt');
|
||||
animOutInfo = FramesJSFLParser.parse(animInfoPath + '/Out.txt');
|
||||
|
||||
if (animInInfo == null) trace(" ERROR ".bg_red().bold() + " Failed to load data for animInInfo, is the path provided correct?");
|
||||
if (animOutInfo == null) trace(" ERROR ".bg_red().bold() + " Failed to load data for animOutInfo, is the path provided correct?");
|
||||
}
|
||||
|
||||
playAnimation("idle", true, false, false);
|
||||
anim.play("idle", true);
|
||||
|
||||
updateHitbox();
|
||||
}
|
||||
@@ -207,10 +135,3 @@ class CharSelectGF extends FlxAtlasSprite implements IBPMSyncedScriptedClass
|
||||
|
||||
public function onUpdate(event:UpdateScriptEvent):Void {};
|
||||
}
|
||||
|
||||
enum FadeStatus
|
||||
{
|
||||
OFF;
|
||||
FADE_OUT;
|
||||
FADE_IN;
|
||||
}
|
||||
|
||||
@@ -1,41 +1,60 @@
|
||||
package funkin.ui.charSelect;
|
||||
|
||||
import funkin.graphics.adobeanimate.FlxAtlasSprite;
|
||||
import funkin.graphics.FunkinSprite;
|
||||
import funkin.modding.IScriptedClass.IBPMSyncedScriptedClass;
|
||||
import funkin.modding.events.ScriptEvent;
|
||||
|
||||
@:nullSafety
|
||||
class CharSelectPlayer extends FlxAtlasSprite implements IBPMSyncedScriptedClass
|
||||
class CharSelectPlayer extends FunkinSprite implements IBPMSyncedScriptedClass
|
||||
{
|
||||
static final DEFAULT_PATH = "charSelect/bfChill";
|
||||
|
||||
var initialX:Float = 0;
|
||||
var initialY:Float = 0;
|
||||
|
||||
var currentBFPath:Null<String>;
|
||||
|
||||
public function new(x:Float, y:Float)
|
||||
{
|
||||
initialX = x;
|
||||
initialY = y;
|
||||
|
||||
super(x, y, Paths.animateAtlas("charSelect/bfChill"));
|
||||
super(x, y);
|
||||
|
||||
onAnimationComplete.add(function(animLabel:String) {
|
||||
loadTextureAtlas(DEFAULT_PATH,
|
||||
{
|
||||
applyStageMatrix: true,
|
||||
swfMode: true
|
||||
});
|
||||
|
||||
anim.onFinish.add(function(animLabel:String) {
|
||||
switch (animLabel)
|
||||
{
|
||||
case "slidein":
|
||||
if (hasAnimation("slidein idle point"))
|
||||
{
|
||||
playAnimation("slidein idle point", true, false, false);
|
||||
anim.play("slidein idle point", true);
|
||||
}
|
||||
else
|
||||
{
|
||||
playAnimation("idle", true, false, false);
|
||||
anim.play("idle", true);
|
||||
anim.curAnim.looped = true;
|
||||
}
|
||||
case "deselect":
|
||||
playAnimation("deselect loop start", true, false, true);
|
||||
|
||||
anim.play("deselect loop start", true);
|
||||
case "slidein idle point", "cannot select Label", "unlock":
|
||||
playAnimation("idle", true, false, false);
|
||||
anim.play("idle", true);
|
||||
case "idle":
|
||||
trace('Waiting for onBeatHit');
|
||||
|
||||
// TODO: once char select data is refactored, add a `shouldBop` field or something IDK
|
||||
if (currentBFPath != null)
|
||||
{
|
||||
if (currentBFPath.endsWith("locked"))
|
||||
{
|
||||
anim.curAnim.looped = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -50,36 +69,30 @@ class CharSelectPlayer extends FlxAtlasSprite implements IBPMSyncedScriptedClass
|
||||
// but isAnimationFinished() and isLoopComplete() both don't work! What the hell?
|
||||
// danceEvery isn't necessary if that gets fixed.
|
||||
//
|
||||
if (getCurrentAnimation() == "idle")
|
||||
if (getCurrentAnimation() == "idle" && isAnimationFinished())
|
||||
{
|
||||
playAnimation("idle", true, false, false);
|
||||
anim.play("idle", true);
|
||||
}
|
||||
};
|
||||
|
||||
public function updatePosition(str:String)
|
||||
public function switchChar(str:String, playSlideAnim:Bool = true):Void
|
||||
{
|
||||
switch (str)
|
||||
{
|
||||
case "bf" | 'pico' | "random":
|
||||
x = initialX;
|
||||
y = initialY;
|
||||
}
|
||||
}
|
||||
var texture:Null<animate.FlxAnimateFrames> = CharSelectAtlasHandler.loadAtlas('charSelect/${str}Chill');
|
||||
|
||||
public function switchChar(str:String, playSlideAnim:Bool = true)
|
||||
{
|
||||
switch str
|
||||
if (texture != null)
|
||||
{
|
||||
default:
|
||||
loadAtlas(Paths.animateAtlas("charSelect/" + str + "Chill"));
|
||||
frames = texture;
|
||||
}
|
||||
else
|
||||
{
|
||||
trace('Failed to load character atlas for ${str}');
|
||||
return;
|
||||
}
|
||||
|
||||
final animName:String = playSlideAnim ? "slidein" : "idle";
|
||||
playAnimation(animName, true, false, false);
|
||||
anim.play(animName, true);
|
||||
|
||||
updateHitbox();
|
||||
|
||||
updatePosition(str);
|
||||
}
|
||||
|
||||
public function onScriptEvent(event:ScriptEvent):Void {};
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package funkin.ui.charSelect;
|
||||
|
||||
import openfl.filters.BitmapFilter;
|
||||
import flixel.FlxObject;
|
||||
import flixel.FlxSprite;
|
||||
import flixel.group.FlxGroup.FlxTypedGroup;
|
||||
import flixel.group.FlxSpriteGroup;
|
||||
import flixel.math.FlxPoint;
|
||||
@@ -11,33 +9,34 @@ import flixel.system.debug.watch.Tracker.TrackerProfile;
|
||||
import flixel.tweens.FlxEase;
|
||||
import flixel.tweens.FlxTween;
|
||||
import flixel.util.FlxTimer;
|
||||
import flixel.util.FlxColor;
|
||||
import funkin.audio.FunkinSound;
|
||||
import funkin.data.freeplay.player.PlayerData.PlayerCharSelectData;
|
||||
import funkin.data.freeplay.player.PlayerRegistry;
|
||||
import funkin.graphics.adobeanimate.FlxAtlasSprite;
|
||||
import openfl.filters.DropShadowFilter;
|
||||
import funkin.graphics.FunkinCamera;
|
||||
import funkin.graphics.FunkinSprite;
|
||||
import funkin.graphics.shaders.BlueFade;
|
||||
import funkin.modding.events.ScriptEvent;
|
||||
import funkin.modding.events.ScriptEventDispatcher;
|
||||
import funkin.play.stage.Stage;
|
||||
import funkin.save.Save;
|
||||
import funkin.ui.freeplay.charselect.PlayableCharacter;
|
||||
import funkin.ui.freeplay.FreeplayState;
|
||||
import funkin.ui.freeplay.charselect.PlayableCharacter;
|
||||
import funkin.ui.PixelatedIcon;
|
||||
import funkin.util.FramesJSFLParser;
|
||||
import funkin.util.FramesJSFLParser.FramesJSFLInfo;
|
||||
import funkin.util.HapticUtil;
|
||||
import funkin.util.MathUtil;
|
||||
import funkin.vis.dsp.SpectralAnalyzer;
|
||||
import openfl.display.BlendMode;
|
||||
import openfl.filters.ShaderFilter;
|
||||
import funkin.util.FramesJSFLParser;
|
||||
import funkin.util.FramesJSFLParser.FramesJSFLInfo;
|
||||
import funkin.graphics.FunkinSprite;
|
||||
import openfl.filters.BitmapFilter;
|
||||
import openfl.filters.DropShadowFilter;
|
||||
#if FEATURE_NEWGROUNDS
|
||||
import funkin.api.newgrounds.Medals;
|
||||
#end
|
||||
#if FEATURE_TOUCH_CONTROLS
|
||||
import funkin.util.TouchUtil;
|
||||
import funkin.util.SwipeUtil;
|
||||
import funkin.util.HapticUtil;
|
||||
#end
|
||||
|
||||
@:nullSafety
|
||||
class CharSelectSubState extends MusicBeatSubState
|
||||
@@ -45,33 +44,30 @@ class CharSelectSubState extends MusicBeatSubState
|
||||
// what the actual hell
|
||||
// having a hard time trying to make my changes work so i chose to be less stubborn and just remove them for now. - Zack
|
||||
// Left this here so somebody can remind me
|
||||
var cursor:FlxSprite = new FlxSprite(0, 0);
|
||||
var cursor:FunkinSprite;
|
||||
|
||||
var cursorBlue:FlxSprite = new FlxSprite(0, 0);
|
||||
var cursorDarkBlue:FlxSprite = new FlxSprite(0, 0);
|
||||
var grpCursors:FlxTypedGroup<FlxSprite> = new FlxTypedGroup<FlxSprite>();
|
||||
var cursorConfirmed:FlxSprite = new FlxSprite(0, 0);
|
||||
var cursorDenied:FlxSprite = new FlxSprite(0, 0);
|
||||
var cursorBlue:FunkinSprite;
|
||||
var cursorDarkBlue:FunkinSprite;
|
||||
var grpCursors:FlxTypedGroup<FunkinSprite>;
|
||||
var cursorConfirmed:FunkinSprite;
|
||||
var cursorDenied:FunkinSprite;
|
||||
var cursorX:Int = 0;
|
||||
var cursorY:Int = 0;
|
||||
var cursorFactor:Float = 110;
|
||||
var cursorOffsetX:Float = -16;
|
||||
var cursorOffsetY:Float = -48;
|
||||
var cursorLocIntended:FlxPoint = new FlxPoint(0, 0);
|
||||
var tmrFrames:Int = 60;
|
||||
// var currentStage:Stage;
|
||||
var playerChill:CharSelectPlayer = new CharSelectPlayer(0, 0);
|
||||
var playerChillOut:CharSelectPlayer = new CharSelectPlayer(0, 0);
|
||||
var gfChill:CharSelectGF = new CharSelectGF();
|
||||
// var gfChillOut:CharSelectGF;
|
||||
var barthing:FlxAtlasSprite = new FlxAtlasSprite(0, 0, Paths.animateAtlas("charSelect/barThing"));
|
||||
var dipshitBacking:FlxSprite = new FlxSprite(423, -17);
|
||||
var chooseDipshit:FlxSprite = new FlxSprite(426, -13);
|
||||
var dipshitBlur:FlxSprite = new FlxSprite(419, -65);
|
||||
var transitionGradient:FlxSprite = new FlxSprite(0, 0);
|
||||
var playerChill:CharSelectPlayer;
|
||||
var playerChillOut:CharSelectPlayer;
|
||||
var gfChill:CharSelectGF;
|
||||
var barthing:FunkinSprite;
|
||||
var dipshitBacking:FunkinSprite;
|
||||
var chooseDipshit:FunkinSprite;
|
||||
var dipshitBlur:FunkinSprite;
|
||||
var transitionGradient:FunkinSprite;
|
||||
var curChar(default, set):String = Constants.DEFAULT_CHARACTER;
|
||||
var rememberedChar:String;
|
||||
var nametag:Nametag = new Nametag(Constants.DEFAULT_CHARACTER);
|
||||
var nametag:Nametag;
|
||||
var camFollow:FlxObject = new FlxObject(0, 0, 1, 1);
|
||||
var autoFollow:Bool = false;
|
||||
var availableChars:Map<Int, String> = new Map<Int, String>();
|
||||
@@ -98,11 +94,41 @@ class CharSelectSubState extends MusicBeatSubState
|
||||
|
||||
var cutoutSize:Float = 0;
|
||||
|
||||
var fadeShader:BlueFade = new BlueFade();
|
||||
|
||||
public function new(?params:CharSelectSubStateParams)
|
||||
{
|
||||
super();
|
||||
rememberedChar = params?.character ?? Constants.DEFAULT_CHARACTER;
|
||||
loadAvailableCharacters();
|
||||
rememberedChar = params?.character ?? "";
|
||||
|
||||
grpCursors = new FlxTypedGroup<FunkinSprite>();
|
||||
grpHitboxes = new FlxTypedGroup<FlxObject>();
|
||||
|
||||
gfChill = new CharSelectGF(cutoutSize, 0);
|
||||
playerChillOut = new CharSelectPlayer(cutoutSize, 0);
|
||||
playerChill = new CharSelectPlayer(cutoutSize, 0);
|
||||
|
||||
dipshitBlur = new FunkinSprite(cutoutSize + 419, -65);
|
||||
dipshitBacking = new FunkinSprite(cutoutSize + 423, -17);
|
||||
chooseDipshit = new FunkinSprite(cutoutSize + 426, -13);
|
||||
|
||||
nametag = new Nametag(curChar);
|
||||
|
||||
cursor = new FunkinSprite(0, 0);
|
||||
cursorBlue = new FunkinSprite(0, 0);
|
||||
cursorDarkBlue = new FunkinSprite(0, 0);
|
||||
cursorConfirmed = new FunkinSprite(0, 0);
|
||||
cursorDenied = new FunkinSprite(0, 0);
|
||||
|
||||
charHitbox = new FlxObject(FlxG.width * 0.65, FlxG.height * 0.2, 300, 500);
|
||||
|
||||
transitionGradient = new FunkinSprite(0, 0);
|
||||
barthing = new FunkinSprite(0, 0);
|
||||
|
||||
selectSound = new FunkinSound();
|
||||
unlockSound = new FunkinSound();
|
||||
lockedSound = new FunkinSound();
|
||||
staticSound = new FunkinSound();
|
||||
}
|
||||
|
||||
function loadAvailableCharacters():Void
|
||||
@@ -111,9 +137,7 @@ class CharSelectSubState extends MusicBeatSubState
|
||||
|
||||
for (playerId in playerIds)
|
||||
{
|
||||
var player:Null<PlayableCharacter> = PlayerRegistry.instance.fetchEntry(playerId);
|
||||
if (player == null) continue;
|
||||
var playerData = player.getCharSelectData();
|
||||
var playerData:Null<PlayerCharSelectData> = PlayerRegistry.instance.fetchEntry(playerId)?.getCharSelectData();
|
||||
if (playerData == null) continue;
|
||||
|
||||
var targetPosition:Int = playerData.position ?? 0;
|
||||
@@ -124,15 +148,26 @@ class CharSelectSubState extends MusicBeatSubState
|
||||
|
||||
trace('Placing player ${playerId} at position ${targetPosition}');
|
||||
availableChars.set(targetPosition, playerId);
|
||||
}
|
||||
}
|
||||
|
||||
var fadeShader:BlueFade = new BlueFade();
|
||||
CharSelectAtlasHandler.loadAtlas('charSelect/${playerId}Chill');
|
||||
|
||||
var gfPath:Null<String> = playerData.gf?.assetPath;
|
||||
if (gfPath != null)
|
||||
{
|
||||
CharSelectAtlasHandler.loadAtlas(gfPath);
|
||||
}
|
||||
}
|
||||
|
||||
// Mr. Static also needs some caching...
|
||||
CharSelectAtlasHandler.loadAtlas('charSelect/lockedChill', {filterQuality: LOW, cacheOnLoad: true});
|
||||
}
|
||||
|
||||
override public function create():Void
|
||||
{
|
||||
super.create();
|
||||
|
||||
loadAvailableCharacters();
|
||||
|
||||
cutoutSize = FullScreenScaleMode.gameCutoutSize.x / 2;
|
||||
|
||||
bopInfo = FramesJSFLParser.parse(Paths.file("images/charSelect/iconBopInfo/iconBopInfo.txt"));
|
||||
@@ -141,35 +176,39 @@ class CharSelectSubState extends MusicBeatSubState
|
||||
trace(" ERROR ".bg_red().bold() + " Failed to load data for bopInfo, is the path provided correct?");
|
||||
}
|
||||
|
||||
var bg:FlxSprite = new FlxSprite(cutoutSize + -153, -140);
|
||||
var bg:FunkinSprite = new FunkinSprite(cutoutSize + -153, -140);
|
||||
bg.loadGraphic(Paths.image('charSelect/charSelectBG'));
|
||||
bg.scrollFactor.set(0.1, 0.1);
|
||||
add(bg);
|
||||
|
||||
var crowd:FlxAtlasSprite = new FlxAtlasSprite(cutoutSize, 0, Paths.animateAtlas("charSelect/crowd"));
|
||||
crowd.anim.play();
|
||||
crowd.anim.onComplete.add(function() {
|
||||
crowd.anim.play();
|
||||
});
|
||||
var crowd:FunkinSprite = FunkinSprite.createTextureAtlas(cutoutSize, 0, "charSelect/crowd",
|
||||
{
|
||||
applyStageMatrix: true
|
||||
});
|
||||
crowd.anim.play('');
|
||||
crowd.anim.curAnim.looped = true;
|
||||
crowd.scrollFactor.set(0.3, 0.3);
|
||||
add(crowd);
|
||||
|
||||
var stageSpr:FlxAtlasSprite = new FlxAtlasSprite(cutoutSize + -2, 1, Paths.animateAtlas("charSelect/charSelectStage"));
|
||||
stageSpr.anim.play("");
|
||||
stageSpr.anim.onComplete.add(function() {
|
||||
stageSpr.anim.play("");
|
||||
});
|
||||
var stageSpr:FunkinSprite = FunkinSprite.createTextureAtlas(cutoutSize - 2, 1, "charSelect/charSelectStage",
|
||||
{
|
||||
applyStageMatrix: true
|
||||
});
|
||||
stageSpr.anim.play('');
|
||||
stageSpr.anim.curAnim.looped = true;
|
||||
add(stageSpr);
|
||||
|
||||
var curtains:FlxSprite = new FlxSprite(cutoutSize + (-47 - 165), -49 - 50);
|
||||
var curtains:FunkinSprite = new FunkinSprite(cutoutSize + -212, -99);
|
||||
curtains.loadGraphic(Paths.image('charSelect/curtains'));
|
||||
curtains.scrollFactor.set(1.4, 1.4);
|
||||
add(curtains);
|
||||
|
||||
barthing.anim.play("");
|
||||
barthing.anim.onComplete.add(function() {
|
||||
barthing.anim.play("");
|
||||
});
|
||||
barthing.loadTextureAtlas("charSelect/barThing",
|
||||
{
|
||||
applyStageMatrix: true
|
||||
});
|
||||
barthing.anim.play('');
|
||||
barthing.anim.curAnim.looped = true;
|
||||
barthing.blend = BlendMode.MULTIPLY;
|
||||
barthing.scale.x = 2.5;
|
||||
barthing.scrollFactor.set(0, 0);
|
||||
@@ -178,27 +217,23 @@ class CharSelectSubState extends MusicBeatSubState
|
||||
barthing.y += 80;
|
||||
FlxTween.tween(barthing, {y: barthing.y - 80}, 1.3, {ease: FlxEase.expoOut});
|
||||
|
||||
var charLight:FlxSprite = new FlxSprite(cutoutSize + 800, 250);
|
||||
var charLight:FunkinSprite = new FunkinSprite(cutoutSize + 800, 250);
|
||||
charLight.loadGraphic(Paths.image('charSelect/charLight'));
|
||||
add(charLight);
|
||||
|
||||
var charLightGF:FlxSprite = new FlxSprite(cutoutSize + 180, 240);
|
||||
var charLightGF:FunkinSprite = new FunkinSprite(cutoutSize + 180, 240);
|
||||
charLightGF.loadGraphic(Paths.image('charSelect/charLight'));
|
||||
add(charLightGF);
|
||||
|
||||
function setupPlayerChill(character:String)
|
||||
{
|
||||
gfChill = new CharSelectGF();
|
||||
gfChill.switchGF(character);
|
||||
gfChill.x += cutoutSize;
|
||||
add(gfChill);
|
||||
|
||||
playerChillOut = new CharSelectPlayer(cutoutSize, 0);
|
||||
playerChillOut.switchChar(character, false);
|
||||
playerChillOut.visible = false;
|
||||
add(playerChillOut);
|
||||
|
||||
playerChill = new CharSelectPlayer(cutoutSize, 0);
|
||||
playerChill.switchChar(character, false);
|
||||
add(playerChill);
|
||||
}
|
||||
@@ -221,28 +256,27 @@ class CharSelectSubState extends MusicBeatSubState
|
||||
else
|
||||
setupPlayerChill(Constants.DEFAULT_CHARACTER);
|
||||
|
||||
var speakers:FlxAtlasSprite = new FlxAtlasSprite(cutoutSize - 10, 0, Paths.animateAtlas("charSelect/charSelectSpeakers"));
|
||||
speakers.anim.play("");
|
||||
speakers.anim.onComplete.add(function() {
|
||||
speakers.anim.play("");
|
||||
});
|
||||
var speakers:FunkinSprite = FunkinSprite.createTextureAtlas(cutoutSize - 10, 0, "charSelect/charSelectSpeakers",
|
||||
{
|
||||
applyStageMatrix: true
|
||||
});
|
||||
speakers.anim.play('');
|
||||
speakers.anim.curAnim.looped = true;
|
||||
speakers.scrollFactor.set(1.8, 1.8);
|
||||
speakers.scale.set(1.05, 1.05);
|
||||
add(speakers);
|
||||
|
||||
var fgBlur:FlxSprite = new FlxSprite(cutoutSize + -125, 170);
|
||||
var fgBlur:FunkinSprite = new FunkinSprite(cutoutSize + -125, 170);
|
||||
fgBlur.loadGraphic(Paths.image('charSelect/foregroundBlur'));
|
||||
fgBlur.blend = openfl.display.BlendMode.MULTIPLY;
|
||||
fgBlur.blend = BlendMode.MULTIPLY;
|
||||
add(fgBlur);
|
||||
|
||||
dipshitBlur = new FlxSprite(cutoutSize + 419, -65);
|
||||
dipshitBlur.frames = Paths.getSparrowAtlas("charSelect/dipshitBlur");
|
||||
dipshitBlur.animation.addByPrefix('idle', "CHOOSE vertical offset instance 1", 24, true);
|
||||
dipshitBlur.blend = BlendMode.ADD;
|
||||
dipshitBlur.animation.play("idle");
|
||||
add(dipshitBlur);
|
||||
|
||||
dipshitBacking = new FlxSprite(cutoutSize + 423, -17);
|
||||
dipshitBacking.frames = Paths.getSparrowAtlas("charSelect/dipshitBacking");
|
||||
dipshitBacking.animation.addByPrefix('idle', "CHOOSE horizontal offset instance 1", 24, true);
|
||||
dipshitBacking.blend = BlendMode.ADD;
|
||||
@@ -252,7 +286,6 @@ class CharSelectSubState extends MusicBeatSubState
|
||||
dipshitBacking.y += 210;
|
||||
FlxTween.tween(dipshitBacking, {y: dipshitBacking.y - 210}, 1.1, {ease: FlxEase.expoOut});
|
||||
|
||||
chooseDipshit = new FlxSprite(cutoutSize + 426, -13);
|
||||
chooseDipshit.loadGraphic(Paths.image('charSelect/chooseDipshit'));
|
||||
add(chooseDipshit);
|
||||
|
||||
@@ -266,7 +299,6 @@ class CharSelectSubState extends MusicBeatSubState
|
||||
dipshitBacking.scrollFactor.set();
|
||||
dipshitBlur.scrollFactor.set();
|
||||
|
||||
nametag = new Nametag(curChar);
|
||||
nametag.midpointX += cutoutSize;
|
||||
add(nametag);
|
||||
|
||||
@@ -278,31 +310,14 @@ class CharSelectSubState extends MusicBeatSubState
|
||||
|
||||
nametag.scrollFactor.set();
|
||||
|
||||
FlxG.debugger.addTrackerProfile(new TrackerProfile(FlxSprite, ["x", "y", "alpha", "scale", "blend"]));
|
||||
FlxG.debugger.addTrackerProfile(new TrackerProfile(FlxAtlasSprite, ["x", "y"]));
|
||||
FlxG.debugger.addTrackerProfile(new TrackerProfile(FunkinSprite, ["x", "y", "alpha", "scale", "blend"]));
|
||||
FlxG.debugger.addTrackerProfile(new TrackerProfile(FlxSound, ["pitch", "volume"]));
|
||||
|
||||
// FlxG.debugger.track(crowd);
|
||||
// FlxG.debugger.track(stageSpr, "stageSpr");
|
||||
// FlxG.debugger.track(bfChill, "bf chill");
|
||||
// FlxG.debugger.track(playerChill, "player");
|
||||
// FlxG.debugger.track(nametag, "nametag");
|
||||
// FlxG.debugger.track(selectSound, "selectSound");
|
||||
// FlxG.debugger.track(chooseDipshit, "choose dipshit");
|
||||
// FlxG.debugger.track(barthing, "barthing");
|
||||
// FlxG.debugger.track(fgBlur, "fgBlur");
|
||||
// FlxG.debugger.track(dipshitBlur, "dipshitBlur");
|
||||
// FlxG.debugger.track(dipshitBacking, "dipshitBacking");
|
||||
// FlxG.debugger.track(charLightGF, "charLight");
|
||||
// FlxG.debugger.track(gfChill, "gfChill");
|
||||
|
||||
add(grpCursors);
|
||||
|
||||
cursor.loadGraphic(Paths.image('charSelect/charSelector'));
|
||||
cursor.color = 0xFFFFFF00;
|
||||
|
||||
// FFCC00
|
||||
|
||||
cursorBlue.loadGraphic(Paths.image('charSelect/charSelector'));
|
||||
cursorBlue.color = 0xFF3EBBFF;
|
||||
|
||||
@@ -328,7 +343,6 @@ class CharSelectSubState extends MusicBeatSubState
|
||||
grpCursors.add(cursorBlue);
|
||||
grpCursors.add(cursor);
|
||||
|
||||
charHitbox = new FlxObject(FlxG.width * 0.65, FlxG.height * 0.2, 300, 500);
|
||||
charHitbox.active = false;
|
||||
charHitbox.scrollFactor.set();
|
||||
|
||||
@@ -389,25 +403,17 @@ class CharSelectSubState extends MusicBeatSubState
|
||||
|
||||
FlxTween.color(cursor, 0.2, 0xFFFFFF00, 0xFFFFCC00, {type: PINGPONG});
|
||||
|
||||
// FlxG.debugger.track(cursor);
|
||||
|
||||
FlxG.debugger.addTrackerProfile(new TrackerProfile(CharSelectSubState, ["curChar", "grpXSpread", "grpYSpread"]));
|
||||
FlxG.debugger.track(this);
|
||||
|
||||
add(camFollow);
|
||||
camFollow.screenCenter();
|
||||
|
||||
// FlxG.camera.follow(camFollow, LOCKON, 0.01);
|
||||
FlxG.camera.follow(camFollow, LOCKON);
|
||||
|
||||
var fadeShaderFilter:ShaderFilter = new ShaderFilter(fadeShader);
|
||||
FlxG.camera.filters = [fadeShaderFilter];
|
||||
|
||||
var temp:FlxSprite = new FlxSprite();
|
||||
temp.loadGraphic(Paths.image('charSelect/placement'));
|
||||
add(temp);
|
||||
temp.alpha = 0.0;
|
||||
|
||||
Conductor.stepHit.add(spamOnStep);
|
||||
// FlxG.debugger.track(temp, "tempBG");
|
||||
|
||||
@@ -462,6 +468,7 @@ class CharSelectSubState extends MusicBeatSubState
|
||||
FlxG.sound.list.add(introSound);
|
||||
|
||||
openSubState(new IntroSubState());
|
||||
|
||||
subStateClosed.addOnce((_) -> {
|
||||
remove(blackScreen);
|
||||
if (!Save.instance.oldChar.value)
|
||||
@@ -477,6 +484,12 @@ class CharSelectSubState extends MusicBeatSubState
|
||||
});
|
||||
}
|
||||
|
||||
override public function destroy():Void
|
||||
{
|
||||
CharSelectAtlasHandler.clearAtlasCache();
|
||||
super.destroy();
|
||||
}
|
||||
|
||||
function checkNewChar():Void
|
||||
{
|
||||
if (nonLocks.length > 0) selectTimer.start(2, (_) -> {
|
||||
@@ -511,7 +524,7 @@ class CharSelectSubState extends MusicBeatSubState
|
||||
}
|
||||
|
||||
var grpIcons:FlxSpriteGroup = new FlxSpriteGroup();
|
||||
var grpHitboxes:FlxTypedGroup<FlxObject> = new FlxTypedGroup<FlxObject>();
|
||||
var grpHitboxes:FlxTypedGroup<FlxObject>;
|
||||
var grpXSpread(default, set):Float = 107;
|
||||
var grpYSpread(default, set):Float = 127;
|
||||
var nonLocks = [];
|
||||
@@ -521,7 +534,6 @@ class CharSelectSubState extends MusicBeatSubState
|
||||
add(grpIcons);
|
||||
|
||||
FlxG.debugger.addTrackerProfile(new TrackerProfile(FlxSpriteGroup, ["x", "y"]));
|
||||
// FlxG.debugger.track(grpIcons, "iconGrp");
|
||||
|
||||
for (i in 0...9)
|
||||
{
|
||||
@@ -542,12 +554,13 @@ class CharSelectSubState extends MusicBeatSubState
|
||||
var isPlayerUnlocked:Bool = player?.isUnlocked() ?? false;
|
||||
if (availableChars.exists(i) && isPlayerUnlocked) nonLocks.push(i);
|
||||
|
||||
var temp:Lock = new Lock(0, 0, i);
|
||||
temp.ID = 1;
|
||||
var temp:Lock = new Lock(0, 0, i,
|
||||
{
|
||||
swfMode: true,
|
||||
uniqueInCache: true
|
||||
});
|
||||
|
||||
// temp.onAnimationComplete.add(function(anim) {
|
||||
// if (anim == "unlock") playerChill.playAnimation("unlock", true);
|
||||
// });
|
||||
temp.ID = 1;
|
||||
|
||||
grpIcons.add(temp);
|
||||
}
|
||||
@@ -582,6 +595,7 @@ class CharSelectSubState extends MusicBeatSubState
|
||||
var xThing = (copy - index - 2) * -1;
|
||||
// Look, I'd write better code but I had better aneurysms, my bad - Cheems
|
||||
// felt - Zack
|
||||
// Krue - Abnormal
|
||||
cursorY = yThing;
|
||||
cursorX = xThing;
|
||||
|
||||
@@ -592,26 +606,21 @@ class CharSelectSubState extends MusicBeatSubState
|
||||
selectTimer.start(0.5, function(_) {
|
||||
var lock:Lock = cast grpIcons.group.members[index];
|
||||
|
||||
lock.anim.getFrameLabel("unlockAnim").add(function() {
|
||||
playerChillOut.playAnimation("death");
|
||||
lock.anim.play("unlock");
|
||||
lock.anim.onFrameChange.add(function(animName:String, frame:Int, index:Int) {
|
||||
if (frame == 40)
|
||||
{
|
||||
playerChillOut.anim.play("death");
|
||||
}
|
||||
});
|
||||
|
||||
lock.playAnimation("unlock");
|
||||
|
||||
unlockSound.volume = 0.7;
|
||||
unlockSound.play(true);
|
||||
|
||||
// Do not sync the lock, because otherwise the animation will be out of sync!
|
||||
|
||||
/*syncLock = lock;
|
||||
|
||||
sync = true; */
|
||||
|
||||
lock.onAnimationComplete.addOnce(function(_) {
|
||||
// syncLock = null;
|
||||
var char:Null<String> = availableChars.get(index) ?? Constants.DEFAULT_CHARACTER;
|
||||
lock.anim.onFinish.addOnce(function(_) {
|
||||
var char:String = availableChars.get(index) ?? Constants.DEFAULT_CHARACTER;
|
||||
camera.flash(0xFFFFFFFF, 0.1);
|
||||
playerChill.playAnimation("unlock");
|
||||
playerChill.anim.play("unlock");
|
||||
playerChill.visible = true;
|
||||
|
||||
var id = grpIcons.members.indexOf(lock);
|
||||
@@ -631,7 +640,7 @@ class CharSelectSubState extends MusicBeatSubState
|
||||
bopPlay = true;
|
||||
|
||||
updateIconPositions();
|
||||
playerChillOut.onAnimationComplete.addOnce((_) -> if (_ == "death")
|
||||
playerChillOut.anim.onFinish.addOnce((_) -> if (_ == "death")
|
||||
{
|
||||
// sync = false;
|
||||
playerChillOut.visible = false;
|
||||
@@ -671,7 +680,7 @@ class CharSelectSubState extends MusicBeatSubState
|
||||
});
|
||||
}
|
||||
else
|
||||
playerChill.onAnimationComplete.addOnce((_) -> unLock());
|
||||
playerChill.anim.onFinish.addOnce((_) -> unLock());
|
||||
});
|
||||
|
||||
playerChill.visible = false;
|
||||
@@ -685,6 +694,7 @@ class CharSelectSubState extends MusicBeatSubState
|
||||
{
|
||||
grpIcons.x = cutoutSize + 450;
|
||||
grpIcons.y = 120;
|
||||
|
||||
for (index => member in grpIcons.members)
|
||||
{
|
||||
var posX:Float = (index % 3);
|
||||
@@ -710,34 +720,6 @@ class CharSelectSubState extends MusicBeatSubState
|
||||
}
|
||||
}
|
||||
|
||||
var sync:Bool = false;
|
||||
var syncLock:Null<Lock>;
|
||||
var audioBizz:Float = 0;
|
||||
|
||||
function syncAudio(elapsed:Float):Void
|
||||
{
|
||||
@:privateAccess
|
||||
if (sync && unlockSound.time > 0)
|
||||
{
|
||||
// if (playerChillOut.anim.framerate > 0)
|
||||
// {
|
||||
// if (syncLock != null) syncLock.anim.framerate = 0;
|
||||
// playerChillOut.anim.framerate = 0;
|
||||
// }
|
||||
|
||||
playerChillOut.anim._tick = 0;
|
||||
if (syncLock != null) syncLock.anim._tick = 0;
|
||||
|
||||
if ((unlockSound.time - audioBizz) >= ((delay) * 100))
|
||||
{
|
||||
if (syncLock != null) syncLock.anim._tick = delay;
|
||||
|
||||
playerChillOut.anim._tick = delay;
|
||||
audioBizz += delay * 100;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function goToFreeplay():Void
|
||||
{
|
||||
allowInput = false;
|
||||
@@ -762,7 +744,6 @@ class CharSelectSubState extends MusicBeatSubState
|
||||
FlxTween.tween(dipshitBlur, {y: dipshitBlur.y + 220}, 0.8, {ease: FlxEase.backIn});
|
||||
for (index => member in grpIcons.members)
|
||||
{
|
||||
// member.y += 300;
|
||||
FlxTween.tween(member, {y: member.y + 300}, 0.8, {ease: FlxEase.backIn});
|
||||
}
|
||||
FlxG.camera.follow(camFollow, LOCKON);
|
||||
@@ -813,8 +794,6 @@ class CharSelectSubState extends MusicBeatSubState
|
||||
if (controls.UI_UP_R || controls.UI_DOWN_R || controls.UI_LEFT_R || controls.UI_RIGHT_R #if FEATURE_TOUCH_CONTROLS || TouchUtil.justReleased #end)
|
||||
selectSound.pitch = 1;
|
||||
|
||||
syncAudio(elapsed);
|
||||
|
||||
if (allowInput && !pressedSelect)
|
||||
{
|
||||
#if FEATURE_TOUCH_CONTROLS
|
||||
@@ -938,10 +917,10 @@ class CharSelectSubState extends MusicBeatSubState
|
||||
cursorY = -1;
|
||||
}
|
||||
|
||||
if (availableChars.exists(getCurrentSelected())
|
||||
&& PlayerRegistry.instance.isCharacterSeen(availableChars[getCurrentSelected()] ?? Constants.DEFAULT_CHARACTER))
|
||||
var currentCharacter:String = availableChars[getCurrentSelected()] ?? Constants.DEFAULT_CHARACTER;
|
||||
if (availableChars.exists(getCurrentSelected()) && Save.instance.charactersSeen.value.contains(currentCharacter))
|
||||
{
|
||||
var charId = availableChars.get(getCurrentSelected());
|
||||
var charId:String = availableChars.get(getCurrentSelected()) ?? Constants.DEFAULT_CHARACTER;
|
||||
if (charId != null) curChar = charId;
|
||||
|
||||
if (allowInput && pressedSelect && (controls.BACK #if FEATURE_TOUCH_CONTROLS || (mobileDeny && TouchUtil.justReleased) #end))
|
||||
@@ -961,8 +940,8 @@ class CharSelectSubState extends MusicBeatSubState
|
||||
|
||||
FlxTween.globalManager.cancelTweensOf(FlxG.sound.music);
|
||||
FlxTween.tween(FlxG.sound.music, {pitch: 1.0, volume: 1.0}, 1, {ease: FlxEase.quartInOut});
|
||||
playerChill.playAnimation("deselect");
|
||||
gfChill.playAnimation("deselect");
|
||||
playerChill.anim.play("deselect");
|
||||
gfChill.anim.play("deselect");
|
||||
pressedSelect = false;
|
||||
FlxTween.tween(FlxG.sound.music, {pitch: 1.0}, 1,
|
||||
{
|
||||
@@ -970,8 +949,10 @@ class CharSelectSubState extends MusicBeatSubState
|
||||
onComplete: (_) -> {
|
||||
if (playerChill.getCurrentAnimation() == "deselect loop start" || playerChill.getCurrentAnimation() == "deselect")
|
||||
{
|
||||
playerChill.playAnimation("idle", true, false, true);
|
||||
gfChill.playAnimation("idle", true, false, true);
|
||||
playerChill.anim.play("idle", true);
|
||||
playerChill.anim.curAnim.looped = true;
|
||||
gfChill.anim.play("idle", true);
|
||||
gfChill.anim.curAnim.looped = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -1004,8 +985,9 @@ class CharSelectSubState extends MusicBeatSubState
|
||||
|
||||
FlxTween.tween(FlxG.sound.music, {pitch: 0.1}, 1, {ease: FlxEase.quadInOut});
|
||||
FlxTween.tween(FlxG.sound.music, {volume: 0.0}, 1.5, {ease: FlxEase.quadInOut});
|
||||
playerChill.playAnimation("select");
|
||||
gfChill.playAnimation("confirm", true, false, true);
|
||||
playerChill.anim.play("select");
|
||||
gfChill.anim.play("confirm", true);
|
||||
gfChill.anim.curAnim.looped = true;
|
||||
pressedSelect = true;
|
||||
selectTimer.start(1.5, (_) -> {
|
||||
goToFreeplay();
|
||||
@@ -1027,7 +1009,7 @@ class CharSelectSubState extends MusicBeatSubState
|
||||
{
|
||||
cursorDenied.visible = true;
|
||||
|
||||
playerChill.playAnimation("cannot select Label", true);
|
||||
playerChill.anim.play("cannot select Label", true);
|
||||
|
||||
lockedSound.play(true);
|
||||
|
||||
@@ -1140,7 +1122,6 @@ class CharSelectSubState extends MusicBeatSubState
|
||||
{
|
||||
if (spamUp || spamDown || spamLeft || spamRight)
|
||||
{
|
||||
// selectSound.changePitchBySemitone(1);
|
||||
if (selectSound.pitch > 5) selectSound.pitch = 5;
|
||||
selectSound.play(true);
|
||||
|
||||
@@ -1182,22 +1163,20 @@ class CharSelectSubState extends MusicBeatSubState
|
||||
switch (lock.getCurrentAnimation())
|
||||
{
|
||||
case "idle":
|
||||
lock.playAnimation("selected");
|
||||
lock.anim.play("selected");
|
||||
case "selected" | "clicked":
|
||||
if (controls.ACCEPT) lock.playAnimation("clicked", true);
|
||||
if (controls.ACCEPT) lock.anim.play("clicked", true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
lock.playAnimation("idle");
|
||||
lock.anim.play("idle");
|
||||
}
|
||||
case 0:
|
||||
var memb:PixelatedIcon = cast member;
|
||||
|
||||
if (index == getCurrentSelected())
|
||||
{
|
||||
// memb.pixels = memb.withDropShadow.clone();
|
||||
|
||||
if (bopPlay)
|
||||
{
|
||||
if (bopRefX == 0)
|
||||
@@ -1216,19 +1195,15 @@ class CharSelectSubState extends MusicBeatSubState
|
||||
if (autoFollow && !pressedSelect && memb.animation.curAnim?.name != 'idle')
|
||||
{
|
||||
memb.animation.play("confirm", false, true);
|
||||
@:nullSafety(Off)
|
||||
var onFinish:String->Void = null;
|
||||
onFinish = (_) -> {
|
||||
var onFinish:String->Void = (_) -> {
|
||||
member.animation.play('idle');
|
||||
member.animation.onFinish.remove(onFinish);
|
||||
member.animation.onFinish.removeAll();
|
||||
};
|
||||
member.animation.onFinish.add(onFinish);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// memb.pixels = memb.noDropShadow.clone();
|
||||
@:nullSafety(Off)
|
||||
memb.filters = null;
|
||||
memb.scale.set(2, 2);
|
||||
}
|
||||
@@ -1279,23 +1254,23 @@ class CharSelectSubState extends MusicBeatSubState
|
||||
gfChill.visible = false;
|
||||
playerChill.visible = false;
|
||||
playerChillOut.visible = true;
|
||||
playerChillOut.playAnimation("slideout");
|
||||
var index = playerChillOut.anim.getFrameLabel("slideout").index;
|
||||
playerChillOut.onAnimationFrame.removeAll();
|
||||
playerChillOut.onAnimationFrame.add((_, frame:Int) -> {
|
||||
if (frame >= index + 1)
|
||||
playerChillOut.anim.play("slideout");
|
||||
|
||||
playerChillOut.anim.onFrameChange.removeAll();
|
||||
playerChillOut.anim.onFrameChange.add(function(animName:String, frameNumber:Int, index:Int) {
|
||||
if (!playerChill.visible)
|
||||
{
|
||||
playerChill.visible = true;
|
||||
playerChill.switchChar(value);
|
||||
gfChill.switchGF(value);
|
||||
gfChill.visible = true;
|
||||
}
|
||||
if (frame >= index + 2)
|
||||
{
|
||||
playerChillOut.switchChar(value);
|
||||
playerChillOut.visible = false;
|
||||
playerChillOut.onAnimationFrame.removeAll();
|
||||
}
|
||||
});
|
||||
|
||||
playerChillOut.anim.onFinish.addOnce(function(animName:String) {
|
||||
playerChillOut.switchChar(value);
|
||||
playerChillOut.visible = false;
|
||||
playerChillOut.anim.onFrameChange.removeAll();
|
||||
});
|
||||
|
||||
return value;
|
||||
@@ -1321,5 +1296,5 @@ class CharSelectSubState extends MusicBeatSubState
|
||||
*/
|
||||
typedef CharSelectSubStateParams =
|
||||
{
|
||||
?character:String, // ?fromFreeplaySelect:Bool,
|
||||
?character:String
|
||||
};
|
||||
|
||||
@@ -1,38 +1,44 @@
|
||||
package funkin.ui.charSelect;
|
||||
|
||||
import flixel.util.FlxColor;
|
||||
import flxanimate.effects.FlxTint;
|
||||
import funkin.graphics.adobeanimate.FlxAtlasSprite;
|
||||
import funkin.graphics.FunkinSprite;
|
||||
import flixel.FlxCamera;
|
||||
import flixel.math.FlxPoint;
|
||||
|
||||
@:nullSafety
|
||||
class Lock extends FlxAtlasSprite
|
||||
class Lock extends FunkinSprite
|
||||
{
|
||||
var colors:Array<FlxColor> = [
|
||||
0x31F2A5, 0x20ECCD, 0x24D9E8,
|
||||
0x20ECCD, 0x20C8D4, 0x209BDD,
|
||||
0x209BDD, 0x2362C9, 0x243FB9
|
||||
]; // lock colors, in a nx3 matrix format
|
||||
0xFF31F2A5, 0xFF20ECCD, 0xFF24D9E8,
|
||||
0xFF20ECCD, 0xFF20C8D4, 0xFF209BDD,
|
||||
0xFF209BDD, 0xFF2362C9, 0xFF243FB9
|
||||
];
|
||||
|
||||
public function new(x:Float = 0, y:Float = 0, index:Int)
|
||||
public function new(x:Float = 0, y:Float = 0, index:Int, settings:AtlasSpriteSettings)
|
||||
{
|
||||
super(x, y, Paths.animateAtlas("charSelect/lock"));
|
||||
var tint:FlxColor = colors[index];
|
||||
|
||||
var tint:FlxTint = new FlxTint(colors[index], 1);
|
||||
super(x, y);
|
||||
|
||||
var arr:Array<String> = ["lock", "lock top 1", "lock top 2", "lock top 3", "lock base fuck it"];
|
||||
loadTextureAtlas("charSelect/lock",
|
||||
{
|
||||
swfMode: settings.swfMode,
|
||||
uniqueInCache: settings.uniqueInCache,
|
||||
onSymbolCreate: (symbol) -> {
|
||||
if (symbol.timeline.getLayer("color") != null)
|
||||
{
|
||||
var colorSymbol:Null<animate.internal.elements.SymbolInstance> = symbol.timeline.getLayer("color")?.getFrameAtIndex(0)?.convertToSymbol(0,
|
||||
1) ?? null;
|
||||
|
||||
var func = function(name) {
|
||||
var symbol = anim.symbolDictionary[name];
|
||||
if (symbol != null && symbol.timeline.get("color") != null) {
|
||||
var color = symbol.timeline.get("color").get(0);
|
||||
if (color != null) color.colorEffect = tint;
|
||||
}
|
||||
}
|
||||
for (symbol in arr)
|
||||
{
|
||||
func(symbol);
|
||||
}
|
||||
if (colorSymbol != null)
|
||||
{
|
||||
colorSymbol.setColorTransform(0, 0, 0, 1, tint.red, tint.green, tint.blue, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
playAnimation("idle");
|
||||
anim.play("idle");
|
||||
offset.set(230, 110);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ class DebugBoundingState extends FlxState
|
||||
var offsetView:FlxGroup;
|
||||
var dropDownSetup:Bool = false;
|
||||
|
||||
var onionSkinChar:FlxSprite;
|
||||
var onionSkinChar:BaseCharacter;
|
||||
var txtOffsetShit:FlxText;
|
||||
|
||||
var offsetEditorDialog:CollapsibleDialog;
|
||||
@@ -177,16 +177,25 @@ class DebugBoundingState extends FlxState
|
||||
if (swagChar == null) return;
|
||||
if (swagChar.hasAnimation("idle")) swagChar.playAnimation("idle", true);
|
||||
|
||||
onionSkinChar.loadGraphicFromSprite(swagChar);
|
||||
onionSkinChar.frame = swagChar.frame;
|
||||
onionSkinChar.alpha = 0.6;
|
||||
|
||||
if (onionSkinChar.hasAnimation("idle"))
|
||||
{
|
||||
onionSkinChar.playAnimation("idle", true);
|
||||
}
|
||||
else
|
||||
{
|
||||
onionSkinChar.playAnimation(currentAnimationName, true);
|
||||
}
|
||||
|
||||
onionSkinChar.animation.pause();
|
||||
onionSkinChar.flipX = swagChar.flipX;
|
||||
onionSkinChar.scale.set(swagChar.scale.x, swagChar.scale.y);
|
||||
onionSkinChar.updateHitbox();
|
||||
onionSkinChar.offset.x = swagChar.offset.x + (swagChar.animOffsets[0] - swagChar.globalOffsets[0]) * swagChar.scale.x;
|
||||
onionSkinChar.offset.y = swagChar.offset.y + (swagChar.animOffsets[1] - swagChar.globalOffsets[1]) * swagChar.scale.y;
|
||||
|
||||
swagChar.playAnimation(currentAnimationName, true); // reset animation to the one it should be
|
||||
swagChar.playAnimation(currentAnimationName, true);
|
||||
}
|
||||
|
||||
function initOffsetView():Void
|
||||
@@ -194,10 +203,6 @@ class DebugBoundingState extends FlxState
|
||||
offsetView = new FlxGroup();
|
||||
add(offsetView);
|
||||
|
||||
onionSkinChar = new FlxSprite().makeGraphic(FlxG.width * 2, FlxG.height * 2, FlxColor.TRANSPARENT);
|
||||
onionSkinChar.visible = false;
|
||||
offsetView.add(onionSkinChar);
|
||||
|
||||
txtOffsetShit = new FlxText(20, 20, 0, "", 20);
|
||||
txtOffsetShit.setFormat(Paths.font("vcr.ttf"), 26, FlxColor.WHITE, LEFT, FlxTextBorderStyle.OUTLINE, FlxColor.BLACK);
|
||||
txtOffsetShit.cameras = [hudCam];
|
||||
@@ -205,10 +210,6 @@ class DebugBoundingState extends FlxState
|
||||
offsetView.add(txtOffsetShit);
|
||||
|
||||
var characters:Array<String> = CharacterDataParser.listCharacterIds();
|
||||
characters = characters.filter(function(charId:String) {
|
||||
var char = CharacterDataParser.fetchCharacterData(charId);
|
||||
return char.renderType != AnimateAtlas;
|
||||
});
|
||||
characters.sort(SortUtil.alphabetically);
|
||||
|
||||
var charDropdown:DropDown = offsetEditorDialog.findComponent('characterDropdown', DropDown);
|
||||
@@ -520,10 +521,23 @@ class DebugBoundingState extends FlxState
|
||||
swagChar.destroy();
|
||||
}
|
||||
|
||||
if (onionSkinChar != null)
|
||||
{
|
||||
offsetView.remove(onionSkinChar);
|
||||
onionSkinChar.destroy();
|
||||
}
|
||||
|
||||
swagChar = CharacterDataParser.fetchCharacter(char);
|
||||
swagChar.x = onionSkinChar.x = 100;
|
||||
swagChar.y = onionSkinChar.y = 100;
|
||||
swagChar.x = 100;
|
||||
swagChar.y = 100;
|
||||
swagChar.debug = true;
|
||||
|
||||
onionSkinChar = CharacterDataParser.fetchCharacter(char);
|
||||
onionSkinChar.x = swagChar.x;
|
||||
onionSkinChar.y = swagChar.y;
|
||||
onionSkinChar.debug = true;
|
||||
|
||||
offsetView.add(onionSkinChar);
|
||||
offsetView.add(swagChar);
|
||||
|
||||
if (swagChar == null || swagChar.frames == null)
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
package funkin.ui.debug.anim;
|
||||
|
||||
import flixel.FlxG;
|
||||
import funkin.graphics.adobeanimate.FlxAtlasSprite;
|
||||
import funkin.graphics.FunkinSprite;
|
||||
import funkin.ui.MusicBeatState;
|
||||
import funkin.util.macro.ConsoleMacro;
|
||||
import funkin.Paths;
|
||||
|
||||
/**
|
||||
* A simple test of FlxAnimate.
|
||||
* Delete this later?
|
||||
*/
|
||||
class FlxAnimateTest extends MusicBeatState
|
||||
class FlxAnimateTest extends MusicBeatState implements ConsoleClass
|
||||
{
|
||||
var sprite:FlxAtlasSprite;
|
||||
var sprite:FunkinSprite;
|
||||
|
||||
public function new()
|
||||
{
|
||||
@@ -22,18 +24,29 @@ class FlxAnimateTest extends MusicBeatState
|
||||
{
|
||||
super.create();
|
||||
|
||||
sprite = new FlxAtlasSprite(-100, 0, 'week3:assets/week3/images/philly/erect/picoFlowers');
|
||||
sprite = FunkinSprite.createTextureAtlas(0, 0, "charSelect/bfChill",
|
||||
{
|
||||
swfMode: false, // If to render like in a SWF file, rather than the Animate editor.
|
||||
cacheOnLoad: true, // If to precache all animation filters and masks at once, rather than at runtime.
|
||||
filterQuality: MEDIUM // Level of quality used to render filters. (HIGH, MEDIUM, LOW, RUDY)
|
||||
});
|
||||
|
||||
add(sprite);
|
||||
sprite.playAnimation(sprite.anim.stageInstance.symbol.name, false, false, true);
|
||||
}
|
||||
|
||||
public override function update(elapsed:Float):Void
|
||||
{
|
||||
super.update(elapsed);
|
||||
|
||||
if (FlxG.keys.justPressed.SPACE) ((sprite.anim.isPlaying) ? sprite.anim.pause() : sprite.playAnimation(null, false, false, true));
|
||||
if (FlxG.keys.justPressed.SPACE) (!(sprite.isAnimationFinished()) ? sprite.anim.pause() : sprite.anim.resume());
|
||||
|
||||
if (FlxG.keys.anyJustPressed([A, LEFT])) sprite.anim.curFrame--;
|
||||
if (FlxG.keys.anyJustPressed([D, RIGHT])) sprite.anim.curFrame++;
|
||||
if (FlxG.keys.anyJustPressed([A, LEFT])) sprite.anim.curAnim.curFrame--;
|
||||
if (FlxG.keys.anyJustPressed([D, RIGHT])) sprite.anim.curAnim.curFrame++;
|
||||
|
||||
if (FlxG.keys.justPressed.Q) sprite.anim.play("slidein idle point", true);
|
||||
if (FlxG.keys.justPressed.W) sprite.anim.play("slidein", true);
|
||||
if (FlxG.keys.justPressed.E) sprite.anim.play("death", true);
|
||||
if (FlxG.keys.justPressed.R) sprite.anim.play("cannot select Label", true);
|
||||
if (FlxG.keys.justPressed.T) sprite.anim.play("idle", true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ class ResultsDebugSubState extends MusicBeatSubState
|
||||
FlxG.switchState(() -> new funkin.play.ResultState(resultsParams));
|
||||
});
|
||||
});
|
||||
createToggleListItem("Character", PlayerRegistry.instance.listBaseGameEntryIds(), function(result:String) {
|
||||
createToggleListItem("Character", PlayerRegistry.instance.listEntryIds(), function(result:String) {
|
||||
resultsParams.characterId = result;
|
||||
});
|
||||
createToggleListItem("Results Mode", ["Debug", "Story", "Freeplay"], function(result:String) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package funkin.ui.freeplay;
|
||||
|
||||
import funkin.graphics.adobeanimate.FlxAtlasSprite;
|
||||
import funkin.graphics.FunkinSprite;
|
||||
import flixel.group.FlxSpriteGroup;
|
||||
import flixel.util.FlxSort;
|
||||
import flixel.util.FlxTimer;
|
||||
@@ -32,7 +32,9 @@ class AlbumRoll extends FlxSpriteGroup
|
||||
return value;
|
||||
}
|
||||
|
||||
var newAlbumArt:FlxAtlasSprite;
|
||||
final ALBUM_ART_SYMBOL:String = "album art placeholder";
|
||||
|
||||
var newAlbumArt:FunkinSprite;
|
||||
var albumTitle:Null<FunkinSprite> = null;
|
||||
|
||||
var difficultyStars:DifficultyStars;
|
||||
@@ -41,26 +43,14 @@ class AlbumRoll extends FlxSpriteGroup
|
||||
|
||||
var albumData:Null<Album> = null;
|
||||
|
||||
final animNames:Map<String, String> = [
|
||||
"volume1-active" => "ENTRANCE",
|
||||
"volume2-active" => "ENTRANCE VOL2",
|
||||
"volume3-active" => "ENTRANCE VOL3",
|
||||
"volume1-trans" => "VOL1 TRANS",
|
||||
"volume2-trans" => "VOL2 TRANS",
|
||||
"volume3-trans" => "VOL3 TRANS",
|
||||
"volume1-idle" => "VOL1 STILL",
|
||||
"volume2-idle" => "VOL2 STILL",
|
||||
"volume3-idle" => "VOL3 STILL",
|
||||
];
|
||||
|
||||
public function new()
|
||||
{
|
||||
super();
|
||||
|
||||
newAlbumArt = new FlxAtlasSprite((FlxG.width - 640) - FullScreenScaleMode.gameNotchSize.x, 360, Paths.animateAtlas("freeplay/albumRoll/freeplayAlbum"));
|
||||
newAlbumArt = FunkinSprite.createTextureAtlas((FlxG.width + -360) - FullScreenScaleMode.gameNotchSize.x, 220, "freeplay/albumRoll/freeplayAlbum");
|
||||
newAlbumArt.visible = false;
|
||||
|
||||
difficultyStars = new DifficultyStars((FlxG.width - 1140) - FullScreenScaleMode.gameNotchSize.x, 39);
|
||||
difficultyStars = new DifficultyStars((FlxG.width - 330) - FullScreenScaleMode.gameNotchSize.x, 209);
|
||||
difficultyStars.visible = false;
|
||||
|
||||
add(newAlbumArt);
|
||||
@@ -69,7 +59,7 @@ class AlbumRoll extends FlxSpriteGroup
|
||||
buildAlbumTitle("freeplay/albumRoll/volume1-text");
|
||||
if (albumTitle != null) albumTitle.visible = false;
|
||||
|
||||
newAlbumArt.onAnimationComplete.add(onAlbumFinish);
|
||||
newAlbumArt.anim.onFinish.add(onAlbumFinish);
|
||||
}
|
||||
|
||||
function onAlbumFinish(animName:String):Void
|
||||
@@ -77,11 +67,8 @@ class AlbumRoll extends FlxSpriteGroup
|
||||
// Play the idle animation for the current album.
|
||||
if (animName != "idle")
|
||||
{
|
||||
newAlbumArt.playAnimation('idle', true, false, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
newAlbumArt.cleanupAnimation('idle');
|
||||
newAlbumArt.anim.play('idle', true);
|
||||
newAlbumArt.anim.curAnim.looped = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,7 +95,7 @@ class AlbumRoll extends FlxSpriteGroup
|
||||
|
||||
// Update the album art.
|
||||
var albumGraphic = Paths.image(albumData.getAlbumArtAssetKey());
|
||||
newAlbumArt.replaceFrameGraphic(0, albumGraphic);
|
||||
newAlbumArt.replaceSymbolGraphic(ALBUM_ART_SYMBOL, albumGraphic);
|
||||
|
||||
buildAlbumTitle(albumData.getAlbumTitleAssetKey(), albumData.getAlbumTitleOffsets());
|
||||
applyExitMovers();
|
||||
@@ -174,7 +161,7 @@ class AlbumRoll extends FlxSpriteGroup
|
||||
|
||||
if (albumTitle != null) albumTitle.visible = false;
|
||||
newAlbumArt.visible = true;
|
||||
newAlbumArt.playAnimation('intro', true);
|
||||
newAlbumArt.anim.play('intro', true);
|
||||
|
||||
difficultyStars.visible = false;
|
||||
difficultyStars.flameCheck();
|
||||
@@ -190,7 +177,7 @@ class AlbumRoll extends FlxSpriteGroup
|
||||
{
|
||||
this.visible = true;
|
||||
// Weird workaround
|
||||
newAlbumArt.playAnimation('switch', true);
|
||||
newAlbumArt.anim.play('switch', true);
|
||||
if (albumTitle != null) albumTitle.animation.play('switch');
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package funkin.ui.freeplay;
|
||||
|
||||
import flixel.group.FlxSpriteGroup;
|
||||
import funkin.graphics.adobeanimate.FlxAtlasSprite;
|
||||
import funkin.graphics.FunkinSprite;
|
||||
import funkin.graphics.shaders.HSVShader;
|
||||
|
||||
@:nullSafety
|
||||
@@ -18,7 +18,7 @@ class DifficultyStars extends FlxSpriteGroup
|
||||
*/
|
||||
public var difficulty(default, set):Int = 1;
|
||||
|
||||
public var stars:FlxAtlasSprite;
|
||||
public var stars:FunkinSprite;
|
||||
|
||||
public var flames:FreeplayFlames;
|
||||
|
||||
@@ -32,7 +32,7 @@ class DifficultyStars extends FlxSpriteGroup
|
||||
|
||||
flames = new FreeplayFlames(0, 0);
|
||||
|
||||
stars = new FlxAtlasSprite(0, 0, Paths.animateAtlas("freeplay/freeplayStars"));
|
||||
stars = FunkinSprite.createTextureAtlas(0, 0, "freeplay/freeplayStars");
|
||||
stars.anim.play("diff stars");
|
||||
|
||||
add(flames);
|
||||
@@ -56,7 +56,7 @@ class DifficultyStars extends FlxSpriteGroup
|
||||
// ......
|
||||
// 1300-1499: 15 stars
|
||||
// 1500 : 0 stars
|
||||
if (curDifficulty < 15 && stars.anim.curFrame >= (curDifficulty + 1) * 100)
|
||||
if (curDifficulty < 15 && stars.anim.curAnim.curFrame >= (curDifficulty + 1) * 100)
|
||||
{
|
||||
stars.anim.play("diff stars", true, false, curDifficulty * 100);
|
||||
}
|
||||
@@ -97,6 +97,7 @@ class DifficultyStars extends FlxSpriteGroup
|
||||
function set_curDifficulty(value:Int):Int
|
||||
{
|
||||
curDifficulty = value;
|
||||
|
||||
if (curDifficulty == 15)
|
||||
{
|
||||
stars.anim.play("diff stars", true, false, 1500);
|
||||
@@ -104,7 +105,7 @@ class DifficultyStars extends FlxSpriteGroup
|
||||
}
|
||||
else
|
||||
{
|
||||
stars.anim.curFrame = Std.int(curDifficulty * 100);
|
||||
stars.anim.curAnim.curFrame = Std.int(curDifficulty * 100);
|
||||
stars.anim.play("diff stars", true, false, curDifficulty * 100);
|
||||
}
|
||||
|
||||
|
||||
@@ -1564,21 +1564,7 @@ class FreeplayState extends MusicBeatSubState
|
||||
wait: 0.1
|
||||
});
|
||||
add(transitionGradient);
|
||||
// FlxTween.tween(transitionGradient, {alpha: 0}, 1, {ease: FlxEase.circIn});
|
||||
// for (index => capsule in grpCapsules.members)
|
||||
// {
|
||||
// var distFromSelected:Float = Math.abs(index - curSelected) - 1;
|
||||
// if (distFromSelected < 5)
|
||||
// {
|
||||
// capsule.doLerp = false;
|
||||
// exitMoversCharSel.set([capsule],
|
||||
// {
|
||||
// y: -250,
|
||||
// speed: 0.8,
|
||||
// wait: 0.1
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
|
||||
funnyCam.filtersEnabled = true;
|
||||
fadeShader.fade(0.0, 1.0, 0.8, {ease: FlxEase.quadIn, onComplete: (twn) -> funnyCam.filtersEnabled = false});
|
||||
|
||||
@@ -1609,6 +1595,10 @@ class FreeplayState extends MusicBeatSubState
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: i have no fucking idea why the dj position is shitty when entering from char select
|
||||
// the easiest way is to just snap them back into place
|
||||
if (dj != null) dj.setPosition((CUTOUT_WIDTH * DJ_POS_MULTI) + 640, 366);
|
||||
}
|
||||
|
||||
var spamTimer:Float = 0;
|
||||
@@ -1672,7 +1662,8 @@ class FreeplayState extends MusicBeatSubState
|
||||
|
||||
if ((controls.FREEPLAY_CHAR_SELECT && !fromCharSelect #if FEATURE_TOUCH_CONTROLS
|
||||
|| (TouchUtil.pressAction(djHitbox, funnyCam, false) && !SwipeUtil.swipeAny) #end)
|
||||
&& controls.active)
|
||||
&& controls.active
|
||||
&& !FlxG.debugger.visible)
|
||||
{
|
||||
tryOpenCharSelect();
|
||||
}
|
||||
|
||||
@@ -12,7 +12,9 @@ import flixel.util.FlxTimer;
|
||||
import funkin.input.Controls;
|
||||
import funkin.util.SwipeUtil;
|
||||
import funkin.util.TouchUtil;
|
||||
import funkin.graphics.adobeanimate.FlxAtlasSprite;
|
||||
import flixel.math.FlxPoint;
|
||||
import flixel.FlxCamera;
|
||||
import funkin.graphics.FunkinSprite;
|
||||
import funkin.audio.FunkinSound;
|
||||
|
||||
class LetterSort extends FlxSpriteGroup
|
||||
@@ -53,7 +55,7 @@ class LetterSort extends FlxSpriteGroup
|
||||
|
||||
for (i in 0...5)
|
||||
{
|
||||
var letter:FreeplayLetter = new FreeplayLetter(i * 80, 0, i);
|
||||
var letter:FreeplayLetter = new FreeplayLetter(i * 80, 0, i, curSelection);
|
||||
letter.x += 50;
|
||||
letter.y += 50;
|
||||
// letter.visible = false;
|
||||
@@ -247,9 +249,9 @@ class LetterSort extends FlxSpriteGroup
|
||||
}
|
||||
|
||||
/**
|
||||
* The actual FlxAtlasSprite for the letters, with their animation code stuff and regex stuff
|
||||
* The actual FunkinSprite for the letters, with their animation code stuff and regex stuff
|
||||
*/
|
||||
class FreeplayLetter extends FlxAtlasSprite
|
||||
class FreeplayLetter extends FunkinSprite
|
||||
{
|
||||
/**
|
||||
* A preformatted array of letter strings, for use when doing regex
|
||||
@@ -268,9 +270,11 @@ class FreeplayLetter extends FlxAtlasSprite
|
||||
*/
|
||||
public var curLetter:Int = 0;
|
||||
|
||||
public function new(x:Float, y:Float, ?letterInd:Int)
|
||||
public function new(x:Float, y:Float, ?letterInd:Int, curSelected:Int = 0)
|
||||
{
|
||||
super(x, y, Paths.animateAtlas("freeplay/sortedLetters"));
|
||||
super(x, y);
|
||||
|
||||
loadTextureAtlas("freeplay/sortedLetters");
|
||||
|
||||
// this is used for the regex
|
||||
// /^[OR].*/gi doesn't work for showing the song Pico, so now it's
|
||||
@@ -290,11 +294,16 @@ class FreeplayLetter extends FlxAtlasSprite
|
||||
|
||||
if (letterInd != null)
|
||||
{
|
||||
this.anim.play(animLetters[letterInd] + " move");
|
||||
this.anim.pause();
|
||||
this.anim.play(animLetters[letterInd] + " move", true);
|
||||
curLetter = letterInd;
|
||||
this.anim.onComplete.add(function() {
|
||||
this.anim.play(animLetters[curLetter] + " move");
|
||||
|
||||
if (curSelected != curLetter)
|
||||
{
|
||||
this.anim.pause();
|
||||
}
|
||||
|
||||
this.anim.onFinish.add(function(name:String) {
|
||||
this.anim.play(animLetters[curLetter] + " move", true);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -328,6 +337,16 @@ class FreeplayLetter extends FlxAtlasSprite
|
||||
{
|
||||
this.anim.pause();
|
||||
}
|
||||
// updateHitbox();
|
||||
}
|
||||
|
||||
/**
|
||||
* Offset the letter.
|
||||
*/
|
||||
override function getScreenPosition(?result:FlxPoint, ?camera:FlxCamera):FlxPoint
|
||||
{
|
||||
var output:FlxPoint = super.getScreenPosition(result, camera);
|
||||
output.x -= 50;
|
||||
output.y -= 60;
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import flixel.math.FlxPoint;
|
||||
import flixel.tweens.FlxEase;
|
||||
import flixel.tweens.FlxTween;
|
||||
import flixel.util.FlxSpriteUtil;
|
||||
import funkin.graphics.adobeanimate.FlxAtlasSprite;
|
||||
import funkin.graphics.FunkinSprite;
|
||||
import funkin.ui.freeplay.charselect.PlayableCharacter;
|
||||
import openfl.display.BlendMode;
|
||||
@@ -24,7 +23,7 @@ import funkin.util.BitmapUtil;
|
||||
*/
|
||||
class BackingCard extends FlxSpriteGroup implements IBPMSyncedScriptedClass implements IStateChangingScriptedClass implements IFreeplayScriptedClass
|
||||
{
|
||||
public var backingTextYeah:FlxAtlasSprite;
|
||||
public var backingTextYeah:FunkinSprite;
|
||||
public var orangeBackShit:FunkinSprite;
|
||||
public var alsoOrangeLOL:FunkinSprite;
|
||||
public var pinkBack:FunkinSprite;
|
||||
@@ -58,15 +57,7 @@ class BackingCard extends FlxSpriteGroup implements IBPMSyncedScriptedClass impl
|
||||
orangeBackShit = new FunkinSprite(84, 440).makeSolidColor(Std.int(pinkBack.width), 75, 0xFFFEDA00);
|
||||
alsoOrangeLOL = new FunkinSprite(0, orangeBackShit.y).makeSolidColor(100, Std.int(orangeBackShit.height), 0xFFFFD400);
|
||||
confirmGlow2 = new FlxSprite(confirmGlow.x, confirmGlow.y).loadGraphic(Paths.image('freeplay/confirmGlow2'));
|
||||
backingTextYeah = new FlxAtlasSprite((FreeplayState.CUTOUT_WIDTH * FreeplayState.DJ_POS_MULTI) + 640, 370,
|
||||
Paths.animateAtlas("freeplay/backing-text-yeah"), {
|
||||
FrameRate: 24.0,
|
||||
Reversed: false,
|
||||
// ?OnComplete:Void -> Void,
|
||||
ShowPivot: false,
|
||||
Antialiasing: true,
|
||||
ScrollFactor: new FlxPoint(1, 1),
|
||||
});
|
||||
backingTextYeah = FunkinSprite.createTextureAtlas((FreeplayState.CUTOUT_WIDTH * FreeplayState.DJ_POS_MULTI) + -320, 120, "freeplay/backing-text-yeah",);
|
||||
|
||||
pinkBack.color = 0xFFFFD4E9; // sets it to pink!
|
||||
pinkBack.x -= pinkBack.width;
|
||||
|
||||
@@ -6,15 +6,12 @@ import flixel.FlxSprite;
|
||||
import flixel.tweens.FlxEase;
|
||||
import flixel.tweens.FlxTween;
|
||||
import flixel.util.FlxColor;
|
||||
import funkin.graphics.adobeanimate.FlxAtlasSprite;
|
||||
import funkin.modding.events.ScriptEvent;
|
||||
import openfl.display.BlendMode;
|
||||
import funkin.util.BitmapUtil;
|
||||
|
||||
class NewCharacterCard extends BackingCard
|
||||
{
|
||||
var confirmAtlas:FlxAtlasSprite;
|
||||
|
||||
var darkBg:FlxSprite;
|
||||
var lightLayer:FlxSprite;
|
||||
var multiply1:FlxSprite;
|
||||
@@ -261,10 +258,4 @@ class NewCharacterCard extends BackingCard
|
||||
cardGlow.scale.set(1, 1);
|
||||
FlxTween.tween(cardGlow, {alpha: 0, "scale.x": 1.2, "scale.y": 1.2}, 0.25, {ease: FlxEase.sineOut});
|
||||
}
|
||||
|
||||
override public function confirm():Void
|
||||
{
|
||||
// confirmAtlas.visible = true;
|
||||
// confirmAtlas.anim.play("");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package funkin.ui.freeplay.dj;
|
||||
|
||||
import flixel.util.FlxSignal;
|
||||
import funkin.graphics.adobeanimate.FlxAtlasSprite;
|
||||
import funkin.data.freeplay.player.PlayerRegistry;
|
||||
|
||||
/**
|
||||
@@ -11,15 +9,31 @@ import funkin.data.freeplay.player.PlayerRegistry;
|
||||
@:hscriptClass
|
||||
class ScriptedAnimateAtlasFreeplayDJ extends AnimateAtlasFreeplayDJ implements polymod.hscript.HScriptedClass {}
|
||||
|
||||
/**
|
||||
* An AnimateAtlasFreeplayDJ is a Freeplay DJ which is rendered by
|
||||
* displaying an animation derived from an Adobe Animate texture atlas spritesheet file.
|
||||
*
|
||||
* BaseFreeplayDJ has game logic, AnimateAtlasFreeplayDJ has only rendering logic.
|
||||
* KEEP THEM SEPARATE!
|
||||
*/
|
||||
class AnimateAtlasFreeplayDJ extends BaseFreeplayDJ
|
||||
{
|
||||
public function new(x:Float, y:Float, characterId:String)
|
||||
{
|
||||
super(x, y, characterId);
|
||||
loadAtlas(Paths.animateAtlas(playableCharData.getAssetPath()));
|
||||
|
||||
onAnimationComplete.add(onFinishAnim);
|
||||
onAnimationLoop.add(onFinishAnim);
|
||||
loadTextureAtlas(playableCharData?.getAssetPath(),
|
||||
{
|
||||
swfMode: true
|
||||
});
|
||||
|
||||
if (playableCharData?.useApplyStageMatrix() ?? false)
|
||||
{
|
||||
this.applyStageMatrix = true;
|
||||
}
|
||||
|
||||
animation.onFinish.add(onFinishAnim);
|
||||
animation.onLoop.add(onFinishAnim);
|
||||
}
|
||||
|
||||
public override function update(elapsed:Float):Void
|
||||
@@ -29,7 +43,7 @@ class AnimateAtlasFreeplayDJ extends BaseFreeplayDJ
|
||||
case Intro:
|
||||
// Play the intro animation then leave this state immediately.
|
||||
var animPrefix = playableCharData?.getAnimationPrefix('intro');
|
||||
if (animPrefix != null && (getCurrentAnimation() != animPrefix || !this.anim.isPlaying))
|
||||
if (animPrefix != null && (getCurrentAnimation() != animPrefix))
|
||||
{
|
||||
playFlashAnimation(animPrefix, true);
|
||||
}
|
||||
@@ -63,7 +77,7 @@ class AnimateAtlasFreeplayDJ extends BaseFreeplayDJ
|
||||
if (getCurrentAnimation() == animPrefixA)
|
||||
{
|
||||
var endFrame = playableCharData?.getFistPumpIntroEndFrame() ?? 0;
|
||||
if (endFrame > -1 && anim.curFrame >= endFrame)
|
||||
if (endFrame > -1 && anim.curAnim.curFrame >= endFrame)
|
||||
{
|
||||
playFlashAnimation(animPrefixA, true, false, false, playableCharData?.getFistPumpIntroStartFrame());
|
||||
}
|
||||
@@ -71,7 +85,7 @@ class AnimateAtlasFreeplayDJ extends BaseFreeplayDJ
|
||||
else if (getCurrentAnimation() == animPrefixB)
|
||||
{
|
||||
var endFrame = playableCharData?.getFistPumpIntroBadEndFrame() ?? 0;
|
||||
if (endFrame > -1 && anim.curFrame >= endFrame)
|
||||
if (endFrame > -1 && anim.curAnim.curFrame >= endFrame)
|
||||
{
|
||||
playFlashAnimation(animPrefixB, true, false, false, playableCharData?.getFistPumpIntroBadStartFrame());
|
||||
}
|
||||
@@ -88,7 +102,7 @@ class AnimateAtlasFreeplayDJ extends BaseFreeplayDJ
|
||||
if (getCurrentAnimation() == animPrefixA)
|
||||
{
|
||||
var endFrame = playableCharData?.getFistPumpLoopEndFrame() ?? 0;
|
||||
if (endFrame > -1 && anim.curFrame >= endFrame)
|
||||
if (endFrame > -1 && anim.curAnim.curFrame >= endFrame)
|
||||
{
|
||||
playFlashAnimation(animPrefixA, true, false, false, playableCharData?.getFistPumpLoopStartFrame());
|
||||
}
|
||||
@@ -96,7 +110,7 @@ class AnimateAtlasFreeplayDJ extends BaseFreeplayDJ
|
||||
else if (getCurrentAnimation() == animPrefixB)
|
||||
{
|
||||
var endFrame = playableCharData?.getFistPumpLoopBadEndFrame() ?? 0;
|
||||
if (endFrame > -1 && anim.curFrame >= endFrame)
|
||||
if (endFrame > -1 && anim.curAnim.curFrame >= endFrame)
|
||||
{
|
||||
playFlashAnimation(animPrefixB, true, false, false, playableCharData?.getFistPumpLoopBadStartFrame());
|
||||
}
|
||||
@@ -131,14 +145,12 @@ class AnimateAtlasFreeplayDJ extends BaseFreeplayDJ
|
||||
}
|
||||
|
||||
// Call the superclass function AFTER updating the current state and playing the next animation.
|
||||
// This ensures that FlxAnimate starts rendering the new animation immediately.
|
||||
// This ensures that flixel-animate starts rendering the new animation immediately.
|
||||
super.update(elapsed);
|
||||
}
|
||||
|
||||
override function onFinishAnim(name:String):Void
|
||||
{
|
||||
// var name = anim.curSymbol.name;
|
||||
|
||||
if (name == playableCharData?.getAnimationPrefix('intro'))
|
||||
{
|
||||
if (PlayerRegistry.instance.hasNewCharacter())
|
||||
@@ -183,25 +195,6 @@ class AnimateAtlasFreeplayDJ extends BaseFreeplayDJ
|
||||
// trace('Finished loss reaction');
|
||||
currentState = Idle;
|
||||
}
|
||||
else if (name == playableCharData?.getAnimationPrefix('cartoon'))
|
||||
{
|
||||
// trace('Finished cartoon');
|
||||
|
||||
var frame:Int = FlxG.random.bool(33) ? (playableCharData?.getCartoonLoopBlinkFrame() ?? 0) : (playableCharData?.getCartoonLoopFrame() ?? 0);
|
||||
|
||||
// Character switches channels when the video ends, or at a 10% chance each time his idle loops.
|
||||
if (FlxG.random.bool(5))
|
||||
{
|
||||
frame = playableCharData?.getCartoonChannelChangeFrame() ?? 0;
|
||||
// boyfriend switches channel code?
|
||||
// Transefer into bf.hxc in scripts/freeplay/dj
|
||||
// runTvLogic();
|
||||
}
|
||||
trace('Replay idle: ${frame}');
|
||||
var animPrefix = playableCharData?.getAnimationPrefix('cartoon');
|
||||
if (animPrefix != null) playFlashAnimation(animPrefix, true, false, false, frame);
|
||||
// trace('Finished confirm');
|
||||
}
|
||||
else if (name == playableCharData?.getAnimationPrefix('newUnlock'))
|
||||
{
|
||||
// Animation should loop.
|
||||
@@ -216,25 +209,14 @@ class AnimateAtlasFreeplayDJ extends BaseFreeplayDJ
|
||||
}
|
||||
}
|
||||
|
||||
override public function listAnimations():Array<String>
|
||||
{
|
||||
var anims:Array<String> = [];
|
||||
@:privateAccess
|
||||
for (animKey in anim.symbolDictionary)
|
||||
anims.push(animKey.name);
|
||||
|
||||
return anims;
|
||||
}
|
||||
|
||||
override public function getCurrentAnimation():String
|
||||
{
|
||||
if (this.anim == null || this.anim.curSymbol == null) return "";
|
||||
return this.anim.curSymbol.name;
|
||||
}
|
||||
|
||||
override public function playFlashAnimation(id:String, Force:Bool = false, Reverse:Bool = false, Loop:Bool = false, Frame:Int = 0):Void
|
||||
{
|
||||
playAnimation(id, Force, Reverse, Loop, Frame);
|
||||
applyAnimOffset();
|
||||
animation.play(id, Force, Reverse, Frame);
|
||||
|
||||
if (animation.curAnim != null)
|
||||
{
|
||||
animation.curAnim.looped = Loop;
|
||||
}
|
||||
applyAnimationOffset();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,11 @@
|
||||
package funkin.ui.freeplay.dj;
|
||||
|
||||
import flixel.graphics.frames.FlxFrame;
|
||||
import flixel.FlxCamera;
|
||||
import flixel.FlxBasic;
|
||||
import flixel.util.FlxSignal;
|
||||
import funkin.graphics.adobeanimate.FlxAtlasSprite;
|
||||
import funkin.audio.FunkinSound;
|
||||
import funkin.graphics.FunkinSprite;
|
||||
import funkin.data.freeplay.player.PlayerRegistry;
|
||||
import funkin.data.freeplay.player.PlayerData.PlayerFreeplayDJData;
|
||||
import funkin.util.assets.FlxAnimationUtil;
|
||||
import funkin.modding.events.ScriptEvent;
|
||||
import funkin.modding.IScriptedClass.IFreeplayScriptedClass;
|
||||
import funkin.modding.events.ScriptEvent;
|
||||
|
||||
enum FreeplayDJState
|
||||
{
|
||||
@@ -64,7 +59,7 @@ enum FreeplayDJState
|
||||
}
|
||||
|
||||
@:nullSafety
|
||||
class BaseFreeplayDJ extends FlxAtlasSprite implements IFreeplayScriptedClass
|
||||
class BaseFreeplayDJ extends FunkinSprite implements IFreeplayScriptedClass
|
||||
{
|
||||
public var IDLE_EGG_PERIOD:Float = 60.0;
|
||||
public var IDLE_CARTOON_PERIOD:Float = 120.0;
|
||||
@@ -96,7 +91,7 @@ class BaseFreeplayDJ extends FlxAtlasSprite implements IFreeplayScriptedClass
|
||||
final playableChar = PlayerRegistry.instance.fetchEntry(characterId);
|
||||
playableCharData = playableChar?.getFreeplayDJData();
|
||||
|
||||
super(x, y, null, null, false);
|
||||
super(x, y);
|
||||
}
|
||||
|
||||
function onFinishAnim(name:String):Void {}
|
||||
@@ -109,7 +104,7 @@ class BaseFreeplayDJ extends FlxAtlasSprite implements IFreeplayScriptedClass
|
||||
public function playFlashAnimation(id:String, Force:Bool = false, Reverse:Bool = false, Loop:Bool = false, Frame:Int = 0):Void
|
||||
{
|
||||
// playAnimationSimple(id, Force, Reverse, Loop, Frame);
|
||||
applyAnimOffset();
|
||||
applyAnimationOffset();
|
||||
}
|
||||
|
||||
public function onPlayerAction():Void
|
||||
@@ -213,32 +208,36 @@ class BaseFreeplayDJ extends FlxAtlasSprite implements IFreeplayScriptedClass
|
||||
if (animPrefix != null) playFlashAnimation(animPrefix, true, false, false, playableCharData?.getFistPumpLoopBadStartFrame());
|
||||
}
|
||||
|
||||
override public function listAnimations():Array<String>
|
||||
function applyAnimationOffset():Void
|
||||
{
|
||||
// this is base dj dum dum, there's no animations
|
||||
return [];
|
||||
}
|
||||
var animationName:String = getCurrentAnimation();
|
||||
var animationOffsets:Null<Array<Float>> = playableCharData?.getAnimationOffsetsByPrefix(animationName);
|
||||
var globalOffsets:Array<Float> = [this.x, this.y];
|
||||
|
||||
override public function getCurrentAnimation():String
|
||||
{
|
||||
// this is base dj dum dum, there's no animations
|
||||
return "";
|
||||
}
|
||||
|
||||
function applyAnimOffset()
|
||||
{
|
||||
var animName = getCurrentAnimation();
|
||||
trace(animName);
|
||||
var daOffset = playableCharData?.getAnimationOffsetsByPrefix(animName);
|
||||
trace(daOffset);
|
||||
if (daOffset != null)
|
||||
if (animationOffsets != null)
|
||||
{
|
||||
final xValue = daOffset[0];
|
||||
final yValue = daOffset[1];
|
||||
offset.set(xValue, yValue);
|
||||
var finalOffsetX:Float = 0;
|
||||
var finalOffsetY:Float = 0;
|
||||
|
||||
if (this.applyStageMatrix)
|
||||
{
|
||||
finalOffsetX = animationOffsets[0];
|
||||
finalOffsetY = animationOffsets[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
finalOffsetX = globalOffsets[0] - animationOffsets[0] - (FreeplayState.CUTOUT_WIDTH * FreeplayState.DJ_POS_MULTI);
|
||||
finalOffsetY = globalOffsets[1] - animationOffsets[1];
|
||||
}
|
||||
|
||||
trace('Successfully applied offset ($animationName): ' + animationOffsets[0] + ', ' + animationOffsets[1]);
|
||||
offset.set(finalOffsetX, finalOffsetY);
|
||||
}
|
||||
else
|
||||
{
|
||||
trace('No offset found ($animationName), defaulting to: 0, 0');
|
||||
offset.set(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public function onScriptEvent(event:ScriptEvent) {}
|
||||
@@ -299,344 +298,3 @@ class BaseFreeplayDJ extends FlxAtlasSprite implements IFreeplayScriptedClass
|
||||
*/
|
||||
public function onFreeplayClose(event:FreeplayScriptEvent):Void {}
|
||||
}
|
||||
|
||||
// Class for all non-atalas DJ's
|
||||
class FlixelFramedFreeplayDJ extends BaseFreeplayDJ
|
||||
{
|
||||
public function new(x:Float, y:Float, characterId:String)
|
||||
{
|
||||
super(x, y, characterId);
|
||||
|
||||
loadFrames();
|
||||
loadAnimations();
|
||||
|
||||
animation.onFinish.add(onFinishAnim);
|
||||
animation.onLoop.add(onFinishAnim);
|
||||
|
||||
// animation.onFrameChange.add((name, num, index) -> trace('name:$name, num:$num, index:$index'));
|
||||
}
|
||||
|
||||
public function loadFrames():Void
|
||||
{
|
||||
trace("OVERRIDE ME");
|
||||
trace("LOADING FRAMES FUNC");
|
||||
}
|
||||
|
||||
public function loadAnimations():Void
|
||||
{
|
||||
trace('[SPARROWCHAR] Loading ${playableCharData.getAnimationsList().length} animations for ${characterId}');
|
||||
|
||||
FlxAnimationUtil.addAtlasAnimations(this, playableCharData.getAnimationsList());
|
||||
|
||||
var animNames = this.animation.getNameList();
|
||||
trace('[SPARROWCHAR] Successfully loaded ${animNames.length} animations for ${characterId}');
|
||||
}
|
||||
|
||||
override function applyAnimOffset()
|
||||
{
|
||||
var animName = getCurrentAnimation();
|
||||
trace(animName);
|
||||
var daOffset = playableCharData?.getAnimationOffsets(animName);
|
||||
trace(daOffset);
|
||||
if (daOffset != null)
|
||||
{
|
||||
final xValue = daOffset[0];
|
||||
final yValue = daOffset[1];
|
||||
offset.set(xValue, yValue);
|
||||
}
|
||||
else
|
||||
offset.set(0, 0);
|
||||
}
|
||||
|
||||
override public function playFlashAnimation(id:String, Force:Bool = false, Reverse:Bool = false, Loop:Bool = false, Frame:Int = 0):Void
|
||||
{
|
||||
// No LOOP logic, because default Flixel's FlxAnimationController can normaly work with LOOPED animations
|
||||
animation.play(id, Force, Reverse, Frame);
|
||||
applyAnimOffset();
|
||||
}
|
||||
|
||||
override public function listAnimations():Array<String>
|
||||
{
|
||||
return animation.getNameList() ?? [];
|
||||
}
|
||||
|
||||
override public function getCurrentAnimation():String
|
||||
{
|
||||
return animation?.curAnim?.name ?? "";
|
||||
}
|
||||
|
||||
override function updateAnimation(elapsed:Float):Void
|
||||
{
|
||||
animation.update(elapsed);
|
||||
}
|
||||
|
||||
override public function hasAnimation(anim:String):Bool
|
||||
{
|
||||
return listAnimations().contains(anim);
|
||||
}
|
||||
|
||||
override public function toCharSelect():Void
|
||||
{
|
||||
if (hasAnimation('charSelect'))
|
||||
{
|
||||
currentState = CharSelect;
|
||||
playFlashAnimation('charSelect', true, false, false, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
FlxG.log.warn("Freeplay character does not have 'charSelect' animation!");
|
||||
currentState = Confirm;
|
||||
// Call this immediately; otherwise, we get locked out of Character Select.
|
||||
onCharSelectComplete();
|
||||
}
|
||||
}
|
||||
|
||||
override public function fistPumpIntro():Void
|
||||
{
|
||||
// We really don't want to play anything but the new character animation here.
|
||||
if (PlayerRegistry.instance.hasNewCharacter())
|
||||
{
|
||||
currentState = NewUnlock;
|
||||
return;
|
||||
}
|
||||
|
||||
currentState = FistPumpIntro;
|
||||
playFlashAnimation('fistPumpIntro', true, false, false);
|
||||
}
|
||||
|
||||
override public function fistPump():Void
|
||||
{
|
||||
// We really don't want to play anything but the new character animation here.
|
||||
if (PlayerRegistry.instance.hasNewCharacter())
|
||||
{
|
||||
currentState = NewUnlock;
|
||||
return;
|
||||
}
|
||||
|
||||
currentState = FistPump;
|
||||
playFlashAnimation('fistPump', true, false, false);
|
||||
}
|
||||
|
||||
override public function fistPumpLossIntro():Void
|
||||
{
|
||||
// We really don't want to play anything but the new character animation here.
|
||||
if (PlayerRegistry.instance.hasNewCharacter())
|
||||
{
|
||||
currentState = NewUnlock;
|
||||
return;
|
||||
}
|
||||
|
||||
currentState = FistPumpIntro;
|
||||
playFlashAnimation('lossIntro', true, false, false);
|
||||
}
|
||||
|
||||
override public function fistPumpLoss():Void
|
||||
{
|
||||
// We really don't want to play anything but the new character animation here.
|
||||
if (PlayerRegistry.instance.hasNewCharacter())
|
||||
{
|
||||
currentState = NewUnlock;
|
||||
return;
|
||||
}
|
||||
|
||||
currentState = FistPump;
|
||||
playFlashAnimation('loss', true, false, false);
|
||||
}
|
||||
|
||||
public override function update(elapsed:Float):Void
|
||||
{
|
||||
switch (currentState)
|
||||
{
|
||||
case Intro:
|
||||
// Play the intro animation then leave this state immediately.
|
||||
if (hasAnimation('intro') && (getCurrentAnimation() != 'intro' || this.animation.finished)) playFlashAnimation('intro', true);
|
||||
timeIdling = 0;
|
||||
case Idle:
|
||||
// We are in this state the majority of the time.
|
||||
if (hasAnimation('idle') && getCurrentAnimation() != 'idle') playFlashAnimation('idle', true, false, true);
|
||||
|
||||
timeIdling += elapsed;
|
||||
case NewUnlock:
|
||||
if (!hasAnimation('newUnlock'))
|
||||
{
|
||||
currentState = Idle;
|
||||
}
|
||||
if (hasAnimation('newUnlock') && getCurrentAnimation() != 'newUnlock')
|
||||
{
|
||||
playFlashAnimation('newUnlock', true, false, true);
|
||||
}
|
||||
case Confirm:
|
||||
if (hasAnimation('confirm') && getCurrentAnimation() != 'confirm') playFlashAnimation('confirm', false);
|
||||
timeIdling = 0;
|
||||
case FistPumpIntro:
|
||||
// I shit my self - PurSnake
|
||||
case FistPump:
|
||||
// Twice
|
||||
case IdleEasterEgg:
|
||||
if (hasAnimation('idleEasterEgg') && getCurrentAnimation() != 'idleEasterEgg')
|
||||
{
|
||||
onIdleEasterEgg.dispatch();
|
||||
playFlashAnimation('idleEasterEgg', false);
|
||||
seenIdleEasterEgg = true;
|
||||
}
|
||||
timeIdling = 0;
|
||||
case Cartoon:
|
||||
if (!hasAnimation('cartoon'))
|
||||
{
|
||||
currentState = IdleEasterEgg;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (getCurrentAnimation() != 'cartoon') playFlashAnimation('cartoon', true);
|
||||
timeIdling = 0;
|
||||
}
|
||||
default:
|
||||
// I shit myself.
|
||||
}
|
||||
|
||||
super.update(elapsed);
|
||||
}
|
||||
|
||||
override function onFinishAnim(name:String):Void
|
||||
{
|
||||
if (name == 'intro')
|
||||
{
|
||||
if (PlayerRegistry.instance.hasNewCharacter())
|
||||
{
|
||||
currentState = NewUnlock;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentState = Idle;
|
||||
}
|
||||
onIntroDone.dispatch();
|
||||
}
|
||||
else if (name == 'idle')
|
||||
{
|
||||
// trace('Finished idle')
|
||||
if (timeIdling >= IDLE_EGG_PERIOD && !seenIdleEasterEgg)
|
||||
{
|
||||
currentState = IdleEasterEgg;
|
||||
}
|
||||
else if (timeIdling >= IDLE_CARTOON_PERIOD)
|
||||
{
|
||||
currentState = Cartoon;
|
||||
}
|
||||
}
|
||||
else if (name == 'confirm')
|
||||
{
|
||||
// trace('Finished confirm');
|
||||
}
|
||||
else if (name == 'fistPump')
|
||||
{
|
||||
// trace('Finished fist pump');
|
||||
currentState = Idle;
|
||||
}
|
||||
else if (name == 'idleEasterEgg')
|
||||
{
|
||||
// trace('Finished spook');
|
||||
currentState = Idle;
|
||||
}
|
||||
else if (name == 'loss')
|
||||
{
|
||||
// trace('Finished loss reaction');
|
||||
currentState = Idle;
|
||||
}
|
||||
else if (name == 'cartoon')
|
||||
{
|
||||
// trace('Finished cartoon');
|
||||
|
||||
// var frame:Int = FlxG.random.bool(33) ? (playableCharData?.getCartoonLoopBlinkFrame() ?? 0) : (playableCharData?.getCartoonLoopFrame() ?? 0);
|
||||
|
||||
// Character switches channels when the video ends, or at a 10% chance each time his idle loops.
|
||||
/*if (FlxG.random.bool(5))
|
||||
{
|
||||
frame = playableCharData?.getCartoonChannelChangeFrame() ?? 0;
|
||||
// boyfriend switches channel code?
|
||||
// Transefer into bf.hxc in scripts/freeplay/dj
|
||||
// runTvLogic();
|
||||
}
|
||||
trace('Replay idle: ${frame}'); */
|
||||
playFlashAnimation('cartoon', true, false, false);
|
||||
// YOU BETTER REDO THIS IN YOUR SCRIPT WITH DIFFERENT ANIMATIONS
|
||||
// trace('Finished confirm');
|
||||
}
|
||||
else if (name == 'newUnlock')
|
||||
{
|
||||
// Animation should loop.
|
||||
}
|
||||
else if (name == 'charSelect')
|
||||
{
|
||||
onCharSelectComplete();
|
||||
}
|
||||
else
|
||||
{
|
||||
trace('Finished ${name}');
|
||||
}
|
||||
}
|
||||
|
||||
/// Draw - Logic
|
||||
override public function draw():Void
|
||||
{
|
||||
checkEmptyFrame();
|
||||
|
||||
if (alpha == 0 || _frame.type == FlxFrameType.EMPTY) return;
|
||||
|
||||
if (dirty) // rarely
|
||||
calcFrame(useFramePixels);
|
||||
|
||||
for (camera in getCamerasLegacy())
|
||||
{
|
||||
if (!camera.visible || !camera.exists || !isOnScreen(camera)) continue;
|
||||
|
||||
if (isSimpleRender(camera)) drawSimple(camera);
|
||||
else
|
||||
drawComplex(camera);
|
||||
|
||||
#if FLX_DEBUG
|
||||
FlxBasic.visibleCount++;
|
||||
#end
|
||||
}
|
||||
|
||||
#if FLX_DEBUG
|
||||
if (FlxG.debugger.drawDebug) drawDebug();
|
||||
#end
|
||||
}
|
||||
|
||||
@:noCompletion
|
||||
override function drawSimple(camera:FlxCamera):Void
|
||||
{
|
||||
getScreenPosition(_point, camera).subtract(offset);
|
||||
if (isPixelPerfectRender(camera)) _point.floor();
|
||||
|
||||
_point.copyTo(_flashPoint);
|
||||
camera.copyPixels(_frame, framePixels, _flashRect, _flashPoint, colorTransform, blend, antialiasing);
|
||||
}
|
||||
|
||||
@:noCompletion
|
||||
override function drawComplex(camera:FlxCamera):Void
|
||||
{
|
||||
_frame.prepareMatrix(_matrix, FlxFrameAngle.ANGLE_0, checkFlipX(), checkFlipY());
|
||||
_matrix.translate(-origin.x, -origin.y);
|
||||
_matrix.scale(scale.x, scale.y);
|
||||
|
||||
if (bakedRotationAngle <= 0)
|
||||
{
|
||||
updateTrig();
|
||||
|
||||
if (angle != 0) _matrix.rotateWithTrig(_cosAngle, _sinAngle);
|
||||
}
|
||||
|
||||
getScreenPosition(_point, camera).subtract(offset);
|
||||
_point.add(origin.x, origin.y);
|
||||
_matrix.translate(_point.x, _point.y);
|
||||
|
||||
if (isPixelPerfectRender(camera))
|
||||
{
|
||||
_matrix.tx = Math.floor(_matrix.tx);
|
||||
_matrix.ty = Math.floor(_matrix.ty);
|
||||
}
|
||||
|
||||
camera.drawPixels(_frame, framePixels, _matrix, colorTransform, blend, antialiasing, shader);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,8 @@ package funkin.ui.freeplay.dj;
|
||||
|
||||
import flixel.graphics.frames.FlxFramesCollection;
|
||||
import flixel.graphics.frames.FlxAtlasFrames;
|
||||
import funkin.util.assets.FlxAnimationUtil;
|
||||
import funkin.data.freeplay.player.PlayerRegistry;
|
||||
import flixel.graphics.frames.FlxFrame;
|
||||
import flixel.FlxCamera;
|
||||
import flixel.FlxBasic;
|
||||
|
||||
/**
|
||||
* A script that can be tied to a MultiSparrowFreeplayDJ.
|
||||
@@ -14,14 +12,27 @@ import flixel.FlxBasic;
|
||||
@:hscriptClass
|
||||
class ScriptedMultiSparrowFreeplayDJ extends MultiSparrowFreeplayDJ implements polymod.hscript.HScriptedClass {}
|
||||
|
||||
class MultiSparrowFreeplayDJ extends BaseFreeplayDJ.FlixelFramedFreeplayDJ
|
||||
/**
|
||||
* For some Freeplay DJs which use Sparrow atlases, the spritesheets need to be split
|
||||
* into multiple files. This Freeplay DJ renderer concatenates these together into a single sprite.
|
||||
*
|
||||
* BaseFreeplayDJ has game logic, MultiSparrowFreeplayDJ has only rendering logic.
|
||||
* KEEP THEM SEPARATE!
|
||||
*/
|
||||
class MultiSparrowFreeplayDJ extends BaseFreeplayDJ
|
||||
{
|
||||
public function new(x:Float, y:Float, characterId:String)
|
||||
{
|
||||
super(x, y, characterId);
|
||||
|
||||
loadFrames();
|
||||
loadAnimations();
|
||||
|
||||
animation.onFinish.add(onFinishAnim);
|
||||
animation.onLoop.add(onFinishAnim);
|
||||
}
|
||||
|
||||
override public function loadFrames():Void
|
||||
public function loadFrames():Void
|
||||
{
|
||||
trace('Loading assets for Multi-Sparrow character "${characterId}"', flixel.util.FlxColor.fromString("#89CFF0"));
|
||||
|
||||
@@ -57,4 +68,198 @@ class MultiSparrowFreeplayDJ extends BaseFreeplayDJ.FlixelFramedFreeplayDJ
|
||||
}
|
||||
this.frames = texture;
|
||||
}
|
||||
|
||||
public function loadAnimations():Void
|
||||
{
|
||||
trace('[MULTISPARROWDJ] Loading ${playableCharData.getAnimationsList().length} animations for ${characterId}');
|
||||
|
||||
FlxAnimationUtil.addAtlasAnimations(this, playableCharData.getAnimationsList());
|
||||
|
||||
var animationList:Array<String> = this.animation.getNameList();
|
||||
trace('[MULTISPARROWDJ] Successfully loaded ${animationList.length} animations for ${characterId}');
|
||||
}
|
||||
|
||||
public override function update(elapsed:Float):Void
|
||||
{
|
||||
switch (currentState)
|
||||
{
|
||||
case Intro:
|
||||
// Play the intro animation then leave this state immediately.
|
||||
var animPrefix = playableCharData?.getAnimationPrefix('intro');
|
||||
if (animPrefix != null && (getCurrentAnimation() != animPrefix))
|
||||
{
|
||||
playFlashAnimation(animPrefix, true);
|
||||
}
|
||||
timeIdling = 0;
|
||||
case Idle:
|
||||
// We are in this state the majority of the time.
|
||||
var animPrefix = playableCharData?.getAnimationPrefix('idle');
|
||||
if (animPrefix != null && getCurrentAnimation() != animPrefix)
|
||||
{
|
||||
playFlashAnimation(animPrefix, true, false, true);
|
||||
}
|
||||
timeIdling += elapsed;
|
||||
case NewUnlock:
|
||||
var animPrefix = playableCharData?.getAnimationPrefix('newUnlock');
|
||||
if (animPrefix != null && !hasAnimation(animPrefix))
|
||||
{
|
||||
currentState = Idle;
|
||||
}
|
||||
if (animPrefix != null && getCurrentAnimation() != animPrefix)
|
||||
{
|
||||
playFlashAnimation(animPrefix, true, false, true);
|
||||
}
|
||||
case Confirm:
|
||||
var animPrefix = playableCharData?.getAnimationPrefix('confirm');
|
||||
if (animPrefix != null && getCurrentAnimation() != animPrefix) playFlashAnimation(animPrefix, false);
|
||||
timeIdling = 0;
|
||||
case FistPumpIntro:
|
||||
var animPrefixA = playableCharData?.getAnimationPrefix('fistPump');
|
||||
var animPrefixB = playableCharData?.getAnimationPrefix('loss');
|
||||
|
||||
if (getCurrentAnimation() == animPrefixA)
|
||||
{
|
||||
var endFrame = playableCharData?.getFistPumpIntroEndFrame() ?? 0;
|
||||
if (endFrame > -1 && anim.curAnim.curFrame >= endFrame)
|
||||
{
|
||||
playFlashAnimation(animPrefixA, true, false, false, playableCharData?.getFistPumpIntroStartFrame());
|
||||
}
|
||||
}
|
||||
else if (getCurrentAnimation() == animPrefixB)
|
||||
{
|
||||
var endFrame = playableCharData?.getFistPumpIntroBadEndFrame() ?? 0;
|
||||
if (endFrame > -1 && anim.curAnim.curFrame >= endFrame)
|
||||
{
|
||||
playFlashAnimation(animPrefixB, true, false, false, playableCharData?.getFistPumpIntroBadStartFrame());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FlxG.log.warn("Unrecognized animation in FistPumpIntro: " + getCurrentAnimation());
|
||||
}
|
||||
|
||||
case FistPump:
|
||||
var animPrefixA = playableCharData?.getAnimationPrefix('fistPump');
|
||||
var animPrefixB = playableCharData?.getAnimationPrefix('loss');
|
||||
|
||||
if (getCurrentAnimation() == animPrefixA)
|
||||
{
|
||||
var endFrame = playableCharData?.getFistPumpLoopEndFrame() ?? 0;
|
||||
if (endFrame > -1 && anim.curAnim.curFrame >= endFrame)
|
||||
{
|
||||
playFlashAnimation(animPrefixA, true, false, false, playableCharData?.getFistPumpLoopStartFrame());
|
||||
}
|
||||
}
|
||||
else if (getCurrentAnimation() == animPrefixB)
|
||||
{
|
||||
var endFrame = playableCharData?.getFistPumpLoopBadEndFrame() ?? 0;
|
||||
if (endFrame > -1 && anim.curAnim.curFrame >= endFrame)
|
||||
{
|
||||
playFlashAnimation(animPrefixB, true, false, false, playableCharData?.getFistPumpLoopBadStartFrame());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FlxG.log.warn("Unrecognized animation in FistPump: " + getCurrentAnimation());
|
||||
}
|
||||
|
||||
case IdleEasterEgg:
|
||||
var animPrefix = playableCharData?.getAnimationPrefix('idleEasterEgg');
|
||||
if (animPrefix != null && getCurrentAnimation() != animPrefix)
|
||||
{
|
||||
onIdleEasterEgg.dispatch();
|
||||
playFlashAnimation(animPrefix, false);
|
||||
seenIdleEasterEgg = true;
|
||||
}
|
||||
timeIdling = 0;
|
||||
case Cartoon:
|
||||
var animPrefix = playableCharData?.getAnimationPrefix('cartoon');
|
||||
if (animPrefix == null)
|
||||
{
|
||||
currentState = IdleEasterEgg;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (getCurrentAnimation() != animPrefix) playFlashAnimation(animPrefix, true);
|
||||
timeIdling = 0;
|
||||
}
|
||||
default:
|
||||
// I shit myself.
|
||||
}
|
||||
|
||||
// Call the superclass function AFTER updating the current state and playing the next animation.
|
||||
// This ensures that flixel-animate starts rendering the new animation immediately.
|
||||
super.update(elapsed);
|
||||
}
|
||||
|
||||
override function onFinishAnim(name:String):Void
|
||||
{
|
||||
if (name == playableCharData?.getAnimationPrefix('intro'))
|
||||
{
|
||||
if (PlayerRegistry.instance.hasNewCharacter())
|
||||
{
|
||||
currentState = NewUnlock;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentState = Idle;
|
||||
}
|
||||
onIntroDone.dispatch();
|
||||
}
|
||||
else if (name == playableCharData?.getAnimationPrefix('idle'))
|
||||
{
|
||||
// trace('Finished idle');
|
||||
|
||||
if (timeIdling >= IDLE_EGG_PERIOD && !seenIdleEasterEgg)
|
||||
{
|
||||
currentState = IdleEasterEgg;
|
||||
}
|
||||
else if (timeIdling >= IDLE_CARTOON_PERIOD)
|
||||
{
|
||||
currentState = Cartoon;
|
||||
}
|
||||
}
|
||||
else if (name == playableCharData?.getAnimationPrefix('confirm'))
|
||||
{
|
||||
// trace('Finished confirm');
|
||||
}
|
||||
else if (name == playableCharData?.getAnimationPrefix('fistPump'))
|
||||
{
|
||||
// trace('Finished fist pump');
|
||||
currentState = Idle;
|
||||
}
|
||||
else if (name == playableCharData?.getAnimationPrefix('idleEasterEgg'))
|
||||
{
|
||||
// trace('Finished spook');
|
||||
currentState = Idle;
|
||||
}
|
||||
else if (name == playableCharData?.getAnimationPrefix('loss'))
|
||||
{
|
||||
// trace('Finished loss reaction');
|
||||
currentState = Idle;
|
||||
}
|
||||
else if (name == playableCharData?.getAnimationPrefix('newUnlock'))
|
||||
{
|
||||
// Animation should loop.
|
||||
}
|
||||
else if (name == playableCharData?.getAnimationPrefix('charSelect'))
|
||||
{
|
||||
onCharSelectComplete();
|
||||
}
|
||||
else
|
||||
{
|
||||
trace('Finished ${name}');
|
||||
}
|
||||
}
|
||||
|
||||
override public function playFlashAnimation(id:String, Force:Bool = false, Reverse:Bool = false, Loop:Bool = false, Frame:Int = 0):Void
|
||||
{
|
||||
animation.play(id, Force, Reverse, Frame);
|
||||
|
||||
if (animation.curAnim != null)
|
||||
{
|
||||
animation.curAnim.looped = Loop;
|
||||
}
|
||||
applyAnimationOffset();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
package funkin.ui.freeplay.dj;
|
||||
|
||||
import flixel.graphics.frames.FlxFramesCollection;
|
||||
import funkin.util.assets.FlxAnimationUtil;
|
||||
import funkin.data.freeplay.player.PlayerRegistry;
|
||||
import flixel.graphics.frames.FlxFrame;
|
||||
import flixel.FlxCamera;
|
||||
import flixel.FlxBasic;
|
||||
|
||||
/**
|
||||
* A script that can be tied to a PackerFreeplayDJ.
|
||||
@@ -13,21 +11,228 @@ import flixel.FlxBasic;
|
||||
@:hscriptClass
|
||||
class ScriptedPackerFreeplayDJ extends PackerFreeplayDJ implements polymod.hscript.HScriptedClass {}
|
||||
|
||||
class PackerFreeplayDJ extends BaseFreeplayDJ.FlixelFramedFreeplayDJ
|
||||
/**
|
||||
* A PackerFreeplayDJ is a Freeplay DJ which is rendered by
|
||||
* displaying an animation derived from a Packer spritesheet file.
|
||||
*
|
||||
* BaseFreeplayDJ has game logic, PackerFreeplayDJ has only rendering logic.
|
||||
* KEEP THEM SEPARATE!
|
||||
*/
|
||||
class PackerFreeplayDJ extends BaseFreeplayDJ
|
||||
{
|
||||
public function new(x:Float, y:Float, characterId:String)
|
||||
{
|
||||
super(x, y, characterId);
|
||||
|
||||
loadFrames();
|
||||
loadAnimations();
|
||||
|
||||
animation.onFinish.add(onFinishAnim);
|
||||
animation.onLoop.add(onFinishAnim);
|
||||
}
|
||||
|
||||
override public function loadFrames()
|
||||
public function loadFrames():Void
|
||||
{
|
||||
final tex:FlxFramesCollection = Paths.getPackerAtlas(playableCharData.getAssetPath());
|
||||
if (tex == null)
|
||||
{
|
||||
trace('Could not load Packer sprite: ${playableCharData.getAssetPath()}');
|
||||
trace('Could not load sparrow sprite: ' + playableCharData.getAssetPath());
|
||||
return;
|
||||
}
|
||||
this.frames = tex;
|
||||
}
|
||||
|
||||
public function loadAnimations():Void
|
||||
{
|
||||
trace('[PACKERDJ] Loading ${playableCharData.getAnimationsList().length} animations for ${characterId}');
|
||||
|
||||
FlxAnimationUtil.addAtlasAnimations(this, playableCharData.getAnimationsList());
|
||||
|
||||
var animationList:Array<String> = this.animation.getNameList();
|
||||
trace('[PACKERDJ] Successfully loaded ${animationList.length} animations for ${characterId}');
|
||||
}
|
||||
|
||||
public override function update(elapsed:Float):Void
|
||||
{
|
||||
switch (currentState)
|
||||
{
|
||||
case Intro:
|
||||
// Play the intro animation then leave this state immediately.
|
||||
var animPrefix = playableCharData?.getAnimationPrefix('intro');
|
||||
if (animPrefix != null && (getCurrentAnimation() != animPrefix))
|
||||
{
|
||||
playFlashAnimation(animPrefix, true);
|
||||
}
|
||||
timeIdling = 0;
|
||||
case Idle:
|
||||
// We are in this state the majority of the time.
|
||||
var animPrefix = playableCharData?.getAnimationPrefix('idle');
|
||||
if (animPrefix != null && getCurrentAnimation() != animPrefix)
|
||||
{
|
||||
playFlashAnimation(animPrefix, true, false, true);
|
||||
}
|
||||
timeIdling += elapsed;
|
||||
case NewUnlock:
|
||||
var animPrefix = playableCharData?.getAnimationPrefix('newUnlock');
|
||||
if (animPrefix != null && !hasAnimation(animPrefix))
|
||||
{
|
||||
currentState = Idle;
|
||||
}
|
||||
if (animPrefix != null && getCurrentAnimation() != animPrefix)
|
||||
{
|
||||
playFlashAnimation(animPrefix, true, false, true);
|
||||
}
|
||||
case Confirm:
|
||||
var animPrefix = playableCharData?.getAnimationPrefix('confirm');
|
||||
if (animPrefix != null && getCurrentAnimation() != animPrefix) playFlashAnimation(animPrefix, false);
|
||||
timeIdling = 0;
|
||||
case FistPumpIntro:
|
||||
var animPrefixA = playableCharData?.getAnimationPrefix('fistPump');
|
||||
var animPrefixB = playableCharData?.getAnimationPrefix('loss');
|
||||
|
||||
if (getCurrentAnimation() == animPrefixA)
|
||||
{
|
||||
var endFrame = playableCharData?.getFistPumpIntroEndFrame() ?? 0;
|
||||
if (endFrame > -1 && anim.curAnim.curFrame >= endFrame)
|
||||
{
|
||||
playFlashAnimation(animPrefixA, true, false, false, playableCharData?.getFistPumpIntroStartFrame());
|
||||
}
|
||||
}
|
||||
else if (getCurrentAnimation() == animPrefixB)
|
||||
{
|
||||
var endFrame = playableCharData?.getFistPumpIntroBadEndFrame() ?? 0;
|
||||
if (endFrame > -1 && anim.curAnim.curFrame >= endFrame)
|
||||
{
|
||||
playFlashAnimation(animPrefixB, true, false, false, playableCharData?.getFistPumpIntroBadStartFrame());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FlxG.log.warn("Unrecognized animation in FistPumpIntro: " + getCurrentAnimation());
|
||||
}
|
||||
|
||||
case FistPump:
|
||||
var animPrefixA = playableCharData?.getAnimationPrefix('fistPump');
|
||||
var animPrefixB = playableCharData?.getAnimationPrefix('loss');
|
||||
|
||||
if (getCurrentAnimation() == animPrefixA)
|
||||
{
|
||||
var endFrame = playableCharData?.getFistPumpLoopEndFrame() ?? 0;
|
||||
if (endFrame > -1 && anim.curAnim.curFrame >= endFrame)
|
||||
{
|
||||
playFlashAnimation(animPrefixA, true, false, false, playableCharData?.getFistPumpLoopStartFrame());
|
||||
}
|
||||
}
|
||||
else if (getCurrentAnimation() == animPrefixB)
|
||||
{
|
||||
var endFrame = playableCharData?.getFistPumpLoopBadEndFrame() ?? 0;
|
||||
if (endFrame > -1 && anim.curAnim.curFrame >= endFrame)
|
||||
{
|
||||
playFlashAnimation(animPrefixB, true, false, false, playableCharData?.getFistPumpLoopBadStartFrame());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FlxG.log.warn("Unrecognized animation in FistPump: " + getCurrentAnimation());
|
||||
}
|
||||
|
||||
case IdleEasterEgg:
|
||||
var animPrefix = playableCharData?.getAnimationPrefix('idleEasterEgg');
|
||||
if (animPrefix != null && getCurrentAnimation() != animPrefix)
|
||||
{
|
||||
onIdleEasterEgg.dispatch();
|
||||
playFlashAnimation(animPrefix, false);
|
||||
seenIdleEasterEgg = true;
|
||||
}
|
||||
timeIdling = 0;
|
||||
case Cartoon:
|
||||
var animPrefix = playableCharData?.getAnimationPrefix('cartoon');
|
||||
if (animPrefix == null)
|
||||
{
|
||||
currentState = IdleEasterEgg;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (getCurrentAnimation() != animPrefix) playFlashAnimation(animPrefix, true);
|
||||
timeIdling = 0;
|
||||
}
|
||||
default:
|
||||
// I shit myself.
|
||||
}
|
||||
|
||||
// Call the superclass function AFTER updating the current state and playing the next animation.
|
||||
// This ensures that flixel-animate starts rendering the new animation immediately.
|
||||
super.update(elapsed);
|
||||
}
|
||||
|
||||
override function onFinishAnim(name:String):Void
|
||||
{
|
||||
if (name == playableCharData?.getAnimationPrefix('intro'))
|
||||
{
|
||||
if (PlayerRegistry.instance.hasNewCharacter())
|
||||
{
|
||||
currentState = NewUnlock;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentState = Idle;
|
||||
}
|
||||
onIntroDone.dispatch();
|
||||
}
|
||||
else if (name == playableCharData?.getAnimationPrefix('idle'))
|
||||
{
|
||||
// trace('Finished idle');
|
||||
|
||||
if (timeIdling >= IDLE_EGG_PERIOD && !seenIdleEasterEgg)
|
||||
{
|
||||
currentState = IdleEasterEgg;
|
||||
}
|
||||
else if (timeIdling >= IDLE_CARTOON_PERIOD)
|
||||
{
|
||||
currentState = Cartoon;
|
||||
}
|
||||
}
|
||||
else if (name == playableCharData?.getAnimationPrefix('confirm'))
|
||||
{
|
||||
// trace('Finished confirm');
|
||||
}
|
||||
else if (name == playableCharData?.getAnimationPrefix('fistPump'))
|
||||
{
|
||||
// trace('Finished fist pump');
|
||||
currentState = Idle;
|
||||
}
|
||||
else if (name == playableCharData?.getAnimationPrefix('idleEasterEgg'))
|
||||
{
|
||||
// trace('Finished spook');
|
||||
currentState = Idle;
|
||||
}
|
||||
else if (name == playableCharData?.getAnimationPrefix('loss'))
|
||||
{
|
||||
// trace('Finished loss reaction');
|
||||
currentState = Idle;
|
||||
}
|
||||
else if (name == playableCharData?.getAnimationPrefix('newUnlock'))
|
||||
{
|
||||
// Animation should loop.
|
||||
}
|
||||
else if (name == playableCharData?.getAnimationPrefix('charSelect'))
|
||||
{
|
||||
onCharSelectComplete();
|
||||
}
|
||||
else
|
||||
{
|
||||
trace('Finished ${name}');
|
||||
}
|
||||
}
|
||||
|
||||
override public function playFlashAnimation(id:String, Force:Bool = false, Reverse:Bool = false, Loop:Bool = false, Frame:Int = 0):Void
|
||||
{
|
||||
animation.play(id, Force, Reverse, Frame);
|
||||
|
||||
if (animation.curAnim != null)
|
||||
{
|
||||
animation.curAnim.looped = Loop;
|
||||
}
|
||||
applyAnimationOffset();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
package funkin.ui.freeplay.dj;
|
||||
|
||||
import flixel.graphics.frames.FlxFramesCollection;
|
||||
import funkin.util.assets.FlxAnimationUtil;
|
||||
import funkin.data.freeplay.player.PlayerRegistry;
|
||||
import flixel.graphics.frames.FlxFrame;
|
||||
import flixel.FlxCamera;
|
||||
import flixel.FlxBasic;
|
||||
|
||||
/**
|
||||
* A script that can be tied to a SparrowFreeplayDJ.
|
||||
@@ -13,21 +11,228 @@ import flixel.FlxBasic;
|
||||
@:hscriptClass
|
||||
class ScriptedSparrowFreeplayDJ extends SparrowFreeplayDJ implements polymod.hscript.HScriptedClass {}
|
||||
|
||||
class SparrowFreeplayDJ extends BaseFreeplayDJ.FlixelFramedFreeplayDJ
|
||||
/**
|
||||
* A SparrowFreeplayDJ is a Freeplay DJ which is rendered by
|
||||
* displaying an animation derived from a SparrowV2 atlas spritesheet file.
|
||||
*
|
||||
* BaseFreeplayDJ has game logic, SparrowFreeplayDJ has only rendering logic.
|
||||
* KEEP THEM SEPARATE!
|
||||
*/
|
||||
class SparrowFreeplayDJ extends BaseFreeplayDJ
|
||||
{
|
||||
public function new(x:Float, y:Float, characterId:String)
|
||||
{
|
||||
super(x, y, characterId);
|
||||
|
||||
loadFrames();
|
||||
loadAnimations();
|
||||
|
||||
animation.onFinish.add(onFinishAnim);
|
||||
animation.onLoop.add(onFinishAnim);
|
||||
}
|
||||
|
||||
override public function loadFrames()
|
||||
public function loadFrames():Void
|
||||
{
|
||||
final tex:FlxFramesCollection = Paths.getSparrowAtlas(playableCharData.getAssetPath());
|
||||
if (tex == null)
|
||||
{
|
||||
trace('Could not load Sparrow sprite: ' + playableCharData.getAssetPath());
|
||||
trace('Could not load sparrow sprite: ' + playableCharData.getAssetPath());
|
||||
return;
|
||||
}
|
||||
this.frames = tex;
|
||||
}
|
||||
|
||||
public function loadAnimations():Void
|
||||
{
|
||||
trace('[SPARROWDJ] Loading ${playableCharData.getAnimationsList().length} animations for ${characterId}');
|
||||
|
||||
FlxAnimationUtil.addAtlasAnimations(this, playableCharData.getAnimationsList());
|
||||
|
||||
var animationList:Array<String> = this.animation.getNameList();
|
||||
trace('[SPARROWDJ] Successfully loaded ${animationList.length} animations for ${characterId}');
|
||||
}
|
||||
|
||||
public override function update(elapsed:Float):Void
|
||||
{
|
||||
switch (currentState)
|
||||
{
|
||||
case Intro:
|
||||
// Play the intro animation then leave this state immediately.
|
||||
var animPrefix = playableCharData?.getAnimationPrefix('intro');
|
||||
if (animPrefix != null && (getCurrentAnimation() != animPrefix))
|
||||
{
|
||||
playFlashAnimation(animPrefix, true);
|
||||
}
|
||||
timeIdling = 0;
|
||||
case Idle:
|
||||
// We are in this state the majority of the time.
|
||||
var animPrefix = playableCharData?.getAnimationPrefix('idle');
|
||||
if (animPrefix != null && getCurrentAnimation() != animPrefix)
|
||||
{
|
||||
playFlashAnimation(animPrefix, true, false, true);
|
||||
}
|
||||
timeIdling += elapsed;
|
||||
case NewUnlock:
|
||||
var animPrefix = playableCharData?.getAnimationPrefix('newUnlock');
|
||||
if (animPrefix != null && !hasAnimation(animPrefix))
|
||||
{
|
||||
currentState = Idle;
|
||||
}
|
||||
if (animPrefix != null && getCurrentAnimation() != animPrefix)
|
||||
{
|
||||
playFlashAnimation(animPrefix, true, false, true);
|
||||
}
|
||||
case Confirm:
|
||||
var animPrefix = playableCharData?.getAnimationPrefix('confirm');
|
||||
if (animPrefix != null && getCurrentAnimation() != animPrefix) playFlashAnimation(animPrefix, false);
|
||||
timeIdling = 0;
|
||||
case FistPumpIntro:
|
||||
var animPrefixA = playableCharData?.getAnimationPrefix('fistPump');
|
||||
var animPrefixB = playableCharData?.getAnimationPrefix('loss');
|
||||
|
||||
if (getCurrentAnimation() == animPrefixA)
|
||||
{
|
||||
var endFrame = playableCharData?.getFistPumpIntroEndFrame() ?? 0;
|
||||
if (endFrame > -1 && anim.curAnim.curFrame >= endFrame)
|
||||
{
|
||||
playFlashAnimation(animPrefixA, true, false, false, playableCharData?.getFistPumpIntroStartFrame());
|
||||
}
|
||||
}
|
||||
else if (getCurrentAnimation() == animPrefixB)
|
||||
{
|
||||
var endFrame = playableCharData?.getFistPumpIntroBadEndFrame() ?? 0;
|
||||
if (endFrame > -1 && anim.curAnim.curFrame >= endFrame)
|
||||
{
|
||||
playFlashAnimation(animPrefixB, true, false, false, playableCharData?.getFistPumpIntroBadStartFrame());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FlxG.log.warn("Unrecognized animation in FistPumpIntro: " + getCurrentAnimation());
|
||||
}
|
||||
|
||||
case FistPump:
|
||||
var animPrefixA = playableCharData?.getAnimationPrefix('fistPump');
|
||||
var animPrefixB = playableCharData?.getAnimationPrefix('loss');
|
||||
|
||||
if (getCurrentAnimation() == animPrefixA)
|
||||
{
|
||||
var endFrame = playableCharData?.getFistPumpLoopEndFrame() ?? 0;
|
||||
if (endFrame > -1 && anim.curAnim.curFrame >= endFrame)
|
||||
{
|
||||
playFlashAnimation(animPrefixA, true, false, false, playableCharData?.getFistPumpLoopStartFrame());
|
||||
}
|
||||
}
|
||||
else if (getCurrentAnimation() == animPrefixB)
|
||||
{
|
||||
var endFrame = playableCharData?.getFistPumpLoopBadEndFrame() ?? 0;
|
||||
if (endFrame > -1 && anim.curAnim.curFrame >= endFrame)
|
||||
{
|
||||
playFlashAnimation(animPrefixB, true, false, false, playableCharData?.getFistPumpLoopBadStartFrame());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FlxG.log.warn("Unrecognized animation in FistPump: " + getCurrentAnimation());
|
||||
}
|
||||
|
||||
case IdleEasterEgg:
|
||||
var animPrefix = playableCharData?.getAnimationPrefix('idleEasterEgg');
|
||||
if (animPrefix != null && getCurrentAnimation() != animPrefix)
|
||||
{
|
||||
onIdleEasterEgg.dispatch();
|
||||
playFlashAnimation(animPrefix, false);
|
||||
seenIdleEasterEgg = true;
|
||||
}
|
||||
timeIdling = 0;
|
||||
case Cartoon:
|
||||
var animPrefix = playableCharData?.getAnimationPrefix('cartoon');
|
||||
if (animPrefix == null)
|
||||
{
|
||||
currentState = IdleEasterEgg;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (getCurrentAnimation() != animPrefix) playFlashAnimation(animPrefix, true);
|
||||
timeIdling = 0;
|
||||
}
|
||||
default:
|
||||
// I shit myself.
|
||||
}
|
||||
|
||||
// Call the superclass function AFTER updating the current state and playing the next animation.
|
||||
// This ensures that flixel-animate starts rendering the new animation immediately.
|
||||
super.update(elapsed);
|
||||
}
|
||||
|
||||
override function onFinishAnim(name:String):Void
|
||||
{
|
||||
if (name == playableCharData?.getAnimationPrefix('intro'))
|
||||
{
|
||||
if (PlayerRegistry.instance.hasNewCharacter())
|
||||
{
|
||||
currentState = NewUnlock;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentState = Idle;
|
||||
}
|
||||
onIntroDone.dispatch();
|
||||
}
|
||||
else if (name == playableCharData?.getAnimationPrefix('idle'))
|
||||
{
|
||||
// trace('Finished idle');
|
||||
|
||||
if (timeIdling >= IDLE_EGG_PERIOD && !seenIdleEasterEgg)
|
||||
{
|
||||
currentState = IdleEasterEgg;
|
||||
}
|
||||
else if (timeIdling >= IDLE_CARTOON_PERIOD)
|
||||
{
|
||||
currentState = Cartoon;
|
||||
}
|
||||
}
|
||||
else if (name == playableCharData?.getAnimationPrefix('confirm'))
|
||||
{
|
||||
// trace('Finished confirm');
|
||||
}
|
||||
else if (name == playableCharData?.getAnimationPrefix('fistPump'))
|
||||
{
|
||||
// trace('Finished fist pump');
|
||||
currentState = Idle;
|
||||
}
|
||||
else if (name == playableCharData?.getAnimationPrefix('idleEasterEgg'))
|
||||
{
|
||||
// trace('Finished spook');
|
||||
currentState = Idle;
|
||||
}
|
||||
else if (name == playableCharData?.getAnimationPrefix('loss'))
|
||||
{
|
||||
// trace('Finished loss reaction');
|
||||
currentState = Idle;
|
||||
}
|
||||
else if (name == playableCharData?.getAnimationPrefix('newUnlock'))
|
||||
{
|
||||
// Animation should loop.
|
||||
}
|
||||
else if (name == playableCharData?.getAnimationPrefix('charSelect'))
|
||||
{
|
||||
onCharSelectComplete();
|
||||
}
|
||||
else
|
||||
{
|
||||
trace('Finished ${name}');
|
||||
}
|
||||
}
|
||||
|
||||
override public function playFlashAnimation(id:String, Force:Bool = false, Reverse:Bool = false, Loop:Bool = false, Frame:Int = 0):Void
|
||||
{
|
||||
animation.play(id, Force, Reverse, Frame);
|
||||
|
||||
if (animation.curAnim != null)
|
||||
{
|
||||
animation.curAnim.looped = Loop;
|
||||
}
|
||||
applyAnimationOffset();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package funkin.util.assets;
|
||||
|
||||
import flixel.FlxSprite;
|
||||
import funkin.graphics.FunkinSprite;
|
||||
import flixel.graphics.frames.FlxFramesCollection;
|
||||
import funkin.data.animation.AnimationData;
|
||||
|
||||
@@ -13,22 +14,55 @@ class FlxAnimationUtil
|
||||
public static function addAtlasAnimation(target:FlxSprite, anim:AnimationData):Void
|
||||
{
|
||||
if (anim.prefix == null) return;
|
||||
var frameRate = anim.frameRate == null ? 24 : anim.frameRate;
|
||||
var looped = anim.looped == null ? false : anim.looped;
|
||||
var flipX = anim.flipX == null ? false : anim.flipX;
|
||||
var flipY = anim.flipY == null ? false : anim.flipY;
|
||||
|
||||
var frameRate:Int = anim.frameRate ?? 24;
|
||||
var looped:Bool = anim.looped ?? false;
|
||||
var flipX:Bool = anim.flipX ?? false;
|
||||
var flipY:Bool = anim.flipY ?? false;
|
||||
|
||||
if (anim.frameIndices != null && anim.frameIndices.length > 0)
|
||||
{
|
||||
// trace('addByIndices(${anim.name}, ${anim.prefix}, ${anim.frameIndices}, ${frameRate}, ${looped}, ${flipX}, ${flipY})');
|
||||
target.animation.addByIndices(anim.name, anim.prefix, anim.frameIndices, '', frameRate, looped, flipX, flipY);
|
||||
// trace('RESULT:${target.animation.getAnimationList()}');
|
||||
}
|
||||
else
|
||||
{
|
||||
// trace('addByPrefix(${anim.name}, ${anim.prefix}, ${frameRate}, ${looped}, ${flipX}, ${flipY})');
|
||||
target.animation.addByPrefix(anim.name, anim.prefix, frameRate, looped, flipX, flipY);
|
||||
// trace('RESULT:${target.animation.getAnimationList()}');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Properly adds an animation to a texture atlas sprite based on the provided animation data.
|
||||
*/
|
||||
public static function addTextureAtlasAnimation(target:FunkinSprite, anim:AnimationData):Void
|
||||
{
|
||||
if (!target.isAnimate) return;
|
||||
if (anim.prefix == null) return;
|
||||
|
||||
var frameRate:Int = anim.frameRate ?? 24;
|
||||
var looped:Bool = anim.looped ?? false;
|
||||
var flipX:Bool = anim.flipX ?? false;
|
||||
var flipY:Bool = anim.flipY ?? false;
|
||||
var animType:String = anim.animType ?? "framelabel";
|
||||
|
||||
if (anim.frameIndices != null && anim.frameIndices.length > 0)
|
||||
{
|
||||
switch (animType)
|
||||
{
|
||||
case "framelabel":
|
||||
target.anim.addByFrameLabelIndices(anim.name, anim.prefix, anim.frameIndices, frameRate, looped, flipX, flipY);
|
||||
case "symbol":
|
||||
target.anim.addBySymbolIndices(anim.name, anim.prefix, anim.frameIndices, frameRate, looped, flipX, flipY);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (animType)
|
||||
{
|
||||
case "framelabel":
|
||||
target.anim.addByFrameLabel(anim.name, anim.prefix, frameRate, looped, flipX, flipY);
|
||||
case "symbol":
|
||||
target.anim.addBySymbol(anim.name, anim.prefix, frameRate, looped, flipX, flipY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +77,17 @@ class FlxAnimationUtil
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Properly adds multiple animations to a texture atlas sprite based on the provided animation data.
|
||||
*/
|
||||
public static function addTextureAtlasAnimations(target:FunkinSprite, animations:Array<AnimationData>):Void
|
||||
{
|
||||
for (anim in animations)
|
||||
{
|
||||
addTextureAtlasAnimation(target, anim);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Combine two FlxFramesCollection objects into one.
|
||||
* @param a The first FlxFramesCollection
|
||||
|
||||
@@ -6,24 +6,28 @@ import flixel.group.FlxContainer.FlxTypedContainer;
|
||||
import flixel.text.FlxText;
|
||||
import funkin.audio.FunkinSound;
|
||||
import flixel.graphics.FlxGraphic;
|
||||
import funkin.graphics.adobeanimate.FlxAtlasSprite;
|
||||
import funkin.graphics.FunkinSprite;
|
||||
import flixel.math.FlxRect;
|
||||
import funkin.api.newgrounds.Medals;
|
||||
import funkin.util.macro.ConsoleMacro;
|
||||
import funkin.ui.FullScreenScaleMode;
|
||||
|
||||
@:nullSafety
|
||||
class NewgroundsMedalPlugin extends FlxTypedContainer<FlxBasic>
|
||||
class NewgroundsMedalPlugin extends FlxTypedContainer<FlxBasic> implements ConsoleClass
|
||||
{
|
||||
var medal:FlxAtlasSprite;
|
||||
public static var instance:Null<NewgroundsMedalPlugin> = null;
|
||||
|
||||
var medal:FunkinSprite;
|
||||
var points:FlxText;
|
||||
var name:FlxText;
|
||||
|
||||
public static var instance:Null<NewgroundsMedalPlugin> = null;
|
||||
var moveText:Bool = false;
|
||||
var medalQueue:Array<Void->Void> = [];
|
||||
|
||||
var tween:Bool = false;
|
||||
var textSpeed:Float = 20;
|
||||
|
||||
var am:Float = 20;
|
||||
|
||||
var funcs:Array<Void->Void> = [];
|
||||
final MEDAL_X = (FlxG.width - 250) * 0.5;
|
||||
final MEDAL_Y = FlxG.height - 100;
|
||||
|
||||
public function new()
|
||||
{
|
||||
@@ -34,12 +38,15 @@ class NewgroundsMedalPlugin extends FlxTypedContainer<FlxBasic>
|
||||
FlxG.console.registerClass(Medals);
|
||||
#end
|
||||
|
||||
var ecs = (FlxG.width - 250) * 0.5;
|
||||
var why = FlxG.height - 100;
|
||||
FlxGraphic.defaultPersist = true;
|
||||
medal = new FlxAtlasSprite(ecs, why, Paths.animateAtlas("ui/medal"));
|
||||
|
||||
points = new FlxText(171 + ecs, 10 + why, 50, 12, false);
|
||||
medal = FunkinSprite.createTextureAtlas((MEDAL_X) + (FullScreenScaleMode.gameCutoutSize.x / 2), MEDAL_Y, "ui/medal",
|
||||
{
|
||||
swfMode: true,
|
||||
filterQuality: HIGH
|
||||
});
|
||||
|
||||
points = new FlxText((171 + MEDAL_X) + (FullScreenScaleMode.gameCutoutSize.x / 2), 17 + MEDAL_Y, 50, 12, false);
|
||||
points.fieldHeight = 18;
|
||||
points.systemFont = "Arial";
|
||||
points.bold = true;
|
||||
@@ -50,64 +57,73 @@ class NewgroundsMedalPlugin extends FlxTypedContainer<FlxBasic>
|
||||
points.visible = false;
|
||||
points.scrollFactor.set();
|
||||
|
||||
name = new FlxText(73 + ecs, 34 + why, 0, 26);
|
||||
name = new FlxText((73 + MEDAL_X) + (FullScreenScaleMode.gameCutoutSize.x / 2), 37 + MEDAL_Y, 0, 26);
|
||||
name.font = Paths.font("ShareTechMono-Regular.ttf");
|
||||
name.letterSpacing = -2;
|
||||
|
||||
name.text = "Ono Boners Deluxe";
|
||||
|
||||
name.clipRect = FlxRect.get(0, 0, 164, 35.2);
|
||||
|
||||
name.visible = false;
|
||||
name.scrollFactor.set();
|
||||
|
||||
medal.scrollFactor.set();
|
||||
medal.anim.swfRender = true;
|
||||
medal.visible = false;
|
||||
|
||||
var fr = medal.anim.curSymbol.timeline.get(0).get(0);
|
||||
if (fr != null) fr.name = "START"; // woerkaround
|
||||
// fr.add(() -> FunkinSound.playOnce(Paths.sound('NGFadeIn'), 1.));
|
||||
|
||||
medal.anim.getFrameLabel("show").add(function() {
|
||||
points.visible = true;
|
||||
name.visible = true;
|
||||
if (name.width > name.clipRect.width)
|
||||
medal.anim.onFrameLabel.add(function(label:String) {
|
||||
switch (label)
|
||||
{
|
||||
// TODO: Remove this once FlxText.get_size deals with TextFormat's nullable size properly
|
||||
@:nullSafety(Off)
|
||||
am = (name.text.length * (name.size + 2) * 1.25) / name.clipRect.width * 10;
|
||||
tween = true;
|
||||
// FlxTimer.wait(0.3, () -> tween = true);
|
||||
case "show":
|
||||
points.visible = true;
|
||||
name.visible = true;
|
||||
if (name.width > name.clipRect.width)
|
||||
{
|
||||
@:nullSafety(Off)
|
||||
textSpeed = (name.text.length * (name.size + 2) * 1.25) / name.clipRect.width * 10;
|
||||
moveText = true;
|
||||
}
|
||||
case "fade":
|
||||
FunkinSound.playOnce(Paths.sound('NGFadeOut'), 1.0);
|
||||
case "hide":
|
||||
points.visible = false;
|
||||
name.visible = false;
|
||||
moveText = false;
|
||||
name.offset.x = 0;
|
||||
name.clipRect.x = 0;
|
||||
name.resetFrame();
|
||||
}
|
||||
});
|
||||
// medal.anim.getFrameLabel("idle").add(() -> medal.anim.pause());
|
||||
medal.anim.getFrameLabel("fade").add(() -> FunkinSound.playOnce(Paths.sound('NGFadeOut'), 1.));
|
||||
|
||||
medal.anim.getFrameLabel("hide").add(function() {
|
||||
points.visible = false;
|
||||
name.visible = false;
|
||||
tween = false;
|
||||
name.offset.x = 0;
|
||||
name.clipRect.x = 0;
|
||||
name.resetFrame();
|
||||
medal.replaceFrameGraphic(3, null);
|
||||
medal.anim.onFinish.add(function(name:String) {
|
||||
medal.visible = false;
|
||||
});
|
||||
|
||||
add(medal);
|
||||
add(points);
|
||||
add(name);
|
||||
|
||||
FlxGraphic.defaultPersist = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the positions of the medal atlas in case the resolution changes!
|
||||
*/
|
||||
function updatePositions():Void
|
||||
{
|
||||
medal.x = MEDAL_X + (FullScreenScaleMode.gameCutoutSize.x / 2);
|
||||
points.x = (175 + MEDAL_X) + (FullScreenScaleMode.gameCutoutSize.x / 2);
|
||||
name.x = (79 + MEDAL_X) + (FullScreenScaleMode.gameCutoutSize.x / 2);
|
||||
}
|
||||
|
||||
override public function update(elapsed:Float)
|
||||
{
|
||||
super.update(elapsed);
|
||||
if (tween)
|
||||
if (moveText)
|
||||
{
|
||||
var val = am * elapsed;
|
||||
name.offset.x += val;
|
||||
name.clipRect.x += val;
|
||||
var textX:Float = textSpeed * elapsed;
|
||||
|
||||
name.offset.x += textX;
|
||||
name.clipRect.x += textX;
|
||||
name.resetFrame();
|
||||
}
|
||||
}
|
||||
@@ -120,10 +136,10 @@ class NewgroundsMedalPlugin extends FlxTypedContainer<FlxBasic>
|
||||
|
||||
// instance is defined above so there's no need to worry about null safety here
|
||||
@:nullSafety(Off)
|
||||
instance.medal.anim?.onComplete.add(function() {
|
||||
if (instance.funcs.length > 0)
|
||||
instance.medal.anim.onFinish.add(function(name:String) {
|
||||
if (instance.medalQueue.length > 0)
|
||||
{
|
||||
instance.funcs.shift()();
|
||||
instance.medalQueue.shift()();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -131,22 +147,24 @@ class NewgroundsMedalPlugin extends FlxTypedContainer<FlxBasic>
|
||||
public static function play(points:Int = 100, name:String = "I LOVE CUM I LOVE CUM I LOVE CUM I LOVE CUM", ?graphic:FlxGraphic)
|
||||
{
|
||||
if (instance == null) return;
|
||||
var func = function() {
|
||||
|
||||
var playMedal:Void->Void = function() {
|
||||
instance.points.visible = false;
|
||||
instance.name.visible = false;
|
||||
instance.points.text = Std.string(points);
|
||||
instance.name.text = name;
|
||||
|
||||
instance.medal.visible = true;
|
||||
instance.medal.replaceFrameGraphic(3, graphic);
|
||||
instance.medal.anim.play(true);
|
||||
instance.updatePositions();
|
||||
|
||||
FunkinSound.playOnce(Paths.sound('NGFadeIn'), 1.0);
|
||||
instance.medal.anim.play("");
|
||||
|
||||
instance.medal.visible = true;
|
||||
instance.medal.replaceSymbolGraphic("NGMEDAL", graphic);
|
||||
}
|
||||
|
||||
if (!instance.medal.anim.isPlaying && instance.funcs.length == 0) func();
|
||||
if (instance.medal.isAnimationFinished() && instance.medalQueue.length == 0) playMedal();
|
||||
else
|
||||
instance.funcs.push(func);
|
||||
instance.medalQueue.push(playMedal);
|
||||
}
|
||||
}
|
||||
#end
|
||||
|
||||
Reference in New Issue
Block a user