diff --git a/.gitignore b/.gitignore
index 77035890a7..a78fed33c7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,3 +13,4 @@ addons/*
addons/*
!addons/readme.txt
dev-libs/
+.vs/
\ No newline at end of file
diff --git a/assets/languages/en/Editors.xml b/assets/languages/en/Editors.xml
index c36b63b5e1..fe60cf0f53 100644
--- a/assets/languages/en/Editors.xml
+++ b/assets/languages/en/Editors.xml
@@ -357,6 +357,7 @@
Icon
Icon Color
Sing Duration
+ Default FPS
Use Sing Duration?
Custom Values (Advanced)
diff --git a/source/funkin/editors/character/CharacterAnimsWindow.hx b/source/funkin/editors/character/CharacterAnimsWindow.hx
index bcdd4e5184..b31a071f34 100644
--- a/source/funkin/editors/character/CharacterAnimsWindow.hx
+++ b/source/funkin/editors/character/CharacterAnimsWindow.hx
@@ -150,7 +150,7 @@ class CharacterAnimsWindow extends UIButtonList {
var animData:AnimData = {
name: animName,
anim: __autoCompleteAnims[0],
- fps: 24, loop: false,
+ fps: character.defaultAimFPS, loop: false,
x: 0, y: 0,
indices: [],
animType: NONE,
diff --git a/source/funkin/editors/character/CharacterInfoScreen.hx b/source/funkin/editors/character/CharacterInfoScreen.hx
index f085d2c338..383bece7df 100644
--- a/source/funkin/editors/character/CharacterInfoScreen.hx
+++ b/source/funkin/editors/character/CharacterInfoScreen.hx
@@ -8,6 +8,8 @@ import openfl.geom.Point;
import openfl.geom.Rectangle;
import openfl.display.BitmapData;
import funkin.game.Character;
+import funkin.backend.utils.XMLUtil.AnimData;
+import funkin.editors.character.CharacterEditor;
using funkin.backend.utils.BitmapUtil;
@@ -15,6 +17,7 @@ typedef CharacterExtraInfo = {
var icon:String;
var iconColor:Null;
var holdTime:Float;
+ var defaultAimFPS:Float;
var customProperties:Map;
}
@@ -26,12 +29,15 @@ class CharacterInfoScreen extends UISubstateWindow {
public var useDurationCheckbox:UICheckbox;
public var durationStepper:UINumericStepper;
+ public var defaultAimFPS:UINumericStepper;
public var customPropertiesButtonList:UIButtonList;
public var saveButton:UIButton;
public var closeButton:UIButton;
+ var oldDefFPS:Float;
+
public var onSave:(info:CharacterExtraInfo) -> Void = null;
public function new(character:Character, onSave:(info:CharacterExtraInfo) -> Void) {
@@ -77,6 +83,11 @@ class CharacterInfoScreen extends UISubstateWindow {
durationStepper.selectable = useDurationCheckbox.checked;
+ defaultAimFPS = new UINumericStepper(iconColorPicker.x, durationStepper.y + durationStepper.height + 40, character.defaultAimFPS, 0.001, 2, 0, 9999999, 74);
+ add(defaultAimFPS);
+ addLabelOn(defaultAimFPS, translate("defaultFPS"));
+ oldDefFPS = character.defaultAimFPS;
+
customPropertiesButtonList = new UIButtonList(iconColorWheel.x+iconColorWheel.bWidth+22, iconColorWheel.y, 290, 200, '', FlxPoint.get(280, 35), null, 5);
customPropertiesButtonList.frames = Paths.getFrames('editors/ui/inputbox');
customPropertiesButtonList.cameraSpacing = 0;
@@ -107,10 +118,25 @@ class CharacterInfoScreen extends UISubstateWindow {
public function saveCharacterInfo() {
UIUtil.confirmUISelections(this);
+ if (defaultAimFPS.value != oldDefFPS){
+ var daIDForButton:Int = 0;
+ for (anim in character.getAnimOrder()){
+ var animName = character.animDatas.get(anim);
+ if (animName.fps == oldDefFPS){
+ var poop:CharacterAnimButton = CharacterEditor.instance.characterAnimsWindow.buttons.members[daIDForButton];
+ trace(poop);
+ poop.changeFPS(defaultAimFPS.value);
+ }
+ daIDForButton++;
+ }
+ }
+
+
if (onSave != null) onSave({
icon: iconColorPicker.iconTextBox.label.text,
iconColor: iconColorWheel.curColor,
holdTime: useDurationCheckbox.checked ? durationStepper.value : -1,
+ defaultAimFPS: defaultAimFPS.value,
customProperties: [
for (val in customPropertiesButtonList.buttons.members)
val.propertyText.label.text => val.valueText.label.text
diff --git a/source/funkin/editors/character/CharacterPropertiesWindow.hx b/source/funkin/editors/character/CharacterPropertiesWindow.hx
index b877743265..0920f808d4 100644
--- a/source/funkin/editors/character/CharacterPropertiesWindow.hx
+++ b/source/funkin/editors/character/CharacterPropertiesWindow.hx
@@ -159,12 +159,14 @@ class CharacterPropertiesWindow extends UISliceSprite {
icon: character.icon,
iconColor: character.iconColor,
holdTime: character.holdTime,
+ defaultAimFPS: character.defaultAimFPS,
customProperties: character.extra.copy()
};
character.icon = info.icon;
character.iconColor = info.iconColor;
character.holdTime = info.holdTime;
+ character.defaultAimFPS = info.defaultAimFPS;
character.extra = info.customProperties.copy();
if (addToUndo) CharacterEditor.undos.addToUndo(CCharEditInfo(oldInfo, info));
diff --git a/source/funkin/game/Character.hx b/source/funkin/game/Character.hx
index 7ecbe0b777..5c08a7001d 100644
--- a/source/funkin/game/Character.hx
+++ b/source/funkin/game/Character.hx
@@ -46,6 +46,8 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
public var icon:String = null;
public var iconColor:Null = null;
public var gameOverCharacter:String = Character.FALLBACK_DEAD_CHARACTER;
+ public var defaultAimFPS:Float = 24;
+
public var cameraOffset:FlxPoint = FlxPoint.get(0, 0);
public var globalOffset:FlxPoint = FlxPoint.get(0, 0);
@@ -373,6 +375,7 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
if (xml.x.exists("x")) globalOffset.x = Std.parseFloat(xml.x.get("x"));
if (xml.x.exists("y")) globalOffset.y = Std.parseFloat(xml.x.get("y"));
if (xml.x.exists("gameOverChar")) gameOverCharacter = xml.x.get("gameOverChar");
+ if (xml.x.exists("defFps")) defaultAimFPS = Std.parseFloat(xml.x.get("defFps"));
if (xml.x.exists("camx")) cameraOffset.x = Std.parseFloat(xml.x.get("camx"));
if (xml.x.exists("camy")) cameraOffset.y = Std.parseFloat(xml.x.get("camy"));
if (xml.x.exists("holdTime")) holdTime = Std.parseFloat(xml.x.get("holdTime")).getDefaultFloat(4);
@@ -408,7 +411,13 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
loadSprite(Paths.image('characters/$sprite'));
for(node in xml.elements) {
switch(node.name) {
- case "anim":
+ case "anim":
+ if (defaultAimFPS != 24){
+ if (!node.x.exists("fps")) {
+ node.x.set('fps', Std.string(defaultAimFPS));
+ }
+ }
+
XMLUtil.addXMLAnimation(this, node);
case "use-extension" | "extension" | "ext":
if (XMLImportedScriptInfo.shouldLoadBefore(node)) continue;
@@ -439,7 +448,8 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
public static var characterProperties:Array = [
"x", "y", "sprite", "scale", "antialiasing",
"flipX", "camx", "camy", "isPlayer", "icon",
- "color", "gameOverChar", "holdTime", "applyStageMatrix"
+ "color", "gameOverChar", "holdTime", "applyStageMatrix",
+ "defFps"
];
public static var characterAnimProperties:Array = [
"name", "anim", "label", "x", "y", "fps", "loop", "indices"
@@ -463,6 +473,7 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
if (gameOverCharacter != Character.FALLBACK_DEAD_CHARACTER) xml.set("gameOverChar", gameOverCharacter);
if (iconColor != null) xml.set("color", iconColor.toWebString());
+ if (defaultAimFPS != 24) xml.set("defFps", Std.string(defaultAimFPS));
if (sprite != curCharacter) xml.set("sprite", sprite);
if (scale.x != 1) xml.set("scale", Std.string(FlxMath.roundDecimal(scale.x, 4)));
@@ -485,7 +496,7 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
animXml.set("name", anim.name);
animXml.set("anim", anim.anim);
if (anim.loop) animXml.set("loop", Std.string(anim.loop));
- if (FlxMath.roundDecimal(anim.fps, 2) != 24) animXml.set("fps", Std.string(FlxMath.roundDecimal(anim.fps, 2)));
+ if (FlxMath.roundDecimal(anim.fps, 2) != defaultAimFPS) animXml.set("fps", Std.string(FlxMath.roundDecimal(anim.fps, 2)));
var offset:FlxPoint = getAnimOffset(anim.name);
if (FlxMath.roundDecimal(offset.x, 2) != 0) animXml.set("x", Std.string(FlxMath.roundDecimal(offset.x, 2)));