Tobee_Night_Funkin_PSYCH/source/editors/MenuCharacterEditorState.hx

443 lines
14 KiB
Haxe

package editors;
#if desktop
import Discord.DiscordClient;
#end
import flixel.FlxG;
import flixel.FlxSprite;
import flixel.addons.display.FlxGridOverlay;
import flixel.addons.transition.FlxTransitionableState;
import flixel.group.FlxGroup.FlxTypedGroup;
import flixel.math.FlxMath;
import flixel.text.FlxText;
import flixel.util.FlxColor;
import flixel.system.FlxSound;
import flixel.addons.ui.FlxInputText;
import flixel.addons.ui.FlxUI9SliceSprite;
import flixel.addons.ui.FlxUI;
import flixel.addons.ui.FlxUICheckBox;
import flixel.addons.ui.FlxUIInputText;
import flixel.addons.ui.FlxUINumericStepper;
import flixel.addons.ui.FlxUITabMenu;
import flixel.ui.FlxButton;
import MenuCharacter;
import openfl.net.FileReference;
import openfl.events.Event;
import openfl.events.IOErrorEvent;
import flash.net.FileFilter;
import haxe.Json;
#if sys
import sys.io.File;
#end
using StringTools;
class MenuCharacterEditorState extends MusicBeatState
{
var grpWeekCharacters:FlxTypedGroup<MenuCharacter>;
var characterFile:MenuCharacterFile = null;
var txtOffsets:FlxText;
var defaultCharacters:Array<String> = ['dad', 'bf', 'gf'];
override function create() {
characterFile = {
image: 'Menu_Dad',
scale: 1,
position: [0, 0],
idle_anim: 'M Dad Idle',
confirm_anim: 'M Dad Idle'
};
#if desktop
// Updating Discord Rich Presence
DiscordClient.changePresence("Menu Character Editor", "Editting: " + characterFile.image);
#end
grpWeekCharacters = new FlxTypedGroup<MenuCharacter>();
for (char in 0...3)
{
var weekCharacterThing:MenuCharacter = new MenuCharacter((FlxG.width * 0.25) * (1 + char) - 150, defaultCharacters[char]);
weekCharacterThing.y += 70;
weekCharacterThing.alpha = 0.2;
grpWeekCharacters.add(weekCharacterThing);
}
add(new FlxSprite(0, 56).makeGraphic(FlxG.width, 386, 0xFFF9CF51));
add(grpWeekCharacters);
txtOffsets = new FlxText(20, 10, 0, "[0, 0]", 32);
txtOffsets.setFormat("VCR OSD Mono", 32, FlxColor.WHITE, CENTER);
txtOffsets.alpha = 0.7;
add(txtOffsets);
var tipText:FlxText = new FlxText(0, 540, FlxG.width,
"Arrow Keys - Change Offset (Hold shift for 10x speed)
\nSpace - Play \"Start Press\" animation (Boyfriend Character Type)", 16);
tipText.setFormat(Paths.font("vcr.ttf"), 16, FlxColor.WHITE, CENTER);
tipText.scrollFactor.set();
add(tipText);
addEditorBox();
FlxG.mouse.visible = true;
updateCharTypeBox();
super.create();
}
var UI_typebox:FlxUITabMenu;
var UI_mainbox:FlxUITabMenu;
var blockPressWhileTypingOn:Array<FlxUIInputText> = [];
function addEditorBox() {
var tabs = [
{name: 'Character Type', label: 'Character Type'},
];
UI_typebox = new FlxUITabMenu(null, tabs, true);
UI_typebox.resize(120, 180);
UI_typebox.x = 100;
UI_typebox.y = FlxG.height - UI_typebox.height - 50;
UI_typebox.scrollFactor.set();
addTypeUI();
add(UI_typebox);
var tabs = [
{name: 'Character', label: 'Character'},
];
UI_mainbox = new FlxUITabMenu(null, tabs, true);
UI_mainbox.resize(240, 180);
UI_mainbox.x = FlxG.width - UI_mainbox.width - 100;
UI_mainbox.y = FlxG.height - UI_mainbox.height - 50;
UI_mainbox.scrollFactor.set();
addCharacterUI();
add(UI_mainbox);
var loadButton:FlxButton = new FlxButton(0, 480, "Load Character", function() {
loadCharacter();
});
loadButton.screenCenter(X);
loadButton.x -= 60;
add(loadButton);
var saveButton:FlxButton = new FlxButton(0, 480, "Save Character", function() {
saveCharacter();
});
saveButton.screenCenter(X);
saveButton.x += 60;
add(saveButton);
}
var opponentCheckbox:FlxUICheckBox;
var boyfriendCheckbox:FlxUICheckBox;
var girlfriendCheckbox:FlxUICheckBox;
var curTypeSelected:Int = 0; //0 = Dad, 1 = BF, 2 = GF
function addTypeUI() {
var tab_group = new FlxUI(null, UI_typebox);
tab_group.name = "Character Type";
opponentCheckbox = new FlxUICheckBox(10, 20, null, null, "Opponent", 100);
opponentCheckbox.callback = function()
{
curTypeSelected = 0;
updateCharTypeBox();
};
boyfriendCheckbox = new FlxUICheckBox(opponentCheckbox.x, opponentCheckbox.y + 40, null, null, "Boyfriend", 100);
boyfriendCheckbox.callback = function()
{
curTypeSelected = 1;
updateCharTypeBox();
};
girlfriendCheckbox = new FlxUICheckBox(boyfriendCheckbox.x, boyfriendCheckbox.y + 40, null, null, "Girlfriend", 100);
girlfriendCheckbox.callback = function()
{
curTypeSelected = 2;
updateCharTypeBox();
};
tab_group.add(opponentCheckbox);
tab_group.add(boyfriendCheckbox);
tab_group.add(girlfriendCheckbox);
UI_typebox.addGroup(tab_group);
}
var imageInputText:FlxUIInputText;
var idleInputText:FlxUIInputText;
var confirmInputText:FlxUIInputText;
var confirmDescText:FlxText;
var scaleStepper:FlxUINumericStepper;
function addCharacterUI() {
var tab_group = new FlxUI(null, UI_mainbox);
tab_group.name = "Character";
imageInputText = new FlxUIInputText(10, 20, 80, characterFile.image, 8);
blockPressWhileTypingOn.push(imageInputText);
idleInputText = new FlxUIInputText(10, imageInputText.y + 35, 100, characterFile.idle_anim, 8);
blockPressWhileTypingOn.push(idleInputText);
confirmInputText = new FlxUIInputText(10, idleInputText.y + 35, 100, characterFile.confirm_anim, 8);
blockPressWhileTypingOn.push(confirmInputText);
var reloadImageButton:FlxButton = new FlxButton(10, confirmInputText.y + 30, "Reload Char", function() {
reloadSelectedCharacter();
});
scaleStepper = new FlxUINumericStepper(140, imageInputText.y, 0.05, 1, 0.1, 30, 2);
confirmDescText = new FlxText(10, confirmInputText.y - 18, 0, 'Start Press animation on the .XML:');
tab_group.add(new FlxText(10, imageInputText.y - 18, 0, 'Image file name:'));
tab_group.add(new FlxText(10, idleInputText.y - 18, 0, 'Idle animation on the .XML:'));
tab_group.add(new FlxText(scaleStepper.x, scaleStepper.y - 18, 0, 'Scale:'));
tab_group.add(reloadImageButton);
tab_group.add(confirmDescText);
tab_group.add(imageInputText);
tab_group.add(idleInputText);
tab_group.add(confirmInputText);
tab_group.add(scaleStepper);
UI_mainbox.addGroup(tab_group);
}
function updateCharTypeBox() {
opponentCheckbox.checked = false;
boyfriendCheckbox.checked = false;
girlfriendCheckbox.checked = false;
switch(curTypeSelected) {
case 0:
opponentCheckbox.checked = true;
case 1:
boyfriendCheckbox.checked = true;
case 2:
girlfriendCheckbox.checked = true;
}
updateCharacters();
}
function updateCharacters() {
for (i in 0...3) {
var char:MenuCharacter = grpWeekCharacters.members[i];
char.alpha = 0.2;
char.character = '';
char.changeCharacter(defaultCharacters[i]);
}
reloadSelectedCharacter();
}
function reloadSelectedCharacter() {
var char:MenuCharacter = grpWeekCharacters.members[curTypeSelected];
char.alpha = 1;
char.frames = Paths.getSparrowAtlas('menucharacters/' + characterFile.image);
char.animation.addByPrefix('idle', characterFile.idle_anim, 24);
if(curTypeSelected == 1) char.animation.addByPrefix('confirm', characterFile.confirm_anim, 24, false);
char.scale.set(characterFile.scale, characterFile.scale);
char.updateHitbox();
char.animation.play('idle');
confirmDescText.visible = (curTypeSelected == 1);
confirmInputText.visible = (curTypeSelected == 1);
updateOffset();
#if desktop
// Updating Discord Rich Presence
DiscordClient.changePresence("Menu Character Editor", "Editting: " + characterFile.image);
#end
}
override function getEvent(id:String, sender:Dynamic, data:Dynamic, ?params:Array<Dynamic>) {
if(id == FlxUIInputText.CHANGE_EVENT && (sender is FlxUIInputText)) {
if(sender == imageInputText) {
characterFile.image = imageInputText.text;
} else if(sender == idleInputText) {
characterFile.idle_anim = idleInputText.text;
} else if(sender == confirmInputText) {
characterFile.confirm_anim = confirmInputText.text;
}
} else if(id == FlxUINumericStepper.CHANGE_EVENT && (sender is FlxUINumericStepper)) {
if (sender == scaleStepper) {
characterFile.scale = scaleStepper.value;
reloadSelectedCharacter();
}
}
}
override function update(elapsed:Float) {
var blockInput:Bool = false;
for (inputText in blockPressWhileTypingOn) {
if(inputText.hasFocus) {
FlxG.sound.muteKeys = [];
FlxG.sound.volumeDownKeys = [];
FlxG.sound.volumeUpKeys = [];
blockInput = true;
if(FlxG.keys.justPressed.ENTER) inputText.hasFocus = false;
break;
}
}
if(!blockInput) {
FlxG.sound.muteKeys = TitleState.muteKeys;
FlxG.sound.volumeDownKeys = TitleState.volumeDownKeys;
FlxG.sound.volumeUpKeys = TitleState.volumeUpKeys;
if(FlxG.keys.justPressed.ESCAPE) {
FlxG.mouse.visible = false;
MusicBeatState.switchState(new editors.MasterEditorMenu());
FlxG.sound.playMusic(Paths.music('freakyMenu'));
}
var shiftMult:Int = 1;
if(FlxG.keys.pressed.SHIFT) shiftMult = 10;
if(FlxG.keys.justPressed.LEFT) {
characterFile.position[0] += shiftMult;
updateOffset();
}
if(FlxG.keys.justPressed.RIGHT) {
characterFile.position[0] -= shiftMult;
updateOffset();
}
if(FlxG.keys.justPressed.UP) {
characterFile.position[1] += shiftMult;
updateOffset();
}
if(FlxG.keys.justPressed.DOWN) {
characterFile.position[1] -= shiftMult;
updateOffset();
}
if(FlxG.keys.justPressed.SPACE && curTypeSelected == 1) {
grpWeekCharacters.members[curTypeSelected].animation.play('confirm', true);
}
}
var char:MenuCharacter = grpWeekCharacters.members[1];
if(char.animation.curAnim != null && char.animation.curAnim.name == 'confirm' && char.animation.curAnim.finished) {
char.animation.play('idle', true);
}
super.update(elapsed);
}
function updateOffset() {
var char:MenuCharacter = grpWeekCharacters.members[curTypeSelected];
char.offset.set(characterFile.position[0], characterFile.position[1]);
txtOffsets.text = '' + characterFile.position;
}
var _file:FileReference = null;
function loadCharacter() {
var jsonFilter:FileFilter = new FileFilter('JSON', 'json');
_file = new FileReference();
_file.addEventListener(Event.SELECT, onLoadComplete);
_file.addEventListener(Event.CANCEL, onLoadCancel);
_file.addEventListener(IOErrorEvent.IO_ERROR, onLoadError);
_file.browse([jsonFilter]);
}
function onLoadComplete(_):Void
{
_file.removeEventListener(Event.SELECT, onLoadComplete);
_file.removeEventListener(Event.CANCEL, onLoadCancel);
_file.removeEventListener(IOErrorEvent.IO_ERROR, onLoadError);
#if sys
var fullPath:String = null;
var jsonLoaded = cast Json.parse(Json.stringify(_file)); //Exploit(???) for accessing a private variable
if(jsonLoaded.__path != null) fullPath = jsonLoaded.__path; //I'm either a genious or dangerously dumb
if(fullPath != null) {
var rawJson:String = File.getContent(fullPath);
if(rawJson != null) {
var loadedChar:MenuCharacterFile = cast Json.parse(rawJson);
if(loadedChar.idle_anim != null && loadedChar.confirm_anim != null) //Make sure it's really a character
{
var cutName:String = _file.name.substr(0, _file.name.length - 5);
trace("Successfully loaded file: " + cutName);
characterFile = loadedChar;
reloadSelectedCharacter();
imageInputText.text = characterFile.image;
idleInputText.text = characterFile.image;
confirmInputText.text = characterFile.image;
scaleStepper.value = characterFile.scale;
updateOffset();
_file = null;
return;
}
}
}
_file = null;
#else
trace("File couldn't be loaded! You aren't on Desktop, are you?");
#end
}
/**
* Called when the save file dialog is cancelled.
*/
function onLoadCancel(_):Void
{
_file.removeEventListener(Event.SELECT, onLoadComplete);
_file.removeEventListener(Event.CANCEL, onLoadCancel);
_file.removeEventListener(IOErrorEvent.IO_ERROR, onLoadError);
_file = null;
trace("Cancelled file loading.");
}
/**
* Called if there is an error while saving the gameplay recording.
*/
function onLoadError(_):Void
{
_file.removeEventListener(Event.SELECT, onLoadComplete);
_file.removeEventListener(Event.CANCEL, onLoadCancel);
_file.removeEventListener(IOErrorEvent.IO_ERROR, onLoadError);
_file = null;
trace("Problem loading file");
}
function saveCharacter() {
var data:String = Json.stringify(characterFile, "\t");
if (data.length > 0)
{
var splittedImage:Array<String> = imageInputText.text.trim().split('_');
var characterName:String = splittedImage[splittedImage.length-1].toLowerCase().replace(' ', '');
_file = new FileReference();
_file.addEventListener(Event.COMPLETE, onSaveComplete);
_file.addEventListener(Event.CANCEL, onSaveCancel);
_file.addEventListener(IOErrorEvent.IO_ERROR, onSaveError);
_file.save(data, characterName + ".json");
}
}
function onSaveComplete(_):Void
{
_file.removeEventListener(Event.COMPLETE, onSaveComplete);
_file.removeEventListener(Event.CANCEL, onSaveCancel);
_file.removeEventListener(IOErrorEvent.IO_ERROR, onSaveError);
_file = null;
FlxG.log.notice("Successfully saved file.");
}
/**
* Called when the save file dialog is cancelled.
*/
function onSaveCancel(_):Void
{
_file.removeEventListener(Event.COMPLETE, onSaveComplete);
_file.removeEventListener(Event.CANCEL, onSaveCancel);
_file.removeEventListener(IOErrorEvent.IO_ERROR, onSaveError);
_file = null;
}
/**
* Called if there is an error while saving the gameplay recording.
*/
function onSaveError(_):Void
{
_file.removeEventListener(Event.COMPLETE, onSaveComplete);
_file.removeEventListener(Event.CANCEL, onSaveCancel);
_file.removeEventListener(IOErrorEvent.IO_ERROR, onSaveError);
_file = null;
FlxG.log.error("Problem saving file");
}
}