diff --git a/assets/data/editors/layouts/stage/characterEditScreen.xml b/assets/data/editors/layouts/stage/characterEditScreen.xml
index e9140123..2658f4ac 100644
--- a/assets/data/editors/layouts/stage/characterEditScreen.xml
+++ b/assets/data/editors/layouts/stage/characterEditScreen.xml
@@ -52,7 +52,7 @@
-
+
diff --git a/libs.xml b/libs.xml
index df46404b..e4247aca 100644
--- a/libs.xml
+++ b/libs.xml
@@ -25,6 +25,7 @@
+
diff --git a/project.xml b/project.xml
index 09502ba7..c3aabcfb 100644
--- a/project.xml
+++ b/project.xml
@@ -198,6 +198,7 @@
+
diff --git a/source/funkin/backend/FunkinSprite.hx b/source/funkin/backend/FunkinSprite.hx
index 58048dbc..fe96e7fb 100644
--- a/source/funkin/backend/FunkinSprite.hx
+++ b/source/funkin/backend/FunkinSprite.hx
@@ -181,7 +181,7 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I
public function copyAtlasValues()
{
@:privateAccess {
- animateAtlas.cameras = cameras;
+ animateAtlas.cameras = cameras; // investigate if we can use _cameras
animateAtlas.scrollFactor = scrollFactor;
animateAtlas.scale = scale;
animateAtlas.offset = offset;
diff --git a/source/funkin/backend/system/FakeCamera.hx b/source/funkin/backend/system/FakeCamera.hx
index 83d66eee..b350f4a5 100644
--- a/source/funkin/backend/system/FakeCamera.hx
+++ b/source/funkin/backend/system/FakeCamera.hx
@@ -40,7 +40,7 @@ class FakeCamera extends FlxCamera {
class FakeCallCamera extends FakeCamera {
public static final instance = new FakeCallCamera();
- public dynamic function onDraw(?frame:FlxFrame, ?pixels:BitmapData, matrix:FlxMatrix, ?transform:ColorTransform, ?blend:BlendMode, ?smoothing:Bool = false, ?shader:FlxShader, smooth:Bool = false, ?shader:FlxShader) {
+ public dynamic function onDraw(?frame:FlxFrame, ?pixels:BitmapData, matrix:FlxMatrix, ?transform:ColorTransform, ?blend:BlendMode, ?smoothing:Bool = false, ?shader:FlxShader) {
}
override function drawPixels(?frame:FlxFrame, ?pixels:BitmapData, matrix:FlxMatrix, ?transform:ColorTransform, ?blend:BlendMode, ?smoothing:Bool = false, ?shader:FlxShader) {
diff --git a/source/funkin/backend/utils/MatrixUtil.hx b/source/funkin/backend/utils/MatrixUtil.hx
index 97e2d7a9..68cc7921 100644
--- a/source/funkin/backend/utils/MatrixUtil.hx
+++ b/source/funkin/backend/utils/MatrixUtil.hx
@@ -25,6 +25,8 @@ interface IPrePostDraw {
@:access(flixel.FlxCamera)
@:access(flixel.FlxSprite)
+@:access(flixel.math.FlxMatrix)
+@:access(openfl.geom.Matrix)
final class MatrixUtil {
public static function getMatrixPosition(sprite:FlxSprite, points:OneOfTwo>, ?camera:FlxCamera, _width:Float = 1, _height:Float = 1):Array
{
@@ -42,46 +44,149 @@ final class MatrixUtil {
nc.scroll.set(camera.scroll.x, camera.scroll.y);
nc.pixelPerfectRender = camera.pixelPerfectRender;
+ var points:Array = cast points;
+
if(isAnimateAtlas) {
- var nc = FakeCallCamera.instance;
+ var cnc = FakeCallCamera.instance;
var sprite = funkinSprite;
- var oldOnDraw = nc.onDraw;
- nc.onDraw = function(?frame:FlxFrame, ?pixels:BitmapData, matrix:FlxMatrix, ?transform:ColorTransform, ?blend:BlendMode, ?smoothing:Bool = false, ?shader:FlxShader, smooth:Bool = false, ?shader:FlxShader) {
- // pixels is null
-
+ var oldOnDraw = cnc.onDraw;
+
+ // TODO: fix rotation
+ // TODO: fix skew
+
+ var boundsTopLeft = new FlxPoint(Math.POSITIVE_INFINITY, Math.POSITIVE_INFINITY);
+ var boundsTopRight = new FlxPoint(Math.NEGATIVE_INFINITY, Math.POSITIVE_INFINITY);
+ var boundsBottomLeft = new FlxPoint(Math.POSITIVE_INFINITY, Math.NEGATIVE_INFINITY);
+ var boundsBottomRight = new FlxPoint(Math.NEGATIVE_INFINITY, Math.NEGATIVE_INFINITY);
+
+ cnc.onDraw = function(?frame:FlxFrame, ?pixels:BitmapData, matrix:FlxMatrix, ?transform:ColorTransform, ?blend:BlendMode, ?smoothing:Bool = false, ?shader:FlxShader) {
+ // cnc is already perfect rn
+ // so we need to manually process the limb
+ var points = [
+ // corners
+ FlxPoint.get(0, 0),
+ FlxPoint.get(1, 0),
+ FlxPoint.get(0, 1),
+ FlxPoint.get(1, 1)
+ ];
+
+ rawTransformPoints(points, matrix, frame.sourceSize.x, frame.sourceSize.y);
+
+ // sort the points, incase like they were transformed with a scale -1 matrix
+
+ var topLeft:FlxPoint = points[0];
+ var topRight:FlxPoint = points[1];
+ var bottomLeft:FlxPoint = points[2];
+ var bottomRight:FlxPoint = points[3];
+
+ if (points[0].y > points[1].y) {
+ topLeft = points[1];
+ topRight = points[0];
+ }
+ if (points[2].y > points[3].y) {
+ bottomLeft = points[3];
+ bottomRight = points[2];
+ }
+
+ if (topLeft.x > topRight.x) {
+ var temp = topLeft;
+ topLeft = topRight;
+ topRight = temp;
+ }
+
+ if (bottomLeft.x > bottomRight.x) {
+ var temp = bottomLeft;
+ bottomLeft = bottomRight;
+ bottomRight = temp;
+ }
+
+ // points should now contain the corners of the limb
+
+ trace("Corners of limb: " + points);
+
+ // set the max bounds of each corner of the limb
+ if(topLeft.x < boundsTopLeft.x) boundsTopLeft.x = topLeft.x;
+ if(topLeft.y < boundsTopLeft.y) boundsTopLeft.y = topLeft.y;
+ if(topRight.x > boundsTopRight.x) boundsTopRight.x = topRight.x;
+ if(topRight.y < boundsTopRight.y) boundsTopRight.y = topRight.y;
+ if(bottomLeft.x < boundsBottomLeft.x) boundsBottomLeft.x = bottomLeft.x;
+ if(bottomLeft.y > boundsBottomLeft.y) boundsBottomLeft.y = bottomLeft.y;
+ if(bottomRight.x > boundsBottomRight.x) boundsBottomRight.x = bottomRight.x;
+ if(bottomRight.y > boundsBottomRight.y) boundsBottomRight.y = bottomRight.y;
+
+ // recycle the points
+ for(point in points) point.put();
}
- var cameras = sprite.animateAtlas._cameras;
- sprite.animateAtlas._cameras = [nc];
- if(sprite is IPrePostDraw) {
- var postDraw = cast(sprite, IPrePostDraw);
- postDraw.preDraw();
- @:privateAccess sprite.draw();
- postDraw.postDraw();
- } else {
- @:privateAccess sprite.draw();
+ var cameras = sprite._cameras;
+ var oldVisible = cnc.visible;
+ cnc.visible = true;
+ sprite._cameras = [cnc];
+ sprite.draw();
+ sprite._cameras = cameras;
+ cnc.visible = oldVisible;
+ cnc.onDraw = oldOnDraw;
+
+ // fake transform
+
+ //trace("");
+ //trace("topLeft: " + boundsTopLeft);
+ //trace("topRight: " + boundsTopRight);
+ //trace("bottomLeft: " + boundsBottomLeft);
+ //trace("bottomRight: " + boundsBottomRight);
+ //trace("Before transform");
+ //trace(points);
+
+ for(point in points) {
+ //var x = matrix.__transformX(point.x * _width, point.y * _height);
+ //var y = matrix.__transformY(point.x * _width, point.y * _height);
+ // apply using boundsTopLeft, boundsTopRight, boundsBottomLeft, boundsBottomRight
+ // matrix doesnt work for this
+ var x = FlxMath.lerp(
+ FlxMath.lerp(boundsTopLeft.x, boundsTopRight.x, point.x),
+ FlxMath.lerp(boundsBottomLeft.x, boundsBottomRight.x, point.x),
+ point.y
+ );
+ var y = FlxMath.lerp(
+ FlxMath.lerp(boundsTopLeft.y, boundsTopRight.y, point.x),
+ FlxMath.lerp(boundsBottomLeft.y, boundsBottomRight.y, point.x),
+ point.y
+ );
+
+ //trace("(" + point.x + ", " + point.y + ") -> (" + x + ", " + y + ")");
+
+ // reset to ingame coords
+ x += camera.scroll.x;
+ y += camera.scroll.y;
+
+ if(isFunkinSprite) {
+ var ratio = 1 - FlxMath.lerp(1 / camera.zoom, 1, funkinSprite.zoomFactor);
+ x += camera.width / 2 * ratio;
+ y += camera.height / 2 * ratio;
+ }
+ point.set(x, y);
}
- sprite.animateAtlas._cameras = cameras;
- nc.onDraw = oldOnDraw;
+
+ //trace("After transform");
+ //trace(points);
} else {
if(sprite is IPrePostDraw) {
var postDraw = cast(sprite, IPrePostDraw);
postDraw.preDraw();
- @:privateAccess sprite.drawComplex(nc);
+ sprite.drawComplex(nc);
postDraw.postDraw();
} else {
- @:privateAccess sprite.drawComplex(nc);
+ sprite.drawComplex(nc);
}
+ transformPoints(sprite, points, sprite._matrix, camera, _width, _height);
}
- var points:Array = cast points;
- transformPoints(sprite, points, sprite._matrix, camera, _width, _height);
return points;
}
/**
* Warning: modifies the points in the array
**/
- public static function transformPoints(sprite:FlxSprite, points:Array, matrix:FlxMatrix, ?camera:FlxCamera, _width:Float = 1, _height:Float = 1):Array {
+ public static function transformPoints(sprite:FlxSprite, points:Array, matrix:FlxMatrix, ?camera:FlxCamera, _width:Float = 1, _height:Float = 1, doCameraTransform:Bool = true):Array {
var isFunkinSprite = sprite is FunkinSprite;
var funkinSprite:FunkinSprite = null;
if(isFunkinSprite) funkinSprite = cast sprite;
@@ -90,17 +195,29 @@ final class MatrixUtil {
var x = matrix.__transformX(point.x * _width, point.y * _height);
var y = matrix.__transformY(point.x * _width, point.y * _height);
- // reset to ingame coords
- x += camera.scroll.x;
- y += camera.scroll.y;
+ if(doCameraTransform) {
+ // reset to ingame coords
+ x += camera.scroll.x;
+ y += camera.scroll.y;
- if(isFunkinSprite) {
- var ratio = 1 - FlxMath.lerp(1 / camera.zoom, 1, funkinSprite.zoomFactor);
- x += camera.width / 2 * ratio;
- y += camera.height / 2 * ratio;
+ if(isFunkinSprite) {
+ var ratio = 1 - FlxMath.lerp(1 / camera.zoom, 1, funkinSprite.zoomFactor);
+ x += camera.width / 2 * ratio;
+ y += camera.height / 2 * ratio;
+ }
}
point.set(x, y);
}
return points;
}
+
+ private static function rawTransformPoints(points:Array, matrix:FlxMatrix, _width:Float = 1, _height:Float = 1):Array {
+ for(point in points) {
+ var x = matrix.__transformX(point.x * _width, point.y * _height);
+ var y = matrix.__transformY(point.x * _width, point.y * _height);
+
+ point.set(x, y);
+ }
+ return points;
+ }
}
\ No newline at end of file
diff --git a/source/funkin/editors/UIDebugState.hx b/source/funkin/editors/UIDebugState.hx
index 679b2f6f..e7270da6 100644
--- a/source/funkin/editors/UIDebugState.hx
+++ b/source/funkin/editors/UIDebugState.hx
@@ -4,6 +4,7 @@ import funkin.editors.ui.old.OldUISliceSpriteTri;
import funkin.editors.ui.UITopMenu.UITopMenuButton;
import flixel.tweens.FlxTween;
import funkin.editors.ui.*;
+import funkin.game.Character;
class UIDebugState extends UIState {
public var topMenuSpr:UITopMenu;
@@ -21,6 +22,10 @@ class UIDebugState extends UIState {
bg.scrollFactor.set();
add(bg);
+ var gf = new Character(0, 0, "gf");
+ gf.dance();
+ add(gf);
+
add(topMenuSpr = new UITopMenu([
{
label: "File",
diff --git a/source/funkin/editors/character/CharacterAnimButton.hx b/source/funkin/editors/character/CharacterAnimButton.hx
index 79eeb1e4..82fc04f7 100644
--- a/source/funkin/editors/character/CharacterAnimButton.hx
+++ b/source/funkin/editors/character/CharacterAnimButton.hx
@@ -268,6 +268,14 @@ class CharacterAnimButton extends UIButton {
flxAnimation.prefix = newAnim;
refreshFlxAnimationFrames(flxAnimation, animData);
+
+ if (valid) {
+ parent.buildAnimDisplay(anim, data);
+ animationDisplayBG.alpha = 1;
+ } else {
+ parent.removeAnimDisplay(anim);
+ animationDisplayBG.alpha = 0.4;
+ }
}
if (parent.character.getAnimName() == anim)
@@ -356,14 +364,6 @@ class CharacterAnimButton extends UIButton {
trace('ERROR REFRESHING FLXANIMATION FRAMES: $e');
invalidate();
}
-
- if (valid) {
- parent.buildAnimDisplay(anim, flxAnimation);
- animationDisplayBG.alpha = 1;
- } else {
- parent.removeAnimDisplay(anim);
- animationDisplayBG.alpha = 0.4;
- }
}
public inline function refreshSymbolKeyFrames(symbol:FlxSymbolAnimation, animData:AnimData) @:privateAccess {
@@ -466,6 +466,14 @@ class CharacterAnimButton extends UIButton {
@:noCompletion function __refreshAnimation() @:privateAccess {
if (parent.character.animateAtlas != null) refreshSymbolKeyFrames(__getAnimationSymbol(), data);
else refreshFlxAnimationFrames(__getFlxAnimation(), data);
+
+ if (valid) {
+ parent.buildAnimDisplay(anim, data);
+ animationDisplayBG.alpha = 1;
+ } else {
+ parent.removeAnimDisplay(anim);
+ animationDisplayBG.alpha = 0.4;
+ }
}
public inline function checkValid()
diff --git a/source/funkin/editors/character/CharacterAnimsWindow.hx b/source/funkin/editors/character/CharacterAnimsWindow.hx
index a3e67ea8..9910c0eb 100644
--- a/source/funkin/editors/character/CharacterAnimsWindow.hx
+++ b/source/funkin/editors/character/CharacterAnimsWindow.hx
@@ -58,28 +58,26 @@ class CharacterAnimsWindow extends UIButtonList {
if (character.animateAtlas == null) {
var anim:FlxAnimation = character.animation._animations[anim.name];
if (anim == null || anim.frames.length <= 0) return;
-
+
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: anim.frames.getDefault([0])[0], scale: 104/animBounds.height, animBounds: animBounds});
} else {
- var ogFrame:Int = character.animateAtlas.anim.curFrame;
- var oldTick:Float = character.animateAtlas.anim._tick;
- var oldPlaying:Bool = character.animateAtlas.anim.isPlaying;
+ character.storeAtlasState();
+ /*
character.animateAtlas.anim.play(anim.name, true, false, 0);
character.animateAtlas.anim.stop();
var animBounds:Rectangle = MatrixUtil.getBounds(character).copyToFlash();
displayAnimsFramesList.set(name, {frame: anim.anim, scale: 104/animBounds.height, animBounds: animBounds});
+ */
- character.animateAtlas.anim.play(atlasPlayingAnim, true, false, ogFrame);
- character.animateAtlas.anim._tick = oldTick;
- character.animateAtlas.anim.isPlaying = oldPlaying;
+ character.restoreAtlasState();
}
}
@@ -90,11 +88,11 @@ class CharacterAnimsWindow extends UIButtonList {
if (buttons.members.length <= 1) return;
if (character.getAnimName() == button.anim)
@:privateAccess CharacterEditor.instance._animation_down(null);
-
+
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);
-
+
remove(button); button.destroy();
}
@@ -128,8 +126,7 @@ class CharacterAnimsWindow extends UIButtonList {
if (newButton.valid) {
XMLUtil.addAnimToSprite(character, animData);
- if (character.animation._animations[animData.name] != null)
- buildAnimDisplay(animData.name, character.animation._animations[animData.name]);
+ buildAnimDisplay(animData.name, animData);
}
}
diff --git a/source/funkin/editors/character/CharacterGhost.hx b/source/funkin/editors/character/CharacterGhost.hx
index d1aabfee..2cf8bb3e 100644
--- a/source/funkin/editors/character/CharacterGhost.hx
+++ b/source/funkin/editors/character/CharacterGhost.hx
@@ -20,10 +20,7 @@ class CharacterGhost extends Character {
colorTransform.__identity();
if (animateAtlas != null) {
- var ogFrame:Int = animateAtlas.anim.curFrame;
- var oldTick:Float = animateAtlas.anim._tick;
- var oldPlaying:Bool = animateAtlas.anim.isPlaying;
-
+ storeAtlasState();
for (anim in ghosts) {
animateAtlas.anim.play(anim, true, false, 0);
setAnimOffset(anim);
@@ -32,11 +29,7 @@ class CharacterGhost extends Character {
super.draw();
}
- if (ghosts.length > 0) {
- animateAtlas.anim.play(atlasPlayingAnim, true, false, ogFrame);
- animateAtlas.anim._tick = oldTick;
- animateAtlas.anim.isPlaying = oldPlaying;
- }
+ if (ghosts.length > 0) restoreAtlasState();
}
else {
for (anim in ghosts) @:privateAccess {
@@ -75,8 +68,9 @@ class CharacterGhost extends Character {
offset.set(globalOffset.x * (isPlayer != playerOffsets ? 1 : -1), -globalOffset.y);
}
- var atlasState:AtlasState = null;
- public function storeAtlasState():AtlasState {
+ // This gets annoying lmao -lunar
+ private var atlasState:AtlasState = null;
+ public function storeAtlasState():AtlasState @:privateAccess {
return atlasState = {
oldAnim: atlasPlayingAnim,
oldFrame: animateAtlas.anim.curFrame,
@@ -84,4 +78,12 @@ class CharacterGhost extends Character {
oldPlaying: animateAtlas.anim.isPlaying,
};
}
+
+ public function restoreAtlasState(state:AtlasState = null) @:privateAccess {
+ if (state == null) state = atlasState;
+
+ animateAtlas.anim.play(state.oldAnim, true, false, state.oldFrame);
+ animateAtlas.anim._tick = state.oldTick;
+ animateAtlas.anim.isPlaying = state.oldPlaying;
+ }
}
\ No newline at end of file
diff --git a/source/funkin/editors/stage/StageEditor.hx b/source/funkin/editors/stage/StageEditor.hx
index 042711fa..62950a01 100644
--- a/source/funkin/editors/stage/StageEditor.hx
+++ b/source/funkin/editors/stage/StageEditor.hx
@@ -19,6 +19,7 @@ import openfl.ui.Mouse;
using funkin.backend.utils.MatrixUtil;
+@:access(flixel.FlxSprite)
class StageEditor extends UIState {
static var __stage:String;
public var stage:Stage;
@@ -491,9 +492,11 @@ class StageEditor extends UIState {
var sprites = getRealSprites();
for (i in 0...sprites.length) {
var sprite = sprites[sprites.length - i - 1];
- if(sprite.animateAtlas != null) continue;
+ //if(sprite.animateAtlas != null) continue;
calcSpriteBounds(sprite);
+ //trace("Sprite: " + sprite);
+ //trace("Sprite bounds: " + sprite.extra.get(exID("bounds")));
if (cast(sprite.extra.get(exID("bounds")), FlxRect).containsPoint(point)) {
selectSprite(sprite); break;
}
@@ -923,10 +926,8 @@ class StageEditor extends UIState {
super.draw();
for(sprite in selection) {
- if(sprite is FunkinSprite) {
- @:privateAccess if(sprite._frame == null) continue;
+ if(sprite is FunkinSprite)
drawGuides(cast sprite);
- }
}
}
@@ -934,7 +935,10 @@ class StageEditor extends UIState {
var circleColor:FlxColor = 0xFF99a8f2;
var hollowColor:FlxColor = 0xffb2beff;
- function drawGuides(sprite:FlxSprite) {
+ function drawGuides(sprite:FunkinSprite) {
+ //if(sprite._frame == null) return;
+ if(sprite.offset == null) return; // destroyed
+
var corners = calcSpriteBounds(sprite);
var funkinSprite = sprite is FunkinSprite ? cast(sprite, FunkinSprite) : null;
@@ -991,7 +995,6 @@ class StageEditor extends UIState {
DrawUtil.dot.animation.play("hollow");
buttonBoxes.push(corner);
}
-
}
if(funkinSprite == null) {
@@ -1037,6 +1040,9 @@ class StageEditor extends UIState {
FlxPoint.get(0.5, -0.5), // angle
FlxPoint.get(1, 0) // rotate corner
], sprite.camera, sprite.frameWidth, sprite.frameHeight);
+ //if(sprite.animateAtlas != null) {
+ // trace("Corners: " + corners);
+ //}
@:privateAccess corners[corners.length - 1].add(
dotCheckSize * 0.35 * sprite._cosAngle + dotCheckSize * 0.35 * sprite._sinAngle,
-dotCheckSize * 0.35 * sprite._cosAngle + dotCheckSize * 0.35 * sprite._sinAngle