* added flixel-animate to libs * I BROKE THE OFFSETS FUCK . * yes it does blehtty wdym?? * Removed flxanimate entirely + Fixed transition + Fixed offsets * Deleted caches (relaunching actions) * frameOffset for atlases (need testing) * useRenderTexture added to xml * Fixed atlases for mods * a simple redirect so old mods wont break that badly (#879) * Update FunkinSprite.hx * remove trace * Fixed ghosts for atlases * usable character editor (not entirely fixed) * fixed zoomFactor + temp fix for atlas popping * early structure for character editor atlas anim display * zoomFactor for atlases * Animate Atlas character editor previews * FlxAnimateSettings specification * fixed animation swapping on atlas characters * OH YEA BABY NEW FLXANIMATE UPDATE THAT MADE CODE 50X SIMPLER **AGAIN** 💖 /pos * Added label checkbox in Character Editor * Fixed culling issues with FlxAnimate (fr this time) * fixed some atlases * HOW THE FUCK * gf sad fix + AliasMacro (for backwards comp, manual impl for now) * Fixed pop-ins * Update Character.hx * applyStageMatrix * FlxAnimate pull request merge (empty commit) * flixel-animate** i'm clumsy asf i meant the lib not the class name fuck * Update libs.xml * Merge changes from main (#892) * repair ScriptableShader.shader = null (#877) * optimize updateInput (#878) * optimize updateInput * resize optimize * __notePerStrum optimize * Update StrumLine.hx * NoteGroup optimize (#881) * NoteGroup optimize * CoolUtil.bound faster * makes commit with github desktop W FRAKITS * Use fastSinCos for notes (#889) * Update Strum.hx * Update Strum.hx --------- Co-authored-by: HEIHUAa <112499486+HEIHUAa@users.noreply.github.com> Co-authored-by: Frakits <90983918+Frakits@users.noreply.github.com> --------- Co-authored-by: Kolze <53307233+KolzeYT@users.noreply.github.com> Co-authored-by: HEIHUAa <112499486+HEIHUAa@users.noreply.github.com> Co-authored-by: Frakits <90983918+Frakits@users.noreply.github.com>
195 lines
6.4 KiB
Haxe
195 lines
6.4 KiB
Haxe
package funkin.editors.character;
|
|
|
|
import flixel.util.typeLimit.OneOfTwo;
|
|
import funkin.backend.utils.XMLUtil.AnimData;
|
|
import flixel.animation.FlxAnimation;
|
|
import flixel.graphics.FlxGraphic;
|
|
import openfl.geom.Rectangle;
|
|
import flixel.graphics.frames.FlxFrame;
|
|
import funkin.game.Character;
|
|
import flixel.animation.FlxPrerotatedAnimation;
|
|
import animate.internal.RenderTexture;
|
|
import flixel.graphics.frames.FlxFrame;
|
|
import funkin.backend.system.Logs;
|
|
|
|
using funkin.backend.utils.BitmapUtil;
|
|
|
|
typedef DisplayAnimFrameEntry = {
|
|
scale:Float,
|
|
frame:FlxFrame,
|
|
renderTexture:RenderTexture
|
|
}
|
|
|
|
class CharacterAnimsWindow extends UIButtonList<CharacterAnimButton> {
|
|
public var character:CharacterGhost;
|
|
|
|
public var displayWindowSprite:FlxSprite;
|
|
public var displayWindowGraphic:FlxGraphic;
|
|
public var displayAnimsFramesList:Map<String, DisplayAnimFrameEntry> = [];
|
|
|
|
public var animButtons:Map<String, CharacterAnimButton> = [];
|
|
public var animsList:Array<String> = [];
|
|
|
|
public function new(x:Float, y:Float, character:CharacterGhost) {
|
|
super(x, y, Std.int(500-16), 419, null, FlxPoint.get(Std.int(500-16-32), 208));
|
|
this.character = character;
|
|
|
|
cameraSpacing = 0;
|
|
frames = Paths.getFrames('editors/ui/inputbox');
|
|
|
|
buttonCameras.pixelPerfectRender = true;
|
|
|
|
if (Assets.exists(Paths.image('characters/${character.sprite}')))
|
|
displayWindowGraphic = FlxG.bitmap.add(Assets.getBitmapData(Paths.image('characters/${character.sprite}'), true, false));
|
|
|
|
displayWindowSprite = new FlxSprite();
|
|
displayWindowSprite.loadGraphicFromSprite(character);
|
|
displayWindowSprite.antialiasing = character.antialiasing;
|
|
displayWindowSprite.flipX = character.flipX;
|
|
|
|
alpha = 0.7;
|
|
|
|
for (anim in character.getAnimOrder())
|
|
addAnimation(character.animDatas.get(anim), -1, false);
|
|
addButton.callback = generateAnimation;
|
|
|
|
setAnimAutoComplete(CoolUtil.getAnimsListFromSprite(character));
|
|
|
|
nextscrollY = 0;
|
|
// dragCallback = (button:CharacterAnimButton, oldID:Int, newID:Int) -> {}
|
|
}
|
|
|
|
public var ghosts:Array<String> = [];
|
|
var __movingAnimOldOrder:Int = -1;
|
|
public override function update(elapsed:Float) {
|
|
var __oldMoving:CharacterAnimButton = curMoving;
|
|
super.update(elapsed);
|
|
|
|
if (curMoving != null && __oldMoving == null)
|
|
__movingAnimOldOrder = curMoving.ID;
|
|
|
|
if (curMoving == null && __oldMoving != null)
|
|
if (__movingAnimOldOrder != __oldMoving.ID)
|
|
CharacterEditor.undos.addToUndo(CAnimEditOrder(__movingAnimOldOrder, __oldMoving.ID));
|
|
|
|
|
|
animsList = [for (button in buttons) button.anim];
|
|
character.ghosts = ghosts;
|
|
}
|
|
|
|
public function buildAnimDisplay(name:String, anim:AnimData) @:privateAccess {
|
|
var anim:FlxAnimation = character.animation._animations[anim.name];
|
|
if (anim == null) return;
|
|
|
|
if (character.isAnimate) {
|
|
var renderTex = character.generateRenderTextureForAnim(name);
|
|
var frame = renderTex.graphic.imageFrame.frame;
|
|
|
|
displayAnimsFramesList.set(name, {
|
|
frame: frame,
|
|
scale: 104/Math.max(frame.frame.width, frame.frame.height),
|
|
renderTexture: renderTex
|
|
});
|
|
} else {
|
|
var frameIndex:Int = anim.frames.getDefault([0])[0];
|
|
var frame:FlxFrame = displayWindowSprite.frames.frames[frameIndex];
|
|
|
|
var frameRect:Rectangle = new Rectangle(frame.offset.x, frame.offset.y, frame.sourceSize.x, frame.sourceSize.y);
|
|
var animBounds:Rectangle = displayWindowGraphic != null ? displayWindowGraphic.bitmap.bounds(frameRect) : frameRect;
|
|
|
|
displayAnimsFramesList.set(name, {
|
|
frame: character.frames.frames[anim.frames.getDefault([0])[0]],
|
|
scale: 104/Math.max(animBounds.width, animBounds.height),
|
|
renderTexture: null
|
|
});
|
|
}
|
|
|
|
}
|
|
|
|
public function removeAnimDisplay(name:String) {
|
|
if (displayAnimsFramesList.exists(name))
|
|
displayAnimsFramesList[name].renderTexture?.destroy();
|
|
displayAnimsFramesList.remove(name);
|
|
}
|
|
|
|
public function clearDisplayAnims() {
|
|
for(k=>e in displayAnimsFramesList)
|
|
e.renderTexture?.destroy();
|
|
displayAnimsFramesList.clear();
|
|
}
|
|
|
|
public function deleteAnimation(button:CharacterAnimButton, addToUndo:Bool = true) {
|
|
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_DELETE_SOUND));
|
|
if (buttons.members.length <= 1) return;
|
|
if (character.getAnimName() == button.anim)
|
|
@:privateAccess CharacterEditor.instance._animation_down(null);
|
|
|
|
character.ghosts.remove(button.anim);
|
|
character.removeAnimation(button.anim);
|
|
if (character.animOffsets.exists(button.anim)) character.animOffsets.remove(button.anim);
|
|
if (character.animDatas.exists(button.anim)) character.animDatas.remove(button.anim);
|
|
|
|
if (addToUndo) CharacterEditor.undos.addToUndo(CAnimDelete(button.ID, button.data));
|
|
remove(button); button.destroy();
|
|
}
|
|
|
|
public function generateAnimation() {
|
|
var newAnim = TU.translate("characterEditor.characterAnim.defaultAnimName");
|
|
var animName:String = newAnim;
|
|
var animNames:Array<String> = character.getNameList();
|
|
|
|
var newAnimCount:Int = 0;
|
|
while (animNames.indexOf(animName) != -1) {
|
|
newAnimCount++;
|
|
animName = '$newAnim - $newAnimCount';
|
|
}
|
|
|
|
if (__autoCompleteAnims.length <= 0)
|
|
setAnimAutoComplete(CoolUtil.getAnimsListFromSprite(character));
|
|
|
|
var animData:AnimData = {
|
|
name: animName,
|
|
anim: __autoCompleteAnims[0],
|
|
fps: 24, loop: false,
|
|
x: 0, y: 0,
|
|
indices: [],
|
|
animType: NONE,
|
|
label: false
|
|
};
|
|
addAnimation(animData);
|
|
}
|
|
|
|
public function addAnimation(animData:AnimData, animID:Int = -1, addToUndo:Bool = true) @:privateAccess {
|
|
XMLUtil.addAnimToSprite(character, animData);
|
|
|
|
var newButton:CharacterAnimButton = new CharacterAnimButton(0, 0, animData, this);
|
|
newButton.alpha = 0.25; animButtons.set(animData.name, newButton);
|
|
newButton.animTextBox.suggestItems = __autoCompleteAnims;
|
|
|
|
if (animID == -1) add(newButton);
|
|
else insert(newButton, animID);
|
|
|
|
if (newButton.valid)
|
|
buildAnimDisplay(animData.name, animData);
|
|
|
|
if (addToUndo)
|
|
CharacterEditor.undos.addToUndo(CAnimCreate(newButton.ID, newButton.data));
|
|
|
|
var nextButtonY:Float = 0;
|
|
for (buttonID in 0...newButton.ID)
|
|
nextButtonY += buttons.members[buttonID].bHeight + buttonOffset.y;
|
|
nextscrollY = nextButtonY;
|
|
}
|
|
|
|
@:noCompletion var __autoCompleteAnims:Array<String> = [];
|
|
public inline function setAnimAutoComplete(anims:Array<String>) {
|
|
__autoCompleteAnims = anims.copy();
|
|
for (button in buttons)
|
|
button.animTextBox.suggestItems = __autoCompleteAnims;
|
|
}
|
|
|
|
public function findValid():Null<String> {
|
|
for (button in buttons) if (button.valid) return button.anim;
|
|
return null;
|
|
}
|
|
} |