Character editor work in progress i think pls test

This commit is contained in:
lunarcleint
2023-07-09 15:32:39 -07:00
parent 3daab89d13
commit 48a1f5bf77
21 changed files with 843 additions and 1748 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 193 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 246 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 287 B

+2 -2
View File
@@ -135,7 +135,7 @@ class XMLUtil {
if (anim.has.name) animData.name = anim.att.name;
if (anim.has.type) animData.animType = XMLAnimType.fromString(anim.att.type, animData.animType);
if (anim.has.anim) animData.anim = anim.att.anim;
if (anim.has.fps) animData.fps = Std.parseInt(anim.att.fps);
if (anim.has.fps) animData.fps = Std.parseFloat(anim.att.fps);
if (anim.has.x) animData.x = Std.parseFloat(anim.att.x);
if (anim.has.y) animData.y = Std.parseFloat(anim.att.y);
if (anim.has.loop) animData.loop = anim.att.loop == "true";
@@ -208,7 +208,7 @@ class XMLUtil {
typedef AnimData = {
var name:String;
var anim:String;
var fps:Int;
var fps:Float;
var loop:Bool;
var x:Float;
var y:Float;
@@ -1,113 +0,0 @@
package funkin.editors.character;
import flixel.math.FlxPoint;
import funkin.backend.utils.XMLUtil.AnimData;
import funkin.game.Character;
class AddAnimScreen extends UISubstateWindow
{
public var char:Character;
public var ghostChar:Character;
public var addButton:UIButton;
public var cancelButton:UIButton;
public var editor:CharacterEditor;
public var animName:UITextBox;
public var animPrefix:UITextBox;
public var fps:UITextBox;
public var loop:UICheckbox;
public var indices:UITextBox;
public function new(char:Character, ghostChar, editor:CharacterEditor)
{
super();
this.char = char;
this.ghostChar = ghostChar;
}
public override function create()
{
winTitle = "Add new Animation";
winWidth = 960;
super.create();
// Animation Properties
var y_ofs = 60;
var y_text_ofs = 20;
animName = new UITextBox(windowSpr.x + 50, windowSpr.y + y_ofs, "");
add(animName);
add(new UIText(animName.x, animName.y - y_text_ofs, -1, "Animation Name: "));
animPrefix = new UITextBox(animName.x, animName.y + y_ofs, "");
add(animPrefix);
add(new UIText(animPrefix.x, animPrefix.y - y_text_ofs, -1, "Animation on XML/Atlas file: "));
fps = new UITextBox(animPrefix.x, animPrefix.y + y_ofs, "24");
add(fps);
add(new UIText(fps.x, fps.y - y_text_ofs, -1, "Framerate: "));
loop = new UICheckbox(fps.x, fps.y + y_ofs, "Looped", false);
add(loop);
indices = new UITextBox(loop.x, loop.y + y_ofs, "");
add(indices);
add(new UIText(indices.x, indices.y - y_text_ofs, -1, "Indices (0, 1, 2, 3): "));
// Add / Cancel Buttons
addButton = new UIButton(windowSpr.x + 1, windowSpr.y + 31, "", function()
{
newAnimation(animName.label.text, animPrefix.label.text, Std.parseInt(fps.label.text), loop.checked, indices.label.text);
close();
});
addButton.bWidth = addButton.bHeight = 30;
add(addButton);
var addButtonIcon = new FlxSprite(addButton.x, addButton.y, Paths.image('editors/charter/add-button'));
addButtonIcon.x += (30 - addButtonIcon.width) / 2;
addButtonIcon.y += (30 - addButtonIcon.height) / 2;
add(addButtonIcon);
cancelButton = new UIButton(addButton.x - 10, addButton.y, "Cancel", function()
{
close();
});
cancelButton.x -= cancelButton.bWidth;
add(cancelButton);
}
public function newAnimation(name:String, anim:String, ?fps:Int = 24, ?looped:Bool = false, ?indices:String)
{
var animType:XMLAnimType = NONE;
var animData:AnimData = {
name: name,
anim: anim,
fps: fps,
loop: looped,
animType: animType,
indices: [],
x: 0,
y: 0
};
if (indices != "" && indices != null)
{
var indicesSplit = indices.split(",");
for (indice in indicesSplit)
{
var i = Std.parseInt(indice.trim());
if (i != null)
animData.indices.push(i);
}
}
XMLUtil.addAnimToSprite(char, animData);
XMLUtil.addAnimToSprite(ghostChar, animData);
char.characterData.animations.push(animData);
ghostChar.characterData.animations.push(animData);
}
}
@@ -0,0 +1,57 @@
package funkin.editors.character;
import flixel.math.FlxPoint;
import flixel.animation.FlxAnimation;
class CharacterAnimScreen extends UISubstateWindow {
public var animName:Null<String>;
public var offset:FlxPoint;
public var flxanim:FlxAnimation;
public var saveButton:UIButton;
public var closeButton:UIButton;
private var onSave:(animName:Null<String>, offset:FlxPoint, flxanim:FlxAnimation) -> Void = null;
public function new(animName:Null<String>, ?offset:FlxPoint, ?flxanim:FlxAnimation, ?onSave:(animName:Null<String>, offset:FlxPoint, flxanim:FlxAnimation) -> Void) {
this.animName = animName;
this.offset = offset;
this.flxanim = flxanim;
this.onSave = onSave;
super();
}
public override function create() {
var creatingAnim:Bool = animName == null;
if (creatingAnim) {
animName = "";
offset = FlxPoint.get(0,0);
flxanim = new FlxAnimation(CharacterEditor.instance.character.animation, animName, []);
}
winTitle = creatingAnim ? 'Creating Animation' : 'Animation $animName properties';
winWidth = 690; winHeight = 334;
super.create();
function addLabelOn(ui:UISprite, text:String)
add(new UIText(ui.x, ui.y - 24, 0, text));
var title:UIText;
add(title = new UIText(windowSpr.x + 20, windowSpr.y + 30 + 16, 0, creatingAnim ? "Create New Animation" : "Edit Animation Properties", 28));
saveButton = new UIButton(windowSpr.x + windowSpr.bWidth - 20 - 125, windowSpr.y + windowSpr.bHeight - 16 - 32, "Save & Close", function() {
// saveStrumline();
close();
}, 125);
add(saveButton);
closeButton = new UIButton(saveButton.x - 20, saveButton.y, creatingAnim ? "Cancel" : "Close", function() {
if (creatingAnim) onSave(null, null, null);
close();
}, 125);
add(closeButton);
closeButton.x -= closeButton.bWidth;
}
}
@@ -0,0 +1,159 @@
package funkin.editors.character;
import flixel.group.FlxGroup;
import flixel.util.FlxColor;
import flixel.math.FlxPoint;
import funkin.game.Character;
class CharacterAnimsWindow extends UIWindow {
public var character:Character;
public var buttons:FlxGroup = new FlxGroup();
public var animButtons:Map<String, CharacterAnimButtons> = [];
public var addButton:UIButton;
public var addIcon:FlxSprite;
public function new(character:Character) {
super(800-23,140+23+46,450+23,511-23, "Character Animations");
this.character = character;
createButtons();
buttons.cameras = [CharacterEditor.instance.animsCamera];
members.push(buttons);
}
public function createButtons() {
for (i=>anim in character.animation.getNameList())
createNewButton(anim, character.getAnimOffset(anim), false, -1, false);
addButton = new UIButton(25, 16+((32+16)*(character.animation.getNameList().length)), "", null, 429);
addButton.cameras = [CharacterEditor.instance.animsCamera];
addButton.color = FlxColor.GREEN;
addIcon = new FlxSprite(addButton.x + (440/2) - 8, addButton.y + (32/2) - 8).loadGraphic(Paths.image('editors/charter/add-button'));
addIcon.cameras = [CharacterEditor.instance.animsCamera];
addIcon.antialiasing = false;
addButton.members.push(addIcon);
members.push(addButton);
updateButtonsPos();
}
public function createNewButton(anim:String, offset:FlxPoint, ghost:Bool = false, id:Int = -1, updatePos:Bool = true) {
var button = new CharacterAnimButtons(0,0, anim, offset);
if (id == -1) buttons.add(button);
else buttons.members.insert(id, button);
animButtons.set(anim,button);
if (updatePos)
updateButtonsPos();
}
public function removeButton(anim:String) {
var button:CharacterAnimButtons = animButtons.get(anim);
buttons.members.remove(button);
button.destroy();
updateButtonsPos();
}
public function updateButtonsPos() {
for (i => button in buttons.members)
cast(button, CharacterAnimButtons).updatePos(23,16+((32+16)*i));
addButton.y = 16+((32+16)*(character.animation.getNameList().length));
addIcon.x = addButton.x + (440/2) - 8; addIcon.y = addButton.y + (32/2) - 8;
}
public inline function changeCurAnim(anim:String) {
for (_anim => button in animButtons)
button.alpha = _anim == anim ? 1 : 0.25;
}
var scrollY:Float = 0;
public override function update(elapsed:Float) {
super.update(elapsed);
__rect.x = x; __rect.y = y;
__rect.width = bWidth; __rect.height = bHeight;
if(UIState.state.isOverlapping(this, __rect)) {
var nextscrollY = scrollY - FlxG.mouse.wheel * 12;
if (nextscrollY >= 0 && nextscrollY + CharacterEditor.instance.animsCamera.height <= (addButton.y +32 +26))
scrollY = nextscrollY;
hovered = true;
} else
hovered = false;
CharacterEditor.instance.animsCamera.scroll.y = FlxMath.lerp(CharacterEditor.instance.animsCamera.scroll.y, scrollY, 1/3);
//addButtonIndicator.y = CharacterEditor.instance.animsCamera.height - addButtonIndicator.width + CharacterEditor.instance.animsCamera.scroll.y;
}
}
class CharacterAnimButtons extends UIButton {
public var anim:String = "";
public var editButton:UIButton;
public var editIcon:FlxSprite;
public var ghostButton:UIButton;
public var ghostIcon:FlxSprite;
public var deleteButton:UIButton;
public var deleteIcon:FlxSprite;
public function new(x:Float,y:Float,anim:String, offset:FlxPoint) {
this.anim = anim;
super(x,y, '$anim (${offset.x}, ${offset.y})', function () {
CharacterEditor.instance.playAnimation(this.anim);
}, 282);
ghostButton = new UIButton(x+282+17, y, "", null, 32);
members.push(ghostButton);
ghostIcon = new FlxSprite(ghostButton.x + 8, ghostButton.y + 8).loadGraphic(Paths.image('editors/character/ghost-button'), true, 16, 16);
ghostIcon.animation.add("alive", [0]);
ghostIcon.animation.add("dead", [1]);
ghostIcon.animation.play("dead"); ghostIcon.alpha = 0.5;
ghostIcon.antialiasing = false;
ghostIcon.updateHitbox();
members.push(ghostIcon);
editButton = new UIButton(ghostButton.x+32+17, y, "", null, 32);
members.push(editButton);
editIcon = new FlxSprite(editButton.x + 8, editButton.y + 8).loadGraphic(Paths.image('editors/character/edit-button'));
editIcon.antialiasing = false;
members.push(editIcon);
deleteButton = new UIButton(editButton.x+32+17, y, "", function () {
CharacterEditor.instance.deleteAnim(this.anim);
}, 32);
deleteButton.color = FlxColor.RED;
members.push(deleteButton);
deleteIcon = new FlxSprite(deleteButton.x + (15/2), deleteButton.y + 8).loadGraphic(Paths.image('editors/character/delete-button'));
deleteIcon.antialiasing = false;
members.push(deleteIcon);
}
public function updateInfo(anim:String, offset:FlxPoint, ghost:Bool) {
this.anim = anim;
field.text = '$anim (${offset.x}, ${offset.y})';
ghostIcon.animation.play(ghost ? "alive" : "dead");
ghostIcon.alpha = ghost ? 1 : 0.5;
}
public inline function updatePos(x:Float, y:Float) {
// buttons
this.x = x; this.y = ghostButton.y = editButton.y = deleteButton.y = y;
deleteButton.x = (editButton.x = (ghostButton.x = (x+282+17))+32+17)+32+17;
// icons
ghostIcon.x = ghostButton.x + 8; ghostIcon.y = ghostButton.y + 8;
editIcon.x = editButton.x + 8; editIcon.y = editButton.y + 8;
deleteIcon.x = deleteButton.x + (15/2); deleteIcon.y = deleteButton.y + 8;
}
}
@@ -1,169 +0,0 @@
package funkin.editors.character;
import haxe.xml.Printer;
import sys.io.File;
import sys.FileSystem;
import flixel.group.FlxGroup;
import haxe.xml.Access;
import haxe.Exception;
import flixel.util.FlxColor;
import funkin.game.Character.CharacterData;
class CharacterConfig extends FlxTypedGroup<FlxBasic>
{
public static var DEFAULT_CHARACTER = "bf";
public static function loadCharacterData(character:String):CharacterData
{
var xml:Access;
var charData:CharacterData = {
offsetY: 0,
offsetX: 0,
camOffsetY: 0,
camOffsetX: 0,
quickDancer: false,
icon: character,
iconColor: FlxColor.fromRGB(94, 94, 94),
scale: 1,
isPlayer: false,
isGF: false,
holdTime: 4,
flipX: false,
antialiasing: true,
sprite: "bf",
name: character,
animations: []
}
var xmlPath = Paths.xml('characters/$character');
var plainXML = Assets.getText(xmlPath);
try
{
var charXML = Xml.parse(plainXML).firstElement();
if (charXML == null)
throw new Exception("Missing \"character\" node in XML.");
xml = new Access(charXML);
}
charData = xmlToData(charData, xml);
return charData;
}
public static function xmlToData(charData:CharacterData, xml:Access):CharacterData
{
if (xml.has.isPlayer)
charData.isPlayer = (xml.att.isPlayer == "true");
if (xml.has.isGF)
charData.isGF = (xml.att.isGF == "true");
if (xml.has.x)
charData.offsetX = Std.parseFloat(xml.att.x);
if (xml.has.y)
charData.offsetY = Std.parseFloat(xml.att.y);
if (xml.has.gameOverChar)
charData.gameOverChar = xml.att.gameOverChar;
if (xml.has.camx)
charData.camOffsetX = Std.parseFloat(xml.att.camx);
if (xml.has.camy)
charData.camOffsetY = Std.parseFloat(xml.att.camy);
if (xml.has.holdTime)
charData.holdTime = CoolUtil.getDefault(Std.parseFloat(xml.att.holdTime), 4);
if (xml.has.flipX)
charData.flipX = (xml.att.flipX == "true");
if (xml.has.icon)
charData.icon = xml.att.icon;
if (xml.has.iconColor)
charData.iconColor = stringToRGB(xml.att.iconColor);
if (xml.has.scale)
{
var scale = Std.parseFloat(xml.att.scale).getDefault(1);
charData.scale = scale;
}
if (xml.has.antialiasing)
charData.antialiasing = (xml.att.antialiasing == "true");
if (xml.has.sprite)
charData.sprite = xml.att.sprite;
else
charData.sprite = charData.name;
for (anim in xml.nodes.anim)
{
var animType:XMLAnimType = NONE;
var animloop = false;
if (anim.has.loop)
animloop = (anim.att.loop == "true");
charData.animations.push(XMLUtil.extractAnimFromXML(anim, animType, animloop));
}
return charData;
}
public static function dataToXml(charData:CharacterData):Access
{
var root = Xml.createElement("character");
root.set("x", Std.string(Math.floor(charData.offsetX)));
root.set("y", Std.string(Math.floor(charData.offsetY)));
root.set("gameOverChar", Std.string(charData.gameOverChar));
root.set("camx", Std.string(Math.floor(charData.camOffsetX)));
root.set("camy", Std.string(Math.floor(charData.camOffsetY)));
root.set("holdTime", Std.string(charData.holdTime));
root.set("flipX", Std.string(charData.flipX));
root.set("icon", charData.icon);
root.set("iconColor", Std.string(charData.iconColor));
root.set("scale", Std.string(charData.scale));
root.set("antialiasing", Std.string(charData.antialiasing));
root.set("sprite", Std.string(charData.sprite));
root.set("isPlayer", Std.string(charData.isPlayer));
root.set("isGF", Std.string(charData.isGF));
for (anim in charData.animations)
{
var child:Xml = Xml.createElement('anim');
child.set("name", Std.string(anim.name));
child.set("anim", Std.string(anim.anim));
child.set("loop", Std.string(anim.loop));
child.set("fps", Std.string(anim.fps));
child.set("x", Std.string(anim.x));
child.set("y", Std.string(anim.y));
child.set("indices", Std.string(anim.indices));
root.addChild(child);
}
var xml = new Access(Xml.parse('${root.toString()}'));
return xml;
}
public static function stringToRGB(rgbColor:String):FlxColor
{
var colorSplit = rgbColor.split(",");
if (colorSplit[0] != null && colorSplit[1] != null && colorSplit[2] != null)
return FlxColor.fromRGB(Std.parseInt(colorSplit[0]), Std.parseInt(colorSplit[1]), Std.parseInt(colorSplit[2]));
else
return FlxColor.fromRGB(90,90,90);
}
public static function rgbToString(color:FlxColor):String
return '${color.red}, ${color.green}, ${color.blue}';
/**
* @return Saves the character to the specific song folder path.
*/
public static function save(character:String, xml:String):String
{
var charPath = '';
var data = '$xml';
#if sys
var charPath = '${character}';
File.saveContent(charPath, data);
// idk how null reacts to it so better be sure
#end
return xml;
}
public static function generateCharXML(xml:Xml):String
{
return '<!DOCTYPE codename-engine-character>\n${Printer.print(xml, true)}';
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,125 @@
package funkin.editors.character;
import funkin.game.Character;
class CharacterInfoScreen extends UISubstateWindow {
public var character:Character;
public var spriteTextBox:UITextBox;
public var iconTextBox:UITextBox;
public var iconSprite:FlxSprite;
public var gameOverCharTextBox:UITextBox;
public var positionXStepper:UINumericStepper;
public var positionYStepper:UINumericStepper;
public var cameraXStepper:UINumericStepper;
public var cameraYStepper:UINumericStepper;
public var scaleStepper:UINumericStepper;
public var singTimeStepper:UINumericStepper;
public var isPlayerCheckbox:UICheckbox;
public var isGFCheckbox:UICheckbox;
public var flipXCheckbox:UICheckbox;
public var antialiasingCheckbox:UICheckbox;
public var saveButton:UIButton;
public var closeButton:UIButton;
public function new(character:Character) {
super();
this.character = character;
}
public override function create() {
winTitle = "Editing Character";
winWidth = 960;
super.create();
function addLabelOn(ui:UISprite, text:String)
add(new UIText(ui.x, ui.y - 24, 0, text));
var title:UIText;
add(title = new UIText(windowSpr.x + 20, windowSpr.y + 30 + 16, 0, "Sprite Data", 28));
spriteTextBox = new UITextBox(title.x, title.y + title.height + 38, character.sprite, 200);
add(spriteTextBox);
addLabelOn(spriteTextBox, "Sprite");
iconTextBox = new UITextBox(spriteTextBox.x + 200 + 26, spriteTextBox.y, character.getIcon(), 150);
iconTextBox.onChange = (newIcon:String) -> {updateIcon(newIcon);}
add(iconTextBox);
addLabelOn(iconTextBox, "Icon");
updateIcon(character.getIcon());
gameOverCharTextBox = new UITextBox(iconTextBox.x + 150 + (75 + 16), iconTextBox.y, character.gameOverCharacter, 200);
add(gameOverCharTextBox);
addLabelOn(gameOverCharTextBox, "Game Over Character");
add(title = new UIText(spriteTextBox.x, spriteTextBox.y + 10 + 46, 0, "Character Data", 28));
positionXStepper = new UINumericStepper(title.x, title.y + title.height + 36, character.globalOffset.x, 0.001, 2, null, null, 84);
add(positionXStepper);
addLabelOn(positionXStepper, "Position (X,Y)");
add(new UIText(positionXStepper.x + 84 - 32 + 0, positionXStepper.y + 9, 0, ",", 22));
positionYStepper = new UINumericStepper(positionXStepper.x + 84 - 32 + 26, positionXStepper.y, character.globalOffset.y, 0.001, 2, null, null, 84);
add(positionYStepper);
cameraXStepper = new UINumericStepper(positionYStepper.x + 36 + 84 - 32, positionYStepper.y, character.cameraOffset.x, 0.001, 2, null, null, 84);
add(cameraXStepper);
addLabelOn(cameraXStepper, "Camera Position (X,Y)");
add(new UIText(cameraXStepper.x + 84 - 32 + 0, cameraXStepper.y + 9, 0, ",", 22));
cameraYStepper = new UINumericStepper(cameraXStepper.x + 84 - 32 + 26, cameraXStepper.y, character.cameraOffset.y, 0.001, 2, null, null, 84);
add(cameraYStepper);
scaleStepper = new UINumericStepper(cameraYStepper.x + 84 - 32 + 90, cameraYStepper.y, character.scale.x, 0.001, 2, null, null, 74);
add(scaleStepper);
addLabelOn(scaleStepper, "Scale");
singTimeStepper = new UINumericStepper(scaleStepper.x + 74 - 32 + 36, scaleStepper.y, character.holdTime, 0.001, 2, null, null, 74);
add(singTimeStepper);
addLabelOn(singTimeStepper, "Sing Time");
isPlayerCheckbox = new UICheckbox(positionXStepper.x, positionXStepper.y + 10 + 32 + 26, "isPlayer", character.playerOffsets);
add(isPlayerCheckbox);
addLabelOn(isPlayerCheckbox, "Is Player");
for (checkbox in [isPlayerCheckbox])
{checkbox.y += 6; checkbox.x += 4;}
saveButton = new UIButton(windowSpr.x + windowSpr.bWidth - 20, windowSpr.y + windowSpr.bHeight- 20, "Save & Close", function() {
close();
}, 125);
saveButton.x -= saveButton.bWidth;
saveButton.y -= saveButton.bHeight;
closeButton = new UIButton(saveButton.x - 20, saveButton.y, "Close", function() {
close();
}, 125);
closeButton.x -= closeButton.bWidth;
add(closeButton);
add(saveButton);
}
function updateIcon(icon:String) {
if (iconSprite == null) add(iconSprite = new FlxSprite());
if (iconSprite.animation.exists(icon)) return;
@:privateAccess iconSprite.animation.clearAnimations();
var path:String = Paths.image('icons/$icon');
if (!Assets.exists(path)) path = Paths.image('icons/face');
iconSprite.loadGraphic(path, true, 150, 150);
iconSprite.animation.add(icon, [0], 0, false);
iconSprite.antialiasing = true;
iconSprite.animation.play(icon);
iconSprite.scale.set(0.5, 0.5);
iconSprite.updateHitbox();
iconSprite.setPosition(iconTextBox.x + 150 + 8, (iconTextBox.y + 16) - (iconSprite.height/2));
}
}
@@ -1,123 +0,0 @@
package funkin.editors.character;
import flixel.util.FlxColor;
import flixel.math.FlxPoint;
import funkin.backend.utils.XMLUtil.AnimData;
import funkin.game.Character;
class CharacterProperties extends UISubstateWindow
{
public var char:Character;
public var ghostChar:Character;
public var charData:CharacterData;
public var applyButton:UIButton;
public var cancelButton:UIButton;
public var editor:CharacterEditor;
public var curAnimData:AnimData;
public var spriteFile:UITextBox;
public var gameOverChar:UITextBox;
public var healthIcon:UITextBox;
public var iconColor:UITextBox;
public var flipX:UICheckbox;
public var isPlayer:UICheckbox;
public var isGF:UICheckbox;
public var antialiasing:UICheckbox;
public var scale:UITextBox;
public var holdTime:UITextBox;
public function new(char:Character, ghostChar, editor:CharacterEditor)
{
super();
this.char = char;
this.ghostChar = ghostChar;
this.charData = char.characterData;
this.editor = editor;
}
public override function create()
{
winTitle = 'Character Properties';
winWidth = 960;
super.create();
// Offsets
var y_ofs = 60;
var x_ofs = 60;
var y_text_ofs = 20;
// Character Properties
spriteFile = new UITextBox(windowSpr.x + 50, windowSpr.y + 10 + y_ofs, charData.sprite);
add(spriteFile);
add(new UIText(spriteFile.x, spriteFile.y - y_text_ofs, -1, "Image/Atlas file: "));
gameOverChar = new UITextBox(spriteFile.x + spriteFile.bWidth + x_ofs, spriteFile.y, charData.gameOverChar);
add(gameOverChar);
add(new UIText(gameOverChar.x, gameOverChar.y - y_text_ofs, -1, "Game Over character: "));
healthIcon = new UITextBox(spriteFile.x, spriteFile.y + y_ofs, charData.icon);
add(healthIcon);
add(new UIText(healthIcon.x, healthIcon.y - y_text_ofs, -1, "Health Icon: "));
iconColor = new UITextBox(healthIcon.x + healthIcon.bWidth + x_ofs, healthIcon.y, CharacterConfig.rgbToString(charData.iconColor));
add(iconColor);
add(new UIText(iconColor.x, iconColor.y - y_text_ofs, -1, "Icon color (RGB): "));
flipX = new UICheckbox(healthIcon.x, healthIcon.y + y_ofs, "Flip X", charData.flipX);
add(flipX);
antialiasing = new UICheckbox(flipX.x + flipX.field.width + x_ofs, flipX.y, "Antialiasing", charData.antialiasing);
add(antialiasing);
isPlayer = new UICheckbox(flipX.x, flipX.y + y_ofs, "Playable Character", charData.isPlayer);
add(isPlayer);
isGF = new UICheckbox(isPlayer.x + isPlayer.field.width + x_ofs, isPlayer.y, "GF Character", charData.isGF);
add(isGF);
scale = new UITextBox(isPlayer.x, isPlayer.y + y_ofs, Std.string(charData.scale));
add(scale);
add(new UIText(scale.x, scale.y - y_text_ofs, -1, "Scale: "));
holdTime = new UITextBox(scale.x + scale.bWidth + y_ofs, scale.y, Std.string(charData.holdTime));
add(holdTime);
add(new UIText(holdTime.x, holdTime.y - y_text_ofs, -1, "Hold time: "));
// Apply / Cancel buttons
applyButton = new UIButton(windowSpr.x - x_ofs - 70, windowSpr.y, "Apply", function()
{
updateProperties(spriteFile.label.text, healthIcon.label.text, CharacterConfig.stringToRGB(healthIcon.label.text), flipX.checked,
antialiasing.checked, isPlayer.checked, isGF.checked, Std.parseFloat(scale.label.text), Std.parseFloat(holdTime.label.text), gameOverChar.label.text);
});
add(applyButton);
cancelButton = new UIButton(applyButton.x, applyButton.y + y_ofs, "Cancel", function()
{
close();
});
add(cancelButton);
}
public function updateProperties(sprite:String, healthIcon:String, iconColor:FlxColor, flipX:Bool, antialiasing:Bool, isPlayer:Bool, isGF:Bool,
scale:Float, holdTime:Float, gameOverChar:String)
{
// Character Data
charData.sprite = sprite;
charData.icon = healthIcon;
charData.iconColor = iconColor;
charData.flipX = flipX;
charData.antialiasing = antialiasing;
charData.isPlayer = isPlayer;
charData.isGF = isGF;
charData.scale = scale;
charData.holdTime = holdTime;
charData.gameOverChar = gameOverChar;
editor.reloadChar(!char.isPlayer, charData);
editor.reloadStage();
editor.loadCross();
}
}
@@ -0,0 +1,5 @@
package funkin.editors.character;
class CharacterPropertiesWindow extends UIWindow {
}
@@ -3,7 +3,6 @@ package funkin.editors.character;
import flixel.util.FlxColor;
import funkin.game.Character;
import funkin.backend.chart.Chart;
import funkin.game.Character.CharacterData;
import funkin.options.type.TextOption;
import funkin.options.type.IconOption;
import funkin.options.OptionsScreen;
@@ -11,77 +10,18 @@ import sys.FileSystem;
class CharacterSelection extends EditorTreeMenu
{
public var characterList:CharactersList;
public override function create()
{
bgType = "charter";
super.create();
characterList = CharactersList.get();
main = new OptionsScreen("Character Editor", "Select a character to edit", [
for (s in characterList.characters)
new IconOption(s.name, "Press ACCEPT to edit this character.", s.icon, function()
{
FlxG.switchState(new CharacterEditor(s.name));
for (char in Character.getList())
new IconOption(char, "Press ACCEPT to edit this character.", Character.getIconFromCharName(char),
function() {
FlxG.switchState(new CharacterEditor(char));
})
]);
}
public override function update(elapsed:Float)
{
super.update(elapsed);
}
public override function onMenuChange() {
super.onMenuChange();
if (optionsTree.members.length > 1) { // selected a song
// small flashbang
var color:FlxColor = characterList.characters[main.curSelected].iconColor.getDefault(0xFFFFFFFF);
bg.colorTransform.redOffset = 0.25 * color.red;
bg.colorTransform.greenOffset = 0.25 * color.green;
bg.colorTransform.blueOffset = 0.25 * color.blue;
bg.colorTransform.redMultiplier = FlxMath.lerp(1, color.redFloat, 0.25);
bg.colorTransform.greenMultiplier = FlxMath.lerp(1, color.greenFloat, 0.25);
bg.colorTransform.blueMultiplier = FlxMath.lerp(1, color.blueFloat, 0.25);
}
}
}
class CharactersList
{
public var characters:Array<CharacterData> = [];
public function new()
{
}
public function getCharactersFromSource(source:funkin.backend.assets.AssetsLibraryList.AssetSource)
{
var charactersFound:Array<String> = [];
charactersFound = Paths.getFolderContent('data/characters', false, source);
if (charactersFound.length > 0)
{
for (s in charactersFound)
if (s.endsWith(".xml"))
characters.push(CharacterConfig.loadCharacterData(s.replace(".xml", "")));
return true;
}
return false;
}
public static function get()
{
var characterList = new CharactersList();
if (!characterList.getCharactersFromSource(MODS))
characterList.getCharactersFromSource(SOURCE);
return characterList;
}
}
}
@@ -1,73 +0,0 @@
package funkin.editors.character;
import flixel.math.FlxRandom;
import cpp.Random;
import flixel.math.FlxPoint;
import funkin.backend.utils.XMLUtil.AnimData;
import funkin.game.Character;
class DeleteAnimScreen extends UISubstateWindow
{
public var char:Character;
public var ghostChar:Character;
public var editor:CharacterEditor;
public var animButtons:FlxTypedSpriteGroup<UIButton> = new FlxTypedSpriteGroup<UIButton>(365, -10);
public var closeButton:UIButton;
public function new(char:Character, ghostChar:Character, editor:CharacterEditor)
{
super();
this.char = char;
this.ghostChar = ghostChar;
this.editor = editor;
}
public override function create()
{
winTitle = "Delete Animation";
winWidth = 480;
winHeight = 700;
super.create();
add(animButtons);
var y_adder = 60;
for (anim in char.characterData.animations)
{
var newbutton = new UIButton(animButtons.x - 550, animButtons.y + y_adder, anim.name, function()
{
removeAnimation(anim);
close();
});
animButtons.add(newbutton);
y_adder += 45;
}
closeButton = new UIButton(windowSpr.x - 120, windowSpr.y + 20, "Close", function() {
close();
});
add(closeButton);
}
public function removeAnimation(anim:AnimData)
{
var refreshAnim:Bool = false;
if (char.animation.curAnim != null && anim.name == char.animation.curAnim.name)
refreshAnim = true;
if (char.animation.getByName(anim.name) != null)
char.animation.remove(anim.name);
if (char.animOffsets.exists(anim.name))
char.animOffsets.remove(anim.name);
char.characterData.animations.remove(anim);
if (refreshAnim && char.characterData.animations.length > 0)
char.playAnim(char.characterData.animations[0].name, true);
editor.reloadGhost();
}
}
@@ -1,71 +0,0 @@
package funkin.editors.character;
class HelpControls extends UISubstateWindow
{
var helpTextTitles:Array<String> = [
"Camera:",
"Offset:",
"Animation:"
];
var helpTexts:Array<String> = [
"\n\n- WASD: Move camera\n\n- I & O: Zoom In & Out.",
"\n\n- Arrows keys: Move the offset of the current\nanimation.\n\n- Left Click: Move the offset of the character",
"\n\n- K & L: Switch to previous & next animation\n\n- SPACE: Play current animation.\n\n"
];
var curPage:Int = 0;
public function new()
{
super();
}
public override function create()
{
winTitle = 'Help: Controls';
winWidth = 960;
super.create();
var helpTextTitle = new UIText(windowSpr.x + 25, windowSpr.y + 45, -1, helpTextTitles[0], 36);
add(helpTextTitle);
var helpText = new UIText(windowSpr.x + 25, windowSpr.y + 50, -1, helpTexts[0], 24);
add(helpText);
var pageText = new UIText(855, windowSpr.y + 460, -1, '${curPage+1}/${helpTexts.length}', 24);
add(pageText);
var rightButton = new UIButton(885, windowSpr.y + 500, ">", function()
{
if(curPage == helpTexts.length-1)
curPage = 0;
else
curPage++;
pageText.text = '${curPage+1}/${helpTexts.length}';
helpText.text = helpTexts[curPage];
helpTextTitle.text = helpTextTitles[curPage];
}, 32);
var leftButton = new UIButton(845, windowSpr.y + 500, "<", function()
{
if(curPage == 0)
curPage = helpTexts.length-1;
else
curPage--;
pageText.text = '${curPage+1}/${helpTexts.length}';
helpText.text = helpTexts[curPage];
helpTextTitle.text = helpTextTitles[curPage];
}, 32);
var closeButton = new UIButton(helpText.x, windowSpr.y + 500, "Close", function()
{
close();
});
add(closeButton);
add(leftButton);
add(rightButton);
}
}
@@ -1,172 +0,0 @@
package funkin.editors.character;
import flixel.util.FlxColor;
import flixel.math.FlxPoint;
import funkin.backend.utils.XMLUtil.AnimData;
import funkin.game.Character;
import funkin.editors.character.DeleteAnimScreen;
class NewCharacter extends UISubstateWindow
{
public var char:Character;
public var ghostChar:Character;
public var charData:CharacterData;
public var applyButton:UIButton;
public var nextButton:UIButton;
public var cancelButton:UIButton;
public var editor:CharacterEditor;
public var curAnimData:AnimData;
public var spriteFile:UITextBox;
public var gameOverChar:UITextBox;
public var healthIcon:UITextBox;
public var iconColor:UITextBox;
public var flipX:UICheckbox;
public var isPlayer:UICheckbox;
public var isGF:UICheckbox;
public var antialiasing:UICheckbox;
public var scale:UITextBox;
public var holdTime:UITextBox;
public function new(char:Character, ghostChar, editor:CharacterEditor)
{
super();
this.char = char;
this.ghostChar = ghostChar;
this.charData = char.characterData;
this.editor = editor;
//TO DO: Generate and Save
//----
//Delete old Animaitons
}
public override function create()
{
winTitle = 'Create New Character';
winWidth = 960;
super.create();
// Offsets
var y_ofs = 60;
var x_ofs = 60;
var y_text_ofs = 20;
// Character Properties
spriteFile = new UITextBox(windowSpr.x + 50, windowSpr.y + 10 + y_ofs, charData.sprite);
add(spriteFile);
add(new UIText(spriteFile.x, spriteFile.y - y_text_ofs, -1, "Image/Atlas file: "));
gameOverChar = new UITextBox(spriteFile.x + spriteFile.bWidth + x_ofs, spriteFile.y, charData.gameOverChar);
add(gameOverChar);
add(new UIText(gameOverChar.x, gameOverChar.y - y_text_ofs, -1, "Game Over character: "));
healthIcon = new UITextBox(spriteFile.x, spriteFile.y + y_ofs, charData.icon);
add(healthIcon);
add(new UIText(healthIcon.x, healthIcon.y - y_text_ofs, -1, "Health Icon: "));
iconColor = new UITextBox(healthIcon.x + healthIcon.bWidth + x_ofs, healthIcon.y, CharacterConfig.rgbToString(charData.iconColor));
add(iconColor);
add(new UIText(iconColor.x, iconColor.y - y_text_ofs, -1, "Icon color (RGB): "));
flipX = new UICheckbox(healthIcon.x, healthIcon.y + y_ofs, "Flip X", charData.flipX);
add(flipX);
antialiasing = new UICheckbox(flipX.x + flipX.field.width + x_ofs, flipX.y, "Antialiasing", charData.antialiasing);
add(antialiasing);
isPlayer = new UICheckbox(flipX.x, flipX.y + y_ofs, "Playable Character", charData.isPlayer);
add(isPlayer);
isGF = new UICheckbox(isPlayer.x + isPlayer.field.width + x_ofs, isPlayer.y, "GF Character", charData.isGF);
add(isGF);
scale = new UITextBox(isPlayer.x, isPlayer.y + y_ofs, Std.string(charData.scale));
add(scale);
add(new UIText(scale.x, scale.y - y_text_ofs, -1, "Scale: "));
holdTime = new UITextBox(scale.x + scale.bWidth + y_ofs, scale.y, Std.string(charData.holdTime));
add(holdTime);
add(new UIText(holdTime.x, holdTime.y - y_text_ofs, -1, "Hold time: "));
// Apply / Cancel buttons
applyButton = new UIButton(windowSpr.x - x_ofs - 70, windowSpr.y, "Apply", function()
{
updateProperties(spriteFile.label.text, healthIcon.label.text, CharacterConfig.stringToRGB(healthIcon.label.text), flipX.checked,
antialiasing.checked, isPlayer.checked, isGF.checked, Std.parseFloat(scale.label.text), Std.parseFloat(holdTime.label.text), gameOverChar.label.text);
//TO DO: Add a Animation
});
add(applyButton);
cancelButton = new UIButton(applyButton.x, applyButton.y + y_ofs, "Cancel", function()
{
close();
});
add(cancelButton);
}
public function updateProperties(sprite:String, healthIcon:String, iconColor:FlxColor, flipX:Bool, antialiasing:Bool, isPlayer:Bool, isGF:Bool,
scale:Float, holdTime:Float, gameOverChar:String)
{
// Character Data
charData.sprite = sprite;
charData.icon = healthIcon;
charData.iconColor = iconColor;
charData.flipX = flipX;
charData.antialiasing = antialiasing;
charData.isPlayer = isPlayer;
charData.isGF = isGF;
charData.scale = scale;
charData.holdTime = holdTime;
charData.gameOverChar = gameOverChar;
//Bring Player Char to Default Position for User
if(isPlayer)
{
charData.offsetX = 770;
charData.offsetY = 410;
}
else if(isGF)
{
charData.offsetX = 400;
charData.offsetY = 330;
}
else
{
charData.offsetX = 100;
charData.offsetY = 410;
}
editor.reloadChar(!char.isPlayer, charData);
editor.reloadStage();
editor.loadCross();
}
public function removeAnimation(anim:AnimData)
{
var refreshAnim:Bool = false;
if (char.animation.curAnim != null && anim.name == char.animation.curAnim.name)
refreshAnim = true;
if (char.animation.getByName(anim.name) != null)
char.animation.remove(anim.name);
if (char.animOffsets.exists(anim.name))
char.animOffsets.remove(anim.name);
char.characterData.animations.remove(anim);
}
}
@@ -1,62 +0,0 @@
package funkin.editors.character;
import flixel.math.FlxRandom;
import cpp.Random;
import flixel.math.FlxPoint;
import funkin.backend.utils.XMLUtil.AnimData;
import funkin.game.Character;
class SelectAnimUpdate extends UISubstateWindow
{
public var cancelButton:UIButton;
public var char:Character;
public var ghostChar:Character;
public var editor:CharacterEditor;
public var animButtons:FlxTypedSpriteGroup<UIButton> = new FlxTypedSpriteGroup<UIButton>(365, -10);
public function new(char:Character, ghostChar:Character, editor:CharacterEditor)
{
super();
this.char = char;
this.ghostChar = ghostChar;
this.editor = editor;
}
public override function create()
{
winTitle = "Select an animation to update:";
winWidth = 480;
winHeight = 700;
super.create();
add(animButtons);
var y_ofs = 60;
for (anim in char.characterData.animations)
{
var newbutton = new UIButton(animButtons.x - 550, animButtons.y + y_ofs, anim.name, function()
{
openUpdateWindow(anim);
});
animButtons.add(newbutton);
y_ofs += 45;
}
cancelButton = new UIButton(windowSpr.x - 10, windowSpr.y + 20, "Cancel", function()
{
close();
});
cancelButton.x -= cancelButton.bWidth;
add(cancelButton);
}
public function openUpdateWindow(anim:AnimData)
{
openSubState(new UpdateAnimScreen(this.char, this.ghostChar, anim, this.editor));
}
}
@@ -1,125 +0,0 @@
package funkin.editors.character;
import flixel.math.FlxPoint;
import funkin.backend.utils.XMLUtil.AnimData;
import funkin.game.Character;
class UpdateAnimScreen extends UISubstateWindow
{
public var char:Character;
public var ghostChar:Character;
public var updateButton:UIButton;
public var cancelButton:UIButton;
public var editor:CharacterEditor;
public var curAnimData:AnimData;
public var animName:UITextBox;
public var animPrefix:UITextBox;
public var fps:UITextBox;
public var loop:UICheckbox;
public var indices:UITextBox;
public function new(char:Character, ghostChar, animData:AnimData, editor:CharacterEditor)
{
super();
this.char = char;
this.ghostChar = ghostChar;
this.curAnimData = animData;
this.editor = editor;
}
public override function create()
{
winTitle = 'Update Animation: ${curAnimData.name}';
winWidth = 960;
super.create();
// Animation Properties
var y_ofs = 60;
var y_text_ofs = 20;
animName = new UITextBox(windowSpr.x + 50, windowSpr.y + y_ofs, curAnimData.name);
add(animName);
add(new UIText(animName.x, animName.y - y_text_ofs, -1, "Animation Name: "));
animPrefix = new UITextBox(animName.x, animName.y + y_ofs, curAnimData.anim);
add(animPrefix);
add(new UIText(animPrefix.x, animPrefix.y - y_text_ofs, -1, "Animation on XML/Atlas file: "));
fps = new UITextBox(animPrefix.x, animPrefix.y + y_ofs, Std.string(curAnimData.fps));
add(fps);
add(new UIText(fps.x, fps.y - y_text_ofs, -1, "Framerate: "));
loop = new UICheckbox(fps.x, fps.y + y_ofs, "Looped", curAnimData.loop);
add(loop);
indices = new UITextBox(loop.x, loop.y + y_ofs, Std.string(curAnimData.indices).replace("[", "").replace("]", ""));
add(indices);
add(new UIText(indices.x, indices.y - y_text_ofs, -1, "Indices (0, 1, 2, 3): "));
// Add / Cancel Buttons
updateButton = new UIButton(windowSpr.x - 10, windowSpr.y + 31, "Update", function()
{
updateAnimation(animName.label.text, animPrefix.label.text, Std.parseInt(fps.label.text), loop.checked, indices.label.text);
close();
});
updateButton.x -= updateButton.bWidth;
add(updateButton);
cancelButton = new UIButton(updateButton.x, updateButton.y + y_ofs, "Cancel", function()
{
close();
});
add(cancelButton);
}
public function updateAnimation(name:String, ?anim:String, ?fps:Int = 24, ?looped:Bool = false, ?indices:String)
{
var animType:XMLAnimType = NONE;
var selectedAnim:AnimData = null;
var selectedAnimIndex = 0;
for (i in 0...char.characterData.animations.length)
{
if (char.characterData.animations[i].name == name)
{
selectedAnim = char.characterData.animations[i];
selectedAnimIndex = i;
}
}
if (selectedAnim == null)
return;
var animData:AnimData = {
name: name,
anim: anim,
fps: fps,
loop: looped,
animType: animType,
indices: [],
x: selectedAnim.x,
y: selectedAnim.y
};
if (indices != "" && indices != null)
{
var indicesSplit = indices.split(",");
for (indice in indicesSplit)
{
var i = Std.parseInt(indice.trim());
if (i != null)
animData.indices.push(i);
}
}
XMLUtil.addAnimToSprite(char, animData);
char.characterData.animations[selectedAnimIndex] = animData;
char.playAnim(name, true);
}
}
-1
View File
@@ -317,7 +317,6 @@ class Charter extends UIState {
}
];
hitsound = FlxG.sound.load(Paths.sound('editors/charter/hitsound'));
metronome = FlxG.sound.load(Paths.sound('editors/charter/metronome'));
+12
View File
@@ -0,0 +1,12 @@
package funkin.editors.ui;
class UIWindow extends UISliceSprite {
public var titleSpr:UIText;
public override function new(x:Float, y:Float, w:Int, h:Int, title:String) {
super(x, y, w, h, "editors/ui/warning-popup");
members.push(titleSpr = new UIText(x + 25, y, bWidth - 50, title, 15, -1));
titleSpr.y = y + ((30 - titleSpr.height) / 2);
}
}
+87 -97
View File
@@ -1,6 +1,5 @@
package funkin.game;
import funkin.editors.character.CharacterConfig;
import flixel.util.FlxColor;
import funkin.backend.FunkinSprite;
import flixel.graphics.frames.FlxFrame;
@@ -23,27 +22,6 @@ import funkin.backend.scripting.events.PlayAnimEvent.PlayAnimContext;
using StringTools;
typedef CharacterData =
{
var offsetX:Float;
var offsetY:Float;
var camOffsetX:Float;
var camOffsetY:Float;
var quickDancer:Bool;
var holdTime:Float;
var isPlayer:Bool;
var flipX:Bool;
var antialiasing:Bool;
var sprite:String;
var name:String;
var animations:Array<AnimData>;
@:optional var isGF:Bool;
@:optional var icon:String;
@:optional var iconColor:FlxColor;
@:optional var scale:Float;
@:optional var gameOverChar:String;
}
@:allow(funkin.desktop.editors.CharacterEditor)
@:allow(funkin.game.StrumLine)
@:allow(funkin.game.PlayState)
@@ -67,6 +45,7 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
public var isPlayer:Bool = false;
public var isGF:Bool = false;
public var curCharacter:String = 'bf';
public var sprite:String = 'bf';
public var lastHit:Float = Math.NEGATIVE_INFINITY;
public var holdTime:Float = 4;
@@ -82,21 +61,14 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
public var script:Script;
public var xml:Access;
public var characterData:CharacterData;
public var shadowFrame:CharacterShadowFrame;
public inline function getCameraPosition()
{
var midpoint = getMidpoint();
var event = EventManager.get(PointEvent)
.recycle(midpoint.x
+ (isPlayer ? -100 : 150)
+ globalOffset.x
+ cameraOffset.x, midpoint.y
- 100
+ globalOffset.y
+ cameraOffset.y);
var event = EventManager.get(PointEvent).recycle(
midpoint.x + (isPlayer ? -100 : 150) + globalOffset.x + cameraOffset.x,
midpoint.y - 100 + globalOffset.y + cameraOffset.y);
script.call("onGetCamPos", [event]);
midpoint.put();
// this event cannot be cancelled
@@ -141,59 +113,16 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
try
{
var charXML = Xml.parse(plainXML).firstElement();
if (charXML == null)
throw new Exception("Missing \"character\" node in XML.");
if (charXML == null) throw new Exception("Missing \"character\" node in XML.");
xml = new Access(charXML);
}
catch (e)
{
} catch (e) {
Logs.trace('Error while loading character ${curCharacter}: ${e}', ERROR);
curCharacter = "bf";
continue;
}
var sprite:String = curCharacter;
applyXML(xml);
if (xml.has.isPlayer)
playerOffsets = (xml.att.isPlayer == "true");
if (xml.has.isGF)
isGF = (xml.att.isGF == "true");
if (xml.has.x)
globalOffset.x = Std.parseFloat(xml.att.x);
if (xml.has.y)
globalOffset.y = Std.parseFloat(xml.att.y);
if (xml.has.gameOverChar)
gameOverCharacter = xml.att.gameOverChar;
if (xml.has.camx)
cameraOffset.x = Std.parseFloat(xml.att.camx);
if (xml.has.camy)
cameraOffset.y = Std.parseFloat(xml.att.camy);
if (xml.has.holdTime)
holdTime = CoolUtil.getDefault(Std.parseFloat(xml.att.holdTime), 4);
if (xml.has.flipX)
flipX = (xml.att.flipX == "true");
if (xml.has.icon)
icon = xml.att.icon;
if (xml.has.scale)
{
var scale = Std.parseFloat(xml.att.scale).getDefault(1);
this.scale.set(scale, scale);
updateHitbox();
}
if (xml.has.antialiasing)
antialiasing = (xml.att.antialiasing == "true");
if (xml.has.sprite)
sprite = xml.att.sprite;
loadSprite(Paths.image('characters/$sprite'));
for (anim in xml.nodes.anim)
{
XMLUtil.addXMLAnimation(this, anim);
}
characterData = CharacterConfig.loadCharacterData(curCharacter);
// Loads the script and calls it's "create" function
// Loads the script and calls it's "create" functions
script = Script.create(Paths.script(Path.withoutExtension(xmlPath), null, true));
script.setParent(this);
script.load();
@@ -226,7 +155,6 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
// flipX = true;
fixChar(true);
dance();
script.call("postCreate");
}
@@ -242,6 +170,7 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
// [ true ][ false ][ true ]
// [ false ][ true ][ true ]
// [ true ][ true ][ false ]
// bros provided evedince :skull:
if (isPlayer != playerOffsets && switchAnims)
{
// character is flipped
@@ -302,8 +231,7 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
public function tryDance()
{
switch (lastAnimContext)
{
switch (lastAnimContext) {
case SING | MISS:
if (lastHit + (Conductor.stepCrochet * holdTime) < Conductor.songPosition)
dance();
@@ -328,8 +256,7 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
*/
public var danceInterval:Int = 1;
public override function beatHit(curBeat:Int)
{
public override function beatHit(curBeat:Int) {
script.call("beatHit", [curBeat]);
if (danceInterval < 1)
danceInterval = 1;
@@ -340,16 +267,14 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
}
}
public override function stepHit(curStep:Int)
{
public override function stepHit(curStep:Int) {
script.call("stepHit", [curStep]);
// nothing
}
var __reverseDrawProcedure:Bool = false;
public override function getScreenBounds(?newRect:FlxRect, ?camera:FlxCamera):FlxRect
{
public override function getScreenBounds(?newRect:FlxRect, ?camera:FlxCamera):FlxRect {
if (__reverseDrawProcedure)
{
scale.x *= -1;
@@ -363,10 +288,8 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
var __drawingShadowFrame = false;
var __oldColorTransform = new ColorTransform();
public override function draw()
{
if (!__drawingShadowFrame && shadowFrame != null)
{
public override function draw() {
if (!__drawingShadowFrame && shadowFrame != null) {
__drawingShadowFrame = true;
var oldFrame = _frame;
@@ -436,17 +359,77 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
globalOffset.put();
}
public inline function getIcon()
{
return (icon != null) ? icon : curCharacter;
public function applyXML(xml:Access) {
sprite = curCharacter;
if (xml.has.isPlayer) playerOffsets = (xml.att.isPlayer == "true");
if (xml.has.isGF) isGF = (xml.att.isGF == "true");
if (xml.has.x) globalOffset.x = Std.parseFloat(xml.att.x); //
if (xml.has.y) globalOffset.y = Std.parseFloat(xml.att.y); //
if (xml.has.gameOverChar) gameOverCharacter = xml.att.gameOverChar; //
if (xml.has.camx) cameraOffset.x = Std.parseFloat(xml.att.camx); //
if (xml.has.camy) cameraOffset.y = Std.parseFloat(xml.att.camy); //
if (xml.has.holdTime) holdTime = CoolUtil.getDefault(Std.parseFloat(xml.att.holdTime), 4); //
if (xml.has.flipX) flipX = (xml.att.flipX == "true");
if (xml.has.icon) icon = xml.att.icon; //
if (xml.has.scale) { //
var scale = Std.parseFloat(xml.att.scale).getDefault(1);
this.scale.set(scale, scale);
updateHitbox();
}
if (xml.has.antialiasing) antialiasing = (xml.att.antialiasing == "true");
if (xml.has.sprite) sprite = xml.att.sprite; //
loadSprite(Paths.image('characters/$sprite'));
animation.destroyAnimations();
for (anim in xml.nodes.anim)
{
XMLUtil.addXMLAnimation(this, anim);
}
}
public function buildXML():Xml {
var xml = Xml.createElement("character");
xml.set("isPlayer", playerOffsets == true ? "true" : "false");
xml.set("isGF", isGF == true ? "true" : "false");
xml.set("x", Std.string(globalOffset.x));
xml.set("y", Std.string(globalOffset.y));
xml.set("gameOverChar", gameOverCharacter);
xml.set("camx", Std.string(cameraOffset.x));
xml.set("camy", Std.string(cameraOffset.y));
xml.set("holdTime", Std.string(holdTime));
xml.set("flipX", Std.string(flipX));
xml.set("icon", icon);
xml.set("scale", Std.string(scale.x));
xml.set("antialiasing", antialiasing == true ? "true" : "false");
xml.set("sprite", sprite);
for (anim in animation.getAnimationList())
{
var animXml:Xml = Xml.createElement('anim');
animXml.set("name", anim.name);
animXml.set("anim", anim.prefix);
animXml.set("loop", Std.string(anim.looped));
animXml.set("fps", Std.string(anim.frameRate));
var offset:FlxPoint = getAnimOffset(anim.name);
animXml.set("x", Std.string(offset.x));
animXml.set("y", Std.string(offset.y));
if (anim.usesIndicies)
animXml.set("indices", anim.frames.join(","));
xml.addChild(animXml);
}
return xml;
}
public inline function getIcon()
return (icon != null) ? icon : curCharacter;
public static function getIconFromCharName(?curCharacter:String) {
if(curCharacter == null) return "face";
var icon = curCharacter;
while(true) {
switch (curCharacter)
{
switch (curCharacter) {
// case 'your-char': // To hardcode characters icons
default:
// load xml
@@ -474,6 +457,13 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
}
return icon;
}
public static function getList():Array<String> {
return [
for (path in Paths.getFolderContent('data/characters/', true, BOTH))
if (Path.extension(path) == "xml") Path.withoutDirectory(Path.withoutExtension(path))
];
}
}
typedef CharacterShadowFrame =