character creatioin tests >:D

This commit is contained in:
lunarcleint
2025-03-04 19:18:55 -07:00
parent 892f914780
commit 0cbdbfee0b
4 changed files with 222 additions and 21 deletions
@@ -417,7 +417,7 @@ class CharacterAnimButton extends UIButton {
public function updateText() {
field.applyMarkup(
'${anim} ' + (valid ? '(${parent.character.animOffsets[anim].x} ${parent.character.animOffsets[anim].y})' : "|(Invalid Animation!!!)|"),
'${anim} ' + (valid ? '(${parent.character.animOffsets[anim].x}, ${parent.character.animOffsets[anim].y})' : "|(Invalid Animation!!!)|"),
[new FlxTextFormatMarkerPair(new FlxTextFormat(0xFFF29797), "|")]);
}
@@ -0,0 +1,149 @@
package funkin.editors.character;
import haxe.xml.Printer;
import funkin.game.Character;
import funkin.editors.ui.UIImageExplorer.ImageSaveData;
import flixel.text.FlxText.FlxTextFormat;
import flixel.text.FlxText.FlxTextFormatMarkerPair;
class CharacterCreationScreen extends UISubstateWindow {
private var onSave:(String, ImageSaveData, Xml)-> Void = null;
public var characterNameTextBox:UITextBox;
public var imageExplorer:UIImageExplorer;
public var saveButton:UIButton;
public var closeButton:UIButton;
public function new(?onSave:(String, ImageSaveData, Xml)->Void) {
super();
if (onSave != null) this.onSave = onSave;
}
public override function create() {
winTitle = "Creating New Character";
winWidth = 360;
winHeight = 520;
super.create();
function addLabelOn(ui:UISprite, text:String):UIText {
var text:UIText = new UIText(ui.x, ui.y - 24, 0, text);
ui.members.push(text);
return text;
}
characterNameTextBox = new UITextBox(windowSpr.x + 20, windowSpr.y + 30 + 16 + 20, "character", 320);
characterNameTextBox.onChange = (_) -> {checkRequired();};
add(characterNameTextBox);
addLabelOn(characterNameTextBox, "Character File Name").applyMarkup(
"Character File Name $* Required$",
[new FlxTextFormatMarkerPair(new FlxTextFormat(0xFFAD1212), "$")]);
imageExplorer = new UIImageExplorer(characterNameTextBox.x, characterNameTextBox.y + 30 + 16 + 20, null, 320, 58, (_, _) -> {onLoadImage();});
add(imageExplorer);
addLabelOn(imageExplorer, "Character Image File").applyMarkup(
"Character Image File $* Required$",
[new FlxTextFormatMarkerPair(new FlxTextFormat(0xFFAD1212), "$")]);
imageExplorer.maxSize.y -= 100;
saveButton = new UIButton(windowSpr.x + windowSpr.bWidth - 20 - 125, windowSpr.y + windowSpr.bHeight - 16 - 32, "Save & Close", function() {
createCharacter();
close();
}, 125);
add(saveButton);
closeButton = new UIButton(saveButton.x - 20 - saveButton.bWidth, saveButton.y, "Cancel", function() {
close();
}, 125);
add(closeButton);
closeButton.color = 0xFFFF0000;
onLoadImage();
}
public function onLoadImage() {
refreshWindowSize();
checkRequired();
}
public function refreshWindowSize() {
if (imageExplorer == null) return;
windowSpr.bWidth = 20 + imageExplorer.bWidth + 20;
windowSpr.bHeight = 30 + 16 + 20 + 32 + 30 + 10 + imageExplorer.bHeight + 14 + saveButton.bHeight + 14;
saveButton.x = windowSpr.x + windowSpr.bWidth - 20 - saveButton.bWidth;
closeButton.x = saveButton.x - 20 - closeButton.bWidth;
closeButton.y = saveButton.y = imageExplorer.y + imageExplorer.bHeight + 14;
}
public function checkRequired() {
saveButton.selectable = characterNameTextBox.label.text.length > 0 && !CoolUtil.isMapEmpty(imageExplorer.imageFiles) && (imageExplorer.animationList.length > 0);
}
public function createCharacter() {
var imageSaveData:ImageSaveData = imageExplorer.getSaveData();
var xml:Xml = Xml.createElement("character");
xml.attributeOrder = Character.characterProperties.copy();
xml.set("sprite", imageSaveData.imageName);
// Look for animations >:D
var animationList:Array<String> = imageExplorer.animationList.copy();
animationList.sort((a, b) -> {
var lengthCompare = a.length - b.length;
if (lengthCompare != 0) return lengthCompare;
// Miss animations don't show up as regular if they are the same length as regular >:D
var aIsMiss = a.toLowerCase().contains("miss");
var bIsMiss = b.toLowerCase().contains("miss");
return aIsMiss == bIsMiss ? 0 : (aIsMiss ? 1 : -1);
});
var animationsFound:Map<String, String> = [
"singLEFT" => null,
"singRIGHT" => null,
"singUP" => null,
"singDOWN" => null,
"idle" => null
];
for (anim => found in animationsFound) {
var animToLookFor:String = StringTools.replace(anim, "sing", "").toLowerCase();
for (imageAnim in animationList)
if (imageAnim.toLowerCase().contains(animToLookFor)) {
animationsFound.set(anim, imageAnim);
animationList.remove(imageAnim);
break;
}
}
// Add said animations >:D
for (anim => found in animationsFound) {
var animXml:Xml = Xml.createElement('anim');
animXml.attributeOrder = Character.characterAnimProperties;
animXml.set("name", anim);
animXml.set("anim", found.getDefault(animationList[0]));
xml.addChild(animXml);
}
// Do the rest only if not atlas (until we make it animations and not symbols >:D)
if (!imageSaveData.isAtlas) {
for (imageAnim in animationList) {
var animXml:Xml = Xml.createElement('anim');
animXml.attributeOrder = Character.characterAnimProperties;
animXml.set("name", imageAnim);
animXml.set("anim", imageAnim);
xml.addChild(animXml);
}
}
if (onSave != null) onSave(characterNameTextBox.label.text, imageSaveData, xml);
}
}
@@ -1,5 +1,7 @@
package funkin.editors.character;
import haxe.xml.Printer;
import funkin.editors.ui.UIImageExplorer.ImageSaveData;
import funkin.game.Character;
import funkin.options.OptionsScreen;
import funkin.options.type.IconOption;
@@ -44,9 +46,7 @@ class CharacterSelection extends EditorTreeMenu
}
list.insert(0, new NewOption("New Character", "New Character", function() {
openSubState(new UIWarningSubstate("New Character: Feature Not Implemented!", "This feature isn't implemented yet. Please wait for more cne updates to have this functional.\n\n\n- Codename Devs", [
{label: "Ok", color: 0xFFFF0000, onClick: function(t) {}}
]));
openSubState(new CharacterCreationScreen(createCharacter));
}));
return list;
@@ -64,4 +64,30 @@ class CharacterSelection extends EditorTreeMenu
main.changeSelection(1);
}
public function createCharacter(name:String, imageSaveData:ImageSaveData, xml:Xml) {
var characterAlreadyExists:Bool = Character.getList().contains(name);
if (characterAlreadyExists) {
openSubState(new UIWarningSubstate("Creating Character: Error!", "The character you are trying to create already exists, if you would like to override it delete the character first!", [
{label: "Ok", color: 0xFFFF0000, onClick: function(t) {}}
]));
return;
}
// Save Data file
var characterPath:String = '${Paths.getAssetsRoot()}/data/characters/${name}.xml';
CoolUtil.safeSaveFile(characterPath, "<!DOCTYPE codename-engine-character>\n" + Printer.print(xml, true));
// Save Image files
UIImageExplorer.saveFilesGlobal(imageSaveData, '${Paths.getAssetsRoot()}/images/characters');
// Add to Menu >:D
var option:IconOption = new IconOption(name, "Press ACCEPT to edit this character.", Character.getIconFromCharName(name),
function() {
FlxG.switchState(new CharacterEditor(name));
}
);
optionsTree.members[optionsTree.members.length-1].insert(1, option);
}
}
+43 -17
View File
@@ -16,6 +16,12 @@ using StringTools;
using funkin.backend.utils.BitmapUtil;
using flixel.util.FlxSpriteUtil;
typedef ImageSaveData = {
var imageName:String;
var isAtlas:Bool;
var imageFiles:Map<String, OneOfTwo<String, Bytes>>;
}
// TODO: make this limited if on web
class UIImageExplorer extends UIFileExplorer {
private var allowAtlases:Bool = true;
@@ -29,13 +35,15 @@ class UIImageExplorer extends UIFileExplorer {
deleteButton.bWidth = 26;
deleteButton.bHeight = 26;
var fullImagePath:String = '${Path.normalize(Sys.getCwd())}/${Paths.image(image)}'.replace('/', '\\');
var noExt = Path.withoutExtension(fullImagePath);
if (FileSystem.exists('$noExt\\spritemap1.png'))
fullImagePath = '$noExt\\spritemap1.png';
if (FileSystem.exists(fullImagePath))
loadFile(fullImagePath);
if (image != null) {
var fullImagePath:String = '${Path.normalize(Sys.getCwd())}/${Paths.image(image)}'.replace('/', '\\');
var noExt = Path.withoutExtension(fullImagePath);
if (FileSystem.exists('$noExt\\spritemap1.png'))
fullImagePath = '$noExt\\spritemap1.png';
if (FileSystem.exists(fullImagePath))
loadFile(fullImagePath);
}
}
public var isAtlas:Bool = false;
@@ -50,6 +58,8 @@ class UIImageExplorer extends UIFileExplorer {
public var fileText:UIText;
public var maxSize:FlxPoint = FlxPoint.get(700, 500);
public function uploadImage(filePath:String, file:Bytes) {
__resetData();
@@ -139,7 +149,7 @@ class UIImageExplorer extends UIFileExplorer {
} else {
var dataPathExt:String = CoolUtil.imageHasFrameData(imagePath);
var dataPath:String = Path.withExtension(imagePath, dataPathExt);
var dataPathFile:String = !isAtlas ? File.getContent(dataPath) : null;
var dataPathFile:String = !isAtlas && dataPathExt != null ? File.getContent(dataPath) : null;
if (dataPathExt != null) {
frames = CoolUtil.loadFramesFromData(dataPathFile, dataPathExt);
@@ -182,10 +192,10 @@ class UIImageExplorer extends UIFileExplorer {
// DISPLAY IMAGE!!
uiElement = new FlxSprite().loadGraphic(image);
var imageScale:Float = 1;
if (uiElement.width < 300 || uiElement.height < 200)
imageScale = Math.max(300/uiElement.width, 200/uiElement.height);
else if (uiElement.width > 700 || uiElement.height > 500)
imageScale = Math.min(700/uiElement.width, 500/uiElement.height);
if (uiElement.width < Math.min(300, maxSize.x) || uiElement.height < Math.min(200, maxSize.y))
imageScale = Math.max(Math.min(300, maxSize.x) / uiElement.width, Math.min(200, maxSize.y) / uiElement.height);
else if (uiElement.width > maxSize.x || uiElement.height > maxSize.y)
imageScale = Math.min(maxSize.x/uiElement.width, maxSize.y/uiElement.height);
uiElement.scale.set(imageScale, imageScale);
uiElement.updateHitbox();
@@ -231,14 +241,25 @@ class UIImageExplorer extends UIFileExplorer {
deleteIcon.x = deleteButton.x + deleteButton.bWidth/2 - 8;
deleteIcon.y = deleteButton.y + deleteButton.bHeight/2 - 8;
}
public function saveFiles(directory:String, ?onFinishSaving:Void->Void, ?checkExisting:Bool = true) {
if (isAtlas) directory += '/$imageName';
public inline function getSaveData():ImageSaveData {
return {
imageName: this.imageName,
isAtlas: this.isAtlas,
imageFiles: this.imageFiles
};
}
public inline function saveFiles(directory:String, ?onFinishSaving:Void->Void, ?checkExisting:Bool = true) {
UIImageExplorer.saveFilesGlobal(getSaveData(), directory, onFinishSaving, checkExisting);
}
public static function saveFilesGlobal(imageData:ImageSaveData, directory:String, ?onFinishSaving:Void->Void, ?checkExisting:Bool = true) {
if (imageData.isAtlas) directory += '/${imageData.imageName}';
var alreadlyExistingFiles:Array<String> = [];
for (name => file in imageFiles)
for (name => file in imageData.imageFiles)
if (FileSystem.exists('$directory/$name'))
alreadlyExistingFiles.push('$directory/$name');
@@ -249,7 +270,7 @@ class UIImageExplorer extends UIFileExplorer {
}
function acuttalySaveFiles() {
for (name => file in imageFiles)
for (name => file in imageData.imageFiles)
if (!alreadlyExistingFiles.contains('$directory/$name')) {
CoolUtil.safeSaveFile('$directory/$name', file);
trace('SAVED: $directory/$name');
@@ -290,4 +311,9 @@ class UIImageExplorer extends UIFileExplorer {
@:noCompletion inline function __resetData() {
imageFiles.clear(); isAtlas = false; animationList = [];
}
public override function destroy() {
maxSize.put();
super.destroy();
}
}