Merge branch 'CodenameCrew:main' into patch-1
This commit is contained in:
@@ -26,50 +26,55 @@ class DebugOptions extends TreeMenu {
|
||||
|
||||
class DebugOptionsScreen extends OptionsScreen {
|
||||
public override function new() {
|
||||
super("Debug Options", "Use this menu to change debug options.");
|
||||
prefix = "DebugOptions.";
|
||||
super(translate("title"), translate("desc"), prefix);
|
||||
#if windows
|
||||
add(new TextOption(
|
||||
"Show Console",
|
||||
"Select this to show the debug console, which contains log information about the game.",
|
||||
getName("showConsole"),
|
||||
getDesc("showConsole"),
|
||||
function() {
|
||||
NativeAPI.allocConsole();
|
||||
}));
|
||||
#end
|
||||
add(new Checkbox(
|
||||
"Resizable Editors",
|
||||
"If checked, this will allow the editors to render beyond the base 1280x720 resolution of FNF (allowing for more detail and space...)",
|
||||
getName("editorsResizable"),
|
||||
getDesc("editorsResizable"),
|
||||
"editorsResizable"));
|
||||
add(new Checkbox(
|
||||
"Editor SFXs",
|
||||
"If checked, will play sound effects when working on editors (ex: will play SFXs when checking checkboxes...)",
|
||||
getName("bypassEditorsResize"),
|
||||
getDesc("bypassEditorsResize"),
|
||||
"bypassEditorsResize"));
|
||||
add(new Checkbox(
|
||||
getName("editorSFX"),
|
||||
getDesc("editorSFX"),
|
||||
"editorSFX"));
|
||||
add(new Checkbox(
|
||||
"Pretty Print",
|
||||
"If checked, the saved files from the editor will be formatted to be easily viewable (does not apply to XMLs...)",
|
||||
getName("editorPrettyPrint"),
|
||||
getDesc("editorPrettyPrint"),
|
||||
"editorPrettyPrint"));
|
||||
add(new Checkbox(
|
||||
"Intensive Blur",
|
||||
"If checked, will use more intensive blur that may be laggier but look better.",
|
||||
getName("intensiveBlur"),
|
||||
getDesc("intensiveBlur"),
|
||||
"intensiveBlur"));
|
||||
add(new Checkbox(
|
||||
"Editor Autosaves",
|
||||
"If checked, this will autosave your files in the editor, with the settings listed below.",
|
||||
getName("charterAutoSaves"),
|
||||
getDesc("charterAutoSaves"),
|
||||
"charterAutoSaves"));
|
||||
add(new NumOption(
|
||||
"Autosaving Time",
|
||||
"This controls how often the editor will autosave your file (in seconds...)",
|
||||
getName("charterAutoSaveTime"),
|
||||
getDesc("charterAutoSaveTime"),
|
||||
60, 60*10, 30,
|
||||
"charterAutoSaveTime"
|
||||
));
|
||||
add(new NumOption(
|
||||
"Save Warning Time",
|
||||
"This controls how long the editor will warn you before it autosaves (in seconds..., 0 to disable)",
|
||||
getName("charterAutoSaveWarningTime"),
|
||||
getDesc("charterAutoSaveWarningTime"),
|
||||
0, 15, 1,
|
||||
"charterAutoSaveWarningTime"
|
||||
));
|
||||
add(new Checkbox(
|
||||
"Autosaves Folder",
|
||||
"If checked, this will autosave your file in a separate folder with a time stamp instead of overriding your current file. (song/autosaves/)",
|
||||
getName("charterAutoSavesSeparateFolder"),
|
||||
getDesc("charterAutoSavesSeparateFolder"),
|
||||
"charterAutoSavesSeparateFolder"));
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,13 @@ import flixel.math.FlxPoint;
|
||||
class EditorPicker extends MusicBeatSubstate {
|
||||
public var bg:FlxSprite;
|
||||
|
||||
// Name is for backwards compatibility, don't use it, use id instead
|
||||
public var options:Array<Editor> = [
|
||||
{
|
||||
name: "Alphabet Editor",
|
||||
id: "alphabet",
|
||||
state: funkin.editors.alphabet.AlphabetSelection
|
||||
},
|
||||
{
|
||||
name: "Chart Editor",
|
||||
id: "chart",
|
||||
@@ -22,7 +28,7 @@ class EditorPicker extends MusicBeatSubstate {
|
||||
id: "stage",
|
||||
state: funkin.editors.stage.StageSelection
|
||||
},
|
||||
#if debug
|
||||
#if (debug || debug_ui)
|
||||
{
|
||||
name: "UI Debug State",
|
||||
id: "uiDebug",
|
||||
@@ -74,7 +80,8 @@ class EditorPicker extends MusicBeatSubstate {
|
||||
|
||||
optionHeight = FlxG.height / options.length;
|
||||
for(k=>o in options) {
|
||||
var spr = new EditorPickerOption(o.name, o.id, optionHeight);
|
||||
var visualName = (o.id != null) ? TU.translate("editor." + o.id + ".name") : o.name;
|
||||
var spr = new EditorPickerOption(visualName, o.id, optionHeight);
|
||||
spr.y = k * optionHeight;
|
||||
add(spr);
|
||||
sprites.push(spr);
|
||||
@@ -176,13 +183,16 @@ class EditorPickerOption extends FlxTypedSpriteGroup<FlxSprite> {
|
||||
|
||||
FlxG.mouse.visible = true;
|
||||
iconSpr = new FlxSprite();
|
||||
iconSpr.loadGraphic(Paths.image('editors/icons/$iconID'));
|
||||
if(iconID != null)
|
||||
iconSpr.loadGraphic(Paths.image('editors/icons/$iconID'));
|
||||
else
|
||||
iconSpr.exists = false;
|
||||
iconSpr.antialiasing = true;
|
||||
iconSpr.setUnstretchedGraphicSize(110, 110, false);
|
||||
iconSpr.x = 25 + ((height - iconSpr.width) / 2);
|
||||
iconSpr.y = (height - iconSpr.height) / 2;
|
||||
|
||||
label = new Alphabet(25 + iconSpr.width + 25, 0, name, true);
|
||||
label = new Alphabet(25 + iconSpr.width + 25, 0, name, "bold");
|
||||
label.y = (height - label.height) / 2;
|
||||
|
||||
selectionBG = new FlxSprite().makeGraphic(1, 1, -1);
|
||||
|
||||
@@ -7,6 +7,7 @@ import funkin.options.*;
|
||||
class EditorTreeMenu extends TreeMenu {
|
||||
public var bg:FlxBackdrop;
|
||||
public var bgType:String = "default";
|
||||
public var bgMovement:FlxPoint = new FlxPoint();
|
||||
|
||||
public override function create() {
|
||||
super.create();
|
||||
@@ -18,16 +19,16 @@ class EditorTreeMenu extends TreeMenu {
|
||||
bg = new FlxBackdrop();
|
||||
bg.loadGraphic(Paths.image('editors/bgs/${bgType}'));
|
||||
bg.antialiasing = true;
|
||||
setBackgroundRotation(-5);
|
||||
add(bg);
|
||||
}
|
||||
|
||||
public inline function setBackgroundRotation(rotation:Float) {
|
||||
bg.rotation = rotation;
|
||||
bg.velocity.set(85, 0).degrees = bg.rotation;
|
||||
}
|
||||
|
||||
public override function exit() {
|
||||
FlxG.switchState(new MainMenuState());
|
||||
}
|
||||
|
||||
public override function update(elapsed:Float) {
|
||||
super.update(elapsed);
|
||||
bg.x += elapsed * 125;
|
||||
bg.y += elapsed * 125;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package funkin.editors;
|
||||
|
||||
import funkin.backend.assets.ModsFolderLibrary;
|
||||
import flixel.FlxState;
|
||||
|
||||
class ModConfigWarning extends UIState {
|
||||
public static var hadPopup:Bool = false;
|
||||
|
||||
var library:ModsFolderLibrary = null;
|
||||
var goToState:Class<FlxState>;
|
||||
|
||||
public static inline var defaultModConfigText =
|
||||
'[Common]
|
||||
MOD_NAME="YOUR MOD NAME HERE"
|
||||
MOD_DESCRIPTION="YOUR MOD DESCRIPTION HERE"
|
||||
MOD_AUTHOR="YOU/YOUR TEAM HERE"
|
||||
MOD_VERSION="YOUR MOD\'S VERSION HERE"
|
||||
|
||||
# DO NOT EDIT!! this is used to check for version compatibility!
|
||||
MOD_API_VERSION=1
|
||||
|
||||
MOD_DOWNLOAD_LINK="YOUR MOD PAGE LINK HERE"
|
||||
|
||||
# Not supported yet
|
||||
;MOD_ICON64="path/to/icon64.png"
|
||||
;MOD_ICON32="path/to/icon32.png"
|
||||
;MOD_ICON16="path/to/icon16.png"
|
||||
MOD_ICON="path/to/icon.png"
|
||||
|
||||
[Flags]
|
||||
DISABLE_WARNING_SCREEN=true
|
||||
# Set this to false if you want to bring back the warning state (prior to 1.0.0)
|
||||
# NOTE: Beta warning state has been renamed from BetaWarningState.hx to WarningState.hx
|
||||
|
||||
[Discord]
|
||||
MOD_DISCORD_CLIENT_ID=""
|
||||
MOD_DISCORD_LOGO_KEY=""
|
||||
MOD_DISCORD_LOGO_TEXT=""';
|
||||
|
||||
public function new(library:ModsFolderLibrary, ?goToState:Class<FlxState>) {
|
||||
super();
|
||||
this.library = library;
|
||||
this.goToState = goToState != null ? goToState : funkin.menus.TitleState;
|
||||
}
|
||||
|
||||
override function createPost() {
|
||||
super.createPost();
|
||||
hadPopup = true;
|
||||
|
||||
var substate = new UIWarningSubstate("Missing mod folder configuration!", "Your mod is currently missing a mod config file!\n\nWould you like to automatically generate one?\n\n(PS: If this is not your mod please disable Developer mode to stop this popup from appearing.)", [
|
||||
{
|
||||
label: "Not Now",
|
||||
color: 0x969533,
|
||||
onClick: function (_) {
|
||||
MusicBeatState.skipTransOut = MusicBeatState.skipTransIn = false;
|
||||
FlxG.switchState(cast Type.createInstance(goToState, []));
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "Yes",
|
||||
onClick: function(_) {
|
||||
var path = '${library.folderPath}/data/config/modpack.ini';
|
||||
CoolUtil.safeSaveFile(path, defaultModConfigText);
|
||||
openSubState(new UIWarningSubstate("Created mod config!", "Your mod config file has been created at " + path + "!", [
|
||||
{
|
||||
label: "Ok",
|
||||
onClick: function (_) {
|
||||
MusicBeatState.skipTransOut = MusicBeatState.skipTransIn = false;
|
||||
FlxG.switchState(cast Type.createInstance(goToState, []));
|
||||
}
|
||||
},
|
||||
], false));
|
||||
}
|
||||
}
|
||||
], false);
|
||||
openSubState(substate);
|
||||
}
|
||||
}
|
||||
@@ -21,15 +21,15 @@ class SaveWarning {
|
||||
if (warningFunc != null) warningFunc(closingWindow);
|
||||
|
||||
if (FlxG.state != null && FlxG.state is UIState) {
|
||||
FlxG.state.openSubState(new UIWarningSubstate("Unsaved Changes!", "Your changes will be lost if you don't save them. (Can't be recovered)\n\n\nWould you like to Cancel?",
|
||||
FlxG.state.openSubState(new UIWarningSubstate(TU.translate("saveWarning.warningTitle"), TU.translate("saveWarning.warningDesc"),
|
||||
[
|
||||
{
|
||||
label: "Cancel",
|
||||
label: TU.translate("saveWarning.cancel"),
|
||||
color: 0x969533,
|
||||
onClick: function (_) {if (closingWindow) WindowUtils.resetClosing();}
|
||||
},
|
||||
{
|
||||
label: closingWindow ? "Exit Game" : "Exit To Menu",
|
||||
label: closingWindow ? TU.translate("saveWarning.exitGame") : TU.translate("saveWarning.exitToMenu"),
|
||||
color: 0x969533,
|
||||
onClick: function(_) {
|
||||
if (!closingWindow) {
|
||||
@@ -37,12 +37,12 @@ class SaveWarning {
|
||||
if (closingWindow) WindowUtils.resetClosing();
|
||||
} else {
|
||||
WindowUtils.preventClosing = false; WindowUtils.resetClosing();
|
||||
Sys.exit(0);
|
||||
openfl.system.System.exit(0);
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: closingWindow ? "Save & Exit Game" : "Save & Exit To Menu",
|
||||
label: closingWindow ? TU.translate("saveWarning.saveAndExitGame") : TU.translate("saveWarning.saveAndExitToMenu"),
|
||||
color: 0x969533,
|
||||
onClick: function(_) {
|
||||
if (saveFunc != null) saveFunc();
|
||||
@@ -51,7 +51,7 @@ class SaveWarning {
|
||||
if (closingWindow) WindowUtils.resetClosing();
|
||||
} else {
|
||||
WindowUtils.preventClosing = false; WindowUtils.resetClosing();
|
||||
Sys.exit(0);
|
||||
openfl.system.System.exit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,499 @@
|
||||
package funkin.editors.alphabet;
|
||||
|
||||
import flixel.math.FlxPoint;
|
||||
import flixel.util.FlxColor;
|
||||
import funkin.backend.system.framerate.Framerate;
|
||||
import funkin.backend.utils.XMLUtil.AnimData;
|
||||
import funkin.editors.ui.UIContextMenu.UIContextMenuOption;
|
||||
import funkin.game.Character;
|
||||
import haxe.xml.Access;
|
||||
import haxe.xml.Printer;
|
||||
|
||||
@:access(funkin.menus.ui.Alphabet)
|
||||
class AlphabetEditor extends UIState {
|
||||
static var __typeface:String;
|
||||
|
||||
public static var instance(get, null):AlphabetEditor;
|
||||
|
||||
private static inline function get_instance()
|
||||
return FlxG.state is AlphabetEditor ? cast FlxG.state : null;
|
||||
|
||||
public var topMenu:Array<UIContextMenuOption>;
|
||||
public var topMenuSpr:UITopMenu;
|
||||
|
||||
public var uiGroup:FlxTypedGroup<FlxSprite> = new FlxTypedGroup<FlxSprite>();
|
||||
|
||||
var editorCamera:FlxCamera;
|
||||
var uiCamera:FlxCamera;
|
||||
|
||||
public function new(typeface:String) {
|
||||
super();
|
||||
if (typeface != null) __typeface = typeface;
|
||||
}
|
||||
|
||||
inline function translate(id:String, ?args:Array<Dynamic>)
|
||||
return TU.translate("editor.alphabet." + id, args);
|
||||
|
||||
public var brokenWarning:UIText;
|
||||
public var tape:Alphabet;
|
||||
public var bigLetter:Alphabet;
|
||||
public var curLetter:Int = 0;
|
||||
public var targetX:Float = 0;
|
||||
|
||||
public var queueReorder:Bool = false;
|
||||
public var componentList:UIButtonList<ComponentButton>;
|
||||
|
||||
public var infoWindow:GlyphInfoWindow;
|
||||
public var curSelectedComponent:AlphabetComponent = null;
|
||||
public var curSelectedData:AlphabetLetterData = null;
|
||||
public var outlineIdx:Int = -1;
|
||||
|
||||
public var defaultTmr:Float = 0.0;
|
||||
public var charsForDefault:Array<Array<String>> = [];
|
||||
|
||||
public override function create() {
|
||||
super.create();
|
||||
|
||||
WindowUtils.suffix = " (" + translate("name") + ")";
|
||||
SaveWarning.selectionClass = AlphabetSelection;
|
||||
SaveWarning.saveFunc = () -> {_file_save(null);};
|
||||
|
||||
topMenu = [
|
||||
{
|
||||
label: translate("topBar.file"),
|
||||
childs: [
|
||||
{
|
||||
label: translate("file.save"),
|
||||
keybind: [CONTROL, S],
|
||||
onSelect: _file_save
|
||||
},
|
||||
{
|
||||
label: translate("file.saveAs"),
|
||||
keybind: [CONTROL, SHIFT, S],
|
||||
onSelect: _file_saveas
|
||||
},
|
||||
null,
|
||||
{
|
||||
label: translate("file.exit"),
|
||||
onSelect: _file_exit
|
||||
}
|
||||
]
|
||||
// TODO: add more options
|
||||
},
|
||||
{
|
||||
label: translate("topBar.edit"),
|
||||
childs: [
|
||||
{
|
||||
label: translate("edit.undo"),
|
||||
// TODO: add undo
|
||||
},
|
||||
{
|
||||
label: translate("edit.redo"),
|
||||
// TODO: add redo
|
||||
},
|
||||
{
|
||||
label: "Edit Main Data", // TODO: add translations
|
||||
onSelect: _edit_main
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: translate("topBar.glyph"),
|
||||
childs: [
|
||||
{
|
||||
label: translate("glyph.newGlyph"),
|
||||
//onSelect: _glyph_new
|
||||
},
|
||||
{
|
||||
label: translate("glyph.editGlyph"),
|
||||
//onSelect: _glyph_edit
|
||||
},
|
||||
{
|
||||
label: translate("glyph.deleteGlyph"),
|
||||
//onSelect: _glyph_delete
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: translate("topBar.view"),
|
||||
childs: [
|
||||
{
|
||||
label: translate("view.zoomIn"),
|
||||
keybind: [CONTROL, NUMPADPLUS],
|
||||
//onSelect: _view_zoomin
|
||||
},
|
||||
{
|
||||
label: translate("view.zoomOut"),
|
||||
keybind: [CONTROL, NUMPADMINUS],
|
||||
//onSelect: _view_zoomout
|
||||
},
|
||||
{
|
||||
label: translate("view.resetZoom"),
|
||||
keybind: [CONTROL, NUMPADZERO],
|
||||
//onSelect: _view_zoomreset
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "Tape",
|
||||
childs: [
|
||||
{
|
||||
label: "Move Tape Left",
|
||||
keybind: [LEFT],
|
||||
onSelect: _tape_left
|
||||
},
|
||||
{
|
||||
label: "Move Tape Right",
|
||||
keybind: [RIGHT],
|
||||
onSelect: _tape_right
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
editorCamera = FlxG.camera;
|
||||
|
||||
uiCamera = new FlxCamera();
|
||||
uiCamera.bgColor = 0;
|
||||
|
||||
FlxG.cameras.add(uiCamera, false);
|
||||
|
||||
var bg = new FlxSprite(0, 0).makeSolid(Std.int(FlxG.width + 100), Std.int(FlxG.height + 100), 0xFF7f7f7f);
|
||||
bg.cameras = [editorCamera];
|
||||
bg.scrollFactor.set();
|
||||
bg.screenCenter();
|
||||
add(bg);
|
||||
|
||||
topMenuSpr = new UITopMenu(topMenu);
|
||||
topMenuSpr.cameras = uiGroup.cameras = [uiCamera];
|
||||
|
||||
tape = new Alphabet(0, 70, "", __typeface);
|
||||
tape.alignment = CENTER;
|
||||
tape.renderMode = MONOSPACE;
|
||||
add(tape);
|
||||
|
||||
bigLetter = new Alphabet(0, 0, "", "<SKIP>");
|
||||
bigLetter.copyData(tape);
|
||||
bigLetter.alignment = CENTER;
|
||||
bigLetter.scale.set(4, 4);
|
||||
// TODO: fix the offset issues, when not using updateHitbox
|
||||
bigLetter.updateHitbox();
|
||||
bigLetter.screenCenter();
|
||||
add(bigLetter);
|
||||
|
||||
tape.x = targetX;
|
||||
|
||||
brokenWarning = new UIText(0, 550, FlxG.width, "", 32);
|
||||
brokenWarning.alignment = CENTER;
|
||||
brokenWarning.color = 0xFFFF6969;
|
||||
add(brokenWarning);
|
||||
|
||||
infoWindow = new GlyphInfoWindow();
|
||||
uiGroup.add(infoWindow);
|
||||
|
||||
componentList = new UIButtonList<ComponentButton>(0, 720 - 170 - 30, 230, 170, "Components:", FlxPoint.get(230, 50), FlxPoint.get(0, 0), 0);
|
||||
componentList.dragCallback = (button, oldID, newID) -> {
|
||||
queueReorder = true; // not do it for every button reordered
|
||||
}
|
||||
componentList.addButton.callback = () -> {
|
||||
curSelectedComponent = {
|
||||
anim: "",
|
||||
x: 0,
|
||||
y: 0,
|
||||
|
||||
shouldRotate: false,
|
||||
angle: 0,
|
||||
sin: 0,
|
||||
cos: 1,
|
||||
|
||||
scaleX: 1,
|
||||
scaleY: 1,
|
||||
|
||||
hasColorMode: false,
|
||||
colorMode: bigLetter.colorMode
|
||||
};
|
||||
curSelectedData.components.push(curSelectedComponent);
|
||||
var newButton = new ComponentButton(curSelectedComponent);
|
||||
componentList.add(newButton);
|
||||
newButton.ID = componentList.buttons.members.length - 1; // readjust it because UIButtonList doesnt generate the id properly
|
||||
findOutline();
|
||||
infoWindow.updateInfo();
|
||||
}
|
||||
|
||||
updateTape();
|
||||
uiGroup.add(componentList);
|
||||
|
||||
add(topMenuSpr);
|
||||
add(uiGroup);
|
||||
|
||||
if(Framerate.isLoaded) {
|
||||
Framerate.fpsCounter.alpha = 0.4;
|
||||
Framerate.memoryCounter.alpha = 0.4;
|
||||
Framerate.codenameBuildField.alpha = 0.4;
|
||||
}
|
||||
|
||||
DiscordUtil.call("onEditorLoaded", ["Alphabet Editor", __typeface]);
|
||||
}
|
||||
|
||||
override function destroy() {
|
||||
super.destroy();
|
||||
if(Framerate.isLoaded) {
|
||||
Framerate.fpsCounter.alpha = 1;
|
||||
Framerate.memoryCounter.alpha = 1;
|
||||
Framerate.codenameBuildField.alpha = 1;
|
||||
}
|
||||
}
|
||||
|
||||
public override function update(elapsed:Float) {
|
||||
super.update(elapsed);
|
||||
if (queueReorder) {
|
||||
queueReorder = false;
|
||||
var outlines = [];
|
||||
var newList = [];
|
||||
for (button in componentList.buttons.members) {
|
||||
var com = button.component;
|
||||
if (com.outIndex != null) {
|
||||
var out = curSelectedData.components[com.outIndex];
|
||||
out.refIndex = newList.length;
|
||||
com.outIndex = outlines.length;
|
||||
outlines.push(out);
|
||||
}
|
||||
newList.push(com);
|
||||
}
|
||||
curSelectedData.components = outlines.concat(newList);
|
||||
|
||||
findOutline();
|
||||
for (i in 0...curSelectedData.components.length) {
|
||||
var anim = bigLetter.text + i;
|
||||
bigLetter.animation.remove(anim);
|
||||
tape.animation.remove(anim);
|
||||
}
|
||||
}
|
||||
|
||||
if (currentFocus == null) {
|
||||
if(FlxG.keys.justPressed.ANY)
|
||||
UIUtil.processShortcuts(topMenu);
|
||||
|
||||
if(curSelectedComponent != null) {
|
||||
if(FlxG.keys.pressed.K) {
|
||||
curSelectedComponent.y -= 100 * elapsed;
|
||||
}
|
||||
if(FlxG.keys.pressed.I) {
|
||||
curSelectedComponent.y += 100 * elapsed;
|
||||
}
|
||||
if(FlxG.keys.pressed.L) {
|
||||
curSelectedComponent.x -= 100 * elapsed;
|
||||
}
|
||||
if(FlxG.keys.pressed.J) {
|
||||
curSelectedComponent.x += 100 * elapsed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var lastIdx = Math.floor(defaultTmr);
|
||||
defaultTmr += elapsed * 0.5;
|
||||
if (lastIdx != (lastIdx = Math.floor(defaultTmr))) {
|
||||
tape.text = "";
|
||||
for (def in charsForDefault)
|
||||
tape.text += def[Std.int(lastIdx % def.length)] + " ";
|
||||
tape.text += tape.manualLetters.join(" ");
|
||||
}
|
||||
tape.x = lerp(tape.x, targetX, 0.25);
|
||||
}
|
||||
|
||||
function updateTape() {
|
||||
charsForDefault = [[], [], []];
|
||||
|
||||
for (i in 33...127) {
|
||||
var letter = String.fromCharCode(i);
|
||||
if (tape.manualLetters.contains(letter)) continue;
|
||||
|
||||
var data = tape.fastGetData(letter);
|
||||
for (i => com in data.components) {
|
||||
if (tape.fastGetLetterAnim(letter, data, com, i) == null)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i >= 'A'.code && i <= 'Z'.code && tape.defaults[CaseMode.UPPER] != null)
|
||||
charsForDefault[CaseMode.UPPER].push(letter);
|
||||
else if (i >= 'a'.code && i <= 'z'.code && tape.defaults[CaseMode.LOWER] != null)
|
||||
charsForDefault[CaseMode.LOWER].push(letter);
|
||||
else if (tape.defaults[CaseMode.NONE] != null)
|
||||
charsForDefault[CaseMode.NONE].push(letter);
|
||||
}
|
||||
|
||||
var i = charsForDefault.length - 1;
|
||||
while (i >= 0) {
|
||||
if (charsForDefault[i].length <= 0)
|
||||
charsForDefault.splice(i, 1);
|
||||
--i;
|
||||
}
|
||||
|
||||
tape.text = "";
|
||||
for (def in charsForDefault)
|
||||
tape.text += def[Math.floor(defaultTmr % def.length)] + " ";
|
||||
tape.text += tape.manualLetters.join(" ");
|
||||
changeLetter(0);
|
||||
}
|
||||
|
||||
function changeLetter(inc:Int) {
|
||||
curLetter = CoolUtil.positiveModuloInt(curLetter + inc, tape.manualLetters.length + charsForDefault.length);
|
||||
targetX = FlxG.width * 0.5 - tape.defaultAdvance * (0.5 + curLetter * 2);
|
||||
bigLetter.text = (curLetter < charsForDefault.length) ? charsForDefault[curLetter][0] : tape.manualLetters[curLetter - charsForDefault.length];
|
||||
bigLetter.updateHitbox();
|
||||
bigLetter.screenCenter();
|
||||
|
||||
checkForFailed();
|
||||
|
||||
while (componentList.buttons.members.length > 0)
|
||||
componentList.remove(componentList.buttons.members[0]);
|
||||
for (i in curSelectedData.startIndex...curSelectedData.components.length) {
|
||||
var newButton = new ComponentButton(curSelectedData.components[i]);
|
||||
componentList.add(newButton);
|
||||
newButton.ID = componentList.buttons.members.length - 1; // readjust it because UIButtonList doesnt generate the id properly
|
||||
}
|
||||
curSelectedComponent = (curSelectedData.components.length > 0) ? curSelectedData.components[curSelectedData.startIndex] : null;
|
||||
findOutline();
|
||||
infoWindow.updateInfo();
|
||||
}
|
||||
|
||||
public function findOutline() {
|
||||
outlineIdx = -1;
|
||||
for (i in curSelectedData.startIndex...curSelectedData.components.length) {
|
||||
var com = curSelectedData.components[i];
|
||||
if (com.outIndex != null)
|
||||
outlineIdx = com.outIndex;
|
||||
if (com == curSelectedComponent)
|
||||
break;
|
||||
}
|
||||
++outlineIdx;
|
||||
}
|
||||
|
||||
public function checkForFailed() {
|
||||
bigLetter.failedLetters.remove(bigLetter.text);
|
||||
curSelectedData = bigLetter.fastGetData(bigLetter.text);
|
||||
brokenWarning.text = "";
|
||||
bigLetter.draw();
|
||||
if (!bigLetter.failedLetters.contains(bigLetter.text))
|
||||
return false;
|
||||
|
||||
for (i in 0...curSelectedData.components.length) {
|
||||
var com = curSelectedData.components[i];
|
||||
if (!bigLetter.animation.exists(bigLetter.text + Std.string(i)))
|
||||
brokenWarning.text += " Unable to find: " + com.anim + "\n";
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function _tape_left(_) {
|
||||
changeLetter(-1);
|
||||
}
|
||||
function _tape_right(_) {
|
||||
changeLetter(1);
|
||||
}
|
||||
|
||||
function _file_save(_) {
|
||||
#if sys
|
||||
CoolUtil.safeSaveFile(
|
||||
'${Paths.getAssetsRoot()}/data/alphabet/${__typeface}.xml',
|
||||
""//alphabet.buildXML()
|
||||
);
|
||||
#else
|
||||
_file_saveas(_);
|
||||
#end
|
||||
}
|
||||
|
||||
function _file_saveas(_) {
|
||||
openSubState(new SaveSubstate(""/*alphabet.buildXML()*/, {
|
||||
defaultSaveFile: '${__typeface}.xml'
|
||||
}));
|
||||
}
|
||||
|
||||
function _file_exit(_) {
|
||||
/*if (undos.unsaved) SaveWarning.triggerWarning();
|
||||
else */FlxG.switchState(new AlphabetSelection());
|
||||
}
|
||||
|
||||
function _edit_main(_) {
|
||||
FlxG.state.openSubState(new AlphabetMainDataScreen());
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
/===============\
|
||||
| Components |
|
||||
|===============|
|
||||
|[ Component 1 ]|
|
||||
|[ Component 2 ]|
|
||||
|[ Component 3 ]|
|
||||
|[ Component 4 ]|
|
||||
|[ Component 5 ]|
|
||||
|[ Add component]|
|
||||
\===============/
|
||||
|
||||
*/
|
||||
|
||||
class ComponentButton extends UIButton {
|
||||
public var component:AlphabetComponent;
|
||||
|
||||
public var selected:Bool = false;
|
||||
|
||||
public var deleteButton:UIButton;
|
||||
public var deleteIcon:FlxSprite;
|
||||
|
||||
public function new(component:AlphabetComponent) {
|
||||
super(0, 0, component.anim, function() {
|
||||
AlphabetEditor.instance.curSelectedComponent = component;
|
||||
AlphabetEditor.instance.findOutline();
|
||||
AlphabetEditor.instance.infoWindow.updateInfo();
|
||||
}, 230, 50);
|
||||
this.component = component;
|
||||
|
||||
deleteButton = new UIButton(bWidth - 32 - 10, 0, "", function() {
|
||||
var data = AlphabetEditor.instance.curSelectedData;
|
||||
|
||||
var outlineDrop = (component.outIndex != null) ? 1 : 0;
|
||||
var componentIndex = data.components.indexOf(component);
|
||||
for (i in 0...data.components.length) {
|
||||
var nextCompon = data.components[i];
|
||||
var anim = AlphabetEditor.instance.bigLetter.text + i;
|
||||
AlphabetEditor.instance.bigLetter.animation.remove(anim);
|
||||
AlphabetEditor.instance.tape.animation.remove(anim);
|
||||
|
||||
if (i > componentIndex && nextCompon.outIndex != null) {
|
||||
--data.components[nextCompon.outIndex].refIndex;
|
||||
nextCompon.outIndex -= outlineDrop;
|
||||
}
|
||||
}
|
||||
|
||||
if (component.outIndex != null) {
|
||||
data.components.splice(component.outIndex, 1);
|
||||
--data.startIndex;
|
||||
}
|
||||
|
||||
data.components.remove(component);
|
||||
AlphabetEditor.instance.curSelectedComponent = (AlphabetEditor.instance.curSelectedComponent == component) ? null : AlphabetEditor.instance.curSelectedComponent;
|
||||
AlphabetEditor.instance.findOutline();
|
||||
AlphabetEditor.instance.infoWindow.updateInfo();
|
||||
|
||||
AlphabetEditor.instance.componentList.remove(this);
|
||||
}, 32);
|
||||
deleteButton.color = FlxColor.RED;
|
||||
deleteButton.autoAlpha = false;
|
||||
members.push(deleteButton);
|
||||
|
||||
deleteIcon = new FlxSprite(deleteButton.x + (15/2), deleteButton.y + 8).loadGraphic(Paths.image('editors/delete-button'));
|
||||
deleteIcon.antialiasing = false;
|
||||
members.push(deleteIcon);
|
||||
}
|
||||
|
||||
override function update(elapsed:Float) {
|
||||
super.update(elapsed);
|
||||
|
||||
deleteButton.y = y + 10;
|
||||
deleteIcon.x = deleteButton.x + (15/2);
|
||||
deleteIcon.y = deleteButton.y + 8;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
package funkin.editors.alphabet;
|
||||
|
||||
import flixel.math.FlxRect;
|
||||
import funkin.editors.alphabet.AlphabetEditor;
|
||||
import funkin.menus.ui.Alphabet;
|
||||
|
||||
using StringTools;
|
||||
|
||||
// TODO: add translations
|
||||
class AlphabetMainDataScreen extends UISubstateWindow {
|
||||
var bigLetter:Alphabet;
|
||||
var tape:Alphabet;
|
||||
|
||||
public var saveButton:UIButton;
|
||||
public var closeButton:UIButton;
|
||||
public var helpButton:UIButton;
|
||||
|
||||
public var fpsStepper:UINumericStepper;
|
||||
public var advanceStepper:UINumericStepper;
|
||||
public var lineGapStepper:UINumericStepper;
|
||||
|
||||
public var caseDropdown:UIDropDown;
|
||||
public var colorDropdown:UIDropDown;
|
||||
|
||||
public var regTextBox:UITextBox;
|
||||
public var upperTextBox:UITextBox;
|
||||
public var lowerTextBox:UITextBox;
|
||||
|
||||
public var targetPercent:Float = 0.0;
|
||||
public var helpBG:UISliceSprite;
|
||||
public var helpTxt:UIText;
|
||||
public var helpScroll:UIScrollBar;
|
||||
|
||||
public override function create() {
|
||||
bigLetter = AlphabetEditor.instance.bigLetter;
|
||||
tape = AlphabetEditor.instance.tape;
|
||||
|
||||
// temporary until translated
|
||||
var helpDesc =
|
||||
"FPS - Frames per second. You get it.
|
||||
Advance - Actually a backup advance value for missing characters and spaces.
|
||||
Line Gap - How many pixels to move down when a new line is created.
|
||||
|
||||
Force Casing - Whether to force the text casing to uppercase, lowercase, or neither.
|
||||
Color Mode - How to color the alphabet.
|
||||
TINT muliplies the channels, perfect for white letters.
|
||||
OFFSETS adds to the channels, perfect for black letters.
|
||||
NONE simply disables coloring.
|
||||
|
||||
Backup Prefixes - What prefix to use if a glyph is not predefined.
|
||||
Uppercase and Lowercase will default to regular if it is left blank.
|
||||
LETTER will be substituted with the provided glyph.
|
||||
UPPERLETTER will be substituted with the uppercase glyph.
|
||||
LOWERLETTER will be substituted with the lowercase glyph.
|
||||
UPPERLETTER and LOWERLETTTER can be used in any backup prefix.";
|
||||
|
||||
winTitle = "Alphabet Properties";
|
||||
winWidth = 420; winHeight = 520;
|
||||
|
||||
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, "Edit Main Alphabet Data", 28));
|
||||
|
||||
fpsStepper = new UINumericStepper(title.x, title.y + title.height + 38, AlphabetEditor.instance.bigLetter.fps, 1, 2, 0.01, null, 120);
|
||||
add(fpsStepper);
|
||||
addLabelOn(fpsStepper, "FPS");
|
||||
|
||||
advanceStepper = new UINumericStepper(fpsStepper.x + 140, fpsStepper.y, AlphabetEditor.instance.bigLetter.defaultAdvance, 1, 2, 0, null, 120);
|
||||
add(advanceStepper);
|
||||
addLabelOn(advanceStepper, "Advance");
|
||||
|
||||
lineGapStepper = new UINumericStepper(advanceStepper.x + 140, advanceStepper.y, AlphabetEditor.instance.bigLetter.lineGap, 1, 2, 0, null, 120);
|
||||
add(lineGapStepper);
|
||||
addLabelOn(lineGapStepper, "Line Gap");
|
||||
|
||||
caseDropdown = new UIDropDown(fpsStepper.x, fpsStepper.y + fpsStepper.bHeight + 50, 206, 32, ["NONE", "UPPER", "LOWER"], tape.forceCase);
|
||||
add(caseDropdown);
|
||||
addLabelOn(caseDropdown, "Force Casing");
|
||||
|
||||
colorDropdown = new UIDropDown(caseDropdown.x + caseDropdown.bWidth + 20, caseDropdown.y, 206, 32, ["TINT", "OFFSETS", "NONE"], tape.colorMode);
|
||||
add(colorDropdown);
|
||||
addLabelOn(colorDropdown, "Color Mode");
|
||||
|
||||
var regPrefix = (tape.defaults[0] != null) ? tape.defaults[0].components[0].anim : "";
|
||||
regTextBox = new UITextBox(caseDropdown.x, caseDropdown.y + caseDropdown.bHeight + 50, regPrefix, 368);
|
||||
add(regTextBox);
|
||||
addLabelOn(regTextBox, "Backup Prefix (Regular)");
|
||||
|
||||
var upperPrefix = (tape.defaults[1] != null) ? tape.defaults[1].components[0].anim : "";
|
||||
upperTextBox = new UITextBox(regTextBox.x, regTextBox.y + regTextBox.bHeight + 35, upperPrefix, 368);
|
||||
add(upperTextBox);
|
||||
addLabelOn(upperTextBox, "Backup Prefix (Uppercase)");
|
||||
|
||||
var lowerPrefix = (tape.defaults[2] != null) ? tape.defaults[2].components[0].anim : "";
|
||||
lowerTextBox = new UITextBox(upperTextBox.x, upperTextBox.y + upperTextBox.bHeight + 35, lowerPrefix, 368);
|
||||
add(lowerTextBox);
|
||||
addLabelOn(lowerTextBox, "Backup Prefix (Lowercase)");
|
||||
|
||||
saveButton = new UIButton(windowSpr.x + windowSpr.bWidth - 20 - 125, windowSpr.y + windowSpr.bHeight - 16 - 32, TU.translate("editor.saveClose"), function() {
|
||||
saveInfo();
|
||||
close();
|
||||
}, 125);
|
||||
add(saveButton);
|
||||
|
||||
closeButton = new UIButton(saveButton.x - 20, saveButton.y, TU.translate("editor.close"), function() {
|
||||
close();
|
||||
}, 125);
|
||||
add(closeButton);
|
||||
closeButton.color = 0xFFFF0000;
|
||||
closeButton.x -= closeButton.bWidth;
|
||||
|
||||
helpButton = new UIButton(closeButton.x - 20, closeButton.y, "?", function() {
|
||||
targetPercent = 1.0 - targetPercent;
|
||||
}, 32);
|
||||
add(helpButton);
|
||||
helpButton.x -= helpButton.bWidth;
|
||||
|
||||
helpBG = new UISliceSprite(windowSpr.x + windowSpr.bWidth - 20, windowSpr.y, 340, windowSpr.bHeight, 'editors/ui/context-bg');
|
||||
helpBG.alpha = 0.0;
|
||||
add(helpBG);
|
||||
|
||||
helpTxt = new UIText(helpBG.x + 20, helpBG.y + 20, helpBG.bWidth - 60, helpDesc);
|
||||
helpTxt.clipRect = new FlxRect(0, 0, helpTxt.width, helpBG.bHeight - 40);
|
||||
helpTxt.alpha = 0.0;
|
||||
add(helpTxt);
|
||||
|
||||
helpScroll = new UIScrollBar(helpBG.x + helpBG.bWidth - 30, helpBG.y + 20, (helpTxt.height - helpTxt.clipRect.height), 0, (helpTxt.clipRect.height / helpTxt.height), 20, helpBG.bHeight - 40);
|
||||
helpScroll.size *= helpScroll.height;
|
||||
helpScroll.start -= helpScroll.size * 0.5;
|
||||
helpScroll.alpha = 0.0;
|
||||
helpScroll.onChange = function(v) {
|
||||
helpTxt.clipRect.y = v;
|
||||
helpTxt.offset.y = v;
|
||||
helpTxt.clipRect = helpTxt.clipRect;
|
||||
helpScroll.start = v - helpScroll.size * 0.5;
|
||||
}
|
||||
add(helpScroll);
|
||||
}
|
||||
|
||||
override function update(elapsed:Float) {
|
||||
super.update(elapsed);
|
||||
|
||||
helpBG.alpha = FlxMath.lerp(helpBG.alpha, targetPercent, elapsed * 15);
|
||||
helpBG.x = windowSpr.x + windowSpr.bWidth - 20 + 40 * helpBG.alpha;
|
||||
helpTxt.alpha = helpBG.alpha;
|
||||
helpTxt.x = helpBG.x + 20;
|
||||
helpScroll.alpha = helpScroll.thumb.alpha = helpScroll.thumbIcon.alpha = helpBG.alpha;
|
||||
helpScroll.x = helpBG.x + helpBG.bWidth - 30;
|
||||
}
|
||||
|
||||
public function saveInfo() {
|
||||
@:privateAccess fpsStepper.__onChange(fpsStepper.label.text);
|
||||
@:privateAccess advanceStepper.__onChange(advanceStepper.label.text);
|
||||
@:privateAccess lineGapStepper.__onChange(lineGapStepper.label.text);
|
||||
|
||||
bigLetter.fps = tape.fps = fpsStepper.value;
|
||||
bigLetter.defaultAdvance = tape.defaultAdvance = advanceStepper.value;
|
||||
bigLetter.lineGap = tape.lineGap = lineGapStepper.value;
|
||||
|
||||
bigLetter.forceCase = tape.forceCase = caseDropdown.index;
|
||||
bigLetter.colorMode = tape.colorMode = colorDropdown.index;
|
||||
|
||||
// give the letter loading another chance
|
||||
tape.animation.destroyAnimations();
|
||||
bigLetter.animation.destroyAnimations();
|
||||
tape.failedLetters.splice(1, tape.failedLetters.length - 1);
|
||||
|
||||
setDefault(0, regTextBox.label.text);
|
||||
setDefault(1, upperTextBox.label.text);
|
||||
setDefault(2, lowerTextBox.label.text);
|
||||
}
|
||||
|
||||
function setDefault(index:Int, name:String) {
|
||||
tape.loaded[index].splice(0, tape.loaded[index].length);
|
||||
if (name.trim() == "") {
|
||||
tape.defaults[index] = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (tape.defaults[index] != null) {
|
||||
tape.defaults[index].components[0].anim = name;
|
||||
trace(tape.defaults[index].components[0].anim);
|
||||
return;
|
||||
}
|
||||
|
||||
final newData:AlphabetLetterData = {
|
||||
isDefault: true,
|
||||
advance: 0.0,
|
||||
advanceEmpty: true,
|
||||
components: [{
|
||||
anim: name,
|
||||
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
scaleX: 1.0,
|
||||
scaleY: 1.0,
|
||||
|
||||
shouldRotate: false,
|
||||
angle: 0.0,
|
||||
cos: 1.0,
|
||||
sin: 0.0,
|
||||
|
||||
hasColorMode: false,
|
||||
colorMode: 0
|
||||
}],
|
||||
startIndex: 0
|
||||
};
|
||||
tape.defaults[index] = newData;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package funkin.editors.alphabet;
|
||||
|
||||
import haxe.xml.Access;
|
||||
import funkin.game.Character;
|
||||
import funkin.options.OptionsScreen;
|
||||
import funkin.options.type.IconOption;
|
||||
import funkin.options.type.NewOption;
|
||||
import funkin.options.type.TextOption;
|
||||
import funkin.options.type.OptionType;
|
||||
import haxe.io.Path;
|
||||
|
||||
class AlphabetSelection extends EditorTreeMenu
|
||||
{
|
||||
inline function translate(id:String, ?args:Array<Dynamic>)
|
||||
return TU.translate("editor.alphabet.selection." + id, args);
|
||||
|
||||
public override function create()
|
||||
{
|
||||
bgType = "charter";
|
||||
super.create();
|
||||
|
||||
var modsList:Array<String> = [];
|
||||
for(file in Paths.getFolderContent('data/alphabet/', true, BOTH)) // mods ? MODS : BOTH
|
||||
if (Path.extension(file) == "xml") modsList.push(CoolUtil.getFilename(file));
|
||||
|
||||
var list:Array<OptionType> = [
|
||||
for (typeface in modsList)
|
||||
new AlphabetIconOption(typeface, translate("acceptTypeface"), typeface, function() {
|
||||
FlxG.switchState(new AlphabetEditor(typeface));
|
||||
})
|
||||
];
|
||||
|
||||
var newChar = translate("newTypeface");
|
||||
list.insert(0, new NewOption(newChar, newChar, function() {
|
||||
openSubState(new UIWarningSubstate(translate("warnings.notImplemented-title"), translate("warnings.notImplemented-body"), [
|
||||
{label: TU.translate("editor.ok"), color: 0xFFFF0000, onClick: function(t) {}}
|
||||
]));
|
||||
}));
|
||||
|
||||
main = new OptionsScreen(TU.translate("editor.alphabet.name"), translate("desc"), list);
|
||||
|
||||
DiscordUtil.call("onEditorTreeLoaded", ["Alphabet Editor"]);
|
||||
}
|
||||
|
||||
override function createPost() {
|
||||
super.createPost();
|
||||
|
||||
main.changeSelection(1);
|
||||
}
|
||||
}
|
||||
|
||||
class AlphabetIconOption extends TextOption {
|
||||
public var iconSpr:FlxSprite;
|
||||
|
||||
public function new(name:String, desc:String, typeface:String, callback:Void->Void) {
|
||||
super(name, desc, callback);
|
||||
|
||||
var xml = Xml.parse(Assets.getText(Paths.xml('alphabet/$typeface'))).firstElement();
|
||||
var spritesheet = null;
|
||||
for(node in xml.elements()) {
|
||||
if (node.nodeName == "spritesheet") {
|
||||
spritesheet = node.firstChild().nodeValue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var useColorOffsets = xml.get("useColorOffsets").getDefault("false") == "true";
|
||||
|
||||
|
||||
// todo fix crash if invalid spritesheet;
|
||||
|
||||
iconSpr = new FlxSprite();
|
||||
iconSpr.frames = Paths.getFrames(spritesheet);
|
||||
iconSpr.antialiasing = true;
|
||||
var frameToUse = iconSpr.frames.frames[0];
|
||||
for(frame in iconSpr.frames.frames) {
|
||||
if (frame.name.toUpperCase().startsWith("A")) {
|
||||
frameToUse = frame;
|
||||
break;
|
||||
}
|
||||
}
|
||||
iconSpr.frame = frameToUse;
|
||||
if(useColorOffsets) {
|
||||
iconSpr.colorTransform.color = -1;
|
||||
}
|
||||
iconSpr.setPosition(90 - iconSpr.width - 20, (__text.height - iconSpr.height) / 2);
|
||||
add(iconSpr);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,274 @@
|
||||
package funkin.editors.alphabet;
|
||||
|
||||
import flixel.math.FlxAngle;
|
||||
|
||||
class GlyphInfoWindow extends UIWindow {
|
||||
public var prefixBox:UITextBox;
|
||||
public var xBox:UINumericStepper; // LIVE
|
||||
public var yBox:UINumericStepper; // MJWF
|
||||
public var scaleXBox:UINumericStepper;
|
||||
public var scaleYBox:UINumericStepper;
|
||||
public var angleBox:UINumericStepper;
|
||||
public var colorModeDrop:UIDropDown;
|
||||
|
||||
public var outlineTitle:UIText;
|
||||
public var outlineSep:UISprite;
|
||||
public var outlineCheck:UICheckbox;
|
||||
public var outlineBoxTitle:UIText;
|
||||
public var outlineBox:UITextBox;
|
||||
public var outlineOffTitle:UIText;
|
||||
public var outlineXBox:UINumericStepper;
|
||||
public var outlineYBox:UINumericStepper;
|
||||
|
||||
public var targetPercent:Float = 0;
|
||||
public var outlinePercent:Float = 0;
|
||||
public var outlineAlphaMults:Array<Float>;
|
||||
public var outlineItems:Array<FlxSprite>;
|
||||
public var initialY:Array<Float> = [];
|
||||
|
||||
var compon(get, never):AlphabetComponent;
|
||||
var data(get, never):AlphabetLetterData;
|
||||
|
||||
public function new() {
|
||||
var width = 360;
|
||||
var height = 255;
|
||||
var margin = 30;
|
||||
var itemMargin = 15; // there was already a buncha variables so i thought why not follow
|
||||
var labelOffset = 24;
|
||||
super(FlxG.width - width - margin, FlxG.height - height - margin, width, height, "Glyph Info");
|
||||
|
||||
function addLabelOn(ui:UISprite, text:String) {
|
||||
var title = new UIText(ui.x, ui.y - labelOffset, 0, text);
|
||||
members.push(title);
|
||||
return title;
|
||||
}
|
||||
|
||||
prefixBox = new UITextBox(x + itemMargin, y + itemMargin + labelOffset * 2, "", 330);
|
||||
prefixBox.onChange = function(val) {
|
||||
var index = data.components.indexOf(compon);
|
||||
compon.anim = val;
|
||||
var anim = AlphabetEditor.instance.bigLetter.text + Std.string(index);
|
||||
AlphabetEditor.instance.bigLetter.animation.remove(anim);
|
||||
AlphabetEditor.instance.tape.animation.remove(anim);
|
||||
AlphabetEditor.instance.checkForFailed();
|
||||
}
|
||||
members.push(prefixBox);
|
||||
addLabelOn(prefixBox, "Animation Prefix");
|
||||
|
||||
xBox = new UINumericStepper(prefixBox.x, prefixBox.y + prefixBox.bHeight + itemMargin + labelOffset, 0, 1, 2, null, null, 80);
|
||||
xBox.onChange = valueSet.bind(xBox, function(val) { // kinda dumb but blame cne ui
|
||||
compon.x = val;
|
||||
});
|
||||
members.push(xBox);
|
||||
|
||||
members.push(new UIText(xBox.x + xBox.bWidth - 2, xBox.y + 9, 0, ",", 22));
|
||||
|
||||
yBox = new UINumericStepper(xBox.x + xBox.bWidth + itemMargin, xBox.y, 0, 1, 2, null, null, 80);
|
||||
yBox.onChange = valueSet.bind(yBox, function(val) { // kinda dumb but blame cne ui
|
||||
compon.y = val;
|
||||
});
|
||||
members.push(yBox);
|
||||
addLabelOn(xBox, "Offset (x,y)");
|
||||
|
||||
scaleXBox = new UINumericStepper(yBox.x + yBox.bWidth + itemMargin * 2, yBox.y, 1, 0.1, 2, null, null, 80);
|
||||
scaleXBox.onChange = valueSet.bind(scaleXBox, function(val) { // kinda dumb but blame cne ui
|
||||
compon.scaleX = val;
|
||||
if (outlineCheck.checked)
|
||||
data.components[compon.outIndex].scaleX = val;
|
||||
});
|
||||
members.push(scaleXBox);
|
||||
|
||||
members.push(new UIText(scaleXBox.x + scaleXBox.bWidth - 2, scaleXBox.y + 9, 0, ",", 22));
|
||||
|
||||
scaleYBox = new UINumericStepper(scaleXBox.x + scaleXBox.bWidth + itemMargin, scaleXBox.y, 1, 0.1, 2, null, null, 80);
|
||||
scaleYBox.onChange = valueSet.bind(scaleYBox, function(val) { // kinda dumb but blame cne ui
|
||||
compon.scaleY = val;
|
||||
if (outlineCheck.checked)
|
||||
data.components[compon.outIndex].scaleY = val;
|
||||
});
|
||||
members.push(scaleYBox);
|
||||
addLabelOn(scaleXBox, "Scale (x,y)");
|
||||
|
||||
angleBox = new UINumericStepper(scaleYBox.x + scaleYBox.bWidth + itemMargin * 2, scaleYBox.y, 0, 1, 2, null, null, 80);
|
||||
angleBox.onChange = valueSet.bind(angleBox, function(val) { // kinda dumb but blame cne ui
|
||||
compon.shouldRotate = val != 0;
|
||||
compon.angle = val;
|
||||
compon.cos = Math.cos(val * FlxAngle.TO_RAD);
|
||||
compon.sin = Math.sin(val * FlxAngle.TO_RAD);
|
||||
|
||||
if (outlineCheck.checked) {
|
||||
data.components[compon.outIndex].shouldRotate = compon.shouldRotate;
|
||||
data.components[compon.outIndex].angle = compon.angle;
|
||||
data.components[compon.outIndex].cos = compon.cos;
|
||||
data.components[compon.outIndex].sin = compon.sin;
|
||||
}
|
||||
});
|
||||
members.push(angleBox);
|
||||
addLabelOn(angleBox, "Angle");
|
||||
|
||||
colorModeDrop = new UIDropDown(xBox.x, xBox.y + xBox.bHeight + itemMargin + labelOffset, 145, 32, ["TINT", "OFFSETS", "NONE"]);
|
||||
colorModeDrop.onChange = function(mode) {
|
||||
compon.colorMode = mode;
|
||||
@:privateAccess compon.hasColorMode = mode != AlphabetEditor.instance.tape.colorMode;
|
||||
if (outlineCheck.checked) {
|
||||
data.components[compon.outIndex].colorMode = mode;
|
||||
@:privateAccess data.components[compon.outIndex].hasColorMode = compon.hasColorMode;
|
||||
}
|
||||
};
|
||||
members.push(colorModeDrop);
|
||||
addLabelOn(colorModeDrop, "Color Mode");
|
||||
|
||||
outlineCheck = new UICheckbox(colorModeDrop.x + colorModeDrop.bWidth + itemMargin * 2, colorModeDrop.y + 4, "Use Outline?");
|
||||
outlineCheck.onChecked = function(val) {
|
||||
targetPercent = val ? 1 : 0;
|
||||
|
||||
for (i in 0...data.components.length) {
|
||||
var anim = AlphabetEditor.instance.bigLetter.text + i;
|
||||
AlphabetEditor.instance.bigLetter.animation.remove(anim);
|
||||
AlphabetEditor.instance.tape.animation.remove(anim);
|
||||
}
|
||||
|
||||
if (val) {
|
||||
compon.outIndex = AlphabetEditor.instance.outlineIdx;
|
||||
|
||||
var newOutline:AlphabetComponent = {
|
||||
refIndex: data.components.indexOf(compon),
|
||||
anim: outlineBox.label.text,
|
||||
|
||||
x: outlineXBox.value,
|
||||
y: outlineYBox.value,
|
||||
scaleX: compon.scaleX,
|
||||
scaleY: compon.scaleY,
|
||||
|
||||
shouldRotate: compon.shouldRotate,
|
||||
angle: compon.angle,
|
||||
cos: compon.cos,
|
||||
sin: compon.sin,
|
||||
|
||||
hasColorMode: compon.hasColorMode,
|
||||
colorMode: compon.colorMode
|
||||
};
|
||||
|
||||
data.components.insert(AlphabetEditor.instance.outlineIdx, newOutline);
|
||||
++data.startIndex;
|
||||
} else {
|
||||
compon.outIndex = null;
|
||||
data.components.splice(AlphabetEditor.instance.outlineIdx - 1, 1);
|
||||
--data.startIndex;
|
||||
}
|
||||
}
|
||||
members.push(outlineCheck);
|
||||
|
||||
outlineTitle = new UIText(colorModeDrop.x, colorModeDrop.y + colorModeDrop.bHeight + itemMargin, 0, "Outline Data");
|
||||
members.push(outlineTitle);
|
||||
outlineSep = new UISprite(x + itemMargin, colorModeDrop.y + colorModeDrop.bHeight + itemMargin + 19);
|
||||
outlineSep.makeSolid(width - itemMargin * 2, 1, 0x40FFFFFF);
|
||||
members.push(outlineSep);
|
||||
|
||||
outlineBox = new UITextBox(colorModeDrop.x, colorModeDrop.y + colorModeDrop.bHeight + itemMargin + labelOffset * 2 + 3, "", 190);
|
||||
outlineBox.onChange = function(val) {
|
||||
data.components[compon.outIndex].anim = val;
|
||||
var anim = AlphabetEditor.instance.bigLetter.text + Std.string(compon.outIndex);
|
||||
AlphabetEditor.instance.bigLetter.animation.remove(anim);
|
||||
AlphabetEditor.instance.tape.animation.remove(anim);
|
||||
AlphabetEditor.instance.checkForFailed();
|
||||
}
|
||||
members.push(outlineBox);
|
||||
outlineBoxTitle = addLabelOn(outlineBox, "Prefix");
|
||||
|
||||
outlineXBox = new UINumericStepper(outlineBox.x + outlineBox.bWidth + itemMargin * 2, outlineBox.y, 0, 1, 2, null, null, 80);
|
||||
outlineXBox.onChange = valueSet.bind(outlineXBox, function(val) { // kinda dumb but blame cne ui
|
||||
if (outlineCheck.checked)
|
||||
data.components[compon.outIndex].x = val;
|
||||
});
|
||||
members.push(outlineXBox);
|
||||
|
||||
members.push(new UIText(outlineXBox.x + outlineXBox.bWidth - 2, outlineXBox.y + 9, 0, ",", 22));
|
||||
|
||||
outlineYBox = new UINumericStepper(outlineXBox.x + outlineXBox.bWidth + itemMargin, outlineXBox.y, 0, 1, 2, null, null, 80);
|
||||
outlineYBox.onChange = valueSet.bind(outlineYBox, function(val) { // kinda dumb but blame cne ui
|
||||
if (outlineCheck.checked)
|
||||
data.components[compon.outIndex].y = val;
|
||||
});
|
||||
members.push(outlineYBox);
|
||||
outlineOffTitle = addLabelOn(outlineXBox, "Offset (x,y)");
|
||||
|
||||
for (mem in members) {
|
||||
if (mem is FlxObject)
|
||||
initialY.push(cast (mem, FlxObject).y);
|
||||
}
|
||||
outlineAlphaMults = [1.0, 0.9, 0.5, 0.15, 0.15, 0.5, 0.15, 0.15, 0.15, 0.15];
|
||||
outlineItems = [outlineTitle, outlineSep, outlineBoxTitle, outlineBox, outlineBox.label, outlineOffTitle, outlineXBox, outlineXBox.label, outlineYBox, outlineYBox.label];
|
||||
}
|
||||
|
||||
override public function update(elapsed:Float) {
|
||||
super.update(elapsed);
|
||||
|
||||
outlinePercent = FlxMath.lerp(outlinePercent, targetPercent, elapsed * 10);
|
||||
bHeight = Std.int(255 + 105 * outlinePercent);
|
||||
y = FlxG.height - bHeight - 30;
|
||||
|
||||
for (i in 0...outlineItems.length)
|
||||
outlineItems[i].alpha = outlinePercent * FlxMath.lerp(outlineAlphaMults[i], 1, outlinePercent * outlinePercent) * alpha;
|
||||
|
||||
var yIdxOff = 0;
|
||||
for (i in 0...members.length) {
|
||||
if (members[i] is FlxObject)
|
||||
cast (members[i], FlxObject).y = initialY[i - yIdxOff] - (bHeight - 255);
|
||||
else
|
||||
++yIdxOff;
|
||||
}
|
||||
}
|
||||
|
||||
public function updateInfo() {
|
||||
var com = compon;
|
||||
if (com == null) {
|
||||
for (item in members) {
|
||||
if (item is UISprite)
|
||||
cast(item, UISprite).selectable = false;
|
||||
}
|
||||
colorModeDrop.dropButton.selectable = false;
|
||||
return;
|
||||
}
|
||||
|
||||
prefixBox.label.text = com.anim;
|
||||
xBox.value = com.x;
|
||||
yBox.value = com.y;
|
||||
scaleXBox.value = com.scaleX;
|
||||
scaleYBox.value = com.scaleY;
|
||||
angleBox.value = com.angle;
|
||||
@:privateAccess colorModeDrop.index = com.hasColorMode ? com.colorMode : AlphabetEditor.instance.tape.colorMode;
|
||||
@:privateAccess colorModeDrop.label.text = colorModeDrop.options[colorModeDrop.index];
|
||||
|
||||
outlineCheck.checked = com.outIndex != null;
|
||||
outlineBox.label.text = "";
|
||||
outlineXBox.value = 0;
|
||||
outlineYBox.value = 0;
|
||||
targetPercent = 0;
|
||||
if (com.outIndex != null) {
|
||||
var out = data.components[com.outIndex];
|
||||
outlineBox.label.text = out.anim;
|
||||
outlineXBox.value = out.x;
|
||||
outlineYBox.value = out.y;
|
||||
targetPercent = 1;
|
||||
}
|
||||
|
||||
for (item in members) {
|
||||
if (item is UISprite)
|
||||
cast(item, UISprite).selectable = true;
|
||||
}
|
||||
colorModeDrop.dropButton.selectable = true;
|
||||
}
|
||||
|
||||
function valueSet(item:UINumericStepper, func:Dynamic, text:String) {
|
||||
@:privateAccess item.__onChange(text);
|
||||
func(item.value);
|
||||
}
|
||||
|
||||
function get_compon() {
|
||||
return AlphabetEditor.instance.curSelectedComponent;
|
||||
}
|
||||
function get_data() {
|
||||
return AlphabetEditor.instance.curSelectedData;
|
||||
}
|
||||
}
|
||||
@@ -48,6 +48,9 @@ class CharacterAnimButton extends UIButton {
|
||||
public var foldableButtons:Array<FlxSprite> = [];
|
||||
public var closed:Bool = true;
|
||||
|
||||
inline function translate(id:String, ?args:Array<Dynamic>)
|
||||
return TU.translate("characterEditor.characterAnim." + id, args);
|
||||
|
||||
public function new(x:Float, y:Float, animData:AnimData, parent:CharacterAnimsWindow) {
|
||||
this.anim = animData.name;
|
||||
this.data = animData;
|
||||
@@ -76,13 +79,13 @@ class CharacterAnimButton extends UIButton {
|
||||
nameTextBox.onChange = (newName:String) -> {this.changeName(newName);};
|
||||
members.push(nameTextBox);
|
||||
foldableButtons.push(nameTextBox);
|
||||
addLabelOn(nameTextBox, "Name", 12);
|
||||
addLabelOn(nameTextBox, translate("name"), 12);
|
||||
|
||||
animTextBox = new UIAutoCompleteTextBox(nameTextBox.x + 100, nameTextBox.y, animData.anim, 156, 22, false, true);
|
||||
animTextBox.onChange = (newAnim:String) -> {this.changeAnim(newAnim);};
|
||||
members.push(animTextBox);
|
||||
foldableButtons.push(animTextBox);
|
||||
addLabelOn(animTextBox, "Animation", 12);
|
||||
addLabelOn(animTextBox, translate("anim"), 12);
|
||||
|
||||
positionXStepper = new UINumericStepper(animTextBox.x, animTextBox.y+32+18, animData.x, 0.001, 2, null, null, 64, 22, true);
|
||||
positionXStepper.onChange = (text:String) -> {
|
||||
@@ -91,7 +94,7 @@ class CharacterAnimButton extends UIButton {
|
||||
};
|
||||
members.push(positionXStepper);
|
||||
foldableButtons.push(positionXStepper);
|
||||
addLabelOn(positionXStepper, "Position (X,Y)", 12);
|
||||
addLabelOn(positionXStepper, translate("position"), 12);
|
||||
|
||||
members.push(XYComma = new UIText(positionXStepper.x+104-32+0, positionXStepper.y + 9, 0, ",", 18));
|
||||
foldableButtons.push(XYComma);
|
||||
@@ -111,7 +114,7 @@ class CharacterAnimButton extends UIButton {
|
||||
};
|
||||
members.push(fpsStepper);
|
||||
foldableButtons.push(fpsStepper);
|
||||
addLabelOn(fpsStepper, "FPS", 12);
|
||||
addLabelOn(fpsStepper, translate("fps"), 12);
|
||||
|
||||
loopedCheckbox = new UICheckbox(fpsStepper.x + 82 - 32 + 26, fpsStepper.y, "Looping?", animData.loop, 0, true);
|
||||
loopedCheckbox.onChecked = (newLooping:Bool) -> {this.changeLooping(newLooping);};
|
||||
@@ -126,7 +129,7 @@ class CharacterAnimButton extends UIButton {
|
||||
}
|
||||
members.push(indicesTextBox);
|
||||
foldableButtons.push(indicesTextBox);
|
||||
addLabelOn(indicesTextBox, "Indices (frames)", 12);
|
||||
addLabelOn(indicesTextBox, translate("indices"), 12);
|
||||
|
||||
animIcon = new FlxSprite(x-(10+16), y+8).loadGraphic(Paths.image("editors/character/anim-icons"), true, 16, 12);
|
||||
animIcon.animation.add("play", [0]);
|
||||
@@ -421,10 +424,12 @@ class CharacterAnimButton extends UIButton {
|
||||
|
||||
public function toggleGhost() {
|
||||
if (valid && parent.ghosts.indexOf(anim) == -1) {
|
||||
FlxG.sound.play(Paths.sound(Flags.DEFAULT_CHARACTER_GHOSTENABLE_SOUND));
|
||||
parent.ghosts.push(anim);
|
||||
ghostIcon.animation.play("alive", true);
|
||||
ghostIcon.color = 0xFFFFFFFF;
|
||||
} else {
|
||||
FlxG.sound.play(Paths.sound(Flags.DEFAULT_CHARACTER_GHOSTDISABLE_SOUND));
|
||||
parent.ghosts.remove(anim);
|
||||
ghostIcon.animation.play("dead", true);
|
||||
ghostIcon.color = 0xFFADADAD;
|
||||
|
||||
@@ -99,6 +99,7 @@ class CharacterAnimsWindow extends UIButtonList<CharacterAnimButton> {
|
||||
displayAnimsFramesList.remove(name);
|
||||
|
||||
public function deleteAnimation(button:CharacterAnimButton, addToUndo:Bool = true) {
|
||||
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_DELETE_SOUND));
|
||||
if (buttons.members.length <= 1) return;
|
||||
if (character.getAnimName() == button.anim)
|
||||
@:privateAccess CharacterEditor.instance._animation_down(null);
|
||||
@@ -112,13 +113,14 @@ class CharacterAnimsWindow extends UIButtonList<CharacterAnimButton> {
|
||||
}
|
||||
|
||||
public function generateAnimation() {
|
||||
var animName:String = "New Anim";
|
||||
var newAnim = TU.translate("characterEditor.characterAnim.defaultAnimName");
|
||||
var animName:String = newAnim;
|
||||
var animNames:Array<String> = character.getNameList();
|
||||
|
||||
var newAnimCount:Int = 0;
|
||||
while (animNames.indexOf(animName) != -1) {
|
||||
newAnimCount++;
|
||||
animName = 'New Anim - $newAnimCount';
|
||||
animName = '$newAnim - $newAnimCount';
|
||||
}
|
||||
|
||||
if (__autoCompleteAnims.length <= 0)
|
||||
|
||||
@@ -15,13 +15,16 @@ class CharacterCreationScreen extends UISubstateWindow {
|
||||
public var saveButton:UIButton;
|
||||
public var closeButton:UIButton;
|
||||
|
||||
inline function translate(id:String, ?args:Array<Dynamic>)
|
||||
return TU.translate("characterCreationScreen." + id, args);
|
||||
|
||||
public function new(?onSave:(String, ImageSaveData, Xml)->Void) {
|
||||
super();
|
||||
if (onSave != null) this.onSave = onSave;
|
||||
}
|
||||
|
||||
public override function create() {
|
||||
winTitle = "Creating New Character";
|
||||
winTitle = translate("win-title");
|
||||
|
||||
winWidth = 360;
|
||||
winHeight = 520;
|
||||
@@ -37,24 +40,24 @@ class CharacterCreationScreen extends UISubstateWindow {
|
||||
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$",
|
||||
addLabelOn(characterNameTextBox, "").applyMarkup(
|
||||
translate("charName"),
|
||||
[new FlxTextFormatMarkerPair(new FlxTextFormat(0xFFAD1212), "$")]);
|
||||
|
||||
imageExplorer = new UIImageExplorer(characterNameTextBox.x, characterNameTextBox.y + 30 + 16 + 20, null, 320, 58, (_, _) -> {onLoadImage();}, "images/characters");
|
||||
add(imageExplorer);
|
||||
addLabelOn(imageExplorer, "Character Image File").applyMarkup(
|
||||
"Character Image File $* Required$",
|
||||
addLabelOn(imageExplorer, "").applyMarkup(
|
||||
translate("charImgName"),
|
||||
[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() {
|
||||
saveButton = new UIButton(windowSpr.x + windowSpr.bWidth - 20 - 125, windowSpr.y + windowSpr.bHeight - 16 - 32, TU.translate("editor.saveClose"), function() {
|
||||
close();
|
||||
createCharacter();
|
||||
}, 125);
|
||||
add(saveButton);
|
||||
|
||||
closeButton = new UIButton(saveButton.x - 20 - saveButton.bWidth, saveButton.y, "Cancel", function() {
|
||||
closeButton = new UIButton(saveButton.x - 20 - saveButton.bWidth, saveButton.y, TU.translate("editor.cancel"), function() {
|
||||
close();
|
||||
}, 125);
|
||||
add(closeButton);
|
||||
|
||||
@@ -65,74 +65,77 @@ class CharacterEditor extends UIState {
|
||||
if (character != null) __character = character;
|
||||
}
|
||||
|
||||
inline function translate(id:String, ?args:Array<Dynamic>)
|
||||
return TU.translate("characterEditor." + id, args);
|
||||
|
||||
public override function create() {
|
||||
super.create();
|
||||
|
||||
undos = new UndoList<CharacterEditorChange>();
|
||||
|
||||
WindowUtils.suffix = " (Character Editor)";
|
||||
WindowUtils.suffix = " (" + TU.translate("editor.character.name") + ")";
|
||||
SaveWarning.selectionClass = CharacterSelection;
|
||||
SaveWarning.saveFunc = () -> {_file_save(null);};
|
||||
|
||||
topMenu = [
|
||||
{
|
||||
label: "File",
|
||||
label: translate("topBar.file"),
|
||||
childs: [
|
||||
{
|
||||
label: "New",
|
||||
label: translate("file.new"),
|
||||
onSelect: _file_new,
|
||||
},
|
||||
null,
|
||||
{
|
||||
label: "Save",
|
||||
label: translate("file.save"),
|
||||
keybind: [CONTROL, S],
|
||||
onSelect: _file_save,
|
||||
},
|
||||
{
|
||||
label: "Save As...",
|
||||
label: translate("file.saveAs"),
|
||||
keybind: [CONTROL, SHIFT, S],
|
||||
onSelect: _file_saveas,
|
||||
},
|
||||
null,
|
||||
{
|
||||
label: "Exit",
|
||||
label: translate("file.exit"),
|
||||
onSelect: _file_exit
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "Edit",
|
||||
label: translate("topBar.edit"),
|
||||
childs: [
|
||||
{
|
||||
label: "Copy Offset",
|
||||
label: translate("edit.copyOffset"),
|
||||
keybind: [CONTROL, C],
|
||||
onSelect: _edit_copy_offset
|
||||
},
|
||||
{
|
||||
label: "Paste Offset",
|
||||
label: translate("edit.pasteOffset"),
|
||||
keybind: [CONTROL, V],
|
||||
onSelect: _edit_paste_offset
|
||||
},
|
||||
null,
|
||||
{
|
||||
label: "Undo",
|
||||
label: translate("edit.undo"),
|
||||
keybind: [CONTROL, Z],
|
||||
onSelect: _edit_undo
|
||||
},
|
||||
{
|
||||
label: "Redo",
|
||||
label: translate("edit.redo"),
|
||||
keybinds: [[CONTROL, Y], [CONTROL, SHIFT, Z]],
|
||||
onSelect: _edit_redo
|
||||
},
|
||||
null,
|
||||
{
|
||||
label: "Edit information",
|
||||
label: translate("edit.editInfo"),
|
||||
color: 0xFF959829, icon: 4,
|
||||
onCreate: function (button:UIContextMenuOptionSpr) {button.label.offset.x = button.icon.offset.x = -2;},
|
||||
onSelect: _edit_info
|
||||
},
|
||||
{
|
||||
label: "Edit sprite",
|
||||
label: translate("edit.editSprite"),
|
||||
color: 0xFF959829, icon: 4,
|
||||
onCreate: function (button:UIContextMenuOptionSpr) {button.label.offset.x = button.icon.offset.x = -2;},
|
||||
onSelect: _edit_sprite
|
||||
@@ -140,128 +143,128 @@ class CharacterEditor extends UIState {
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "Offsets",
|
||||
label: translate("topBar.offset"),
|
||||
childs: [
|
||||
{
|
||||
label: "Move Left",
|
||||
label: translate("offset.moveLeft"),
|
||||
keybind: [LEFT],
|
||||
onSelect: _offsets_left,
|
||||
},
|
||||
{
|
||||
label: "Move Up",
|
||||
label: translate("offset.moveUp"),
|
||||
keybind: [UP],
|
||||
onSelect: _offsets_up,
|
||||
},
|
||||
{
|
||||
label: "Move Down",
|
||||
label: translate("offset.moveDown"),
|
||||
keybind: [DOWN],
|
||||
onSelect: _offsets_down,
|
||||
},
|
||||
{
|
||||
label: "Move Right",
|
||||
label: translate("offset.moveRight"),
|
||||
keybind: [RIGHT],
|
||||
onSelect: _offsets_right,
|
||||
},
|
||||
null,
|
||||
{
|
||||
label: "Move Extra Left",
|
||||
label: translate("offset.moveExtraLeft"),
|
||||
keybind: [SHIFT, LEFT],
|
||||
onSelect: _offsets_extra_left,
|
||||
},
|
||||
{
|
||||
label: "Move Extra Up",
|
||||
label: translate("offset.moveExtraUp"),
|
||||
keybind: [SHIFT, UP],
|
||||
onSelect: _offsets_extra_up,
|
||||
},
|
||||
{
|
||||
label: "Move Extra Down",
|
||||
label: translate("offset.moveExtraDown"),
|
||||
keybind: [SHIFT, DOWN],
|
||||
onSelect: _offsets_extra_down,
|
||||
},
|
||||
{
|
||||
label: "Move Extra Right",
|
||||
label: translate("offset.moveExtraRight"),
|
||||
keybind: [SHIFT, RIGHT],
|
||||
onSelect: _offsets_extra_right,
|
||||
},
|
||||
null,
|
||||
{
|
||||
label: "Drag Offsets With Mouse?",
|
||||
label: translate("offset.dragOffsetsMouse"),
|
||||
onSelect: _offsets_drag_offsets_mouse,
|
||||
icon: Options.characterDragging ? 1 : 0
|
||||
},
|
||||
{
|
||||
label: "Clear Offsets",
|
||||
label: translate("offset.clearOffsets"),
|
||||
keybind: [CONTROL, R],
|
||||
onSelect: _offsets_clear,
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "Stage",
|
||||
label: translate("topBar.stage"),
|
||||
childs: buildStagesUI()
|
||||
},
|
||||
{
|
||||
label: "View",
|
||||
label: translate("topBar.view"),
|
||||
childs: [
|
||||
{
|
||||
label: "Zoom in",
|
||||
label: translate("view.zoomIn"),
|
||||
keybind: [CONTROL, NUMPADPLUS],
|
||||
onSelect: _view_zoomin
|
||||
},
|
||||
{
|
||||
label: "Zoom out",
|
||||
label: translate("view.zoomOut"),
|
||||
keybind: [CONTROL, NUMPADMINUS],
|
||||
onSelect: _view_zoomout
|
||||
},
|
||||
{
|
||||
label: "Reset zoom",
|
||||
label: translate("view.resetZoom"),
|
||||
keybind: [CONTROL, NUMPADZERO],
|
||||
onSelect: _view_zoomreset
|
||||
},
|
||||
null,
|
||||
{
|
||||
label: "Character Hitbox?",
|
||||
label: translate("view.charHitbox"),
|
||||
onSelect: _view_character_show_hitbox,
|
||||
icon: Options.characterHitbox ? 1 : 0
|
||||
},
|
||||
{
|
||||
label: "Character Camera?",
|
||||
label: translate("view.charCam"),
|
||||
onSelect: _view_character_show_camera,
|
||||
icon: Options.characterCamera ? 1 : 0
|
||||
},
|
||||
{
|
||||
label: "XY Axis?",
|
||||
label: translate("view.axis"),
|
||||
onSelect: _view_character_show_axis,
|
||||
icon: Options.characterAxis ? 1 : 0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "Animation >",
|
||||
label: translate("topBar.animation") + " >",
|
||||
childs: [
|
||||
{
|
||||
label: "Play Animation",
|
||||
label: translate("animation.playAnim"),
|
||||
keybind: [SPACE],
|
||||
onSelect: _animation_play,
|
||||
},
|
||||
{
|
||||
label: "Stop Animation",
|
||||
label: translate("animation.stopAnim"),
|
||||
onSelect: _animation_stop
|
||||
},
|
||||
null,
|
||||
{
|
||||
label: "Change Animation ↑",
|
||||
label: translate("animation.changeAnimUp"),
|
||||
keybind: [W],
|
||||
onSelect: _animation_up
|
||||
},
|
||||
{
|
||||
label: "Change Animation ↓",
|
||||
label: translate("animation.changeAnimDown"),
|
||||
keybind: [S],
|
||||
onSelect: _animation_down
|
||||
},
|
||||
null,
|
||||
{
|
||||
label: "Play Animation On Offset?",
|
||||
label: translate("animation.playAnimOnOffset"),
|
||||
onSelect: _animation_toggle_anim_playing_offsets,
|
||||
icon: Options.playAnimOnOffset ? 1 : 0
|
||||
},
|
||||
@@ -433,7 +436,7 @@ class CharacterEditor extends UIState {
|
||||
super.onResize(width, height);
|
||||
if (!UIState.resolutionAware) return;
|
||||
|
||||
if (width < FlxG.initialWidth || height < FlxG.initialHeight) {
|
||||
if ((width < FlxG.initialWidth || height < FlxG.initialHeight) && !Options.bypassEditorsResize) {
|
||||
width = FlxG.initialWidth; height = FlxG.initialHeight;
|
||||
}
|
||||
|
||||
@@ -450,6 +453,7 @@ class CharacterEditor extends UIState {
|
||||
|
||||
function _file_save(_) {
|
||||
#if sys
|
||||
FlxG.sound.play(Paths.sound('editors/save'));
|
||||
CoolUtil.safeSaveFile(
|
||||
'${Paths.getAssetsRoot()}/data/characters/${character.curCharacter}.xml',
|
||||
buildCharacter()
|
||||
@@ -461,6 +465,7 @@ class CharacterEditor extends UIState {
|
||||
}
|
||||
|
||||
function _file_saveas(_) {
|
||||
FlxG.sound.play(Paths.sound('editors/save'));
|
||||
openSubState(new SaveSubstate(buildCharacter(), {
|
||||
defaultSaveFile: '${character.curCharacter}.xml'
|
||||
}));
|
||||
@@ -493,6 +498,7 @@ class CharacterEditor extends UIState {
|
||||
}
|
||||
|
||||
function _undo(undo:CharacterEditorChange) {
|
||||
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_UNDO_SOUND));
|
||||
switch (undo) {
|
||||
case null: // do nothing
|
||||
case CCharEditPosition(oldPos, newPos):
|
||||
@@ -563,6 +569,7 @@ class CharacterEditor extends UIState {
|
||||
}
|
||||
|
||||
function _redo(redo:CharacterEditorChange) {
|
||||
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_REDO_SOUND));
|
||||
switch (redo) {
|
||||
case null: // do nothing
|
||||
case CCharEditPosition(oldPos, newPos):
|
||||
@@ -682,16 +689,17 @@ class CharacterEditor extends UIState {
|
||||
var stageFileList = Stage.getList(true);
|
||||
if (stageFileList.length == 0) stageFileList = Stage.getList(false);
|
||||
|
||||
var stageText = TU.getRaw("characterEditor.stage.useStage");
|
||||
for (stage in stageFileList)
|
||||
newChilds.push({
|
||||
label: 'Use "$stage"?',
|
||||
label: stageText.format([stage]),
|
||||
icon: currentStage == stage ? 1 : 0,
|
||||
onSelect: (_) -> {changeStage(stage);}
|
||||
});
|
||||
|
||||
newChilds.push(null);
|
||||
newChilds.push({
|
||||
label: "Use No Stage?",
|
||||
label: translate("stage.useNone"),
|
||||
icon: currentStage == null ? 1 : 0,
|
||||
onSelect: (_) -> {changeStage(null);}
|
||||
});
|
||||
|
||||
@@ -40,8 +40,11 @@ class CharacterInfoScreen extends UISubstateWindow {
|
||||
super();
|
||||
}
|
||||
|
||||
inline function translate(id:String, ?args:Array<Dynamic>)
|
||||
return TU.translate("characterInfoScreen." + id, args);
|
||||
|
||||
public override function create() {
|
||||
winTitle = 'Edit Character Properties...';
|
||||
winTitle = translate("win-title");
|
||||
winWidth = 824; winHeight = 400;
|
||||
|
||||
super.create();
|
||||
@@ -50,15 +53,15 @@ class CharacterInfoScreen extends UISubstateWindow {
|
||||
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, "Edit Character Info", 28));
|
||||
add(title = new UIText(windowSpr.x + 20, windowSpr.y + 30 + 16, 0, translate("title"), 28));
|
||||
|
||||
iconColorPicker = new UIIconColorPicker(title.x, title.y + title.height + 38, character.getIcon(), character.antialiasing, null);
|
||||
add(iconColorPicker);
|
||||
addLabelOn(iconColorPicker, "Icon");
|
||||
addLabelOn(iconColorPicker, translate("icon"));
|
||||
|
||||
iconColorWheel = new UIColorwheel(iconColorPicker.x+12+125+12+22, iconColorPicker.y, character.iconColor);
|
||||
add(iconColorWheel);
|
||||
addLabelOn(iconColorWheel, "Icon Color");
|
||||
addLabelOn(iconColorWheel, translate("iconColor"));
|
||||
|
||||
if (character.iconColor != null)
|
||||
iconColorWheel.colorChanged = true;
|
||||
@@ -66,9 +69,9 @@ class CharacterInfoScreen extends UISubstateWindow {
|
||||
|
||||
durationStepper = new UINumericStepper(iconColorWheel.x, iconColorWheel.y + 125 + 36, character.holdTime == -1 ? 4 : character.holdTime, 0.001, 2, 0, 9999999, 74);
|
||||
add(durationStepper);
|
||||
addLabelOn(durationStepper, "Sing Duration");
|
||||
addLabelOn(durationStepper, translate("singDuration"));
|
||||
|
||||
useDurationCheckbox = new UICheckbox(durationStepper.x + durationStepper.bWidth + 20, durationStepper.y+6, "Use Sing Duration?", character.holdTime != -1);
|
||||
useDurationCheckbox = new UICheckbox(durationStepper.x + durationStepper.bWidth + 20, durationStepper.y+6, translate("useSingDuration"), character.holdTime != -1);
|
||||
useDurationCheckbox.onChecked = (checked:Bool) -> {durationStepper.selectable = checked;};
|
||||
add(useDurationCheckbox);
|
||||
|
||||
@@ -86,15 +89,15 @@ class CharacterInfoScreen extends UISubstateWindow {
|
||||
customPropertiesButtonList.add(new PropertyButton(prop, val, customPropertiesButtonList));
|
||||
|
||||
add(customPropertiesButtonList);
|
||||
addLabelOn(customPropertiesButtonList, "Custom Values (Advanced)");
|
||||
addLabelOn(customPropertiesButtonList, translate("customValues"));
|
||||
|
||||
saveButton = new UIButton(windowSpr.x + windowSpr.bWidth - 20 - 125, windowSpr.y + windowSpr.bHeight - 16 - 32, "Save & Close", function() {
|
||||
saveButton = new UIButton(windowSpr.x + windowSpr.bWidth - 20 - 125, windowSpr.y + windowSpr.bHeight - 16 - 32, TU.translate("editor.saveClose"), function() {
|
||||
saveCharacterInfo();
|
||||
close();
|
||||
}, 125);
|
||||
add(saveButton);
|
||||
|
||||
closeButton = new UIButton(saveButton.x - 20 - saveButton.bWidth, saveButton.y, "Close", function() {
|
||||
closeButton = new UIButton(saveButton.x - 20 - saveButton.bWidth, saveButton.y, TU.translate("editor.close"), function() {
|
||||
close();
|
||||
}, 125);
|
||||
add(closeButton);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
|
||||
package funkin.editors.character;
|
||||
|
||||
import funkin.backend.FlxAnimate;
|
||||
@@ -26,6 +27,9 @@ class CharacterPropertiesWindow extends UISliceSprite {
|
||||
|
||||
public var labels:Map<UISprite, UIText> = [];
|
||||
|
||||
inline function translate(id:String, ?args:Array<Dynamic>)
|
||||
return TU.translate("characterEditor.characterProperties." + id, args);
|
||||
|
||||
public function new(x:Float, y:Float, character:Character) @:privateAccess {
|
||||
super(x, y, 424+16, 204+20, "editors/ui/inputbox");
|
||||
|
||||
@@ -40,7 +44,7 @@ class CharacterPropertiesWindow extends UISliceSprite {
|
||||
this.changePosition(positionXStepper.value, null);
|
||||
};
|
||||
members.push(positionXStepper);
|
||||
addLabelOn(positionXStepper, "Position (X,Y)");
|
||||
addLabelOn(positionXStepper, translate("position"));
|
||||
|
||||
members.push(positionXYComma = new UIText(positionXStepper.x+104-32+0, positionXStepper.y + 9, 0, ",", 22));
|
||||
|
||||
@@ -57,17 +61,17 @@ class CharacterPropertiesWindow extends UISliceSprite {
|
||||
this.changeScale(scaleStepper.value);
|
||||
};
|
||||
members.push(scaleStepper);
|
||||
addLabelOn(scaleStepper, "Scale");
|
||||
addLabelOn(scaleStepper, translate("scale"));
|
||||
|
||||
editCharacterButton = new UIButton(scaleStepper.x + 90 -32 + 26, scaleStepper.y-20, "Edit Info", editCharacterInfoUI, 120, 24);
|
||||
editCharacterButton = new UIButton(scaleStepper.x + 90 -32 + 26, scaleStepper.y-20, translate("editInfo"), editCharacterInfoUI, 120, 24);
|
||||
editCharacterButton.field.size -= 2;
|
||||
members.push(editCharacterButton);
|
||||
|
||||
editSpriteButton = new UIButton(editCharacterButton.x, editCharacterButton.y+24+6, "Edit Sprite", editCharacterSpriteUI, 120, 24);
|
||||
editSpriteButton = new UIButton(editCharacterButton.x, editCharacterButton.y+24+6, translate("editSprite"), editCharacterSpriteUI, 120, 24);
|
||||
editSpriteButton.field.size -= 2;
|
||||
members.push(editSpriteButton);
|
||||
|
||||
flipXCheckbox = new UICheckbox(scaleStepper.x+22, scaleStepper.y+32+14, "Flipped?", character.isPlayer ? !character.__baseFlipped : character.__baseFlipped);
|
||||
flipXCheckbox = new UICheckbox(scaleStepper.x+22, scaleStepper.y+32+14, translate("charFlipped"), character.isPlayer ? !character.__baseFlipped : character.__baseFlipped);
|
||||
flipXCheckbox.onChecked = (checked:Bool) -> {this.changeFlipX(checked);};
|
||||
members.push(flipXCheckbox);
|
||||
|
||||
@@ -77,7 +81,7 @@ class CharacterPropertiesWindow extends UISliceSprite {
|
||||
this.changeCamPosition(cameraXStepper.value, null);
|
||||
};
|
||||
members.push(cameraXStepper);
|
||||
addLabelOn(cameraXStepper, "Camera Position (X,Y)");
|
||||
addLabelOn(cameraXStepper, translate("camPosition"));
|
||||
|
||||
members.push(cameraXYComma = new UIText(cameraXStepper.x + 104-32+0, cameraXStepper.y+9, 0, ",", 22));
|
||||
|
||||
@@ -88,7 +92,7 @@ class CharacterPropertiesWindow extends UISliceSprite {
|
||||
};
|
||||
members.push(cameraYStepper);
|
||||
|
||||
antialiasingCheckbox = new UICheckbox(scaleStepper.x+22, flipXCheckbox.y+32, "Antialiased?", character.antialiasing);
|
||||
antialiasingCheckbox = new UICheckbox(scaleStepper.x+22, flipXCheckbox.y+32, translate("antialiased"), character.antialiasing);
|
||||
antialiasingCheckbox.onChecked = (checked:Bool) -> {this.changeAntialiasing(checked);};
|
||||
members.push(antialiasingCheckbox);
|
||||
|
||||
@@ -97,7 +101,7 @@ class CharacterPropertiesWindow extends UISliceSprite {
|
||||
CharacterEditor.instance.changeStagePosition(testAsDropDown.options[index]);
|
||||
};
|
||||
members.push(testAsDropDown);
|
||||
addLabelOn(testAsDropDown, "Test Character As...");
|
||||
addLabelOn(testAsDropDown, translate("testCharAs"));
|
||||
testAsDropDown.bWidth = 193; //REFUSES TO FUCKING SET TO 170 PIECE OF SHIT!!
|
||||
|
||||
designedAsDropDown = new UIDropDown(testAsDropDown.x+193+22, testAsDropDown.y, 193, 32, ["BOYFRIEND", "DAD"], character.playerOffsets ? 0 : 1);
|
||||
@@ -105,7 +109,7 @@ class CharacterPropertiesWindow extends UISliceSprite {
|
||||
CharacterEditor.instance.changeCharacterDesginedAs(designedAsDropDown.options[index] == "BOYFRIEND");
|
||||
};
|
||||
members.push(designedAsDropDown);
|
||||
addLabelOn(designedAsDropDown, "Char Desgined As...");
|
||||
addLabelOn(designedAsDropDown, translate("charDesign"));
|
||||
designedAsDropDown.bWidth = 193;
|
||||
|
||||
alpha = 0.7;
|
||||
|
||||
@@ -12,6 +12,9 @@ import funkin.backend.assets.ModsFolder;
|
||||
|
||||
class CharacterSelection extends EditorTreeMenu
|
||||
{
|
||||
inline function translate(id:String, ?args:Array<Dynamic>)
|
||||
return TU.translate("characterSelection." + id, args);
|
||||
|
||||
public var modsList:Array<String> = [];
|
||||
|
||||
public override function create()
|
||||
@@ -34,13 +37,13 @@ class CharacterSelection extends EditorTreeMenu
|
||||
if(char.endsWith("/")) {
|
||||
var folderName = CoolUtil.getFilename(char.substr(0, char.length-1));
|
||||
|
||||
list.push(new TextOption(folderName + " >", "Press ACCEPT to edit this folder.", function() {
|
||||
list.push(new TextOption(folderName + " >", translate("acceptFolder"), function() {
|
||||
var newModsList = Character.getList(isMods, true, char);
|
||||
var newList:Array<OptionType> = generateList(newModsList, isMods, folderPath + folderName + "/");
|
||||
optionsTree.add(new OptionsScreen(folderPath + folderName, "Select a character to edit in " + folderPath + folderName + "/", newList));
|
||||
optionsTree.add(new OptionsScreen(folderPath + folderName, translate("desc-folder", [folderPath + folderName + "/"]), newList));
|
||||
}));
|
||||
} else {
|
||||
list.push(new IconOption(char, "Press ACCEPT to edit this character.", Character.getIconFromCharName(folderPath + char, char),
|
||||
list.push(new IconOption(char, translate("acceptCharacter"), Character.getIconFromCharName(folderPath + char, char),
|
||||
function() {
|
||||
FlxG.switchState(new CharacterEditor(folderPath + char));
|
||||
})
|
||||
@@ -48,7 +51,7 @@ class CharacterSelection extends EditorTreeMenu
|
||||
}
|
||||
}
|
||||
|
||||
list.insert(0, new NewOption("New Character", "Press ACCEPT to create a new character.", function() {
|
||||
list.insert(0, new NewOption(translate("newCharacter"), translate("newCharacterDesc"), function() {
|
||||
openSubState(new CharacterCreationScreen(createCharacter));
|
||||
}));
|
||||
|
||||
@@ -57,7 +60,7 @@ class CharacterSelection extends EditorTreeMenu
|
||||
|
||||
var list = generateList(modsList, isMods);
|
||||
|
||||
main = new OptionsScreen("Character Editor", "Select a character to edit", list);
|
||||
main = new OptionsScreen(TU.translate("editor.character.name"), translate("desc"), list);
|
||||
|
||||
DiscordUtil.call("onEditorTreeLoaded", ["Character Editor"]);
|
||||
}
|
||||
|
||||
@@ -16,6 +16,9 @@ class CharacterSpriteScreen extends UISubstateWindow {
|
||||
public var saveButton:UIButton;
|
||||
public var closeButton:UIButton;
|
||||
|
||||
inline function translate(id:String, ?args:Array<Dynamic>)
|
||||
return TU.translate("characterEditor.characterSpriteScreen." + id, args);
|
||||
|
||||
public function new(imagePath:String, ?onSave:(String, Bool)->Void) {
|
||||
super();
|
||||
this.imagePath = imagePath;
|
||||
@@ -23,7 +26,7 @@ class CharacterSpriteScreen extends UISubstateWindow {
|
||||
}
|
||||
|
||||
public override function create() {
|
||||
winTitle = 'Edit character sprite';
|
||||
winTitle = translate("win-title");
|
||||
winWidth = 360; winHeight = 183;
|
||||
|
||||
function addLabelOn(ui:UISprite, text:String):UIText {
|
||||
@@ -36,13 +39,13 @@ class CharacterSpriteScreen extends UISubstateWindow {
|
||||
|
||||
imageExplorer = new UIImageExplorer(20, windowSpr.y + 30 + 16 + 20, imagePath, 320, 58, (_, _) -> {onLoadImage();}, "images/characters");
|
||||
add(imageExplorer);
|
||||
addLabelOn(imageExplorer, "Character Image File").applyMarkup(
|
||||
"Character Image File $* Required$",
|
||||
addLabelOn(imageExplorer, "").applyMarkup(
|
||||
translate('charImageFile'),
|
||||
[new FlxTextFormatMarkerPair(new FlxTextFormat(0xFFAD1212), "$")]);
|
||||
|
||||
ogImageSaveData = imageExplorer.getSaveData();
|
||||
|
||||
saveButton = new UIButton(windowSpr.x + windowSpr.bWidth - 20, windowSpr.y + windowSpr.bHeight - 20, "Save & Close", function() {
|
||||
saveButton = new UIButton(windowSpr.x + windowSpr.bWidth - 20, windowSpr.y + windowSpr.bHeight - 20, TU.translate("editor.saveClose"), function() {
|
||||
addToUndo(); // should be async?? -lunar
|
||||
imageExplorer.saveFiles('${Paths.getAssetsRoot()}/images/characters', () -> {
|
||||
onSave('${imageExplorer.saveData.directory.length > 0 ? '${imageExplorer.saveData.directory}/' : ""}' + imageExplorer.imageName, imageExplorer.isAtlas);
|
||||
@@ -53,7 +56,7 @@ class CharacterSpriteScreen extends UISubstateWindow {
|
||||
saveButton.y -= saveButton.bHeight;
|
||||
saveButton.selectable = false;
|
||||
|
||||
closeButton = new UIButton(saveButton.x - 20, saveButton.y, "Cancel", function() {
|
||||
closeButton = new UIButton(saveButton.x - 20, saveButton.y, TU.translate("editor.cancel"), function() {
|
||||
close();
|
||||
}, 125);
|
||||
closeButton.color = 0xFFFF0000;
|
||||
|
||||
@@ -26,7 +26,7 @@ class ChartCreationScreen extends UISubstateWindow {
|
||||
}
|
||||
|
||||
public override function create() {
|
||||
winTitle = "Creating New Chart";
|
||||
winTitle = TU.translate("chartCreation.title");
|
||||
|
||||
winWidth = 652;
|
||||
winHeight = 600;
|
||||
@@ -43,11 +43,11 @@ class ChartCreationScreen extends UISubstateWindow {
|
||||
if (charFileList.length == 0) charFileList = Character.getList(false);
|
||||
|
||||
var chartTitle:UIText;
|
||||
add(chartTitle = new UIText(windowSpr.x + 20, windowSpr.y + 30 + 16, 0, "Chart Info", 28));
|
||||
add(chartTitle = new UIText(windowSpr.x + 20, windowSpr.y + 30 + 16, 0, TU.translate("chartCreation.info"), 28));
|
||||
|
||||
difficultyNameTextBox = new UITextBox(chartTitle.x, chartTitle.y + chartTitle.height + 36, "difficulty", 200);
|
||||
add(difficultyNameTextBox);
|
||||
addLabelOn(difficultyNameTextBox, "Difficulty Name");
|
||||
addLabelOn(difficultyNameTextBox, TU.translate("chartCreation.diffName"));
|
||||
|
||||
var stageFileList = Stage.getList(true);
|
||||
if (stageFileList.length == 0) stageFileList = Stage.getList(false);
|
||||
@@ -55,7 +55,7 @@ class ChartCreationScreen extends UISubstateWindow {
|
||||
stageTextBox = new UIAutoCompleteTextBox(difficultyNameTextBox.x + 200 + 26, difficultyNameTextBox.y, "stage", 180);
|
||||
stageTextBox.suggestItems = stageFileList;
|
||||
add(stageTextBox);
|
||||
addLabelOn(stageTextBox, "Stage");
|
||||
addLabelOn(stageTextBox, TU.translate("chartCreation.stage"));
|
||||
|
||||
scrollSpeedTextBox = new UINumericStepper(stageTextBox.x + 180 + 26, difficultyNameTextBox.y, 1.0, 0.1, 2, null, null, 90);
|
||||
scrollSpeedTextBox.onChange = function (text:String) {
|
||||
@@ -65,7 +65,7 @@ class ChartCreationScreen extends UISubstateWindow {
|
||||
button.scrollSpeedStepper.value = scrollSpeedTextBox.value;
|
||||
}
|
||||
add(scrollSpeedTextBox);
|
||||
addLabelOn(scrollSpeedTextBox, "Scroll Speed");
|
||||
addLabelOn(scrollSpeedTextBox, TU.translate("chartCreation.scrollSpeed"));
|
||||
|
||||
strumLineList = new UIButtonList<StrumLineButton>(difficultyNameTextBox.x, difficultyNameTextBox.y+difficultyNameTextBox.bHeight+36, 620, (552-179)-16, null, FlxPoint.get(620, 246), null, 6);
|
||||
strumLineList.frames = Paths.getFrames('editors/ui/inputbox');
|
||||
@@ -113,20 +113,21 @@ class ChartCreationScreen extends UISubstateWindow {
|
||||
}, strumLineList));
|
||||
|
||||
add(strumLineList);
|
||||
addLabelOn(strumLineList, "Strumlines").applyMarkup(
|
||||
"Strumlines $* Atleast 1 Strumline Required$",
|
||||
addLabelOn(strumLineList, "").applyMarkup(
|
||||
TU.translate("chartCreation.strumLineList"),
|
||||
[new FlxTextFormatMarkerPair(new FlxTextFormat(0xFFAD1212), "$")]);
|
||||
|
||||
strumLineList.dragCallback = (object:StrumLineButton, oldIndex:Int, newIndex:Int) -> {object.idText.text = 'Strumline - #${newIndex}';};
|
||||
var strumlineFormat = TU.getRaw("chartCreation.strumLine.format");
|
||||
strumLineList.dragCallback = (object:StrumLineButton, oldIndex:Int, newIndex:Int) -> {object.idText.text = strumlineFormat.format([newIndex]);};
|
||||
scrollSpeedTextBox.onChange(scrollSpeedTextBox.label.text);
|
||||
|
||||
saveButton = new UIButton(windowSpr.x + windowSpr.bWidth - 20 - 125, windowSpr.y + windowSpr.bHeight - 16 - 32, "Save & Close", function() {
|
||||
saveButton = new UIButton(windowSpr.x + windowSpr.bWidth - 20 - 125, windowSpr.y + windowSpr.bHeight - 16 - 32, TU.translate("editor.saveClose"), function() {
|
||||
createChart();
|
||||
close();
|
||||
}, 125);
|
||||
add(saveButton);
|
||||
|
||||
closeButton = new UIButton(saveButton.x - 20 - saveButton.bWidth, saveButton.y, "Cancel", function() {
|
||||
closeButton = new UIButton(saveButton.x - 20 - saveButton.bWidth, saveButton.y, TU.translate("editor.close"), function() {
|
||||
close();
|
||||
}, 125);
|
||||
add(closeButton);
|
||||
@@ -211,12 +212,12 @@ class StrumLineButton extends UIButton {
|
||||
charactersList.cameraSpacing = 0;
|
||||
|
||||
charactersList.addButton.callback = function()
|
||||
charactersList.add(new CompactCharacterButton("New Char", subState.charFileList, charactersList));
|
||||
charactersList.add(new CompactCharacterButton(TU.translate("charterStrumLine.newChar"), subState.charFileList, charactersList));
|
||||
for (character in strumLine.characters)
|
||||
charactersList.add(new CompactCharacterButton(character, subState.charFileList, charactersList));
|
||||
|
||||
members.push(charactersList);
|
||||
idText = addLabelOn(charactersList, 'Strumline - #$id');
|
||||
idText = addLabelOn(charactersList, TU.translate("chartCreation.strumLine.format", [id]));
|
||||
|
||||
cameraClipShader = new CustomShader("engine/cameraClip");
|
||||
cameraClipShader.hset("clipRect", [0, 0, 100, 100]);
|
||||
@@ -224,17 +225,17 @@ class StrumLineButton extends UIButton {
|
||||
|
||||
typeDropdown = new UIDropDown(charactersList.x + charactersList.bWidth + 16, 8+26, 200, 32, ["OPPONENT", "PLAYER", "ADDITIONAL"], strumLine.type);
|
||||
members.push(typeDropdown);
|
||||
addLabelOn(typeDropdown, "Type");
|
||||
addLabelOn(typeDropdown, TU.translate("charterStrumLine.type"));
|
||||
|
||||
var stagePositionI = strumLine.position == null ? strumLine.type : ["DAD", "BOYFRIEND", "GIRLFRIEND"].indexOf(strumLine.position.toUpperCase());
|
||||
|
||||
stagePositionDropdown = new UIDropDown(typeDropdown.x + 200 - 32 + 26, typeDropdown.y, 200, 32, ["DAD", "BOYFRIEND", "GIRLFRIEND"], stagePositionI);
|
||||
members.push(stagePositionDropdown);
|
||||
addLabelOn(stagePositionDropdown, "Stage Position");
|
||||
addLabelOn(stagePositionDropdown, TU.translate("charterStrumLine.stagePos"));
|
||||
|
||||
hudScaleStepper = new UINumericStepper(typeDropdown.x, typeDropdown.y + 64, strumLine.strumScale == null ? 1 : strumLine.strumScale, 0.001, 2, null, null, 74);
|
||||
members.push(hudScaleStepper);
|
||||
addLabelOn(hudScaleStepper, "Scale");
|
||||
addLabelOn(hudScaleStepper, TU.translate("charterStrumLine.scale"));
|
||||
|
||||
var strOffset:Float = strumLine.strumLinePos == null ? (strumLine.type == 1 ? 0.75 : 0.25) : strumLine.strumLinePos;
|
||||
|
||||
@@ -244,22 +245,22 @@ class StrumLineButton extends UIButton {
|
||||
|
||||
hudXStepper = new UINumericStepper(hudScaleStepper.x + 80 - 32 + 26, hudScaleStepper.y, startingPos.x, 0.01, 2, 0, 2, 84);
|
||||
members.push(hudXStepper);
|
||||
addLabelOn(hudXStepper, "Hud Position (X [Ratio 0-1],Y)");
|
||||
addLabelOn(hudXStepper, TU.translate("charterStrumLine.hudPos"));
|
||||
|
||||
members.push(XYComma = new UIText(hudXStepper.x + 84 - 32 + 0, hudXStepper.y + 9, 0, ",", 22));
|
||||
|
||||
hudYStepper = new UINumericStepper(hudXStepper.x + 84 - 32 + 26, hudXStepper.y, startingPos.y, 0.001, 2, null, null, 84);
|
||||
members.push(hudYStepper);
|
||||
|
||||
visibleCheckbox = new UICheckbox(hudYStepper.x + hudYStepper.bWidth + 42, hudYStepper.y + 9, "Visible?", strumLine.visible == null ? true : strumLine.visible);
|
||||
visibleCheckbox = new UICheckbox(hudYStepper.x + hudYStepper.bWidth + 42, hudYStepper.y + 9, TU.translate("charterStrumLine.visible"), strumLine.visible == null ? true : strumLine.visible);
|
||||
members.push(visibleCheckbox);
|
||||
|
||||
scrollSpeedStepper = new UINumericStepper(typeDropdown.x, typeDropdown.y + 128, strumLine.scrollSpeed, 0.1, 2, 0, 10, 82);
|
||||
scrollSpeedStepper.selectable = strumLine.scrollSpeed != null;
|
||||
members.push(scrollSpeedStepper);
|
||||
addLabelOn(scrollSpeedStepper, "Scroll Speed");
|
||||
addLabelOn(scrollSpeedStepper, TU.translate("charterStrumLine.scrollSpeed"));
|
||||
|
||||
usesChartScrollSpeed = new UICheckbox(scrollSpeedStepper.x + 104, typeDropdown.y + 135, "Uses charts scroll speed?", strumLine.scrollSpeed == null);
|
||||
usesChartScrollSpeed = new UICheckbox(scrollSpeedStepper.x + 104, typeDropdown.y + 135, TU.translate("charterStrumLine.useChartScrollSpeed"), strumLine.scrollSpeed == null);
|
||||
usesChartScrollSpeed.onChecked = function(b) {
|
||||
if(b) {
|
||||
scrollSpeedStepper.value = subState.scrollSpeedTextBox.value;
|
||||
@@ -318,7 +319,8 @@ class CompactCharacterButton extends UIButton {
|
||||
autoAlpha = false;
|
||||
|
||||
charIcon = new HealthIcon(funkin.game.Character.getIconFromCharName(char));
|
||||
charIcon.scale.set(0.2, 0.2);
|
||||
var size = Std.int(150 * 0.2);
|
||||
charIcon.setUnstretchedGraphicSize(size, size, true);
|
||||
charIcon.updateHitbox();
|
||||
charIcon.setPosition(10, bHeight/2 - charIcon.height / 2);
|
||||
charIcon.scrollFactor.set(1,1);
|
||||
@@ -330,11 +332,10 @@ class CompactCharacterButton extends UIButton {
|
||||
textBox.antialiasing = true;
|
||||
textBox.onChange = function(char:String) {
|
||||
char = funkin.game.Character.getIconFromCharName(char);
|
||||
var image = Paths.image("icons/" + char);
|
||||
if(!Assets.exists(image))
|
||||
image = Paths.image("icons/" + Flags.DEFAULT_HEALTH_ICON);
|
||||
charIcon.loadGraphic(image, true, 150, 150);
|
||||
charIcon.setIcon(char);
|
||||
charIcon.setUnstretchedGraphicSize(size, size, true);
|
||||
charIcon.updateHitbox();
|
||||
charIcon.setPosition(10, bHeight/2 - charIcon.height / 2);
|
||||
}
|
||||
|
||||
deleteButton = new UIButton(textBox.x + 115 + 16, bHeight/2 - (32/2), null, function () {
|
||||
|
||||
@@ -29,7 +29,7 @@ class ChartDataScreen extends UISubstateWindow {
|
||||
Charter.instance.vocals.pause();
|
||||
for (strumLine in Charter.instance.strumLines.members) strumLine.vocals.pause();
|
||||
|
||||
winTitle = 'Chart Properties';
|
||||
winTitle = TU.translate("chartDataScreen.title");
|
||||
winWidth = 420; winHeight = 230; // guys look, the funny numbers!
|
||||
|
||||
super.create();
|
||||
@@ -38,7 +38,7 @@ class ChartDataScreen extends UISubstateWindow {
|
||||
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, "Edit Chart Data", 28));
|
||||
add(title = new UIText(windowSpr.x + 20, windowSpr.y + 30 + 16, 0, TU.translate("chartDataScreen.editChartData"), 28));
|
||||
|
||||
var stageFileList = Stage.getList(true);
|
||||
if (stageFileList.length == 0) stageFileList = Stage.getList(false);
|
||||
@@ -46,19 +46,19 @@ class ChartDataScreen extends UISubstateWindow {
|
||||
stageTextBox = new UIAutoCompleteTextBox(title.x, title.y + title.height + 38, PlayState.SONG.stage, 200);
|
||||
stageTextBox.suggestItems = stageFileList;
|
||||
add(stageTextBox);
|
||||
addLabelOn(stageTextBox, "Stage");
|
||||
addLabelOn(stageTextBox, TU.translate("chartDataScreen.stage"));
|
||||
|
||||
scrollSpeedStepper = new UINumericStepper(stageTextBox.x + 200 + 26, stageTextBox.y, data.scrollSpeed, 0.1, 2, 0, 10, 82);
|
||||
add(scrollSpeedStepper);
|
||||
addLabelOn(scrollSpeedStepper, "Scroll Speed");
|
||||
addLabelOn(scrollSpeedStepper, TU.translate("chartDataScreen.scrollSpeed"));
|
||||
|
||||
saveButton = new UIButton(windowSpr.x + windowSpr.bWidth - 20 - 125, windowSpr.y + windowSpr.bHeight - 16 - 32, "Save & Close", function() {
|
||||
saveButton = new UIButton(windowSpr.x + windowSpr.bWidth - 20 - 125, windowSpr.y + windowSpr.bHeight - 16 - 32, TU.translate("editor.saveClose"), function() {
|
||||
saveInfo();
|
||||
close();
|
||||
}, 125);
|
||||
add(saveButton);
|
||||
|
||||
closeButton = new UIButton(saveButton.x - 20, saveButton.y, "Close", function() {
|
||||
closeButton = new UIButton(saveButton.x - 20, saveButton.y, TU.translate("editor.close"), function() {
|
||||
close();
|
||||
}, 125);
|
||||
add(closeButton);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -26,7 +26,7 @@ class CharterAutoSaveUI extends UISliceSprite {
|
||||
icon.animation.play("icon"); icon.animation.curAnim.curFrame = 0;
|
||||
members.push(icon);
|
||||
|
||||
members.push(autosavingText = new UIText(x+12+10+4, y+8, 0, "Autosaving in 10 seconds...", 12));
|
||||
members.push(autosavingText = new UIText(x+12+10+4, y+8, 0, TU.translate("editor.autosavingIn", ["?", "..."]), 12));
|
||||
|
||||
progressBarBack = new FlxSprite(x + 10, y + bHeight - 20).makeGraphic(Std.int(bWidth-20), 10, 0x00000000, true);
|
||||
progressBarBack.drawRoundRect(0, 0, progressBarBack.width, progressBarBack.height, 4, 6, 0xFF727272, null, {smoothing: false});
|
||||
@@ -45,7 +45,8 @@ class CharterAutoSaveUI extends UISliceSprite {
|
||||
|
||||
icon.animation.curAnim.curFrame = 2;
|
||||
(new FlxTimer()).start(1, (_) -> {disappearAnimation(true);});
|
||||
autosavingText.text += " (Canceled)!"; cancelButton.visible = false;
|
||||
autosavingText.text += " " + TU.translate("editor.autosavingCanceledSuffix");
|
||||
cancelButton.visible = false;
|
||||
for (member in [this, autosavingText]) member.color = 0xFFE67F7F;
|
||||
for (member in [progressBar, progressBarBack]) member.color = 0xFFE67F7F;
|
||||
}, 24, 14);
|
||||
@@ -68,8 +69,10 @@ class CharterAutoSaveUI extends UISliceSprite {
|
||||
|
||||
__tween = FlxTween.num(0, 1, time, null, (v:Float) -> {
|
||||
if ((progress = v) < .95) {
|
||||
autosavingText.text = 'Autosaving in ${Math.min(Math.round(time), 1+Math.floor(Math.abs(time-(progress*time))))} seconds';
|
||||
autosavingText.text += [for (i in 0...(Math.floor((progress*time*3)%4))) "."].join("");
|
||||
autosavingText.text = TU.translate("editor.autosavingIn", [
|
||||
Math.min(Math.round(time), 1+Math.floor(Math.abs(time-(progress*time)))),
|
||||
".".repeat(Math.floor((progress*time*3)%4))
|
||||
]);
|
||||
}
|
||||
});
|
||||
__timer = new FlxTimer();
|
||||
@@ -79,6 +82,8 @@ class CharterAutoSaveUI extends UISliceSprite {
|
||||
icon.animation.curAnim.curFrame = 1;
|
||||
for (member in [this, progressBar, progressBarBack, autosavingText]) member.color = 0xFFA3EC95;
|
||||
|
||||
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_AUTOSAVE_SOUND));
|
||||
|
||||
(new FlxTimer()).start(1, (_) -> {disappearAnimation();});
|
||||
});
|
||||
}
|
||||
@@ -105,11 +110,11 @@ class CharterAutoSaveUI extends UISliceSprite {
|
||||
bar.follow(this, 10, bHeight-20);
|
||||
bar.alpha = alpha;
|
||||
}
|
||||
progressBar.clipRect = progressBar.clipRect.set(0, 0, progressBar.width*progress,progressBar.height);
|
||||
progressBar.rawClipRect = progressBar.rawClipRect.set(0, 0, progressBar.frameWidth*progress,progressBar.frameHeight);
|
||||
}
|
||||
|
||||
public function appearAnimation() {
|
||||
autosavingText.text = "Autosaving in seconds..."; progress = 0; icon.animation.curAnim.curFrame = 0;
|
||||
progress = 0; icon.animation.curAnim.curFrame = 0;
|
||||
for (member in [this, autosavingText, progressBar, progressBarBack]) member.color = 0xFFFFFFFF;
|
||||
|
||||
x = -(320); alpha=0; FlxTween.cancelTweensOf(this); cancelled = false; cancelButton.visible = true;
|
||||
|
||||
@@ -43,8 +43,6 @@ class CharterBackdropGroup extends FlxTypedGroup<CharterBackdrop> {
|
||||
|
||||
public var conductorSprY:Float = 0;
|
||||
public var bottomLimitY:Float = 0;
|
||||
public var sectionsVisible:Bool = true;
|
||||
public var beatsVisible:Bool = true;
|
||||
|
||||
// Just here so you can update display sprites all dat and above
|
||||
public var strumlinesAmount:Int = 0;
|
||||
@@ -80,8 +78,6 @@ class CharterBackdropGroup extends FlxTypedGroup<CharterBackdrop> {
|
||||
|
||||
grid.conductorFollowerSpr.y = conductorSprY;
|
||||
grid.bottomSeparator.y = (grid.bottomLimit.y = bottomLimitY)-2;
|
||||
grid.sectionSeparator.visible = sectionsVisible;
|
||||
grid.beatSeparator.visible = beatsVisible;
|
||||
|
||||
grid.waveformSprite.shader = strumLine.waveformShader;
|
||||
|
||||
@@ -174,8 +170,7 @@ class CharterBackdrop extends FlxTypedGroup<FlxBasic> {
|
||||
public var waveformSprite:FlxSprite;
|
||||
|
||||
public var conductorFollowerSpr:FlxSprite;
|
||||
public var beatSeparator:FlxBackdrop;
|
||||
public var sectionSeparator:FlxBackdrop;
|
||||
public var beatSeparator:CharterGridSeperator;
|
||||
|
||||
public var notesGroup:NotesDrawGroup = new NotesDrawGroup();
|
||||
public var strumLine:CharterStrumline;
|
||||
@@ -197,23 +192,14 @@ class CharterBackdrop extends FlxTypedGroup<FlxBasic> {
|
||||
waveformSprite.updateHitbox();
|
||||
add(waveformSprite);
|
||||
|
||||
sectionSeparator = new FlxBackdrop(null, Y, 0, 0);
|
||||
sectionSeparator.y = -2;
|
||||
sectionSeparator.visible = Options.charterShowSections;
|
||||
beatSeparator = new CharterGridSeperator();
|
||||
beatSeparator.makeSolid(1, 1, -1);
|
||||
beatSeparator.alpha = 0.5;
|
||||
beatSeparator.scrollFactor.set(1, 1);
|
||||
beatSeparator.scale.set((4 * 40), 2);
|
||||
beatSeparator.updateHitbox();
|
||||
|
||||
beatSeparator = new FlxBackdrop(null, Y, 0, 0);
|
||||
beatSeparator.y = -1;
|
||||
beatSeparator.visible = Options.charterShowBeats;
|
||||
|
||||
for(sep in [sectionSeparator, beatSeparator]) {
|
||||
sep.makeSolid(1, 1, -1);
|
||||
sep.alpha = 0.5;
|
||||
sep.scrollFactor.set(1, 1);
|
||||
sep.scale.set((4 * 40), sep == sectionSeparator ? 4 : 2);
|
||||
sep.updateHitbox();
|
||||
}
|
||||
add(beatSeparator);
|
||||
add(sectionSeparator);
|
||||
add(notesGroup);
|
||||
|
||||
bottomSeparator = new FlxSprite(0,-2);
|
||||
@@ -263,7 +249,7 @@ class CharterBackdrop extends FlxTypedGroup<FlxBasic> {
|
||||
keyCount = strumLine.keyCount;
|
||||
} else alpha = 0.9;
|
||||
|
||||
for (spr in [gridBackDrop, sectionSeparator, beatSeparator, topLimit, bottomLimit,
|
||||
for (spr in [gridBackDrop, beatSeparator, topLimit, bottomLimit,
|
||||
topSeparator, bottomSeparator, conductorFollowerSpr, waveformSprite]) {
|
||||
spr.x = x; if (spr != waveformSprite) spr.alpha = alpha;
|
||||
spr.cameras = this.cameras;
|
||||
@@ -274,9 +260,6 @@ class CharterBackdrop extends FlxTypedGroup<FlxBasic> {
|
||||
if (__lastKeyCount != keyCount) gridShader.hset("segments", keyCount);
|
||||
__lastKeyCount = keyCount;
|
||||
|
||||
sectionSeparator.spacing.y = (10 * Conductor.beatsPerMeasure * Conductor.stepsPerBeat) - 1;
|
||||
beatSeparator.spacing.y = (20 * Conductor.stepsPerBeat) - 1;
|
||||
|
||||
topLimit.scale.set(keyCount * 40, Math.ceil(FlxG.height / cameras[0].zoom));
|
||||
topLimit.updateHitbox();
|
||||
topLimit.y = -topLimit.height;
|
||||
@@ -284,7 +267,7 @@ class CharterBackdrop extends FlxTypedGroup<FlxBasic> {
|
||||
bottomLimit.scale.set(keyCount * 40, Math.ceil(FlxG.height / cameras[0].zoom));
|
||||
bottomLimit.updateHitbox();
|
||||
|
||||
for (spr in [conductorFollowerSpr, sectionSeparator, beatSeparator, topSeparator, bottomSeparator]) {
|
||||
for (spr in [conductorFollowerSpr, beatSeparator, topSeparator, bottomSeparator]) {
|
||||
spr.scale.x = keyCount * 40;
|
||||
spr.updateHitbox();
|
||||
}
|
||||
@@ -310,9 +293,161 @@ class CharterBackdrop extends FlxTypedGroup<FlxBasic> {
|
||||
}
|
||||
}
|
||||
|
||||
class CharterGridSeperatorBase extends FlxSprite {
|
||||
|
||||
private static var minStep:Float = 0;
|
||||
private static var maxStep:Float = 0;
|
||||
|
||||
private static var minBeat:Float = 0;
|
||||
private static var maxBeat:Float = 0;
|
||||
|
||||
private static var minMeasure:Float = 0;
|
||||
private static var maxMeasure:Float = 0;
|
||||
|
||||
private static var lastMinBeat:Float = -1;
|
||||
private static var lastMaxBeat:Float = -1;
|
||||
|
||||
private static var lastMinMeasure:Float = -1;
|
||||
private static var lastMaxMeasure:Float = -1;
|
||||
|
||||
public static var lastConductorSprY:Float = Math.NEGATIVE_INFINITY;
|
||||
|
||||
private static var beatStepTimes:Array<Float> = [];
|
||||
private static var measureStepTimes:Array<Float> = [];
|
||||
private static var timeSignatureChangeGaps:Array<Float> = [];
|
||||
|
||||
private function recalculateBeats() {
|
||||
var conductorSprY = Charter.instance.gridBackdrops.conductorSprY;
|
||||
if (conductorSprY == lastConductorSprY) return;
|
||||
|
||||
var zoomOffset = ((FlxG.height * (1/cameras[0].zoom)) * 0.5);
|
||||
|
||||
minStep = (conductorSprY - zoomOffset)/40;
|
||||
maxStep = (conductorSprY + zoomOffset)/40;
|
||||
|
||||
var minTime:Float = Conductor.getStepsInTime(minStep);
|
||||
var maxTime:Float = Conductor.getStepsInTime(maxStep);
|
||||
|
||||
var minBpmChange = Conductor.bpmChangeMap[Conductor.getTimeInChangeIndex(minTime)];
|
||||
var maxBpmChange = Conductor.bpmChangeMap[Conductor.getTimeInChangeIndex(maxTime)];
|
||||
|
||||
minBeat = Conductor.getTimeInBeats(minTime);
|
||||
maxBeat = Conductor.getTimeInBeats(maxTime);
|
||||
|
||||
minMeasure = minBpmChange.measureTime + (minBeat - minBpmChange.beatTime) / minBpmChange.beatsPerMeasure;
|
||||
maxMeasure = maxBpmChange.measureTime + (maxBeat - maxBpmChange.beatTime) / maxBpmChange.beatsPerMeasure;
|
||||
|
||||
//cap out the beats/measures at the end of the song
|
||||
var endTime = Conductor.getStepsInTime(Charter.instance.__endStep);
|
||||
var endBeat = Conductor.getTimeInBeats(endTime);
|
||||
var endBpmChange = Conductor.bpmChangeMap[Conductor.getTimeInChangeIndex(endTime)];
|
||||
var endMeasure = endBpmChange.measureTime + (endBeat - endBpmChange.beatTime) / endBpmChange.beatsPerMeasure;
|
||||
|
||||
if (maxBeat > endBeat) maxBeat = endBeat;
|
||||
if (maxMeasure > endMeasure) maxMeasure = endMeasure;
|
||||
if (minMeasure < 0) minMeasure = 0;
|
||||
if (minBeat < 0) minBeat = 0;
|
||||
|
||||
//only calculate if needed
|
||||
if ((minBeat != lastMinBeat) || (maxBeat != lastMaxBeat) || (minMeasure != lastMinMeasure) || (maxMeasure != lastMaxMeasure) || lastConductorSprY == Math.NEGATIVE_INFINITY) {
|
||||
calculateTimeSignatureGaps();
|
||||
calculateStepTimes();
|
||||
lastMinBeat = minBeat;
|
||||
lastMaxBeat = maxBeat;
|
||||
lastMinMeasure = minMeasure;
|
||||
lastMaxMeasure = maxMeasure;
|
||||
}
|
||||
|
||||
lastConductorSprY = conductorSprY;
|
||||
}
|
||||
|
||||
private inline function calculateTimeSignatureGaps() {
|
||||
//for time signatures that start mid step
|
||||
timeSignatureChangeGaps.splice(0, timeSignatureChangeGaps.length);
|
||||
for (i => change in Conductor.bpmChangeMap) {
|
||||
if (change.stepTime >= minStep && change.stepTime <= maxStep) {
|
||||
//get step while ignoring the current change
|
||||
var index:Int = CoolUtil.boundInt(i-1, 0, Conductor.bpmChangeMap.length - 1);
|
||||
var step:Float = Conductor.getTimeWithBPMInSteps(change.songTime, index, Conductor.getTimeWithIndexInBPM(change.songTime, index));
|
||||
|
||||
if (Math.ceil(step) - step > 0 && (step - Math.floor(step)) > FlxMath.EPSILON) { //mid step change
|
||||
timeSignatureChangeGaps.push(step);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private inline function calculateStepTimes() {
|
||||
beatStepTimes.splice(0, beatStepTimes.length);
|
||||
for (i in Math.floor(minBeat)...Math.ceil(maxBeat)) {
|
||||
beatStepTimes.push(Conductor.getTimeInSteps(Conductor.getBeatsInTime(i)));
|
||||
}
|
||||
measureStepTimes.splice(0, measureStepTimes.length);
|
||||
for (i in Math.floor(minMeasure)...Math.ceil(maxMeasure)) {
|
||||
measureStepTimes.push(Conductor.getTimeInSteps(Conductor.getMeasuresInTime(i)));
|
||||
}
|
||||
}
|
||||
|
||||
override public function draw() {
|
||||
|
||||
//should only need to recalculate once per frame and will be shared across each instance
|
||||
recalculateBeats();
|
||||
|
||||
drawTimeSignatureChangeGaps();
|
||||
|
||||
if (Options.charterShowBeats) drawBeats();
|
||||
if (Options.charterShowSections) drawMeasures();
|
||||
}
|
||||
|
||||
private function drawBeats(offset:Float = 0.0) {
|
||||
for (i in beatStepTimes) {
|
||||
y = (i*40)+offset;
|
||||
super.draw();
|
||||
}
|
||||
}
|
||||
private function drawMeasures(offset:Float = 0.0) {
|
||||
for (i in measureStepTimes) {
|
||||
y = (i*40)+offset;
|
||||
super.draw();
|
||||
}
|
||||
}
|
||||
private function drawTimeSignatureChangeGaps() {
|
||||
if (timeSignatureChangeGaps.length == 0) return;
|
||||
var prevColor = color;
|
||||
var prevBlend = blend;
|
||||
|
||||
color = 0xFF888888;
|
||||
blend = MULTIPLY;
|
||||
|
||||
for (step in timeSignatureChangeGaps) {
|
||||
y = step*40;
|
||||
var diff = Math.ceil(step) - step;
|
||||
scale.y = diff*40;
|
||||
updateHitbox();
|
||||
|
||||
super.draw();
|
||||
}
|
||||
|
||||
color = prevColor;
|
||||
blend = prevBlend;
|
||||
}
|
||||
}
|
||||
|
||||
class CharterGridSeperator extends CharterGridSeperatorBase {
|
||||
override private function drawBeats(offset:Float = 0.0) {
|
||||
scale.y = 2;
|
||||
updateHitbox();
|
||||
super.drawBeats(-2);
|
||||
}
|
||||
override private function drawMeasures(offset:Float = 0.0) {
|
||||
scale.y = 4;
|
||||
updateHitbox();
|
||||
super.drawMeasures(-3);
|
||||
}
|
||||
}
|
||||
|
||||
class EventBackdrop extends FlxBackdrop {
|
||||
public var eventBeatSeparator:FlxBackdrop;
|
||||
public var eventSecSeparator:FlxBackdrop;
|
||||
public var eventBeatSeparator:CharterEventGridSeperator;
|
||||
|
||||
public var topSeparator:FlxSprite;
|
||||
public var bottomSeparator:FlxSprite;
|
||||
@@ -325,25 +460,11 @@ class EventBackdrop extends FlxBackdrop {
|
||||
alpha = 0.9;
|
||||
|
||||
// Separators
|
||||
eventSecSeparator = new FlxBackdrop(null, Y, 0, 0);
|
||||
eventSecSeparator.y = -2;
|
||||
eventSecSeparator.visible = Options.charterShowSections;
|
||||
|
||||
eventBeatSeparator = new FlxBackdrop(null, Y, 0, 0);
|
||||
eventBeatSeparator.y = -1;
|
||||
eventBeatSeparator.visible = Options.charterShowBeats;
|
||||
|
||||
for(sep in [eventSecSeparator, eventBeatSeparator]) {
|
||||
sep.makeSolid(1, 1, -1);
|
||||
sep.alpha = 0.5;
|
||||
sep.scrollFactor.set(1, 1);
|
||||
}
|
||||
|
||||
eventSecSeparator.scale.set(20, 4);
|
||||
eventSecSeparator.updateHitbox();
|
||||
|
||||
eventBeatSeparator.scale.set(10, 2);
|
||||
eventBeatSeparator.updateHitbox();
|
||||
eventBeatSeparator = new CharterEventGridSeperator();
|
||||
eventBeatSeparator.makeSolid(1, 1, -1);
|
||||
eventBeatSeparator.alpha = 0.5;
|
||||
eventBeatSeparator.scrollFactor.set(1, 1);
|
||||
eventBeatSeparator.global = global;
|
||||
|
||||
bottomSeparator = new FlxSprite(0,-2);
|
||||
bottomSeparator.makeSolid(1, 1, -1);
|
||||
@@ -364,23 +485,34 @@ class EventBackdrop extends FlxBackdrop {
|
||||
public override function draw() {
|
||||
super.draw();
|
||||
|
||||
eventSecSeparator.spacing.y = (10 * Conductor.beatsPerMeasure * Conductor.stepsPerBeat) - 1;
|
||||
eventBeatSeparator.spacing.y = (20 * Conductor.stepsPerBeat) - 1;
|
||||
|
||||
eventSecSeparator.cameras = cameras;
|
||||
eventSecSeparator.x = this.global ? x : ((x+width) - 20);
|
||||
if (eventSecSeparator.visible) eventSecSeparator.draw();
|
||||
|
||||
eventBeatSeparator.cameras = cameras;
|
||||
eventBeatSeparator.x = this.global ? x : ((x+width) - 10);
|
||||
if (eventBeatSeparator.visible) eventBeatSeparator.draw();
|
||||
eventBeatSeparator.xPos = global ? x : x+width;
|
||||
eventBeatSeparator.draw();
|
||||
|
||||
topSeparator.x = this.global ? x : ((x+width) - 20);
|
||||
topSeparator.x = global ? x : (x+width) - 20;
|
||||
topSeparator.cameras = this.cameras;
|
||||
if (!eventSecSeparator.visible) topSeparator.draw();
|
||||
if (!Options.charterShowSections) topSeparator.draw();
|
||||
|
||||
bottomSeparator.x = this.global ? x : ((x+width) - 20);
|
||||
bottomSeparator.x = global ? x : (x+width) - 20;
|
||||
bottomSeparator.cameras = this.cameras;
|
||||
bottomSeparator.draw();
|
||||
}
|
||||
}
|
||||
|
||||
class CharterEventGridSeperator extends CharterGridSeperatorBase {
|
||||
public var xPos:Float = 0.0;
|
||||
public var global:Bool = false;
|
||||
override private function drawBeats(offset:Float = 0.0) {
|
||||
scale.set(10, 2);
|
||||
updateHitbox();
|
||||
x = global ? xPos : xPos-10;
|
||||
super.drawBeats(-2);
|
||||
}
|
||||
override private function drawMeasures(offset:Float = 0.0) {
|
||||
scale.set(20, 4);
|
||||
updateHitbox();
|
||||
x = global ? xPos : xPos-20;
|
||||
super.drawMeasures(-3);
|
||||
}
|
||||
override private function drawTimeSignatureChangeGaps() {}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package funkin.editors.charter;
|
||||
|
||||
import flixel.util.FlxColor;
|
||||
import flixel.math.FlxPoint;
|
||||
import funkin.backend.chart.ChartData.ChartBookmark;
|
||||
|
||||
class CharterBookmarkCreation extends UISubstateWindow {
|
||||
public var saveButton:UIButton;
|
||||
public var closeButton:UIButton;
|
||||
public var textBox:UITextBox;
|
||||
public var colorPicker:UICompactColorwheel;
|
||||
|
||||
var callback:Bool->String->FlxColor->Float->Void;
|
||||
|
||||
var bookmarkStep:Float = 0;
|
||||
|
||||
override public function new(step:Float, close:Bool->String->FlxColor->Float->Void)
|
||||
{
|
||||
super();
|
||||
|
||||
winWidth = 320;
|
||||
winHeight = 250;
|
||||
winTitle = TU.translate("charter.bookmarks.createBookmarkTitle");
|
||||
|
||||
callback = close;
|
||||
|
||||
bookmarkStep = step;
|
||||
}
|
||||
|
||||
public override function create() {
|
||||
FlxG.sound.music.pause();
|
||||
Charter.instance.vocals.pause();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
super.create();
|
||||
|
||||
textBox = new UITextBox(windowSpr.x + 20, windowSpr.y + 70, TU.translate("charter.bookmarks.newBookmarkName"), 276);
|
||||
textBox.antialiasing = true;
|
||||
add(textBox);
|
||||
addLabelOn(textBox, TU.translate("charter.bookmarks.createBookmarkName"));
|
||||
|
||||
colorPicker = new UICompactColorwheel(textBox.x, textBox.y + textBox.bHeight + 34, flixel.util.FlxColor.RED);
|
||||
add(colorPicker);
|
||||
addLabelOn(colorPicker, TU.translate("charter.bookmarks.createBookmarkColor"));
|
||||
|
||||
saveButton = new UIButton(windowSpr.x + windowSpr.bWidth - 20 - 125, windowSpr.y + windowSpr.bHeight - 16 - 32, TU.translate("editor.saveClose"), function() {
|
||||
callback(true, textBox.label.text, colorPicker.curColor, bookmarkStep);
|
||||
close();
|
||||
}, 125);
|
||||
add(saveButton);
|
||||
|
||||
closeButton = new UIButton(saveButton.x - 20, saveButton.y, TU.translate("editor.close"), function() {
|
||||
callback(false, "", FlxColor.TRANSPARENT, -1);
|
||||
close();
|
||||
}, 125);
|
||||
add(closeButton);
|
||||
closeButton.color = 0xFFFF0000;
|
||||
closeButton.x -= closeButton.bWidth;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package funkin.editors.charter;
|
||||
|
||||
import flixel.math.FlxPoint;
|
||||
import funkin.backend.chart.ChartData.ChartBookmark;
|
||||
|
||||
class CharterBookmarkList extends UISubstateWindow {
|
||||
public var bookmarkList:UIButtonList<BookmarkButton>;
|
||||
public var saveButton:UIButton;
|
||||
public var closeButton:UIButton;
|
||||
|
||||
public override function create() {
|
||||
FlxG.sound.music.pause();
|
||||
Charter.instance.vocals.pause();
|
||||
|
||||
winTitle = TU.translate("charter.bookmarks.editBookmarkListTitle");
|
||||
winWidth = 380;
|
||||
winHeight = 390;
|
||||
|
||||
super.create();
|
||||
|
||||
var title:UIText;
|
||||
add(title = new UIText(windowSpr.x + 20, windowSpr.y + 30 + 16, 0, TU.translate("charter.bookmarks.editBookmarksTitle"), 28));
|
||||
|
||||
bookmarkList = new UIButtonList<BookmarkButton>(20, title.y + title.height + 10, winWidth - 40, 342 - 85 - 16, null, FlxPoint.get(winWidth - 40, (342 - 85 - 16)/4), null, 0);
|
||||
bookmarkList.cameraSpacing = 0;
|
||||
|
||||
for (b in Charter.instance.getBookmarkList())
|
||||
bookmarkList.add(new BookmarkButton(0, 0, b, bookmarkList));
|
||||
|
||||
add(bookmarkList);
|
||||
bookmarkList.frames = Paths.getFrames('editors/ui/inputbox');
|
||||
|
||||
bookmarkList.content.remove(bookmarkList.addButton); //i dont need it
|
||||
|
||||
saveButton = new UIButton(windowSpr.x + windowSpr.bWidth - 20 - 125, windowSpr.y + windowSpr.bHeight - 16 - 32, TU.translate("editor.saveClose"), function() {
|
||||
saveList();
|
||||
close();
|
||||
}, 125);
|
||||
add(saveButton);
|
||||
|
||||
closeButton = new UIButton(saveButton.x - 20, saveButton.y, TU.translate("editor.close"), function() {
|
||||
close();
|
||||
}, 125);
|
||||
add(closeButton);
|
||||
closeButton.color = 0xFFFF0000;
|
||||
closeButton.x -= closeButton.bWidth;
|
||||
}
|
||||
|
||||
public function saveList() {
|
||||
var oldList:Array<ChartBookmark> = Charter.instance.getBookmarkList();
|
||||
var newList:Array<ChartBookmark> = [for (btn in bookmarkList.buttons.members) {time: btn.bookmark.time, name: btn.textBox.label.text, color: btn.bookmark.color}];
|
||||
|
||||
PlayState.SONG.bookmarks = newList;
|
||||
Charter.instance.updateBookmarks();
|
||||
Charter.undos.addToUndo(CEditBookmarks(oldList, newList));
|
||||
}
|
||||
}
|
||||
|
||||
class BookmarkButton extends UIButton {
|
||||
public var textBox:UITextBox;
|
||||
public var deleteButton:UIButton;
|
||||
public var deleteIcon:FlxSprite;
|
||||
public var bookmark:ChartBookmark;
|
||||
|
||||
public function new(x:Float, y:Float, bookmark:ChartBookmark, parent:UIButtonList<BookmarkButton>) {
|
||||
super(x, y, '', null, Std.int(parent.buttonSize.x), Std.int(parent.buttonSize.y));
|
||||
this.bookmark = bookmark;
|
||||
autoAlpha = false;
|
||||
|
||||
members.push(textBox = new UITextBox(10, 10, bookmark.name, 276));
|
||||
textBox.antialiasing = true;
|
||||
|
||||
deleteButton = new UIButton(textBox.x + textBox.label.width + 10, textBox.y, null, function () {
|
||||
parent.remove(this);
|
||||
}, 32);
|
||||
deleteButton.color = 0xFFFF0000;
|
||||
deleteButton.autoAlpha = false;
|
||||
members.push(deleteButton);
|
||||
|
||||
deleteIcon = new FlxSprite(deleteButton.x + (15/2), deleteButton.y + 8).loadGraphic(Paths.image('editors/delete-button'));
|
||||
deleteIcon.antialiasing = false;
|
||||
members.push(deleteIcon);
|
||||
}
|
||||
|
||||
override function update(elapsed:Float) {
|
||||
textBox.y = (y + bHeight/2) - (textBox.bHeight/2);
|
||||
deleteButton.x = textBox.x + textBox.bWidth + 14; deleteButton.y = textBox.y;
|
||||
deleteIcon.x = deleteButton.x + (15/2); deleteIcon.y = deleteButton.y + 8;
|
||||
|
||||
deleteButton.selectable = selectable;
|
||||
deleteButton.shouldPress = shouldPress;
|
||||
|
||||
super.update(elapsed);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,14 @@
|
||||
package funkin.editors.charter;
|
||||
|
||||
import openfl.display.BitmapData;
|
||||
import openfl.geom.ColorTransform;
|
||||
import flixel.group.FlxSpriteGroup;
|
||||
import flixel.math.FlxPoint;
|
||||
import flixel.system.FlxAssets.FlxGraphicAsset;
|
||||
import funkin.backend.chart.ChartData.ChartEvent;
|
||||
import funkin.backend.scripting.DummyScript;
|
||||
import funkin.backend.scripting.Script;
|
||||
import funkin.backend.system.Conductor;
|
||||
import funkin.editors.charter.Charter.ICharterSelectable;
|
||||
import funkin.editors.charter.CharterBackdropGroup.EventBackdrop;
|
||||
import funkin.game.Character;
|
||||
@@ -58,8 +63,8 @@ class CharterEvent extends UISliceSprite implements ICharterSelectable {
|
||||
selectedColorTransform(colorTransform);
|
||||
useColorTransform = true;
|
||||
|
||||
for (sprite in icons) {
|
||||
@:privateAccess sprite.colorTransform.__identity();
|
||||
for (sprite in icons) @:privateAccess {
|
||||
sprite.colorTransform.__identity();
|
||||
selectedColorTransform(sprite.colorTransform);
|
||||
}
|
||||
|
||||
@@ -76,25 +81,84 @@ class CharterEvent extends UISliceSprite implements ICharterSelectable {
|
||||
transform.blueOffset += selected ? 168 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pack data is a list of 4 strings separated by `________PACKSEP________`
|
||||
* [0] Event Name
|
||||
* [1] Event Script
|
||||
* [2] Event JSON Info
|
||||
* [3] Event Icon
|
||||
* [4] Event UI Script / Icon Script
|
||||
**/
|
||||
@:dox(hide) public static function getPackData(name:String):Array<String> {
|
||||
var packFile = Paths.pack('events/${name}');
|
||||
if (Assets.exists(packFile)) {
|
||||
return Assets.getText(packFile).split('________PACKSEP________');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@:dox(hide) public static function getUIScript(event:ChartEvent, caller:String):Script {
|
||||
var uiScript = Paths.script('data/events/${event.name}.ui');
|
||||
var script:Script = null;
|
||||
if(Assets.exists(uiScript)) {
|
||||
script = Script.create(uiScript);
|
||||
} else {
|
||||
var packData = getPackData(event.name);
|
||||
if(packData != null) {
|
||||
var scriptFile = packData[4];
|
||||
if(scriptFile != null) {
|
||||
script = Script.fromString(scriptFile, uiScript);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(script != null && !(script is DummyScript)) {
|
||||
// classes and functions
|
||||
script.set("EventIconGroup", EventIconGroup); // automatically imported
|
||||
script.set("EventNumber", EventNumber); // automatically imported
|
||||
script.set("getIconFromStrumline", getIconFromStrumline);
|
||||
script.set("getIconFromCharName", getIconFromCharName);
|
||||
script.set("generateDefaultIcon", generateDefaultIcon);
|
||||
script.set("generateEventIconDurationArrow", generateEventIconDurationArrow);
|
||||
script.set("generateEventIconNumbers", generateEventIconNumbers);
|
||||
script.set("generateEventIconWarning", generateEventIconWarning);
|
||||
script.set("getPackData", getPackData);
|
||||
script.set("getEventComponent", getEventComponent);
|
||||
// data
|
||||
script.set("event", event);
|
||||
script.set("caller", caller);
|
||||
|
||||
script.load();
|
||||
}
|
||||
|
||||
return script;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the default event icon for the wanted event
|
||||
* @param name The name of the event
|
||||
* @return The icon
|
||||
**/
|
||||
private static function generateDefaultIcon(name:String) {
|
||||
var isBase64:Bool = false;
|
||||
var path:String = Paths.image('editors/charter/event-icons/$name');
|
||||
var defaultPath = Paths.image('editors/charter/event-icons/Unknown');
|
||||
if (!Assets.exists(path)) path = defaultPath;
|
||||
if(!Assets.exists(path)) {
|
||||
path = defaultPath;
|
||||
|
||||
var packPath = Paths.pack('events/$name');
|
||||
if (Assets.exists(packPath)) {
|
||||
var packText = Assets.getText(packPath).split('________PACKSEP________');
|
||||
var packImg = packText[3];
|
||||
if(packImg != null && packImg.length > 0) {
|
||||
isBase64 = !packImg.startsWith("assets/");
|
||||
path = packImg;
|
||||
var packData = getPackData(name);
|
||||
if(packData != null) {
|
||||
var packImg = packData[3];
|
||||
if(packImg != null && packImg.length > 0) {
|
||||
isBase64 = !packImg.startsWith("assets/");
|
||||
path = packImg;
|
||||
}
|
||||
}
|
||||
}
|
||||
path = path.trim();
|
||||
|
||||
var graphic:FlxGraphicAsset = try {
|
||||
isBase64 ? openfl.display.BitmapData.fromBase64(path, 'UTF8') : path;
|
||||
isBase64 ? BitmapData.fromBase64(path, 'UTF8') : path;
|
||||
} catch(e:Dynamic) {
|
||||
Logs.trace('Failed to load event icon: ${e.toString()}', ERROR);
|
||||
isBase64 = false;
|
||||
@@ -106,27 +170,176 @@ class CharterEvent extends UISliceSprite implements ICharterSelectable {
|
||||
graphic = defaultPath;
|
||||
}
|
||||
|
||||
return new FlxSprite().loadGraphic(graphic);
|
||||
return new FlxSprite(graphic);
|
||||
}
|
||||
|
||||
public static function generateEventIcon(event:ChartEvent) {
|
||||
return switch(event.name) {
|
||||
default:
|
||||
generateDefaultIcon(event.name);
|
||||
case "Camera Movement":
|
||||
// custom icon for camera movement
|
||||
var state:Charter = cast FlxG.state;
|
||||
if (event.params != null && event.params[0] != null && event.params[0] >= 0 && event.params[0] < state.strumLines.length) {
|
||||
// camera movement, use health icon
|
||||
var icon = Character.getIconFromCharName(state.strumLines.members[event.params[0]].strumLine.characters[0]);
|
||||
var healthIcon = new HealthIcon(icon);
|
||||
healthIcon.setUnstretchedGraphicSize(32, 32, false);
|
||||
healthIcon.scrollFactor.set(1, 1);
|
||||
healthIcon.active = false;
|
||||
healthIcon;
|
||||
} else
|
||||
generateDefaultIcon(event.name);
|
||||
/**
|
||||
* Gets a component sprite from the editors/charter/event-icons/components folder
|
||||
* If you wanna use a number, please use the EventNumber class instead
|
||||
* @param type The type of component to get
|
||||
* @param x The x position of the sprite (optional)
|
||||
* @param y The y position of the sprite (optional)
|
||||
* @return The component sprite
|
||||
**/
|
||||
public static function getEventComponent(type:String, x:Float = 0.0, y:Float = 0.0) {
|
||||
var componentPath = Paths.image("editors/charter/event-icons/components/" + type);
|
||||
if (Assets.exists(componentPath)) return new FlxSprite(x, y, componentPath);
|
||||
|
||||
Logs.trace('Could not find component $type', WARNING);
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expected to be called from inside of a ui script,
|
||||
* calling this elsewhere might cause unexpected results or crashes
|
||||
**/
|
||||
public static function getIconFromStrumline(index:Null<Int>) {
|
||||
var state = cast(FlxG.state, Charter);
|
||||
if (index != null && index >= 0 && index < state.strumLines.length) {
|
||||
return getIconFromCharName(state.strumLines.members[index].strumLine.characters[0]);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function getIconFromCharName(name:String) {
|
||||
var icon = Character.getIconFromCharName(name);
|
||||
var healthIcon = new HealthIcon(icon);
|
||||
CoolUtil.setUnstretchedGraphicSize(healthIcon, 32, 32, false);
|
||||
healthIcon.scrollFactor.set(1, 1);
|
||||
healthIcon.active = false;
|
||||
return healthIcon;
|
||||
}
|
||||
|
||||
public static function generateEventIcon(event:ChartEvent, inMenu:Bool = true):FlxSprite {
|
||||
var script = getUIScript(event, "event-icon");
|
||||
if(script != null && !(script is DummyScript)) {
|
||||
script.set("inMenu", inMenu);
|
||||
if(script.get("generateIcon") != null) {
|
||||
var res:FlxSprite = script.call("generateIcon");
|
||||
if(res != null)
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
switch(event.name) {
|
||||
case "Time Signature Change":
|
||||
if(event.params != null && (event.params[0] >= 0 || event.params[1] >= 0)) {
|
||||
var group = new EventIconGroup();
|
||||
group.add(generateDefaultIcon(event.name));
|
||||
group.add({ // top
|
||||
var num = new EventNumber(9, -1, event.params[0], EventNumber.ALIGN_CENTER);
|
||||
num.active = false;
|
||||
num;
|
||||
});
|
||||
group.add({ // bottom
|
||||
var num = new EventNumber(9, 10, event.params[1], EventNumber.ALIGN_CENTER);
|
||||
num.active = false;
|
||||
num;
|
||||
});
|
||||
if (Conductor.invalidEvents.contains(event)) generateEventIconWarning(group);
|
||||
return group;
|
||||
}
|
||||
case "Continuous BPM Change":
|
||||
if(event.params != null && event.params[1] != null) {
|
||||
var group = new EventIconGroup();
|
||||
group.add(generateDefaultIcon("BPM Change Start"));
|
||||
if (!inMenu) {
|
||||
generateEventIconDurationArrow(group, event.params[1]);
|
||||
group.members[0].y -= 2;
|
||||
generateEventIconNumbers(group, event.params[0], 3);
|
||||
}
|
||||
if (Conductor.invalidEvents.contains(event)) generateEventIconWarning(group);
|
||||
return group;
|
||||
} else {
|
||||
return generateDefaultIcon("BPM Change Start");
|
||||
}
|
||||
case "BPM Change":
|
||||
if(event.params != null && event.params[0] != null) {
|
||||
var group = new EventIconGroup();
|
||||
group.add(generateDefaultIcon(event.name));
|
||||
if (!inMenu) {
|
||||
group.members[0].y -= 2;
|
||||
generateEventIconNumbers(group, event.params[0], 3);
|
||||
}
|
||||
if (Conductor.invalidEvents.contains(event)) generateEventIconWarning(group);
|
||||
return group;
|
||||
}
|
||||
|
||||
case "Scroll Speed Change":
|
||||
if(event.params != null && !inMenu) {
|
||||
var group = new EventIconGroup();
|
||||
group.add(generateDefaultIcon(event.name));
|
||||
if (event.params[0]) generateEventIconDurationArrow(group, event.params[2]);
|
||||
group.members[0].y -= 2;
|
||||
generateEventIconNumbers(group, event.params[1]);
|
||||
return group;
|
||||
}
|
||||
|
||||
case "Camera Movement":
|
||||
// camera movement, use health icon
|
||||
if(event.params != null) {
|
||||
var icon = getIconFromStrumline(event.params[0]);
|
||||
if(icon != null) return icon;
|
||||
}
|
||||
}
|
||||
return generateDefaultIcon(event.name);
|
||||
}
|
||||
|
||||
private static function generateEventIconNumbers(group:EventIconGroup, number:Float, x:Float = 4, y:Float = 15, spacing:Float = 5, precision:Int = 3) {
|
||||
group.add({
|
||||
var num = new EventNumber(x, y, number, EventNumber.ALIGN_CENTER, spacing, precision);
|
||||
if (num.numWidth > 20) {
|
||||
num.scale.x = num.scale.y = 20 / num.numWidth;
|
||||
}
|
||||
num.active = false;
|
||||
num;
|
||||
});
|
||||
}
|
||||
|
||||
private static function generateEventIconDurationArrow(group:EventIconGroup, stepDuration:Float) {
|
||||
//var group = new EventIconGroup();
|
||||
//group.add(generateDefaultIcon(startIcon));
|
||||
|
||||
var xOffset = 4;
|
||||
var yGap = 24;
|
||||
var endGap = 2;
|
||||
|
||||
if (stepDuration >= 0.55) { //min time for showing arrow
|
||||
var tail = new FlxSprite(xOffset, yGap);
|
||||
var arrow = new FlxSprite(xOffset, (stepDuration * 40) + endGap);
|
||||
var arrowSegment = new FlxSprite(xOffset, yGap);
|
||||
tail.frames = arrow.frames = arrowSegment.frames = Paths.getSparrowAtlas("editors/charter/event-icons/components/arrow-down");
|
||||
|
||||
group.add({
|
||||
tail.animation.addByPrefix("tail", "tail");
|
||||
tail.animation.play("tail");
|
||||
tail;
|
||||
});
|
||||
|
||||
group.add({
|
||||
arrowSegment.animation.addByPrefix("segment", "segment");
|
||||
arrowSegment.animation.play("segment");
|
||||
arrowSegment.scale.y = endGap + (stepDuration * 40) - (tail.height + yGap);
|
||||
arrowSegment.updateHitbox();
|
||||
arrowSegment.y += tail.height;
|
||||
arrowSegment;
|
||||
});
|
||||
|
||||
group.add({
|
||||
arrow.animation.addByPrefix("arrow", "arrow");
|
||||
arrow.animation.play("arrow");
|
||||
arrow;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static function generateEventIconWarning(group:EventIconGroup) {
|
||||
for (spr in group) {
|
||||
spr.colorTransform.redMultiplier = spr.colorTransform.greenMultiplier = spr.colorTransform.blueMultiplier = 0.5;
|
||||
spr.colorTransform.redOffset = 100;
|
||||
}
|
||||
group.add(getEventComponent("warning", 16, -8));
|
||||
group.copyColorTransformToChildren = false;
|
||||
}
|
||||
|
||||
public override function onHovered() {
|
||||
@@ -154,19 +367,152 @@ class CharterEvent extends UISliceSprite implements ICharterSelectable {
|
||||
}
|
||||
|
||||
for(event in events) {
|
||||
var spr = generateEventIcon(event);
|
||||
var spr = generateEventIcon(event, false);
|
||||
icons.push(spr);
|
||||
members.push(spr);
|
||||
}
|
||||
|
||||
draggable = true;
|
||||
for (event in events)
|
||||
if (event.name == "BPM Change") {
|
||||
draggable = false;
|
||||
break;
|
||||
}
|
||||
|
||||
bWidth = 37 + (icons.length * 22);
|
||||
x = (snappedToGrid && eventsBackdrop != null && global ? eventsBackdrop.x - bWidth : (global ? 0 : -bWidth));
|
||||
}
|
||||
}
|
||||
|
||||
class EventIconGroup extends FlxSpriteGroup {
|
||||
public var forceWidth:Float = 16;
|
||||
public var forceHeight:Float = 16;
|
||||
public var dontTransformChildren:Bool = false;
|
||||
public var copyColorTransformToChildren:Bool = true;
|
||||
|
||||
public function new() {
|
||||
super();
|
||||
colorTransform = new ColorTransform();
|
||||
scrollFactor.set(1, 1);
|
||||
}
|
||||
|
||||
override function preAdd(sprite:FlxSprite):Void
|
||||
{
|
||||
super.preAdd(sprite);
|
||||
sprite.scrollFactor.set(1, 1);
|
||||
}
|
||||
|
||||
public override function transformChildren<V>(Function:FlxSprite->V->Void, Value:V):Void
|
||||
{
|
||||
if (dontTransformChildren)
|
||||
return;
|
||||
|
||||
super.transformChildren(Function, Value);
|
||||
}
|
||||
|
||||
override function set_x(Value:Float):Float
|
||||
{
|
||||
if (exists && x != Value)
|
||||
transformChildren(xTransform, Value - x); // offset
|
||||
return x = Value;
|
||||
}
|
||||
|
||||
override function set_y(Value:Float):Float
|
||||
{
|
||||
if (exists && y != Value)
|
||||
transformChildren(yTransform, Value - y); // offset
|
||||
return y = Value;
|
||||
}
|
||||
|
||||
override function get_width() {
|
||||
return forceWidth;
|
||||
}
|
||||
override function get_height() {
|
||||
return forceHeight;
|
||||
}
|
||||
|
||||
override public function draw() {
|
||||
@:privateAccess
|
||||
if (copyColorTransformToChildren && colorTransform != null) for (child in members) child.colorTransform.__copyFrom(colorTransform);
|
||||
super.draw();
|
||||
}
|
||||
|
||||
override function update(elapsed:Float) {
|
||||
super.update(elapsed);
|
||||
}
|
||||
}
|
||||
|
||||
class EventNumber extends FlxSprite {
|
||||
public static inline final ALIGN_NORMAL:Int = 0;
|
||||
public static inline final ALIGN_CENTER:Int = 1;
|
||||
|
||||
public var digits:Array<Int> = [];
|
||||
public static inline final FRAME_POINT:Int = 10;
|
||||
public static inline final FRAME_NEGATIVE:Int = 11;
|
||||
|
||||
public var align:Int = ALIGN_NORMAL;
|
||||
public var spacing:Float = 6;
|
||||
|
||||
public function new(x:Float, y:Float, number:Float, ?align:Int, spacing:Float = 6, precision:Int = 3) {
|
||||
super(x, y);
|
||||
this.digits = [];
|
||||
this.align = align == null ? ALIGN_NORMAL : align;
|
||||
this.spacing = spacing;
|
||||
|
||||
if (number == 0) {
|
||||
this.digits.insert(0, 0);
|
||||
}
|
||||
else {
|
||||
var decimals:Float = FlxMath.roundDecimal(Math.abs(number % 1), precision);
|
||||
if (decimals > 0) this.digits.insert(0, FRAME_POINT);
|
||||
while(decimals > 0) {
|
||||
this.digits.push(Math.floor(decimals * 10));
|
||||
decimals = FlxMath.roundDecimal((decimals * 10) % 1, precision);
|
||||
}
|
||||
|
||||
var ints = Std.int(Math.abs(number));
|
||||
if (ints == 0) this.digits.insert(0, 0);
|
||||
while (ints > 0) {
|
||||
this.digits.insert(0, ints % 10);
|
||||
ints = Std.int(ints / 10);
|
||||
}
|
||||
|
||||
if (number < 0) {
|
||||
this.digits.insert(0, FRAME_NEGATIVE);
|
||||
}
|
||||
}
|
||||
|
||||
loadGraphic(Paths.image('editors/charter/event-icons/components/eventNums'), true, 6, 7);
|
||||
}
|
||||
|
||||
override function update(elapsed:Float) {
|
||||
super.update(elapsed);
|
||||
}
|
||||
|
||||
override function draw() {
|
||||
var baseX = x;
|
||||
var offsetX = 0.0;
|
||||
if(align == ALIGN_CENTER) offsetX = -(digits.length - 1) * spacing * Math.abs(scale.x) / 2;
|
||||
|
||||
x = baseX + offsetX;
|
||||
for (i in 0...digits.length) {
|
||||
frame = frames.frames[digits[i]];
|
||||
super.draw();
|
||||
x += spacing * Math.abs(scale.x);
|
||||
}
|
||||
x = baseX;
|
||||
}
|
||||
|
||||
public var numWidth(get, never):Float;
|
||||
private function get_numWidth():Float {
|
||||
return Math.abs(scale.x) * spacing * digits.length;
|
||||
}
|
||||
public var numHeight(get, never):Float;
|
||||
private function get_numHeight():Float {
|
||||
return Math.abs(scale.y) * frameHeight;
|
||||
}
|
||||
|
||||
public override function updateHitbox():Void {
|
||||
var numWidth = this.numWidth;
|
||||
var numHeight = this.numHeight;
|
||||
width = numWidth;
|
||||
height = numHeight;
|
||||
offset.set(-0.5 * (numWidth - spacing * digits.length), -0.5 * (numHeight - frameHeight));
|
||||
centerOrigin();
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@ class CharterEventAdd extends UISliceSprite {
|
||||
|
||||
this.global = flipX = global;
|
||||
|
||||
sideText = new UIText(0, -40, 0, global ? "Global Event" : "Local Event", 12);
|
||||
sideText = new UIText(0, -40, 0, TU.translate("charter.eventType-" + (global ? "global" : "local")), 12);
|
||||
sideText.alignment = "center"; sideText.alpha = 0.75;
|
||||
|
||||
text = new UIText(0, 0, 0, "");
|
||||
@@ -29,15 +29,27 @@ class CharterEventAdd extends UISliceSprite {
|
||||
if (FlxG.mouse.justReleased && FlxG.state.subState == null) {
|
||||
if (curCharterEvent != null)
|
||||
Charter.instance.openSubState(new CharterEventScreenNew(curCharterEvent));
|
||||
else
|
||||
Charter.instance.openSubState(new CharterEventScreen(step, global));
|
||||
else {
|
||||
CharterEventGroup.stopThisFuckingShitDudeIstg = true;
|
||||
var chartEvent = new CharterEvent(step, [], global);
|
||||
chartEvent.global = global;
|
||||
|
||||
Charter.instance.createSelection([chartEvent]);
|
||||
Charter.instance.openSubState(new CharterEventScreenNew(chartEvent));
|
||||
sprAlpha = 0;
|
||||
alpha = sprAlpha * 0.75;
|
||||
text.alpha = sprAlpha;
|
||||
|
||||
sideText.alpha = sprAlpha;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override function update(elapsed:Float) {
|
||||
super.update(elapsed);
|
||||
|
||||
text.follow(this, global ? bWidth - text.width - (text.text == "Add event" ? 15 : 20) : 20, (bHeight - text.height) / 2);
|
||||
if (FlxG.state.subState != null) return;
|
||||
text.follow(this, global ? bWidth - text.width - (text.text == TU.translate("charter.addEvent") ? 15 : 20) : 20, (bHeight - text.height) / 2);
|
||||
sideText.follow(this, (bWidth/2) - (sideText.fieldWidth/2), -(sideText.height + 2));
|
||||
alpha = sprAlpha * 0.75;
|
||||
text.alpha = sprAlpha;
|
||||
@@ -46,18 +58,20 @@ class CharterEventAdd extends UISliceSprite {
|
||||
}
|
||||
|
||||
public function updatePos(step:Float) {
|
||||
if (FlxG.state.subState != null) return;
|
||||
curCharterEvent = null;
|
||||
this.step = step;
|
||||
this.y = (step * 40) - (bHeight / 2);
|
||||
text.text = "Add event";
|
||||
text.text = TU.translate("charter.addEvent");
|
||||
framesOffset = 0; bWidth = 37 + Math.ceil(text.width);
|
||||
x = global ? Charter.instance.strumLines.members[Charter.instance.strumLines.members.length-1].x + (40*Charter.instance.strumLines.members[Charter.instance.strumLines.members.length-1].keyCount) : -(bWidth);
|
||||
}
|
||||
|
||||
public function updateEdit(event:CharterEvent) {
|
||||
if (FlxG.state.subState != null) return;
|
||||
curCharterEvent = event;
|
||||
this.y = event.y;
|
||||
text.text = "Edit";
|
||||
text.text = TU.translate("charter.editEvent");
|
||||
framesOffset = 9; bWidth = 27 + Math.ceil(text.width) + event.bWidth;
|
||||
x = global ? Charter.instance.strumLines.members[Charter.instance.strumLines.members.length-1].x + (40*Charter.instance.strumLines.members[Charter.instance.strumLines.members.length-1].keyCount) : -(bWidth);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import flixel.util.FlxSort;
|
||||
import funkin.editors.charter.CharterBackdropGroup.EventBackdrop;
|
||||
|
||||
class CharterEventGroup extends FlxTypedGroup<CharterEvent> {
|
||||
public static var stopThisFuckingShitDudeIstg = false;
|
||||
public var eventsBackdrop:EventBackdrop;
|
||||
public var eventsRowText:UIText;
|
||||
|
||||
@@ -11,12 +12,12 @@ class CharterEventGroup extends FlxTypedGroup<CharterEvent> {
|
||||
var __lastSort:Int = 0;
|
||||
|
||||
public override function update(elapsed:Float) {
|
||||
filterEvents();
|
||||
super.update(elapsed);
|
||||
if (!CharterEventGroup.stopThisFuckingShitDudeIstg) filterEvents();
|
||||
if (autoSort && members.length != __lastSort)
|
||||
sortEvents();
|
||||
|
||||
eventsRowText.y = FlxMath.lerp(eventsRowText.y, -40 + (members[0] != null ? Math.min(members[0].y, 0) : 0), 1/20);
|
||||
super.update(elapsed);
|
||||
}
|
||||
|
||||
public override function remove(v:CharterEvent, force:Bool = true):CharterEvent {
|
||||
|
||||
@@ -38,7 +38,7 @@ class CharterEventScreen extends UISubstateWindow {
|
||||
var creatingEvent:Bool = chartEvent == null;
|
||||
if (creatingEvent) chartEvent = new CharterEvent(step, [], global);
|
||||
|
||||
winTitle = creatingEvent ? "Create Event Group" : "Edit Event Group";
|
||||
winTitle = TU.translate("charterEventScreen.title" + (creatingEvent ? "-creating" : "-editing"));
|
||||
winWidth = 960;
|
||||
|
||||
super.create();
|
||||
@@ -59,7 +59,7 @@ class CharterEventScreen extends UISubstateWindow {
|
||||
});
|
||||
eventsList.add(new EventButton(events[events.length-1], CharterEvent.generateEventIcon(events[events.length-1]), events.length-1, this, eventsList));
|
||||
changeTab(events.length-1);
|
||||
}));
|
||||
}, chartEvent.step));
|
||||
for (k=>i in events)
|
||||
eventsList.add(new EventButton(i, CharterEvent.generateEventIcon(i), k, this, eventsList));
|
||||
add(eventsList);
|
||||
@@ -67,10 +67,12 @@ class CharterEventScreen extends UISubstateWindow {
|
||||
paramsPanel = new FlxGroup();
|
||||
add(paramsPanel);
|
||||
|
||||
saveButton = new UIButton(windowSpr.x + windowSpr.bWidth - 20, windowSpr.y + windowSpr.bHeight - 16 - 32, "Save & Close", function() {
|
||||
saveButton = new UIButton(windowSpr.x + windowSpr.bWidth - 20, windowSpr.y + windowSpr.bHeight - 16 - 32, TU.translate("editor.saveClose"), function() {
|
||||
saveCurTab();
|
||||
chartEvent.refreshEventIcons();
|
||||
|
||||
Charter.instance.updateBPMEvents();
|
||||
|
||||
if (events.length <= 0 && !creatingEvent)
|
||||
Charter.instance.deleteSelection([chartEvent]);
|
||||
else if (events.length > 0) {
|
||||
@@ -78,12 +80,11 @@ class CharterEventScreen extends UISubstateWindow {
|
||||
chartEvent.events = [for (i in eventsList.buttons.members) i.event];
|
||||
chartEvent.global = global;
|
||||
|
||||
chartEvent.refreshEventIcons();
|
||||
|
||||
if (creatingEvent && events.length > 0)
|
||||
Charter.instance.createSelection([chartEvent]);
|
||||
else {
|
||||
chartEvent.refreshEventIcons();
|
||||
Charter.instance.updateBPMEvents();
|
||||
|
||||
Charter.undos.addToUndo(CEditEvent(chartEvent, oldEvents, [for (event in events) Reflect.copy(event)]));
|
||||
}
|
||||
}
|
||||
@@ -93,7 +94,7 @@ class CharterEventScreen extends UISubstateWindow {
|
||||
saveButton.x -= saveButton.bWidth;
|
||||
add(saveButton);
|
||||
|
||||
closeButton = new UIButton(saveButton.x - 10, saveButton.y, "Close", ()->close());
|
||||
closeButton = new UIButton(saveButton.x - 10, saveButton.y, TU.translate("editor.close"), ()->close());
|
||||
closeButton.color = 0xFFFF0000;
|
||||
closeButton.x -= closeButton.bWidth;
|
||||
add(closeButton);
|
||||
@@ -120,12 +121,20 @@ class CharterEventScreen extends UISubstateWindow {
|
||||
if (id >= 0 && id < events.length) {
|
||||
curEvent = id;
|
||||
var curEvent = events[curEvent];
|
||||
eventName.text = curEvent.name;
|
||||
|
||||
var visualName = curEvent.name;
|
||||
var tuId = "charter.events." + TU.raw2Id(curEvent.name);
|
||||
if(TU.exists(tuId))
|
||||
visualName = TU.translate(tuId);
|
||||
|
||||
eventName.text = visualName;
|
||||
// add new elements
|
||||
var y:Float = eventName.y + eventName.height + 10;
|
||||
for(k=>param in EventsData.getEventParams(curEvent.name)) {
|
||||
function addLabel() {
|
||||
var label:UIText = new UIText(eventName.x, y, 0, param.name);
|
||||
var visualName = param.name;
|
||||
// TODO: add translations
|
||||
var label:UIText = new UIText(eventName.x, y, 0, visualName);
|
||||
y += label.height + 4;
|
||||
paramsPanel.add(label);
|
||||
};
|
||||
@@ -153,7 +162,11 @@ class CharterEventScreen extends UISubstateWindow {
|
||||
numericStepper;
|
||||
case TStrumLine:
|
||||
addLabel();
|
||||
var dropdown = new UIDropDown(eventName.x, y, 320, 32, [for(k=>s in cast(FlxG.state, Charter).strumLines.members) 'Strumline #${k+1} (${s.strumLine.characters[0]})'], cast value);
|
||||
var strumlineFormat = TU.getRaw("charterEventScreen.strumLine.format");
|
||||
var dropdown = new UIDropDown(eventName.x, y, 320, 32, [
|
||||
for(k=>s in cast(FlxG.state, Charter).strumLines.members)
|
||||
strumlineFormat.format([k+1, s.strumLine.characters[0]])
|
||||
], cast value);
|
||||
paramsPanel.add(dropdown); paramsFields.push(dropdown);
|
||||
dropdown;
|
||||
case TColorWheel:
|
||||
@@ -194,7 +207,7 @@ class CharterEventScreen extends UISubstateWindow {
|
||||
y += cast(lastAdded, FlxSprite).height + 6;
|
||||
}
|
||||
} else {
|
||||
eventName.text = "No event";
|
||||
eventName.text = TU.translate("charterEventScreen.noEvent"); // Maybe should be No Events?
|
||||
curEvent = -1;
|
||||
}
|
||||
}
|
||||
@@ -202,11 +215,11 @@ class CharterEventScreen extends UISubstateWindow {
|
||||
public function saveCurTab() {
|
||||
if (curEvent < 0) return;
|
||||
|
||||
events[curEvent].params = [
|
||||
for(p in paramsFields) {
|
||||
var dataParams = EventsData.getEventParams(events[curEvent].name);
|
||||
var params:Array<Dynamic> = [
|
||||
for (i => p in paramsFields) {
|
||||
if (p is UIDropDown) {
|
||||
var dataParams = EventsData.getEventParams(events[curEvent].name);
|
||||
if (dataParams[paramsFields.indexOf(p)].type == TStrumLine) cast(p, UIDropDown).index;
|
||||
if (dataParams[i].type == TStrumLine) cast(p, UIDropDown).index;
|
||||
else cast(p, UIDropDown).label.text;
|
||||
}
|
||||
else if (p is UINumericStepper) {
|
||||
@@ -227,6 +240,14 @@ class CharterEventScreen extends UISubstateWindow {
|
||||
null;
|
||||
}
|
||||
];
|
||||
|
||||
while(dataParams.length > 0 && {
|
||||
var index = params.length - 1;
|
||||
var dataParam = dataParams[index];
|
||||
dataParam.saveIfDefault == false && params[index] == dataParam.defValue;
|
||||
}) params.pop();
|
||||
|
||||
events[curEvent].params = params;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -88,7 +88,7 @@ class CharterEventScreenNew extends MusicBeatSubstate {
|
||||
});
|
||||
eventsList.add(new EventButtonNew(events[events.length-1], CharterEvent.generateEventIcon(events[events.length-1]), events.length-1, this, eventsList));
|
||||
changeTab(events.length-1);
|
||||
}));
|
||||
}, chartEvent.step));
|
||||
};
|
||||
for (k=>i in events)
|
||||
eventsList.add(new EventButtonNew(i, CharterEvent.generateEventIcon(i), k, this, eventsList));
|
||||
@@ -284,19 +284,19 @@ class CharterEventScreenNew extends MusicBeatSubstate {
|
||||
}
|
||||
|
||||
public function quit() {
|
||||
CharterEventGroup.stopThisFuckingShitDudeIstg = false;
|
||||
saveCurTab();
|
||||
chartEvent.refreshEventIcons();
|
||||
|
||||
if (events.length <= 0)
|
||||
Charter.instance.deleteSelection([chartEvent]);
|
||||
else if (events.length > 0) {
|
||||
var oldEvents:Array<ChartEvent> = chartEvent.events.copy();
|
||||
chartEvent.events = [for (i in eventsList.buttons.members) i.event];
|
||||
chartEvent.refreshEventIcons();
|
||||
Charter.instance.updateBPMEvents();
|
||||
|
||||
Charter.undos.addToUndo(CEditEvent(chartEvent, oldEvents, [for (event in events) Reflect.copy(event)]));
|
||||
}
|
||||
chartEvent.refreshEventIcons();
|
||||
Charter.instance.updateBPMEvents();
|
||||
close();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package funkin.editors.charter;
|
||||
|
||||
import funkin.backend.system.Conductor;
|
||||
import funkin.backend.chart.EventsData;
|
||||
|
||||
class CharterEventTypeSelection extends UISubstateWindow {
|
||||
var callback:String->Void;
|
||||
var eventStepTime:Float;
|
||||
|
||||
var buttons:Array<UIButton> = [];
|
||||
|
||||
@@ -13,13 +15,14 @@ class CharterEventTypeSelection extends UISubstateWindow {
|
||||
var upIndicator:UIText;
|
||||
var downIndicator:UIText;
|
||||
|
||||
public function new(callback:String->Void) {
|
||||
public function new(callback:String->Void, eventStepTime:Float) {
|
||||
super();
|
||||
this.callback = callback;
|
||||
this.eventStepTime = eventStepTime;
|
||||
}
|
||||
|
||||
public override function create() {
|
||||
winTitle = "Choose an event type...";
|
||||
winTitle = TU.translate("charterEventTypeSelection.title");
|
||||
super.create();
|
||||
|
||||
var w:Int = winWidth - 20;
|
||||
@@ -32,8 +35,21 @@ class CharterEventTypeSelection extends UISubstateWindow {
|
||||
buttonsBG.frames = Paths.getFrames('editors/ui/inputbox');
|
||||
add(buttonsBG);
|
||||
|
||||
var disableConductorEvents:Bool = false;
|
||||
var disableOnlyContinuousChanges:Bool = false;
|
||||
for (change in Conductor.bpmChangeMap) {
|
||||
if (change.continuous && MathUtil.greaterThanEqual(eventStepTime, change.stepTime) && MathUtil.lessThan(eventStepTime, change.endStepTime)) {
|
||||
disableOnlyContinuousChanges = MathUtil.equal(eventStepTime, change.stepTime); //allow time sig and instant bpm changes on the same event
|
||||
disableConductorEvents = !disableOnlyContinuousChanges;
|
||||
}
|
||||
}
|
||||
|
||||
for(k=>eventName in EventsData.eventsList) {
|
||||
var button = new UIButton(0, (32 * k), eventName, function() {
|
||||
var visualName = eventName;
|
||||
var tuId = "charter.events." + TU.raw2Id(eventName);
|
||||
if(TU.exists(tuId))
|
||||
visualName = TU.translate(tuId);
|
||||
var button = new UIButton(0, (32 * k), visualName, function() {
|
||||
close();
|
||||
callback(eventName);
|
||||
}, w);
|
||||
@@ -52,11 +68,16 @@ class CharterEventTypeSelection extends UISubstateWindow {
|
||||
icon.x = button.x + 8;
|
||||
icon.y = button.y + Math.abs(button.bHeight - icon.height) / 2;
|
||||
add(icon);
|
||||
|
||||
if (disableConductorEvents && (eventName == "Time Signature Change" || eventName == "Continuous BPM Change" || eventName == "BPM Change") || (disableOnlyContinuousChanges && eventName == "Continuous BPM Change")) {
|
||||
button.selectable = button.shouldPress = false;
|
||||
button.autoAlpha = true;
|
||||
}
|
||||
}
|
||||
|
||||
windowSpr.bHeight = 61 + (32 * (17));
|
||||
|
||||
add(new UIButton(10, windowSpr.bHeight-42, "Cancel", function() {
|
||||
add(new UIButton(10, windowSpr.bHeight-42, TU.translate("editor.cancel"), function() {
|
||||
close();
|
||||
}, w));
|
||||
|
||||
@@ -79,7 +100,7 @@ class CharterEventTypeSelection extends UISubstateWindow {
|
||||
sinner += elapsed;
|
||||
|
||||
for (button in buttons)
|
||||
button.selectable = buttonsBG.hovered;
|
||||
if (button.shouldPress) button.selectable = buttonsBG.hovered;
|
||||
|
||||
buttonCameras.zoom = subCam.zoom;
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package funkin.editors.charter;
|
||||
import flixel.math.FlxPoint;
|
||||
import funkin.backend.chart.ChartData.ChartMetaData;
|
||||
import funkin.editors.extra.PropertyButton;
|
||||
import funkin.game.HealthIcon;
|
||||
|
||||
using StringTools;
|
||||
|
||||
@@ -14,12 +15,12 @@ class CharterMetaDataScreen extends UISubstateWindow {
|
||||
public var songNameTextBox:UITextBox;
|
||||
public var bpmStepper:UINumericStepper;
|
||||
public var beatsPerMeasureStepper:UINumericStepper;
|
||||
public var stepsPerBeatStepper :UINumericStepper;
|
||||
public var denominatorStepper:UINumericStepper;
|
||||
public var customPropertiesButtonList:UIButtonList<PropertyButton>;
|
||||
|
||||
public var displayNameTextBox:UITextBox;
|
||||
public var iconTextBox:UITextBox;
|
||||
public var iconSprite:FlxSprite;
|
||||
public var iconSprite:HealthIcon;
|
||||
public var opponentModeCheckbox:UICheckbox;
|
||||
public var coopAllowedCheckbox:UICheckbox;
|
||||
public var colorWheel:UIColorwheel;
|
||||
@@ -30,8 +31,11 @@ class CharterMetaDataScreen extends UISubstateWindow {
|
||||
this.metadata = metadata;
|
||||
}
|
||||
|
||||
inline function translate(id:String):String
|
||||
return TU.translate("charterMetaDataScreen." + id);
|
||||
|
||||
public override function create() {
|
||||
winTitle = "Edit Metadata";
|
||||
winTitle = translate("title");
|
||||
winWidth = 1056;
|
||||
winHeight = 520;
|
||||
|
||||
@@ -45,54 +49,54 @@ class CharterMetaDataScreen extends UISubstateWindow {
|
||||
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, "Song Data", 28));
|
||||
add(title = new UIText(windowSpr.x + 20, windowSpr.y + 30 + 16, 0, translate("songData"), 28));
|
||||
|
||||
songNameTextBox = new UITextBox(title.x, title.y + title.height + 36, metadata.name);
|
||||
add(songNameTextBox);
|
||||
addLabelOn(songNameTextBox, "Song Name");
|
||||
addLabelOn(songNameTextBox, translate("songName"));
|
||||
|
||||
bpmStepper = new UINumericStepper(songNameTextBox.x + 320 + 26, songNameTextBox.y, metadata.bpm, 1, 2, 1, null, 90);
|
||||
add(bpmStepper);
|
||||
addLabelOn(bpmStepper, "BPM");
|
||||
addLabelOn(bpmStepper, translate("bpm"));
|
||||
|
||||
beatsPerMeasureStepper = new UINumericStepper(bpmStepper.x + 60 + 26, bpmStepper.y, metadata.beatsPerMeasure, 1, 0, 1, null, 54);
|
||||
add(beatsPerMeasureStepper);
|
||||
addLabelOn(beatsPerMeasureStepper, "Time Signature");
|
||||
addLabelOn(beatsPerMeasureStepper, translate("timeSignature"));
|
||||
|
||||
add(new UIText(beatsPerMeasureStepper.x + 30, beatsPerMeasureStepper.y + 3, 0, "/", 22));
|
||||
|
||||
stepsPerBeatStepper = new UINumericStepper(beatsPerMeasureStepper.x + 30 + 24, beatsPerMeasureStepper.y, metadata.stepsPerBeat, 1, 0, 1, null, 54);
|
||||
add(stepsPerBeatStepper);
|
||||
denominatorStepper = new UINumericStepper(beatsPerMeasureStepper.x + 30 + 24, beatsPerMeasureStepper.y, Math.floor(16 / metadata.stepsPerBeat), 1, 0, 1, null, 54);
|
||||
add(denominatorStepper);
|
||||
|
||||
add(title = new UIText(songNameTextBox.x, songNameTextBox.y + 10 + 46, 0, "Menus Data (Freeplay/Story)", 28));
|
||||
add(title = new UIText(songNameTextBox.x, songNameTextBox.y + 10 + 46, 0, translate("menusData"), 28));
|
||||
|
||||
displayNameTextBox = new UITextBox(title.x, title.y + title.height + 36, metadata.displayName);
|
||||
add(displayNameTextBox);
|
||||
addLabelOn(displayNameTextBox, "Display Name");
|
||||
addLabelOn(displayNameTextBox, translate("displayName"));
|
||||
|
||||
iconTextBox = new UITextBox(displayNameTextBox.x + 320 + 26, displayNameTextBox.y, metadata.icon, 150);
|
||||
iconTextBox.onChange = (newIcon:String) -> {updateIcon(newIcon);}
|
||||
add(iconTextBox);
|
||||
addLabelOn(iconTextBox, "Icon");
|
||||
addLabelOn(iconTextBox, translate("icon"));
|
||||
|
||||
updateIcon(metadata.icon);
|
||||
|
||||
opponentModeCheckbox = new UICheckbox(displayNameTextBox.x, iconTextBox.y + 10 + 32 + 26, "Opponent Mode", metadata.opponentModeAllowed);
|
||||
opponentModeCheckbox = new UICheckbox(displayNameTextBox.x, iconTextBox.y + 10 + 32 + 26, translate("opponentMode"), metadata.opponentModeAllowed);
|
||||
add(opponentModeCheckbox);
|
||||
addLabelOn(opponentModeCheckbox, "Modes Allowed");
|
||||
addLabelOn(opponentModeCheckbox, translate("modesAllowed"));
|
||||
|
||||
coopAllowedCheckbox = new UICheckbox(opponentModeCheckbox.x + 150 + 26, opponentModeCheckbox.y, "Co-op Mode", metadata.coopAllowed);
|
||||
coopAllowedCheckbox = new UICheckbox(opponentModeCheckbox.x + 150 + 26, opponentModeCheckbox.y, translate("coopAllowed"), metadata.coopAllowed);
|
||||
add(coopAllowedCheckbox);
|
||||
|
||||
colorWheel = new UIColorwheel(iconTextBox.x, coopAllowedCheckbox.y, metadata.parsedColor);
|
||||
colorWheel = new UIColorwheel(iconTextBox.x, coopAllowedCheckbox.y, metadata.color);
|
||||
add(colorWheel);
|
||||
addLabelOn(colorWheel, "Color");
|
||||
addLabelOn(colorWheel, translate("color"));
|
||||
|
||||
difficultiesTextBox = new UITextBox(opponentModeCheckbox.x, opponentModeCheckbox.y + 6 + 32 + 26, metadata.difficulties.join(", "));
|
||||
add(difficultiesTextBox);
|
||||
addLabelOn(difficultiesTextBox, "Difficulties");
|
||||
addLabelOn(difficultiesTextBox, translate("difficulties"));
|
||||
|
||||
customPropertiesButtonList = new UIButtonList<PropertyButton>(stepsPerBeatStepper.x + 80 + 26 + 105, songNameTextBox.y, 290, 316, '', FlxPoint.get(280, 35), null, 5);
|
||||
customPropertiesButtonList = new UIButtonList<PropertyButton>(denominatorStepper.x + 80 + 26 + 105, songNameTextBox.y, 290, 316, '', FlxPoint.get(280, 35), null, 5);
|
||||
customPropertiesButtonList.frames = Paths.getFrames('editors/ui/inputbox');
|
||||
customPropertiesButtonList.cameraSpacing = 0;
|
||||
customPropertiesButtonList.addButton.callback = function() {
|
||||
@@ -101,19 +105,19 @@ class CharterMetaDataScreen extends UISubstateWindow {
|
||||
for (val in Reflect.fields(metadata.customValues))
|
||||
customPropertiesButtonList.add(new PropertyButton(val, Reflect.field(metadata.customValues, val), customPropertiesButtonList));
|
||||
add(customPropertiesButtonList);
|
||||
addLabelOn(customPropertiesButtonList, "Custom Values (Advanced)");
|
||||
addLabelOn(customPropertiesButtonList, translate("customValues"));
|
||||
|
||||
for (checkbox in [opponentModeCheckbox, coopAllowedCheckbox])
|
||||
{checkbox.y += 6; checkbox.x += 4;}
|
||||
|
||||
saveButton = new UIButton(windowSpr.x + windowSpr.bWidth - 20, windowSpr.y + windowSpr.bHeight - 20, "Save & Close", function() {
|
||||
saveButton = new UIButton(windowSpr.x + windowSpr.bWidth - 20, windowSpr.y + windowSpr.bHeight - 20, TU.translate("editor.saveClose"), function() {
|
||||
saveMeta();
|
||||
close();
|
||||
}, 125);
|
||||
saveButton.x -= saveButton.bWidth;
|
||||
saveButton.y -= saveButton.bHeight;
|
||||
|
||||
closeButton = new UIButton(saveButton.x - 20, saveButton.y, "Close", function() {
|
||||
closeButton = new UIButton(saveButton.x - 20, saveButton.y, TU.translate("editor.close"), function() {
|
||||
close();
|
||||
}, 125);
|
||||
closeButton.color = 0xFFFF0000;
|
||||
@@ -124,22 +128,14 @@ class CharterMetaDataScreen extends UISubstateWindow {
|
||||
}
|
||||
|
||||
function updateIcon(icon:String) {
|
||||
if (iconSprite == null) add(iconSprite = new FlxSprite());
|
||||
if (iconSprite == null) add(iconSprite = new HealthIcon());
|
||||
|
||||
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/' + Flags.DEFAULT_HEALTH_ICON);
|
||||
|
||||
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.setIcon(icon);
|
||||
var size = Std.int(150 * 0.5);
|
||||
iconSprite.setUnstretchedGraphicSize(size, size, true);
|
||||
iconSprite.updateHitbox();
|
||||
iconSprite.setPosition(iconTextBox.x + 150 + 8, (iconTextBox.y + 16) - (iconSprite.height/2));
|
||||
iconSprite.setPosition(iconTextBox.x + iconTextBox.bWidth + 8, iconTextBox.y + (iconTextBox.bHeight / 2) - (iconSprite.height / 2));
|
||||
iconSprite.scrollFactor.set(1, 1);
|
||||
}
|
||||
|
||||
public function saveMeta() {
|
||||
@@ -154,11 +150,10 @@ class CharterMetaDataScreen extends UISubstateWindow {
|
||||
name: songNameTextBox.label.text,
|
||||
bpm: bpmStepper.value,
|
||||
beatsPerMeasure: Std.int(beatsPerMeasureStepper.value),
|
||||
stepsPerBeat: Std.int(stepsPerBeatStepper.value),
|
||||
stepsPerBeat: Std.int(16 / denominatorStepper.value),
|
||||
displayName: displayNameTextBox.label.text,
|
||||
icon: iconTextBox.label.text,
|
||||
color: colorWheel.curColorString,
|
||||
parsedColor: colorWheel.curColor,
|
||||
color: colorWheel.curColor,
|
||||
opponentModeAllowed: opponentModeCheckbox.checked,
|
||||
coopAllowed: coopAllowedCheckbox.checked,
|
||||
difficulties: [for (diff in difficultiesTextBox.label.text.split(",")) diff.trim()],
|
||||
|
||||
@@ -15,32 +15,33 @@ class CharterNoteTypesList extends UISubstateWindow {
|
||||
Charter.instance.vocals.pause();
|
||||
for (strumLine in Charter.instance.strumLines.members) strumLine.vocals.pause();
|
||||
|
||||
winTitle = 'Note Types List Editor';
|
||||
winTitle = TU.translate("charterNoteTypesList.title");
|
||||
winWidth = 380; winHeight = 390;
|
||||
|
||||
super.create();
|
||||
|
||||
var title:UIText;
|
||||
add(title = new UIText(windowSpr.x + 20, windowSpr.y + 30 + 16, 0, "Edit Note Types", 28));
|
||||
add(title = new UIText(windowSpr.x + 20, windowSpr.y + 30 + 16, 0, TU.translate("charterNoteTypesList.editNoteTypes"), 28));
|
||||
|
||||
var noteTypesFileList = getNoteTypesList(true);
|
||||
if (noteTypesFileList.length == 0) noteTypesFileList = getNoteTypesList(false);
|
||||
|
||||
noteTypesList = new UIButtonList<NoteTypeButton>(20, title.y + title.height + 10, winWidth - 40, 342 - 85 - 16, null, FlxPoint.get(winWidth - 40, (342 - 85 - 16)/4), null, 0);
|
||||
noteTypesList.addButton.callback = () -> noteTypesList.add(new NoteTypeButton(0, 0, 'Note Type ${noteTypesList.buttons.members.length}', noteTypesList.buttons.members.length, noteTypesFileList, noteTypesList));
|
||||
noteTypesList.addButton.callback = () -> noteTypesList.add(new NoteTypeButton(0, 0, TU.translate("charterNoteTypesList.editType", [noteTypesList.buttons.members.length]), noteTypesList.buttons.members.length, noteTypesFileList, noteTypesList));
|
||||
noteTypesList.cameraSpacing = 0; noteTypesList.dragCallback = (object:NoteTypeButton, oldIndex:Int, newIndex:Int) -> {object.IDText.text = '${newIndex}.';};
|
||||
for (i=>noteType in Charter.instance.noteTypes)
|
||||
// TODO: make this translatable
|
||||
noteTypesList.add(new NoteTypeButton(0, 0, noteType, i, noteTypesFileList, noteTypesList));
|
||||
add(noteTypesList);
|
||||
noteTypesList.frames = Paths.getFrames('editors/ui/inputbox');
|
||||
|
||||
saveButton = new UIButton(windowSpr.x + windowSpr.bWidth - 20 - 125, windowSpr.y + windowSpr.bHeight - 16 - 32, "Save & Close", function() {
|
||||
saveButton = new UIButton(windowSpr.x + windowSpr.bWidth - 20 - 125, windowSpr.y + windowSpr.bHeight - 16 - 32, TU.translate("editor.saveClose"), function() {
|
||||
saveList();
|
||||
close();
|
||||
}, 125);
|
||||
add(saveButton);
|
||||
|
||||
closeButton = new UIButton(saveButton.x - 20, saveButton.y, "Close", function() {
|
||||
closeButton = new UIButton(saveButton.x - 20, saveButton.y, TU.translate("editor.close"), function() {
|
||||
close();
|
||||
}, 125);
|
||||
add(closeButton);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package funkin.editors.charter;
|
||||
|
||||
import funkin.backend.chart.Chart;
|
||||
import funkin.backend.chart.ChartData.ChartMetaData;
|
||||
import funkin.backend.chart.ChartData;
|
||||
import funkin.backend.system.framerate.Framerate;
|
||||
@@ -26,26 +27,32 @@ class CharterSelection extends EditorTreeMenu {
|
||||
freeplayList = FreeplaySonglist.get(false);
|
||||
|
||||
var list:Array<OptionType> = [
|
||||
for(s in freeplayList.songs) new EditorIconOption(s.name, "Press ACCEPT to choose a difficulty to edit.", s.icon, function() {
|
||||
for(s in freeplayList.songs) new EditorIconOption(s.name, TU.translate("charterSelection.acceptSong"), s.icon, function() {
|
||||
curSong = s;
|
||||
var list:Array<OptionType> = [
|
||||
for(d in s.difficulties) if (d != "")
|
||||
new TextOption(d, "Press ACCEPT to edit the chart for the selected difficulty", function() {
|
||||
new TextOption(d, TU.translate("charterSelection.acceptDifficulty"), function() {
|
||||
FlxG.switchState(new Charter(s.name, d));
|
||||
})
|
||||
];
|
||||
list.push(new NewOption("New Difficulty", "Press ACCEPT to create a new difficulty.", function() {
|
||||
#if sys
|
||||
var newDiff = TU.translate("charterSelection.newDifficulty");
|
||||
list.push(new NewOption(newDiff, newDiff, function() {
|
||||
FlxG.state.openSubState(new ChartCreationScreen(saveChart));
|
||||
}));
|
||||
optionsTree.add(new OptionsScreen(s.name, "Select a difficulty to continue.", list));
|
||||
}, s.parsedColor.getDefault(0xFFFFFFFF))
|
||||
#end
|
||||
optionsTree.add(new OptionsScreen(s.name, TU.translate("charterSelection.selectDifficulty"), list));
|
||||
}, s.color.getDefault(0xFFFFFFFF))
|
||||
];
|
||||
|
||||
list.insert(0, new NewOption("New Song", "Press ACCEPT to create a new song.", function() {
|
||||
#if sys
|
||||
var newSong = TU.translate("charterSelection.newSong");
|
||||
list.insert(0, new NewOption(newSong, newSong, function() {
|
||||
FlxG.state.openSubState(new SongCreationScreen(saveSong));
|
||||
}));
|
||||
#end
|
||||
|
||||
main = new OptionsScreen("Chart Editor", "Select a song to modify the charts from.", list);
|
||||
main = new OptionsScreen(TU.translate("editor.chart.name"), TU.translate("charterSelection.desc"), list);
|
||||
|
||||
DiscordUtil.call("onEditorTreeLoaded", ["Chart Editor"]);
|
||||
}
|
||||
@@ -88,12 +95,13 @@ class CharterSelection extends EditorTreeMenu {
|
||||
}
|
||||
}
|
||||
|
||||
public function saveSong(creation:SongCreationData) {
|
||||
#if sys
|
||||
public function saveSong(creation:SongCreationData, ?callback:String -> SongCreationData -> Void) {
|
||||
var songAlreadyExists:Bool = [for (s in freeplayList.songs) s.name.toLowerCase()].contains(creation.meta.name.toLowerCase());
|
||||
|
||||
if (songAlreadyExists) {
|
||||
openSubState(new UIWarningSubstate("Creating Song: Error!", "The song you are trying to create already exists, if you would like to override it delete the song first!", [
|
||||
{label: "Ok", color: 0xFFFF0000, onClick: function(t) {}}
|
||||
openSubState(new UIWarningSubstate(TU.translate("chartCreation.warnings.song-exists-title"), TU.translate("chartCreation.warnings.song-exists-body"), [
|
||||
{label: TU.translate("editor.ok"), color: 0xFFFF0000, onClick: function(t) {}},
|
||||
]));
|
||||
return;
|
||||
}
|
||||
@@ -109,24 +117,31 @@ class CharterSelection extends EditorTreeMenu {
|
||||
sys.FileSystem.createDirectory('$songFolder/charts');
|
||||
|
||||
// Save Files
|
||||
CoolUtil.safeSaveFile('$songFolder/meta.json', Json.stringify(creation.meta, Flags.JSON_PRETTY_PRINT));
|
||||
CoolUtil.safeSaveFile('$songFolder/meta.json', Chart.makeMetaSaveable(creation.meta));
|
||||
if (creation.instBytes != null) sys.io.File.saveBytes('$songFolder/song/Inst.${Flags.SOUND_EXT}', creation.instBytes);
|
||||
if (creation.voicesBytes != null) sys.io.File.saveBytes('$songFolder/song/Voices.${Flags.SOUND_EXT}', creation.voicesBytes);
|
||||
|
||||
if (creation.playerVocals != null) sys.io.File.saveBytes('$songFolder/song/Voices-Player.${Flags.SOUND_EXT}', creation.playerVocals);
|
||||
if (creation.oppVocals != null) sys.io.File.saveBytes('$songFolder/song/Voices-Opponent.${Flags.SOUND_EXT}', creation.oppVocals);
|
||||
#end
|
||||
|
||||
var option = new EditorIconOption(creation.meta.name, "Press ACCEPT to choose a difficulty to edit.", creation.meta.icon, function() {
|
||||
if (callback != null)
|
||||
callback(songFolder, creation);
|
||||
|
||||
var option = new EditorIconOption(creation.meta.name, TU.translate("charterSelection.acceptSong"), creation.meta.icon, function() {
|
||||
curSong = creation.meta;
|
||||
var list:Array<OptionType> = [
|
||||
for(d in creation.meta.difficulties)
|
||||
if (d != "") new TextOption(d, "Press ACCEPT to edit the chart for the selected difficulty", function() {
|
||||
if (d != "") new TextOption(d, TU.translate("charterSelection.acceptDifficulty"), function() {
|
||||
FlxG.switchState(new Charter(creation.meta.name, d));
|
||||
})
|
||||
];
|
||||
list.push(new NewOption("New Difficulty", "Press ACCEPT to create a new difficulty.", function() {
|
||||
var newDiff = TU.translate("charterSelection.newDifficulty");
|
||||
list.push(new NewOption(newDiff, newDiff, function() {
|
||||
FlxG.state.openSubState(new ChartCreationScreen(saveChart));
|
||||
}));
|
||||
optionsTree.insert(1, new OptionsScreen(creation.meta.name, "Select a difficulty to continue.", list));
|
||||
}, creation.meta.parsedColor.getDefault(0xFFFFFFFF));
|
||||
optionsTree.insert(1, new OptionsScreen(creation.meta.name, TU.translate("charterSelection.selectDifficulty"), list));
|
||||
}, creation.meta.color.getDefault(0xFFFFFFFF));
|
||||
|
||||
// Add to List
|
||||
freeplayList.songs.insert(0, creation.meta);
|
||||
@@ -137,8 +152,8 @@ class CharterSelection extends EditorTreeMenu {
|
||||
var difficultyAlreadyExists:Bool = curSong.difficulties.contains(name);
|
||||
|
||||
if (difficultyAlreadyExists) {
|
||||
openSubState(new UIWarningSubstate("Creating Chart: Error!", "The chart you are trying to create already exists, if you would like to override it delete the chart first!", [
|
||||
{label: "Ok", color: 0xFFFF0000, onClick: function(t) {}}
|
||||
openSubState(new UIWarningSubstate(TU.translate("chartCreation.warnings.chart-exists-title"), TU.translate("chartCreation.warnings.chart-exists-body"), [
|
||||
{label: TU.translate("editor.ok"), color: 0xFFFF0000, onClick: function(t) {}},
|
||||
]));
|
||||
return;
|
||||
}
|
||||
@@ -150,8 +165,9 @@ class CharterSelection extends EditorTreeMenu {
|
||||
CoolUtil.safeSaveFile('$songFolder/charts/${name}.json', Json.stringify(data, Flags.JSON_PRETTY_PRINT));
|
||||
|
||||
// Add to List
|
||||
// duplicated code, todo: fix this
|
||||
curSong.difficulties.push(name);
|
||||
var option = new TextOption(name, "Press ACCEPT to edit the chart for the selected difficulty", function() {
|
||||
var option = new TextOption(name, TU.translate("charterSelection.acceptDifficulty"), function() {
|
||||
FlxG.switchState(new Charter(curSong.name, name));
|
||||
});
|
||||
optionsTree.members[optionsTree.members.length-1].insert(optionsTree.members[optionsTree.members.length-1].length-1, option);
|
||||
@@ -160,7 +176,8 @@ class CharterSelection extends EditorTreeMenu {
|
||||
var meta = Json.parse(sys.io.File.getContent('$songFolder/meta.json'));
|
||||
if (meta.difficulties != null && !meta.difficulties.contains(name)) {
|
||||
meta.difficulties.push(name);
|
||||
CoolUtil.safeSaveFile('$songFolder/meta.json', Json.stringify(meta));
|
||||
CoolUtil.safeSaveFile('$songFolder/meta.json', Chart.makeMetaSaveable(meta));
|
||||
}
|
||||
}
|
||||
#end
|
||||
}
|
||||
@@ -88,8 +88,7 @@ class CharterStrumline extends UISprite {
|
||||
button = new CharterStrumlineOptions(this);
|
||||
members.push(button);
|
||||
|
||||
vocals = strumLine.vocalsSuffix.length > 0 ? FlxG.sound.load(Paths.voices(PlayState.SONG.meta.name, PlayState.difficulty, strumLine.vocalsSuffix)) : new FlxSound();
|
||||
vocals.group = FlxG.sound.defaultMusicGroup;
|
||||
updateInfo();
|
||||
|
||||
selectedWaveform = -1;
|
||||
}
|
||||
@@ -136,8 +135,16 @@ class CharterStrumline extends UISprite {
|
||||
healthIcons.add(healthIcon);
|
||||
}
|
||||
|
||||
vocals = null;
|
||||
vocals = strumLine.vocalsSuffix.length > 0 ? FlxG.sound.load(Paths.voices(PlayState.SONG.meta.name, PlayState.difficulty, strumLine.vocalsSuffix)) : new FlxSound();
|
||||
var asset = strumLine.vocalsSuffix.length > 0 ? Assets.getSound(Paths.voices(PlayState.SONG.meta.name, PlayState.difficulty, strumLine.vocalsSuffix)) : null;
|
||||
|
||||
if (vocals == null) FlxG.sound.list.add(vocals = new FlxSound());
|
||||
if (asset != null) {
|
||||
vocals.reset();
|
||||
vocals.loadEmbedded(asset);
|
||||
}
|
||||
else {
|
||||
vocals.destroy();
|
||||
}
|
||||
vocals.group = FlxG.sound.defaultMusicGroup;
|
||||
}
|
||||
}
|
||||
@@ -145,7 +152,8 @@ class CharterStrumline extends UISprite {
|
||||
class CharterStrumlineOptions extends UITopMenuButton {
|
||||
var strLine:CharterStrumline;
|
||||
public function new(parent:CharterStrumline) {
|
||||
super(0, 95, null, "Options ↓", []);
|
||||
// TODO: better id for this
|
||||
super(0, 95, null, TU.translate("charter.strumLine.button-name"), []);
|
||||
strLine = parent;
|
||||
}
|
||||
|
||||
@@ -159,14 +167,14 @@ class CharterStrumlineOptions extends UITopMenuButton {
|
||||
public override function openContextMenu() {
|
||||
contextMenu = [
|
||||
{
|
||||
label: "Hitsounds",
|
||||
label: TU.translate("charter.strumLine.hitsounds"),
|
||||
onSelect: function(_) {
|
||||
strLine.hitsounds = !strLine.hitsounds;
|
||||
},
|
||||
icon: strLine.hitsounds ? 1 : 0
|
||||
},
|
||||
{
|
||||
label: "Mute Vocals",
|
||||
label: TU.translate("charter.strumLine.muteVocals"),
|
||||
onSelect: function(_) {
|
||||
strLine.vocals.volume = strLine.vocals.volume > 0 ? 0 : 1;
|
||||
},
|
||||
@@ -174,7 +182,7 @@ class CharterStrumlineOptions extends UITopMenuButton {
|
||||
},
|
||||
null,
|
||||
{
|
||||
label: "Edit",
|
||||
label: TU.translate("charter.strumLine.edit"),
|
||||
onSelect: function (_) {
|
||||
Charter.instance.editStrumline(strLine.strumLine);
|
||||
},
|
||||
@@ -182,7 +190,7 @@ class CharterStrumlineOptions extends UITopMenuButton {
|
||||
icon: 4
|
||||
},
|
||||
{
|
||||
label: "Delete",
|
||||
label: TU.translate("charter.strumLine.delete"),
|
||||
onSelect: function (_) {
|
||||
Charter.instance.deleteStrumlineFromData(strLine.strumLine);
|
||||
},
|
||||
@@ -192,7 +200,7 @@ class CharterStrumlineOptions extends UITopMenuButton {
|
||||
];
|
||||
|
||||
contextMenu.insert(0, {
|
||||
label: "No Waveform",
|
||||
label: TU.translate("charter.strumLine.noWaveform"),
|
||||
onSelect: function(_) {strLine.selectedWaveform = -1;},
|
||||
icon: strLine.selectedWaveform == -1 ? 1 : 0
|
||||
});
|
||||
|
||||
@@ -59,7 +59,7 @@ class CharterStrumlineButton extends UISprite {
|
||||
super.update(elapsed);
|
||||
|
||||
button.updateSpriteRect();
|
||||
if(UIState.state.curContextMenu == null && UIState.state.isOverlapping(button, button.__rect)) {
|
||||
if (UIState.state.curContextMenu == null && UIState.state.isOverlapping(button, button.__rect) && button.visible) {
|
||||
buttonScale.set(0.95, 0.95);
|
||||
if (FlxG.mouse.justPressed && onClick != null) {
|
||||
onClick();
|
||||
|
||||
@@ -56,7 +56,7 @@ class CharterStrumlineScreen extends UISubstateWindow {
|
||||
keyCount: 4
|
||||
};
|
||||
|
||||
winTitle = creatingStrumLine ? 'Creating strumline #$strumLineID' : 'Editing strumline #$strumLineID properties';
|
||||
winTitle = TU.translate("charterStrumlineScreen.win-title" + (creatingStrumLine ? "-creating" : "-editing"), ["#" + strumLineID]);
|
||||
winWidth = 720; winHeight = 400;
|
||||
|
||||
FlxG.sound.music.pause();
|
||||
@@ -69,26 +69,26 @@ class CharterStrumlineScreen extends UISubstateWindow {
|
||||
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, creatingStrumLine ? "Create New Strumline" : "Edit Strumline Properties", 28));
|
||||
add(title = new UIText(windowSpr.x + 20, windowSpr.y + 30 + 16, 0, TU.translate("charterStrumlineScreen.title" + (creatingStrumLine ? "-creating" : "-editing")), 28));
|
||||
|
||||
var charFileList = Character.getList(true);
|
||||
if (charFileList.length == 0) charFileList = Character.getList(false);
|
||||
|
||||
charactersList = new UIButtonList<CharacterButton>(15, title.y + title.height + 36, 250, 269, null, FlxPoint.get(250, 54), null, 0);
|
||||
charactersList.addButton.callback = () -> charactersList.add(new CharacterButton(0, 0, "New Char", charFileList, charactersList));
|
||||
charactersList.addButton.callback = () -> charactersList.add(new CharacterButton(0, 0, TU.translate("charterStrumLine.newChar"), charFileList, charactersList));
|
||||
charactersList.cameraSpacing = 0;
|
||||
for (i in strumLine.characters)
|
||||
charactersList.add(new CharacterButton(0, 0, i, charFileList, charactersList));
|
||||
add(charactersList);
|
||||
addLabelOn(charactersList, "Characters");
|
||||
addLabelOn(charactersList, TU.translate("charterStrumLine.characters"));
|
||||
|
||||
charactersList.frames = Paths.getFrames('editors/ui/inputbox');
|
||||
|
||||
typeDropdown = new UIDropDown(charactersList.x + charactersList.bWidth + 16, charactersList.y, 200, 32, ["OPPONENT", "PLAYER", "ADDITIONAL"], strumLine.type);
|
||||
add(typeDropdown);
|
||||
addLabelOn(typeDropdown, "Type");
|
||||
addLabelOn(typeDropdown, TU.translate("charterStrumLine.type"));
|
||||
|
||||
usesChartScrollSpeed = new UICheckbox(typeDropdown.x + 104, typeDropdown.y + 135, "Uses charts scroll speed?", strumLine.scrollSpeed == null);
|
||||
usesChartScrollSpeed = new UICheckbox(typeDropdown.x + 104, typeDropdown.y + 135, TU.translate("charterStrumLine.useChartScrollSpeed"), strumLine.scrollSpeed == null);
|
||||
usesChartScrollSpeed.onChecked = function(b) {
|
||||
if(b)
|
||||
{
|
||||
@@ -108,21 +108,21 @@ class CharterStrumlineScreen extends UISubstateWindow {
|
||||
scrollSpeedStepper.selectable = true;
|
||||
}
|
||||
add(scrollSpeedStepper);
|
||||
addLabelOn(scrollSpeedStepper, "Scroll Speed");
|
||||
addLabelOn(scrollSpeedStepper, TU.translate("charterStrumLine.scrollSpeed"));
|
||||
|
||||
var stagePositionI = strumLine.position == null ? strumLine.type : ["DAD", "BOYFRIEND", "GIRLFRIEND"].indexOf(strumLine.position.toUpperCase());
|
||||
|
||||
stagePositionDropdown = new UIDropDown(typeDropdown.x + 200 - 32 + 26, typeDropdown.y, 200, 32, ["DAD", "BOYFRIEND", "GIRLFRIEND"], stagePositionI);
|
||||
add(stagePositionDropdown);
|
||||
addLabelOn(stagePositionDropdown, "Stage Position");
|
||||
addLabelOn(stagePositionDropdown, TU.translate("charterStrumLine.stagePos"));
|
||||
|
||||
hudScaleStepper = new UINumericStepper(typeDropdown.x, typeDropdown.y + 64, strumLine.strumScale == null ? 1 : strumLine.strumScale, 0.001, 2, null, null, 84);
|
||||
add(hudScaleStepper);
|
||||
addLabelOn(hudScaleStepper, "Scale");
|
||||
addLabelOn(hudScaleStepper, TU.translate("charterStrumLine.scale"));
|
||||
|
||||
hudSpacingStepper = new UINumericStepper(hudScaleStepper.x + 80 - 32 + 26, hudScaleStepper.y, strumLine.strumSpacing != null ? strumLine.strumSpacing : 1, 0.001, 10, null, null, 84);
|
||||
add(hudSpacingStepper);
|
||||
addLabelOn(hudSpacingStepper, "Spacing");
|
||||
addLabelOn(hudSpacingStepper, TU.translate("charterStrumLine.spacing"));
|
||||
|
||||
var strOffset:Float = strumLine.strumLinePos == null ? (strumLine.type == 1 ? 0.75 : 0.25) : strumLine.strumLinePos;
|
||||
|
||||
@@ -132,23 +132,23 @@ class CharterStrumlineScreen extends UISubstateWindow {
|
||||
|
||||
hudXStepper = new UINumericStepper(hudSpacingStepper.x + 80, hudScaleStepper.y, startingPos.x, 0.01, 2, 0, 2, 84);
|
||||
add(hudXStepper);
|
||||
addLabelOn(hudXStepper, "Hud Position (X [Ratio 0-1],Y)");
|
||||
addLabelOn(hudXStepper, TU.translate("charterStrumLine.hudPos"));
|
||||
|
||||
add(new UIText(hudXStepper.x + 84 - 32 + 0, hudXStepper.y + 9, 0, ",", 22));
|
||||
|
||||
hudYStepper = new UINumericStepper(hudXStepper.x + 84 - 32 + 26, hudXStepper.y, startingPos.y, 0.001, 2, null, null, 84);
|
||||
add(hudYStepper);
|
||||
|
||||
visibleCheckbox = new UICheckbox(hudYStepper.x + hudYStepper.bWidth + 42, hudYStepper.y + 9, "Visible?", strumLine.visible);
|
||||
visibleCheckbox = new UICheckbox(hudYStepper.x + hudYStepper.bWidth + 42, hudYStepper.y + 9, TU.translate("charterStrumLine.visible"), strumLine.visible);
|
||||
add(visibleCheckbox);
|
||||
|
||||
saveButton = new UIButton(windowSpr.x + windowSpr.bWidth - 20 - 125, windowSpr.y + windowSpr.bHeight - 16 - 32, "Save & Close", function() {
|
||||
saveButton = new UIButton(windowSpr.x + windowSpr.bWidth - 20 - 125, windowSpr.y + windowSpr.bHeight - 16 - 32, TU.translate("editor.saveClose"), function() {
|
||||
saveStrumline();
|
||||
close();
|
||||
}, 125);
|
||||
add(saveButton);
|
||||
|
||||
closeButton = new UIButton(saveButton.x - 20 - saveButton.bWidth, saveButton.y, creatingStrumLine ? "Cancel" : "Close", function() {
|
||||
closeButton = new UIButton(saveButton.x - 20 - saveButton.bWidth, saveButton.y, TU.translate("editor." + (creatingStrumLine ? "cancel" : "close")), function() {
|
||||
if (creatingStrumLine && onSave != null) onSave(null);
|
||||
close();
|
||||
}, 125);
|
||||
@@ -170,12 +170,12 @@ class CharterStrumlineScreen extends UISubstateWindow {
|
||||
|
||||
vocalsSuffixDropDown = new UIDropDown(typeDropdown.x, hudScaleStepper.y + 128, 200, 32, suffixList, strumLine.vocalsSuffix != null && strumLine.vocalsSuffix != "" ? suffixList.indexOf(strumLine.vocalsSuffix) : 0);
|
||||
add(vocalsSuffixDropDown);
|
||||
addLabelOn(vocalsSuffixDropDown, "Vocal Suffix");
|
||||
addLabelOn(vocalsSuffixDropDown, TU.translate("charterStrumLine.vocalSuffix"));
|
||||
|
||||
keyCountStepper = new UINumericStepper(stagePositionDropdown.x, vocalsSuffixDropDown.y, strumLine.keyCount != null ? strumLine.keyCount : 4, 1, 0, 1, 1000, 84);
|
||||
// if (Flags.CHARTER_ADVANCED_SETTINGS) {
|
||||
add(keyCountStepper);
|
||||
addLabelOn(keyCountStepper, "Key Count");
|
||||
addLabelOn(keyCountStepper, TU.translate("charterStrumLine.keyCount"));
|
||||
// }
|
||||
|
||||
strumLineCam = new HudCamera();
|
||||
@@ -242,7 +242,8 @@ class CharacterButton extends UIButton {
|
||||
super(x, y, null, null, 250, 54);
|
||||
|
||||
charIcon = new HealthIcon(Character.getIconFromCharName(char));
|
||||
charIcon.scale.set(0.3, 0.3);
|
||||
var size = Std.int(150 * 0.3);
|
||||
charIcon.setUnstretchedGraphicSize(size, size, true);
|
||||
charIcon.updateHitbox();
|
||||
charIcon.setPosition(x + 10, bHeight/2 - charIcon.height / 2);
|
||||
charIcon.scrollFactor.set(1,1);
|
||||
@@ -254,11 +255,10 @@ class CharacterButton extends UIButton {
|
||||
textBox.antialiasing = true;
|
||||
textBox.onChange = function(char:String) {
|
||||
char = Character.getIconFromCharName(char);
|
||||
var image = Paths.image("icons/" + char);
|
||||
if(!Assets.exists(image))
|
||||
image = Paths.image("icons/" + Flags.DEFAULT_HEALTH_ICON);
|
||||
charIcon.loadGraphic(image, true, 150, 150);
|
||||
charIcon.setIcon(char);
|
||||
charIcon.setUnstretchedGraphicSize(size, size, true);
|
||||
charIcon.updateHitbox();
|
||||
charIcon.setPosition(x + 10, bHeight/2 - charIcon.height / 2);
|
||||
}
|
||||
|
||||
deleteButton = new UIButton(textBox.x + 115 + 16, bHeight/2 - (32/2), null, function () {
|
||||
|
||||
@@ -29,7 +29,7 @@ class CharterWaveformHandler extends FlxBasic {
|
||||
|
||||
public function generateData(name:String, sound:FlxSound):BitmapData {
|
||||
if (!sounds.exists(name)) sounds.set(name, sound);
|
||||
if (!analyzers.exists(name))
|
||||
if (!analyzers.exists(name))
|
||||
analyzers.set(name, new AudioAnalyzer(sound));
|
||||
|
||||
var analyzer:AudioAnalyzer = analyzers.get(name);
|
||||
|
||||
@@ -3,33 +3,53 @@ package funkin.editors.charter;
|
||||
import flixel.group.FlxGroup;
|
||||
import flixel.text.FlxText.FlxTextFormat;
|
||||
import flixel.text.FlxText.FlxTextFormatMarkerPair;
|
||||
import funkin.backend.chart.ChartData.ChartMetaData;
|
||||
import funkin.backend.chart.ChartData;
|
||||
import funkin.backend.chart.FNFLegacyParser.SwagSong;
|
||||
import funkin.backend.chart.PsychParser;
|
||||
import funkin.backend.chart.VSliceParser;
|
||||
import funkin.backend.utils.ZipUtil.ZipReader;
|
||||
import funkin.game.HealthIcon;
|
||||
import haxe.Json;
|
||||
import haxe.io.Bytes;
|
||||
import haxe.io.BytesInput;
|
||||
|
||||
typedef SongCreationData = {
|
||||
var meta:ChartMetaData;
|
||||
var instBytes:Bytes;
|
||||
var voicesBytes:Bytes;
|
||||
@:optional var playerVocals:Bytes;
|
||||
@:optional var oppVocals:Bytes;
|
||||
}
|
||||
|
||||
class SongCreationScreen extends UISubstateWindow {
|
||||
private var onSave:Null<SongCreationData> -> Void = null;
|
||||
private var onSave:Null<SongCreationData> -> Null<String -> SongCreationData -> Void> -> Void = null;
|
||||
|
||||
public var songNameTextBox:UITextBox;
|
||||
public var bpmStepper:UINumericStepper;
|
||||
public var beatsPerMeasureStepper:UINumericStepper;
|
||||
public var stepsPerBeatStepper :UINumericStepper;
|
||||
public var denominatorStepper:UINumericStepper;
|
||||
public var instExplorer:UIFileExplorer;
|
||||
public var voicesExplorer:UIFileExplorer;
|
||||
public var importFrom:UIButton;
|
||||
|
||||
public var displayNameTextBox:UITextBox;
|
||||
public var iconTextBox:UITextBox;
|
||||
public var iconSprite:FlxSprite;
|
||||
public var iconSprite:HealthIcon;
|
||||
public var opponentModeCheckbox:UICheckbox;
|
||||
public var coopAllowedCheckbox:UICheckbox;
|
||||
public var colorWheel:UIColorwheel;
|
||||
public var difficultiesTextBox:UITextBox;
|
||||
|
||||
public var engineDropdown:UIDropDown;
|
||||
public var createSong:UIButton;
|
||||
|
||||
public var importInstExplorer:UIFileExplorer;
|
||||
public var importVoicesExplorer:UIFileExplorer;
|
||||
|
||||
public var importIdTextBox:UITextBox;
|
||||
public var importChartFile:UIFileExplorer;
|
||||
public var importMetaFile:UIFileExplorer;
|
||||
|
||||
public var backButton:UIButton;
|
||||
public var saveButton:UIButton;
|
||||
public var closeButton:UIButton;
|
||||
@@ -37,17 +57,33 @@ class SongCreationScreen extends UISubstateWindow {
|
||||
public var songDataGroup:FlxGroup = new FlxGroup();
|
||||
public var menuDataGroup:FlxGroup = new FlxGroup();
|
||||
|
||||
public var selectFormatGroup:FlxGroup = new FlxGroup();
|
||||
public var importAudioGroup:FlxGroup = new FlxGroup();
|
||||
public var importDataGroup:FlxGroup = new FlxGroup();
|
||||
|
||||
public var pages:Array<FlxGroup> = [];
|
||||
public var pageSizes:Array<FlxPoint> = [];
|
||||
|
||||
public var importPages:Array<FlxGroup> = [];
|
||||
public var importPageSizes:Array<FlxPoint> = [];
|
||||
|
||||
public var isImporting = false;
|
||||
|
||||
public var curPage:Int = 0;
|
||||
|
||||
public function new(?onSave:SongCreationData->Void) {
|
||||
public function new(?onSave:SongCreationData -> Null<String -> SongCreationData -> Void> -> Void) {
|
||||
super();
|
||||
if (onSave != null) this.onSave = onSave;
|
||||
}
|
||||
|
||||
inline function translate(id:String):String
|
||||
return TU.translate("songCreationScreen." + id);
|
||||
|
||||
inline function translateMeta(id:String):String
|
||||
return TU.translate("charterMetaDataScreen." + id);
|
||||
|
||||
public override function create() {
|
||||
winTitle = "Creating New Song";
|
||||
winTitle = translate("win-title");
|
||||
|
||||
winWidth = 748 - 32 + 40;
|
||||
winHeight = 520;
|
||||
@@ -55,32 +91,30 @@ class SongCreationScreen extends UISubstateWindow {
|
||||
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;
|
||||
var label:UIText = new UIText(ui.x, ui.y - 24, 0, text);
|
||||
ui.members.push(label);
|
||||
return label;
|
||||
}
|
||||
|
||||
var songTitle:UIText;
|
||||
songDataGroup.add(songTitle = new UIText(windowSpr.x + 20, windowSpr.y + 30 + 16, 0, "Song Info", 28));
|
||||
songDataGroup.add(songTitle = new UIText(windowSpr.x + 20, windowSpr.y + 30 + 16, 0, translate("title"), 28));
|
||||
|
||||
songNameTextBox = new UITextBox(songTitle.x, songTitle.y + songTitle.height + 36, "Song Name");
|
||||
songNameTextBox = new UITextBox(songTitle.x, songTitle.y + songTitle.height + 36, translateMeta("songName"));
|
||||
songDataGroup.add(songNameTextBox);
|
||||
addLabelOn(songNameTextBox, "Song Name");
|
||||
addLabelOn(songNameTextBox, translateMeta("songName"));
|
||||
|
||||
bpmStepper = new UINumericStepper(songNameTextBox.x + 320 + 26, songNameTextBox.y, 100, 1, 2, 1, null, 90);
|
||||
songDataGroup.add(bpmStepper);
|
||||
addLabelOn(bpmStepper, "BPM");
|
||||
addLabelOn(bpmStepper, translateMeta("bpm"));
|
||||
|
||||
beatsPerMeasureStepper = new UINumericStepper(bpmStepper.x + 60 + 26, bpmStepper.y, 4, 1, 0, 1, null, 54);
|
||||
songDataGroup.add(beatsPerMeasureStepper);
|
||||
addLabelOn(beatsPerMeasureStepper, "Time Signature");
|
||||
addLabelOn(beatsPerMeasureStepper, translateMeta("timeSignature"));
|
||||
|
||||
songDataGroup.add(new UIText(beatsPerMeasureStepper.x + 30, beatsPerMeasureStepper.y + 3, 0, "/", 22));
|
||||
|
||||
stepsPerBeatStepper = new UINumericStepper(beatsPerMeasureStepper.x + 30 + 24, beatsPerMeasureStepper.y, 4, 1, 0, 1, null, 54);
|
||||
songDataGroup.add(stepsPerBeatStepper);
|
||||
|
||||
var voicesUIText:UIText = null;
|
||||
denominatorStepper = new UINumericStepper(beatsPerMeasureStepper.x + 30 + 24, beatsPerMeasureStepper.y, 4, 1, 0, 1, null, 54);
|
||||
songDataGroup.add(denominatorStepper);
|
||||
|
||||
instExplorer = new UIFileExplorer(songNameTextBox.x, songNameTextBox.y + 32 + 36, null, null, Flags.SOUND_EXT, function (path, res) {
|
||||
if (path == null || res == null) return;
|
||||
@@ -89,8 +123,8 @@ class SongCreationScreen extends UISubstateWindow {
|
||||
instExplorer.uiElement = audioPlayer;
|
||||
});
|
||||
songDataGroup.add(instExplorer);
|
||||
addLabelOn(instExplorer, "Inst Audio File").applyMarkup(
|
||||
"Inst Audio File $* Required$",
|
||||
addLabelOn(instExplorer, "").applyMarkup(
|
||||
translate("instAudio"),
|
||||
[new FlxTextFormatMarkerPair(new FlxTextFormat(0xFFAD1212), "$")]);
|
||||
|
||||
voicesExplorer = new UIFileExplorer(instExplorer.x + 320 + 26, instExplorer.y, null, null, Flags.SOUND_EXT, function (path, res) {
|
||||
@@ -100,41 +134,114 @@ class SongCreationScreen extends UISubstateWindow {
|
||||
voicesExplorer.uiElement = audioPlayer;
|
||||
});
|
||||
songDataGroup.add(voicesExplorer);
|
||||
addLabelOn(voicesExplorer, "Vocal Audio File");
|
||||
|
||||
/*voicesUIText = addLabelOn(voicesExplorer, "");
|
||||
voicesUIText.applyMarkup(
|
||||
translate("voicesAudio"),
|
||||
[new FlxTextFormatMarkerPair(new FlxTextFormat(0xFFAD1212), "$")]);*/
|
||||
|
||||
addLabelOn(voicesExplorer, translate("voicesAudio"));
|
||||
|
||||
importFrom = new UIButton(windowSpr.x + 20, windowSpr.y + windowSpr.bHeight - 16 - 32, translate("importFrom"), function() {
|
||||
winTitle = translate("win-title-importing");
|
||||
isImporting = true;
|
||||
updatePagesTexts();
|
||||
refreshPages();
|
||||
}, 150);
|
||||
songDataGroup.add(importFrom);
|
||||
|
||||
var menuTitle:UIText;
|
||||
menuDataGroup.add(menuTitle = new UIText(windowSpr.x + 20, windowSpr.y + 30 + 16, 0, "Menus Data (Freeplay/Story)", 28));
|
||||
menuDataGroup.add(menuTitle = new UIText(windowSpr.x + 20, windowSpr.y + 30 + 16, 0, translateMeta("menusData"), 28));
|
||||
|
||||
displayNameTextBox = new UITextBox(menuTitle.x, menuTitle.y + menuTitle.height + 36, "Display Name");
|
||||
displayNameTextBox = new UITextBox(menuTitle.x, menuTitle.y + menuTitle.height + 36, translateMeta("displayName"));
|
||||
menuDataGroup.add(displayNameTextBox);
|
||||
addLabelOn(displayNameTextBox, "Display Name");
|
||||
addLabelOn(displayNameTextBox, translateMeta("displayName"));
|
||||
|
||||
iconTextBox = new UITextBox(displayNameTextBox.x + 320 + 26, displayNameTextBox.y, "Icon", 150);
|
||||
iconTextBox = new UITextBox(displayNameTextBox.x + 320 + 26, displayNameTextBox.y, "face", 150);
|
||||
iconTextBox.onChange = (newIcon:String) -> {updateIcon(newIcon);}
|
||||
menuDataGroup.add(iconTextBox);
|
||||
addLabelOn(iconTextBox, "Icon");
|
||||
addLabelOn(iconTextBox, translateMeta("icon"));
|
||||
|
||||
updateIcon("Icon");
|
||||
updateIcon("face");
|
||||
|
||||
opponentModeCheckbox = new UICheckbox(displayNameTextBox.x, iconTextBox.y + 10 + 32 + 26, "Opponent Mode", true);
|
||||
opponentModeCheckbox = new UICheckbox(displayNameTextBox.x, iconTextBox.y + 10 + 32 + 26, translateMeta("opponentMode"), true);
|
||||
menuDataGroup.add(opponentModeCheckbox);
|
||||
addLabelOn(opponentModeCheckbox, "Modes Allowed");
|
||||
addLabelOn(opponentModeCheckbox, translateMeta("modesAllowed"));
|
||||
|
||||
coopAllowedCheckbox = new UICheckbox(opponentModeCheckbox.x + 150 + 26, opponentModeCheckbox.y, "Co-op Mode", true);
|
||||
coopAllowedCheckbox = new UICheckbox(opponentModeCheckbox.x + 150 + 26, opponentModeCheckbox.y, translateMeta("coopAllowed"), true);
|
||||
menuDataGroup.add(coopAllowedCheckbox);
|
||||
|
||||
colorWheel = new UIColorwheel(iconTextBox.x, coopAllowedCheckbox.y, 0xFFFFFF);
|
||||
menuDataGroup.add(colorWheel);
|
||||
addLabelOn(colorWheel, "Color");
|
||||
addLabelOn(colorWheel, translateMeta("color"));
|
||||
|
||||
difficultiesTextBox = new UITextBox(opponentModeCheckbox.x, opponentModeCheckbox.y + 6 + 32 + 26, "");
|
||||
menuDataGroup.add(difficultiesTextBox);
|
||||
addLabelOn(difficultiesTextBox, "Difficulties");
|
||||
addLabelOn(difficultiesTextBox, translateMeta("difficulties"));
|
||||
|
||||
for (checkbox in [opponentModeCheckbox, coopAllowedCheckbox])
|
||||
{checkbox.y += 6; checkbox.x += 4;}
|
||||
|
||||
saveButton = new UIButton(windowSpr.x + windowSpr.bWidth - 20 - 125, windowSpr.y + windowSpr.bHeight - 16 - 32, "Save & Close", function() {
|
||||
var menuTitle:UIText;
|
||||
selectFormatGroup.add(menuTitle = new UIText(windowSpr.x + 20, windowSpr.y + 30 + 16, 0, translate("importSource"), 28));
|
||||
|
||||
engineDropdown = new UIDropDown(menuTitle.x, menuTitle.y + menuTitle.height + 36, 480, 32, [translate("legacyOrPsych"), translate("vslice"), translate("vsliceProject")]);
|
||||
selectFormatGroup.add(engineDropdown);
|
||||
addLabelOn(engineDropdown, translate("importChartFormat"));
|
||||
|
||||
createSong = new UIButton(windowSpr.x + 20, windowSpr.y + windowSpr.bHeight - 16 - 32, "< " + translate("back"), function() {
|
||||
winTitle = translate("win-title");
|
||||
isImporting = false;
|
||||
updatePagesTexts();
|
||||
refreshPages();
|
||||
}, 120);
|
||||
selectFormatGroup.add(createSong);
|
||||
|
||||
var menuTitle:UIText;
|
||||
importAudioGroup.add(menuTitle = new UIText(windowSpr.x + 20, windowSpr.y + 30 + 16, 0, translate("importAudios"), 28));
|
||||
|
||||
importInstExplorer = new UIFileExplorer(menuTitle.x, menuTitle.y + menuTitle.height + 36, null, null, Flags.SOUND_EXT, function (path, res) {
|
||||
if (path == null || res == null) return;
|
||||
var audioPlayer:UIAudioPlayer = new UIAudioPlayer(importInstExplorer.x + 8, importInstExplorer.y + 8, res);
|
||||
importInstExplorer.members.push(audioPlayer);
|
||||
importInstExplorer.uiElement = audioPlayer;
|
||||
});
|
||||
importAudioGroup.add(importInstExplorer);
|
||||
addLabelOn(importInstExplorer, "").applyMarkup(
|
||||
translate("instAudio"),
|
||||
[new FlxTextFormatMarkerPair(new FlxTextFormat(0xFFAD1212), "$")]);
|
||||
|
||||
importVoicesExplorer = new UIFileExplorer(importInstExplorer.x + 320 + 26, importInstExplorer.y, null, null, Flags.SOUND_EXT, function (path, res) {
|
||||
if (path == null || res == null) return;
|
||||
var audioPlayer:UIAudioPlayer = new UIAudioPlayer(importVoicesExplorer.x + 8, importVoicesExplorer.y + 8, res);
|
||||
importVoicesExplorer.members.push(audioPlayer);
|
||||
importVoicesExplorer.uiElement = audioPlayer;
|
||||
});
|
||||
importAudioGroup.add(importVoicesExplorer);
|
||||
addLabelOn(importVoicesExplorer, translate("voicesAudio"));
|
||||
|
||||
var menuTitle:UIText;
|
||||
importDataGroup.add(menuTitle = new UIText(windowSpr.x + 20, windowSpr.y + 30 + 16, 0, "Add Data", 28));
|
||||
|
||||
importDataGroup.add(importIdTextBox = new UITextBox(menuTitle.x, menuTitle.y + menuTitle.height + 36));
|
||||
addLabelOn(importIdTextBox, "").applyMarkup(
|
||||
translate("songFileName"),
|
||||
[new FlxTextFormatMarkerPair(new FlxTextFormat(0xFFAD1212), "$")]);
|
||||
|
||||
importChartFile = new UIFileExplorer(importIdTextBox.x, importIdTextBox.y + importIdTextBox.height + 56, null, null, "fnfc", function (_, _) importIdTextBox.label.text = new haxe.io.Path(importChartFile.filePath).file);
|
||||
importDataGroup.add(importChartFile);
|
||||
addLabelOn(importChartFile, "").applyMarkup(
|
||||
translate("songDataFile"),
|
||||
[new FlxTextFormatMarkerPair(new FlxTextFormat(0xFFAD1212), "$")]);
|
||||
|
||||
importMetaFile = new UIFileExplorer(importChartFile.x + 320 + 26, importChartFile.y, null, null, "json");
|
||||
importDataGroup.add(importMetaFile);
|
||||
addLabelOn(importMetaFile, "").applyMarkup(
|
||||
translate("songMetaFile"),
|
||||
[new FlxTextFormatMarkerPair(new FlxTextFormat(0xFFAD1212), "$")]);
|
||||
|
||||
saveButton = new UIButton(windowSpr.x + windowSpr.bWidth - 20 - 125, windowSpr.y + windowSpr.bHeight - 16 - 32, TU.translate("editor.saveClose"), function() {
|
||||
var pages = isImporting ? importPages : pages;
|
||||
if (curPage == pages.length-1) {
|
||||
saveSongInfo();
|
||||
close();
|
||||
@@ -147,7 +254,7 @@ class SongCreationScreen extends UISubstateWindow {
|
||||
}, 125);
|
||||
add(saveButton);
|
||||
|
||||
backButton = new UIButton(saveButton.x - 20 - saveButton.bWidth, saveButton.y, "< Back", function() {
|
||||
backButton = new UIButton(saveButton.x - 20 - saveButton.bWidth, saveButton.y, "< " + translate("back"), function() {
|
||||
curPage--;
|
||||
refreshPages();
|
||||
|
||||
@@ -155,7 +262,7 @@ class SongCreationScreen extends UISubstateWindow {
|
||||
}, 125);
|
||||
add(backButton);
|
||||
|
||||
closeButton = new UIButton(backButton.x - 20 - saveButton.bWidth, saveButton.y, "Cancel", function() {
|
||||
closeButton = new UIButton(backButton.x - 20 - saveButton.bWidth, saveButton.y, TU.translate("editor.close"), function() {
|
||||
close();
|
||||
}, 125);
|
||||
add(closeButton);
|
||||
@@ -167,17 +274,41 @@ class SongCreationScreen extends UISubstateWindow {
|
||||
pages.push(cast add(menuDataGroup));
|
||||
pageSizes.push(FlxPoint.get(748 - 32 + 40, 400));
|
||||
|
||||
importPages.push(cast add(selectFormatGroup));
|
||||
importPageSizes.push(FlxPoint.get(500, 250));
|
||||
|
||||
importPages.push(cast add(importAudioGroup));
|
||||
importPageSizes.push(FlxPoint.get(705, 250));
|
||||
|
||||
importPages.push(cast add(importDataGroup));
|
||||
importPageSizes.push(FlxPoint.get(705, 300));
|
||||
|
||||
refreshPages();
|
||||
updatePagesTexts();
|
||||
}
|
||||
|
||||
public override function update(elapsed:Float) {
|
||||
if (curPage == 0) {
|
||||
if (instExplorer.file != null)
|
||||
if (isImporting)
|
||||
{
|
||||
var name = engineDropdown.options[engineDropdown.index];
|
||||
var project = name == translate("vsliceProject");
|
||||
|
||||
if (curPage == 1) {
|
||||
importInstExplorer.selectable = importVoicesExplorer.selectable = !project;
|
||||
saveButton.selectable = project ? true : (importInstExplorer.file != null);
|
||||
} else if (curPage == 2) {
|
||||
importIdTextBox.selectable = !project;
|
||||
importChartFile.fileType = project ? "fnfc" : "json";
|
||||
importMetaFile.selectable = name == translate("vslice");
|
||||
saveButton.selectable = importChartFile.file != null && (!importMetaFile.selectable || importMetaFile.file != null) && (!importIdTextBox.selectable || importIdTextBox.label.text.trim().length > 0);
|
||||
} else
|
||||
saveButton.selectable = true;
|
||||
else saveButton.selectable = false;
|
||||
} else
|
||||
saveButton.selectable = true;
|
||||
} else {
|
||||
if (curPage == 0)
|
||||
saveButton.selectable = (instExplorer.file != null);
|
||||
else
|
||||
saveButton.selectable = true;
|
||||
}
|
||||
|
||||
saveButton.alpha = saveButton.field.alpha = saveButton.selectable ? 1 : 0.4;
|
||||
super.update(elapsed);
|
||||
@@ -185,70 +316,145 @@ class SongCreationScreen extends UISubstateWindow {
|
||||
|
||||
function refreshPages() {
|
||||
for (i=>page in pages)
|
||||
page.visible = page.exists = i == curPage;
|
||||
page.visible = page.exists = i == curPage && !isImporting;
|
||||
|
||||
for (i=>page in importPages)
|
||||
page.visible = page.exists = i == curPage && isImporting;
|
||||
}
|
||||
|
||||
function updatePagesTexts() {
|
||||
var pageSizes = isImporting ? importPageSizes : pageSizes;
|
||||
var pages = isImporting ? importPages : pages;
|
||||
windowSpr.bWidth = Std.int(pageSizes[curPage].x);
|
||||
windowSpr.bHeight = Std.int(pageSizes[curPage].y);
|
||||
|
||||
titleSpr.x = windowSpr.x + 25;
|
||||
titleSpr.y = windowSpr.y + ((30 - titleSpr.height) / 2);
|
||||
|
||||
saveButton.field.text = curPage == pages.length-1 ? "Save & Close" : 'Next >';
|
||||
titleSpr.text = 'Creating New Song (${curPage+1}/${pages.length})';
|
||||
saveButton.field.text = curPage == pages.length-1 ? TU.translate("editor.saveClose") : translate("next") + ' >';
|
||||
titleSpr.text = '$winTitle (${curPage+1}/${pages.length})';
|
||||
|
||||
backButton.field.text = '< Back';
|
||||
backButton.field.text = '< ' + translate("back");
|
||||
backButton.visible = backButton.exists = curPage > 0;
|
||||
|
||||
backButton.x = (saveButton.x = windowSpr.x + windowSpr.bWidth - 20 - 125) - 20 - saveButton.bWidth;
|
||||
closeButton.x = (curPage > 0 ? backButton : saveButton).x - 20 - saveButton.bWidth;
|
||||
|
||||
for (button in [saveButton, backButton, closeButton])
|
||||
for (button in [saveButton, backButton, closeButton, importFrom, createSong])
|
||||
button.y = windowSpr.y + windowSpr.bHeight - 16 - 32;
|
||||
}
|
||||
|
||||
function updateIcon(icon:String) {
|
||||
if (iconSprite == null) menuDataGroup.add(iconSprite = new FlxSprite());
|
||||
if (iconSprite == null) menuDataGroup.add(iconSprite = new HealthIcon());
|
||||
|
||||
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/' + Flags.DEFAULT_HEALTH_ICON);
|
||||
|
||||
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.setIcon(icon);
|
||||
var size = Std.int(150 * 0.5);
|
||||
iconSprite.setUnstretchedGraphicSize(size, size, true);
|
||||
iconSprite.updateHitbox();
|
||||
iconSprite.setPosition(iconTextBox.x + 150 + 8, (iconTextBox.y + 16) - (iconSprite.height/2));
|
||||
iconSprite.setPosition(iconTextBox.x + iconTextBox.bWidth + 8, iconTextBox.y + (iconTextBox.bHeight / 2) - (iconSprite.height / 2));
|
||||
iconSprite.scrollFactor.set(1, 1);
|
||||
}
|
||||
|
||||
function saveSongInfo() {
|
||||
UIUtil.confirmUISelections(this);
|
||||
if (isImporting)
|
||||
{
|
||||
try switch(engineDropdown.index)
|
||||
{
|
||||
case 0 /*"V-Slice Project (.fnfc)"*/:
|
||||
var files:Map<String, Any> = [];
|
||||
for (field in new ZipReader(new BytesInput(importChartFile.file)).read()) {
|
||||
var fileName = field.fileName;
|
||||
var fileContent = field.data;
|
||||
files.set(fileName, fileContent);
|
||||
}
|
||||
saveFromVSlice(files);
|
||||
case 1 /*"V-Slice"*/:
|
||||
var songId = importIdTextBox.label.text;
|
||||
var files:Map<String, Any> = [];
|
||||
files.set('${songId}-metadata.json', importMetaFile.file);
|
||||
files.set('${songId}-chart.json', importChartFile.file);
|
||||
files.set('Inst.${Flags.SOUND_EXT}', importInstExplorer.file);
|
||||
files.set('Voices.${Flags.SOUND_EXT}', importVoicesExplorer.file);
|
||||
saveFromVSlice(files, songId);
|
||||
default /*"Psych/Legacy FNF"*/:
|
||||
var songId = importIdTextBox.label.text;
|
||||
var oldChart:SwagSong = Json.parse(cast importChartFile.file);
|
||||
var base:ChartData = {
|
||||
strumLines: [],
|
||||
noteTypes: [],
|
||||
events: [],
|
||||
meta: {name: null},
|
||||
scrollSpeed: Flags.DEFAULT_SCROLL_SPEED,
|
||||
stage: Flags.DEFAULT_STAGE,
|
||||
codenameChart: true
|
||||
};
|
||||
PsychParser.parse(oldChart, base);
|
||||
if (onSave != null) onSave({
|
||||
meta: {
|
||||
name: songId,
|
||||
difficulties: [songId],
|
||||
bpm: oldChart.bpm,
|
||||
beatsPerMeasure: oldChart.beatsPerMeasure,
|
||||
stepsPerBeat: oldChart.stepsPerBeat,
|
||||
displayName: oldChart.song
|
||||
},
|
||||
instBytes: importInstExplorer.file,
|
||||
voicesBytes: importVoicesExplorer.file,
|
||||
}, (songFolder:String, creation:SongCreationData) -> {
|
||||
#if sys
|
||||
CoolUtil.safeSaveFile('$songFolder/charts/${songId}.json', Json.stringify(base, Flags.JSON_PRETTY_PRINT));
|
||||
#end
|
||||
});
|
||||
} catch (e) {
|
||||
openSubState(new UIWarningSubstate("Importing Song/Charts: Error!", Std.string(e), [
|
||||
{label: "Ok", color: 0xFFFF0000, onClick: function(t) {}}
|
||||
]));
|
||||
}
|
||||
} else {
|
||||
for (stepper in [bpmStepper, beatsPerMeasureStepper, denominatorStepper])
|
||||
@:privateAccess stepper.__onChange(stepper.label.text);
|
||||
|
||||
var meta:ChartMetaData = {
|
||||
name: songNameTextBox.label.text,
|
||||
bpm: bpmStepper.value,
|
||||
beatsPerMeasure: Std.int(beatsPerMeasureStepper.value),
|
||||
stepsPerBeat: Std.int(stepsPerBeatStepper.value),
|
||||
displayName: displayNameTextBox.label.text,
|
||||
icon: iconTextBox.label.text,
|
||||
color: colorWheel.curColorString,
|
||||
parsedColor: colorWheel.curColor,
|
||||
opponentModeAllowed: opponentModeCheckbox.checked,
|
||||
coopAllowed: coopAllowedCheckbox.checked,
|
||||
difficulties: [for (diff in difficultiesTextBox.label.text.split(",")) diff.trim()],
|
||||
};
|
||||
var meta:ChartMetaData = {
|
||||
name: songNameTextBox.label.text,
|
||||
bpm: bpmStepper.value,
|
||||
beatsPerMeasure: Std.int(beatsPerMeasureStepper.value),
|
||||
stepsPerBeat: Std.int(16 / denominatorStepper.value),
|
||||
displayName: displayNameTextBox.label.text,
|
||||
icon: iconTextBox.label.text,
|
||||
color: colorWheel.curColor,
|
||||
opponentModeAllowed: opponentModeCheckbox.checked,
|
||||
coopAllowed: coopAllowedCheckbox.checked,
|
||||
difficulties: [for (diff in difficultiesTextBox.label.text.split(",")) diff.trim()],
|
||||
};
|
||||
|
||||
if (onSave != null) onSave({
|
||||
meta: meta,
|
||||
instBytes: instExplorer.file,
|
||||
voicesBytes: voicesExplorer.file
|
||||
}, null);
|
||||
}
|
||||
}
|
||||
|
||||
function saveFromVSlice(files:Map<String, Any>, ?name:String) {
|
||||
var songId = name == null && files.exists("manifest.json") ? Json.parse(files.get("manifest.json")).songId : name;
|
||||
var vslicemeta:SwagMetadata = Json.parse(files.get('${songId}-metadata.json'));
|
||||
var vslicechart:NewSwagSong = Json.parse(files.get('${songId}-chart.json'));
|
||||
var playData = vslicemeta.playData;
|
||||
|
||||
var meta:ChartMetaData = {name: songId};
|
||||
var diffCharts:Array<ChartDataWithInfo> = [];
|
||||
VSliceParser.parse(vslicemeta, vslicechart, meta, diffCharts, songId);
|
||||
|
||||
if (onSave != null) onSave({
|
||||
meta: meta,
|
||||
instBytes: instExplorer.file,
|
||||
voicesBytes: voicesExplorer.file
|
||||
instBytes: files.get('Inst.${Flags.SOUND_EXT}'),
|
||||
voicesBytes: files.get('Voices.${Flags.SOUND_EXT}'), //it may exist
|
||||
playerVocals: files.get('Voices-${playData.characters.playerVocals != null ? playData.characters.playerVocals[0] : playData.characters.player}.${Flags.SOUND_EXT}'),
|
||||
oppVocals: files.get('Voices-${playData.characters.opponentVocals != null ? playData.characters.opponentVocals[0] : playData.characters.opponent}.${Flags.SOUND_EXT}'),
|
||||
}, (songFolder:String, creation:SongCreationData) -> {
|
||||
#if sys
|
||||
for (diff in diffCharts) CoolUtil.safeSaveFile('$songFolder/charts/${diff.diffName}.json', Json.stringify(diff.chart, Flags.JSON_PRETTY_PRINT));
|
||||
#end
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -20,8 +20,11 @@ class StageCreationScreen extends UISubstateWindow {
|
||||
if (onSave != null) this.onSave = onSave;
|
||||
}
|
||||
|
||||
inline function translate(id:String, ?args:Array<Dynamic>)
|
||||
return TU.translate("stageCreationScreen." + id, args);
|
||||
|
||||
public override function create() {
|
||||
winTitle = "Creating New Stage";
|
||||
winTitle = translate("win-title");
|
||||
|
||||
winWidth = 748 - 32 + 40;
|
||||
winHeight = 220;
|
||||
@@ -35,20 +38,20 @@ class StageCreationScreen extends UISubstateWindow {
|
||||
}
|
||||
|
||||
var stageInfo:UIText;
|
||||
add(stageInfo = new UIText(windowSpr.x + 20, windowSpr.y + 30 + 16, 0, "Stage Info", 28));
|
||||
add(stageInfo = new UIText(windowSpr.x + 20, windowSpr.y + 30 + 16, 0, translate("title"), 28));
|
||||
|
||||
add(stageNameTextBox = new UITextBox(stageInfo.x, stageInfo.y + stageInfo.height + 36, "Stage Name"));
|
||||
addLabelOn(stageNameTextBox, "Stage Name");
|
||||
add(stageNameTextBox = new UITextBox(stageInfo.x, stageInfo.y + stageInfo.height + 36, translate("stage-name")));
|
||||
addLabelOn(stageNameTextBox, translate("stage-name"));
|
||||
|
||||
add(stagePathTextBox = new UITextBox(stageNameTextBox.x + 320 + 26, stageNameTextBox.y, "Stage Path"));
|
||||
addLabelOn(stagePathTextBox, "Stage Path");
|
||||
add(stagePathTextBox = new UITextBox(stageNameTextBox.x + 320 + 26, stageNameTextBox.y, translate("stage-path")));
|
||||
addLabelOn(stagePathTextBox, translate("stage-path"));
|
||||
|
||||
add(saveButton = new UIButton(windowSpr.x + windowSpr.bWidth - 20 - 125, windowSpr.y + windowSpr.bHeight - 16 - 32, "Save & Close", function() {
|
||||
add(saveButton = new UIButton(windowSpr.x + windowSpr.bWidth - 20 - 125, windowSpr.y + windowSpr.bHeight - 16 - 32, TU.translate("editor.saveClose"), function() {
|
||||
saveStageInfo();
|
||||
close();
|
||||
}, 125));
|
||||
|
||||
add(closeButton = new UIButton(saveButton.x - 20 - saveButton.bWidth, saveButton.y, "Cancel", close, 125));
|
||||
add(closeButton = new UIButton(saveButton.x - 20 - saveButton.bWidth, saveButton.y, TU.translate("editor.cancel"), close, 125));
|
||||
closeButton.color = 0xFFFF0000;
|
||||
}
|
||||
|
||||
|
||||
@@ -71,6 +71,9 @@ class StageEditor extends UIState {
|
||||
return "stageEditor." + id;
|
||||
}
|
||||
|
||||
inline function translate(id:String, ?args:Array<Dynamic>)
|
||||
return TU.translate("stageEditor." + id, args);
|
||||
|
||||
public function new(stage:String) {
|
||||
super();
|
||||
if (stage != null) __stage = stage;
|
||||
@@ -79,47 +82,47 @@ class StageEditor extends UIState {
|
||||
public override function create() {
|
||||
super.create();
|
||||
|
||||
WindowUtils.suffix = " (Stage Editor)";
|
||||
WindowUtils.suffix = " (" + TU.translate("editor.stage.name") + ")";
|
||||
SaveWarning.selectionClass = StageSelection;
|
||||
SaveWarning.saveFunc = () -> {_file_save(null);};
|
||||
|
||||
topMenu = [
|
||||
{
|
||||
label: "File",
|
||||
label: translate("topBar.file"),
|
||||
childs: [
|
||||
{
|
||||
label: "Save",
|
||||
label: translate("file.save"),
|
||||
keybind: [CONTROL, S],
|
||||
onSelect: _file_save,
|
||||
},
|
||||
{
|
||||
label: "Save As...",
|
||||
label: translate("file.saveAs"),
|
||||
keybind: [CONTROL, SHIFT, S],
|
||||
onSelect: _file_saveas,
|
||||
},
|
||||
null,
|
||||
{
|
||||
label: "Exit",
|
||||
label: translate("file.exit"),
|
||||
onSelect: _file_exit
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "Edit",
|
||||
label: translate("topBar.edit"),
|
||||
childs: [
|
||||
{
|
||||
label: "Undo",
|
||||
label: translate("edit.undo"),
|
||||
keybind: [CONTROL, Z],
|
||||
onSelect: _edit_undo,
|
||||
},
|
||||
{
|
||||
label: "Redo",
|
||||
label: translate("edit.redo"),
|
||||
keybind: [CONTROL, SHIFT, Z],
|
||||
onSelect: _edit_redo,
|
||||
},
|
||||
null,
|
||||
{
|
||||
label: "Edit Stage Info",
|
||||
label: translate("edit.editStageInfo"),
|
||||
onSelect: (_) -> {
|
||||
openSubState(new UISoftcodedWindow(
|
||||
"layouts/stage/stageInfoScreen",
|
||||
@@ -130,7 +133,10 @@ class StageEditor extends UIState {
|
||||
"stage" => stage,
|
||||
"Stage" => Stage,
|
||||
"exID" => exID,
|
||||
"CEditInfo" => StageChange.CEditInfo
|
||||
"CEditInfo" => StageChange.CEditInfo,
|
||||
"translate" => function (id:String, ?args:Array<Dynamic>) {
|
||||
return TU.translate("stageInfoScreen." + id, args);
|
||||
}
|
||||
]
|
||||
));
|
||||
},
|
||||
@@ -138,56 +144,56 @@ class StageEditor extends UIState {
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "Select",
|
||||
label: translate("topBar.select"),
|
||||
childs: [
|
||||
{
|
||||
label: "All",
|
||||
label: translate("select.all"),
|
||||
keybind: [CONTROL, A],
|
||||
onSelect: (_) -> _select_all(_),
|
||||
},
|
||||
{
|
||||
label: "Deselect",
|
||||
label: translate("select.deselect"),
|
||||
keybind: [CONTROL, D],
|
||||
onSelect: (_) -> _select_deselect(_),
|
||||
},
|
||||
{
|
||||
label: "Inverse",
|
||||
label: translate("select.inverse"),
|
||||
keybind: [CONTROL, SHIFT, I],
|
||||
onSelect: (_) -> _select_inverse(_),
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "View",
|
||||
label: translate("topBar.view"),
|
||||
childs: [
|
||||
{
|
||||
label: "Zoom in",
|
||||
label: translate("view.zoomIn"),
|
||||
keybind: [CONTROL, NUMPADPLUS],
|
||||
onSelect: _view_zoomin
|
||||
},
|
||||
{
|
||||
label: "Zoom out",
|
||||
label: translate("view.zoomOut"),
|
||||
keybind: [CONTROL, NUMPADMINUS],
|
||||
onSelect: _view_zoomout
|
||||
},
|
||||
{
|
||||
label: "Reset zoom",
|
||||
label: translate("view.zoomReset"),
|
||||
keybind: [CONTROL, NUMPADZERO],
|
||||
onSelect: _view_zoomreset
|
||||
},
|
||||
null,
|
||||
{
|
||||
label: "Focus Dad",
|
||||
label: translate("view.focusDad"),
|
||||
//keybind: [CONTROL, NUMPADZERO],
|
||||
onSelect: _view_focusdad
|
||||
},
|
||||
{
|
||||
label: "Focus Gf",
|
||||
label: translate("view.focusGF"),
|
||||
//keybind: [CONTROL, NUMPADZERO],
|
||||
onSelect: _view_focusgf
|
||||
},
|
||||
{
|
||||
label: "Focus BF",
|
||||
label: translate("view.focusBF"),
|
||||
//keybind: [CONTROL, NUMPADZERO],
|
||||
onSelect: _view_focusbf
|
||||
},
|
||||
@@ -195,10 +201,10 @@ class StageEditor extends UIState {
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "Editor",
|
||||
label: translate("topBar.editor"),
|
||||
childs: [
|
||||
{
|
||||
label: "Show Characters",
|
||||
label: translate("editor.showCharacters"),
|
||||
//keybind: [CONTROL, NUMPADMINUS],
|
||||
onSelect: _editor_showCharacters,
|
||||
icon: showCharacters ? 1 : 0
|
||||
@@ -502,6 +508,7 @@ class StageEditor extends UIState {
|
||||
|
||||
function _file_save(_) {
|
||||
#if sys
|
||||
FlxG.sound.play(Paths.sound('editors/save'));
|
||||
CoolUtil.safeSaveFile(
|
||||
'${Paths.getAssetsRoot()}/data/stages/${__stage}.xml',
|
||||
buildStage()
|
||||
@@ -513,6 +520,7 @@ class StageEditor extends UIState {
|
||||
}
|
||||
|
||||
function _file_saveas(_) {
|
||||
FlxG.sound.play(Paths.sound('editors/save'));
|
||||
openSubState(new SaveSubstate(buildStage(), {
|
||||
defaultSaveFile: '${__stage}.xml'
|
||||
}));
|
||||
@@ -719,12 +727,12 @@ class StageEditor extends UIState {
|
||||
}
|
||||
}
|
||||
|
||||
Logs.trace(Printer.print(xml, true));
|
||||
|
||||
return "<!DOCTYPE codename-engine-stage>\n" + Printer.print(xml, true);
|
||||
var xmlThingYea:String = "<!DOCTYPE codename-engine-stage>\n" + Printer.print(xml, Options.editorPrettyPrint);
|
||||
return Options.editorPrettyPrint ? xmlThingYea : xmlThingYea.replace("\n", "");
|
||||
}
|
||||
|
||||
function _edit_undo(_) {
|
||||
FlxG.sound.play(Flags.DEFAULT_EDITOR_UNDO_SOUND);
|
||||
var undo = undos.undo();
|
||||
switch(undo) {
|
||||
case null:
|
||||
@@ -750,6 +758,7 @@ class StageEditor extends UIState {
|
||||
}
|
||||
|
||||
function _edit_redo(_) {
|
||||
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_REDO_SOUND));
|
||||
var redo = undos.redo();
|
||||
switch(redo) {
|
||||
case null:
|
||||
@@ -810,7 +819,7 @@ class StageEditor extends UIState {
|
||||
var sprite:FunkinSprite = cast sprite;
|
||||
sprite.extra.set(exID("selected"), true);
|
||||
sprite.extra.get(exID("button")).selected = true;
|
||||
Logs.trace("Selected " + sprite.name);
|
||||
//Logs.trace("Selected " + sprite.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1352,6 +1361,9 @@ class StageXMLEditScreen extends UISoftcodedWindow {
|
||||
public var xml:Access;
|
||||
public var saveCallback:Void->Void;
|
||||
|
||||
inline function translate(id:String, ?args:Array<Dynamic>)
|
||||
return TU.translate("stageXMLEditScreen." + id, args);
|
||||
|
||||
public function new(xml:Access, saveCallback:Void->Void, type:String = "Unknown") {
|
||||
this.xml = xml;
|
||||
this.saveCallback = saveCallback;
|
||||
@@ -1359,7 +1371,8 @@ class StageXMLEditScreen extends UISoftcodedWindow {
|
||||
"stage" => StageEditor.instance.stage,
|
||||
"xml" => xml,
|
||||
"exID" => StageEditor.exID,
|
||||
"type" => type
|
||||
"type" => type,
|
||||
"translate" => translate
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,20 +21,20 @@ class StageSelection extends EditorTreeMenu
|
||||
var list:Array<OptionType> = [
|
||||
for (stage in (modsList.length == 0 ? Stage.getList(false, true) : modsList)) {
|
||||
stages.push(stage);
|
||||
new TextOption(stage, "Press ACCEPT to edit this stage.",
|
||||
new TextOption(stage, TU.translate("stageSelection.acceptStage"),
|
||||
function() {
|
||||
FlxG.switchState(new StageEditor(stage));
|
||||
});
|
||||
}
|
||||
];
|
||||
|
||||
list.insert(0, new NewOption("New Stage", "Press ACCEPT to create a new stage.", function() {
|
||||
list.insert(0, new NewOption(TU.translate("stageSelection.newStage"), TU.translate("stageSelection.acceptNewStage"), function() {
|
||||
FlxG.state.openSubState(new StageCreationScreen(saveStage));
|
||||
}));
|
||||
|
||||
main = new OptionsScreen("Stage Editor", "Select a stage to edit. HSCRIPT only stages are not supported.", list);
|
||||
main = new OptionsScreen(TU.translate("editor.stage.name"), TU.translate("stageSelection.desc"), list);
|
||||
|
||||
//DiscordUtil.call("onEditorTreeLoaded", ["Stage Editor"]);
|
||||
DiscordUtil.call("onEditorTreeLoaded", ["Stage Editor"]);
|
||||
}
|
||||
|
||||
public function saveStage(creation:StageCreationData) {
|
||||
@@ -51,7 +51,7 @@ class StageSelection extends EditorTreeMenu
|
||||
#end
|
||||
|
||||
// Add to List
|
||||
var option = new TextOption(creation.name, "Press ACCEPT to edit this stage.", function() {
|
||||
var option = new TextOption(creation.name, TU.translate("stageSelection.acceptStage"), function() {
|
||||
FlxG.switchState(new StageEditor(creation.name));
|
||||
});
|
||||
optionsTree.members[optionsTree.members.length-1].insert(1, option);
|
||||
|
||||
@@ -4,8 +4,12 @@ import funkin.editors.stage.elements.StageElementButton;
|
||||
import funkin.editors.stage.elements.*;
|
||||
|
||||
class StageSpritesWindow extends UIButtonList<StageElementButton> {
|
||||
|
||||
inline function translate(id:String, ?args:Array<Dynamic>)
|
||||
return TU.translate("stageSprites." + id, args);
|
||||
|
||||
public function new(x:Float, y:Float) {
|
||||
super(x, y, 400, FlxG.height-25, "Stage Sprites", FlxPoint.get(400, 64));
|
||||
super(x, y, 400, FlxG.height-25, translate("win-title"), FlxPoint.get(400, 64));
|
||||
|
||||
collapsable = true;
|
||||
topAlpha = 0.9;
|
||||
@@ -23,22 +27,22 @@ class StageSpritesWindow extends UIButtonList<StageElementButton> {
|
||||
var screenPos = addButton.getScreenPosition(null, lastDrawCam == null ? FlxG.camera : lastDrawCam);
|
||||
StageEditor.instance.openContextMenu([
|
||||
{
|
||||
label: "Sprite",
|
||||
label: translate("sprite"),
|
||||
onSelect: StageEditor.instance._sprite_new,
|
||||
color: 0xFF00FF00,
|
||||
icon: 2
|
||||
},
|
||||
{
|
||||
label: "Box",
|
||||
label: translate("box"),
|
||||
onSelect: function(_) {
|
||||
UIState.state.displayNotification(new UIBaseNotification("Creating a box isnt implemented yet!", 2, BOTTOM_LEFT));
|
||||
UIState.state.displayNotification(new UIBaseNotification(translate("warnings.not-implemented"), 2, BOTTOM_LEFT));
|
||||
CoolUtil.playMenuSFX(WARNING, 0.45);
|
||||
},
|
||||
color: 0xFF00FF00,
|
||||
icon: 2
|
||||
},
|
||||
{
|
||||
label: "Character",
|
||||
label: translate("character"),
|
||||
onSelect: StageEditor.instance._character_new,
|
||||
color: 0xFF00FF00,
|
||||
icon: 2
|
||||
|
||||
@@ -12,6 +12,9 @@ class StageCharacterButton extends StageElementButton {
|
||||
public var char:Character;
|
||||
public var charScale(get, null):Float;
|
||||
|
||||
inline function translate(id:String, ?args:Array<Dynamic>)
|
||||
return TU.translate("stageCharacters." + id, args);
|
||||
|
||||
public function new(x:Float,y:Float, char:Character, xml:Access) {
|
||||
this.char = char;
|
||||
super(x,y, xml);
|
||||
@@ -61,9 +64,9 @@ class StageCharacterButton extends StageElementButton {
|
||||
|
||||
public override function onDelete() {
|
||||
if (char.name.startsWith("NO_DELETE_")) {
|
||||
var text = "This character position is required in stages!";
|
||||
if (char.name == "NO_DELTE_girlfriend" && FlxG.random.bool(1))
|
||||
text = "You can't delete Girlfriend";
|
||||
var text = translate("requiredPosition");
|
||||
if (char.name == "NO_DELETE_girlfriend" && FlxG.random.bool(1))
|
||||
text = translate("noDeleteGirlfriend");
|
||||
UIState.state.displayNotification(new UIBaseNotification(text, 2, BOTTOM_LEFT));
|
||||
return;
|
||||
}
|
||||
@@ -100,6 +103,9 @@ class StageCharacterEditScreen extends UISoftcodedWindow {
|
||||
public var button:StageCharacterButton;
|
||||
public var char:Character;
|
||||
|
||||
inline function translate(id:String, ?args:Array<Dynamic>)
|
||||
return TU.translate("stageElementEditScreen." + id, args);
|
||||
|
||||
public function new(button:StageCharacterButton) {
|
||||
this.button = button;
|
||||
this.char = button.char;
|
||||
@@ -117,6 +123,7 @@ class StageCharacterEditScreen extends UISoftcodedWindow {
|
||||
"setEx" => function(name:String, value:Dynamic) {
|
||||
char.extra.set(StageEditor.exID(name), value);
|
||||
},
|
||||
"translate" => translate,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -209,8 +209,10 @@ class StageElementButton extends UIButton {
|
||||
var text = '${getName()} (${CoolUtil.quantize(pos.x, 100)}, ${CoolUtil.quantize(pos.y, 100)})';
|
||||
var sprite = getSprite();
|
||||
if(sprite != null) {
|
||||
text += '\nScale: (${CoolUtil.quantize(sprite.scale.x, 100)}, ${CoolUtil.quantize(sprite.scale.y, 100)})';
|
||||
text += '\nScroll: (${CoolUtil.quantize(sprite.scrollFactor.x, 100)}, ${CoolUtil.quantize(sprite.scrollFactor.y, 100)})';
|
||||
var scaleText = TU.getRaw("stageElement.scale");
|
||||
var scrollText = TU.getRaw("stageElement.scroll");
|
||||
text += '\n${scaleText.format([CoolUtil.quantize(sprite.scale.x, 100), CoolUtil.quantize(sprite.scale.y, 100)])}';
|
||||
text += '\n${scrollText.format([CoolUtil.quantize(sprite.scrollFactor.x, 100), CoolUtil.quantize(sprite.scrollFactor.y, 100)])}';
|
||||
}
|
||||
pos.put();
|
||||
return text;
|
||||
|
||||
@@ -76,6 +76,9 @@ class StageSpriteEditScreen extends UISoftcodedWindow {
|
||||
public var sprite:FunkinSprite;
|
||||
var isSaving:Bool = false;
|
||||
|
||||
inline function translate(id:String, ?args:Array<Dynamic>)
|
||||
return TU.translate("stageElementEditScreen." + id, args);
|
||||
|
||||
public function new(button:StageSpriteButton) {
|
||||
this.button = button;
|
||||
this.sprite = button.getSprite();
|
||||
@@ -91,6 +94,7 @@ class StageSpriteEditScreen extends UISoftcodedWindow {
|
||||
"setEx" => function(name:String, value:Dynamic) {
|
||||
sprite.extra.set(StageEditor.exID(name), value);
|
||||
},
|
||||
"translate" => translate,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ class StageUnknownButton extends StageElementButton {
|
||||
}
|
||||
|
||||
public override function getInfoText():String {
|
||||
return 'Unknown\n<${getName()} />';
|
||||
return TU.translate("stageElement.unknown", [getName()]);
|
||||
}
|
||||
|
||||
public override function updatePos() {
|
||||
@@ -86,13 +86,17 @@ class StageUnknownButton extends StageElementButton {
|
||||
class StageUnknownEditScreen extends UISoftcodedWindow {
|
||||
public var button:StageUnknownButton;
|
||||
|
||||
inline function translate(id:String, ?args:Array<Dynamic>)
|
||||
return TU.translate("stageElementEditScreen." + id, args);
|
||||
|
||||
public function new(button:StageUnknownButton) {
|
||||
this.button = button;
|
||||
super("layouts/stage/unknownEditScreen", [
|
||||
"stage" => StageEditor.instance.stage,
|
||||
"button" => button,
|
||||
"xml" => button.xml,
|
||||
"exID" => StageEditor.exID
|
||||
"exID" => StageEditor.exID,
|
||||
"translate" => translate,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -54,6 +54,7 @@ class UIAudioPlayer extends UIButton {
|
||||
timeBarPlayer = new FlxSprite(timeBar.x, timeBar.y).loadGraphic(Paths.image('editors/ui/audio-time-empty'));
|
||||
timeBarPlayer.colorTransform.color = 0x440364;
|
||||
timeBarPlayer.scale.y = (timeBarPlayer.frameHeight + 2) / timeBarPlayer.frameHeight;
|
||||
timeBarPlayer.pixelPerfectRender = true;
|
||||
members.push(timeBarPlayer);
|
||||
|
||||
timeBarSpr = cast new UISprite(timeBar.x, timeBar.y).makeSolid(timeBar.barWidth, timeBar.barHeight, 0x00FFFFFF);
|
||||
@@ -87,13 +88,13 @@ class UIAudioPlayer extends UIButton {
|
||||
playingSprite.animation.play(sound.playing ? "playing" : "paused");
|
||||
timeText.text = '${FlxStringUtil.formatTime(sound.time/1000, true)} / ${FlxStringUtil.formatTime(sound.length/1000)}';
|
||||
|
||||
if(timeBarPlayer.clipRect == null)
|
||||
timeBarPlayer.clipRect = new FlxRect(0, 0, timeBarPlayer.frameWidth, timeBarPlayer.frameHeight);
|
||||
if(timeBarPlayer.rawClipRect == null)
|
||||
timeBarPlayer.rawClipRect = new FlxRect(0, 0, timeBarPlayer.frameWidth, timeBarPlayer.frameHeight);
|
||||
|
||||
timeBarPlayer.clipRect.x = timeBarPlayer.frameWidth * (sound.time/sound.length);
|
||||
timeBarPlayer.clipRect.width = 2;
|
||||
timeBarPlayer.rawClipRect.x = timeBarPlayer.frameWidth * (sound.time/sound.length);
|
||||
timeBarPlayer.rawClipRect.width = 2;
|
||||
|
||||
timeBarPlayer.clipRect = timeBarPlayer.clipRect;
|
||||
timeBarPlayer.rawClipRect = timeBarPlayer.rawClipRect;
|
||||
|
||||
nextPlayerColor = sound.playing ? 0x732D95 : 0x440364;
|
||||
timeBarPlayer.colorTransform.color = FlxColor.interpolate(timeBarPlayer.colorTransform.color, nextPlayerColor, 1/14);
|
||||
@@ -106,20 +107,17 @@ class UIAudioPlayer extends UIButton {
|
||||
|
||||
var spritePos:FlxPoint = sprite.getScreenPosition(FlxPoint.get(), __lastDrawCameras[0]);
|
||||
|
||||
if (((mousePos.x > (spritePos.x)) && (mousePos.x < (spritePos.x) + timeBar.barWidth))
|
||||
&& ((mousePos.y > (spritePos.y)) && (mousePos.y < (spritePos.y) + timeBar.barHeight))) {
|
||||
if (FlxG.mouse.justPressed) {
|
||||
draggingObj = sprite;
|
||||
wasPlaying = draggingObj == timeBar ? sound.playing : false;
|
||||
}
|
||||
if (((mousePos.x > spritePos.x) && (mousePos.x < spritePos.x + sprite.barWidth))
|
||||
&& ((mousePos.y > spritePos.y) && (mousePos.y < spritePos.y + sprite.barHeight))) {
|
||||
|
||||
if (FlxG.mouse.justPressed) {
|
||||
if ((draggingObj = sprite) == timeBar && (wasPlaying = sound.playing)) sound.pause();
|
||||
}
|
||||
if (FlxG.mouse.pressed) {
|
||||
if (draggingObj == timeBar) {
|
||||
if (sound.playing) sound.pause();
|
||||
if (draggingObj == timeBar)
|
||||
sound.time = FlxMath.remapToRange(mousePos.x - spritePos.x, 0, timeBar.barWidth, 0, sound.length);
|
||||
} else if (draggingObj == volumeBar) {
|
||||
else if (draggingObj == volumeBar)
|
||||
sound.volume = FlxMath.remapToRange(mousePos.x - spritePos.x, 0, volumeBar.barWidth, 0, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
spritePos.put();
|
||||
@@ -127,8 +125,10 @@ class UIAudioPlayer extends UIButton {
|
||||
mousePos.put();
|
||||
|
||||
if (FlxG.mouse.released) {
|
||||
if (draggingObj == timeBar && wasPlaying)
|
||||
sound.play(wasPlaying = false, sound.time);
|
||||
if (draggingObj == timeBar && wasPlaying) {
|
||||
sound.resume();
|
||||
wasPlaying = false;
|
||||
}
|
||||
draggingObj = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,10 @@ class UIButton extends UISliceSprite {
|
||||
|
||||
public override function onHovered() {
|
||||
super.onHovered();
|
||||
if (FlxG.mouse.justPressed) hasBeenPressed = true;
|
||||
if (FlxG.mouse.justPressed) {
|
||||
hasBeenPressed = true;
|
||||
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_BUTTONCLICK_SOUND));
|
||||
}
|
||||
if (FlxG.mouse.justReleased && callback != null && shouldPress && hasBeenPressed) {
|
||||
callback();
|
||||
hasBeenPressed = false;
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
package funkin.editors.ui;
|
||||
|
||||
import flixel.util.FlxColor;
|
||||
import flixel.math.FlxPoint;
|
||||
import funkin.editors.ui.UIColorwheel.UIColorWheelSelector;
|
||||
|
||||
using flixel.util.FlxSpriteUtil;
|
||||
|
||||
class UICompactColorwheel extends UISliceSprite {
|
||||
var colorSlider:FlxSprite;
|
||||
var colorSliderSelector:UIColorWheelSelector;
|
||||
|
||||
var whiteColorButton:UIButton;
|
||||
|
||||
public var curColor:Null<FlxColor>;
|
||||
public var curColorString:String = "#FFFFFF";
|
||||
|
||||
public var hue:Float;
|
||||
|
||||
public function new (x:Float, y:Float, ?color:Int) {
|
||||
super(x, y, Std.int((12.5 * 3) + 116), Std.int((12.5 * 2) + (16)), 'editors/ui/inputbox');
|
||||
|
||||
curColor = cast color;
|
||||
hue = curColor.hue;
|
||||
|
||||
colorSlider = new FlxSprite(x + 12.5, y + 12.5).makeGraphic(100, 16, FlxColor.TRANSPARENT);
|
||||
colorSlider.drawRoundRect(0, 0, 100, 16, 10, 10, FlxColor.WHITE);
|
||||
|
||||
colorSlider.pixels.lock();
|
||||
for (pixelx in 0...Std.int(colorSlider.width)) {
|
||||
var color:Int = FlxColor.fromHSB(pixelx / (colorSlider.width-1) * 360, 1, 1);
|
||||
for (pixely in 0...Std.int(colorSlider.height))
|
||||
if (colorSlider.pixels.getPixel32(pixelx, pixely) != FlxColor.TRANSPARENT) colorSlider.pixels.setPixel32(pixelx, pixely, color);
|
||||
}
|
||||
colorSlider.pixels.unlock();
|
||||
|
||||
whiteColorButton = new UIButton(colorSlider.x + colorSlider.width + 12.5, colorSlider.y, null, () -> {
|
||||
setColor(FlxColor.WHITE);
|
||||
}, 16, 16);
|
||||
whiteColorButton.colorTransform.color = FlxColor.WHITE;
|
||||
|
||||
colorSliderSelector = new UIColorWheelSelector(colorSlider.x, colorSlider.y);
|
||||
|
||||
colorSlider.antialiasing = true;
|
||||
|
||||
for (item in [colorSlider, colorSliderSelector, whiteColorButton])
|
||||
members.push(item);
|
||||
|
||||
updateColor(false);
|
||||
}
|
||||
|
||||
inline function updateColorSliderPickerSelector()
|
||||
colorSliderSelector.selector.setPosition(colorSlider.x + (hue / 360 * (colorSlider.width-1)) - 8, colorSlider.y);
|
||||
|
||||
inline function updateColorSliderMouse(mousePos:FlxPoint)
|
||||
hue = (mousePos.x / colorSlider.width) * 360;
|
||||
|
||||
function setColor(newColor:FlxColor) {
|
||||
this.color = curColor = colorSliderSelector.curColor = newColor;
|
||||
}
|
||||
|
||||
public function updateColor(checkChanged:Bool = true) {
|
||||
if(checkChanged) colorChanged = true;
|
||||
setColor(FlxColor.fromHSB(hue, 1, 1));
|
||||
|
||||
updateColorSliderPickerSelector();
|
||||
curColorString = curColor.toWebString();
|
||||
}
|
||||
|
||||
// For Character Editor
|
||||
public var colorChanged:Bool = false;
|
||||
|
||||
// Make the colorwheel feel better
|
||||
static inline var hitBoxExtenstion:Float = 8;
|
||||
|
||||
var selectedSprite = null;
|
||||
public override function update(elapsed:Float) {
|
||||
var mousePos = FlxG.mouse.getScreenPosition(__lastDrawCameras[0], FlxPoint.get());
|
||||
if (hovered && FlxG.mouse.justPressed) {
|
||||
for (sprite in [colorSlider]) {
|
||||
var spritePos:FlxPoint = sprite.getScreenPosition(FlxPoint.get(), __lastDrawCameras[0]);
|
||||
if (FlxMath.inBounds(mousePos.x, spritePos.x - (hitBoxExtenstion/2), spritePos.x - (hitBoxExtenstion/2) + (sprite.width + hitBoxExtenstion)) && FlxMath.inBounds(mousePos.y, spritePos.y - (hitBoxExtenstion/2), spritePos.y - (hitBoxExtenstion/2) + (sprite.height + hitBoxExtenstion))) {
|
||||
selectedSprite = sprite;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedSprite != null) {
|
||||
var spritePos:FlxPoint = selectedSprite.getScreenPosition(FlxPoint.get(), __lastDrawCameras[0]);
|
||||
mousePos -= FlxPoint.weak(spritePos.x, spritePos.y);
|
||||
mousePos.set(CoolUtil.bound(mousePos.x, 0, selectedSprite.width), CoolUtil.bound(mousePos.y, 0, selectedSprite.height));
|
||||
|
||||
if (selectedSprite == colorSlider) updateColorSliderMouse(mousePos);
|
||||
updateColor();
|
||||
spritePos.put();
|
||||
|
||||
if (FlxG.mouse.justReleased) selectedSprite = null;
|
||||
}
|
||||
mousePos.put();
|
||||
super.update(elapsed);
|
||||
}
|
||||
}
|
||||
@@ -88,6 +88,14 @@ class UIContextMenu extends MusicBeatSubstate {
|
||||
for(o in separators)
|
||||
o.y -= bg.bHeight;
|
||||
}
|
||||
|
||||
if (bg.x + bg.bWidth > FlxG.width && bg.x > FlxG.width*0.5) {
|
||||
bg.x -= bg.bWidth;
|
||||
for(o in contextMenuOptions)
|
||||
o.x -= bg.bWidth;
|
||||
for(o in separators)
|
||||
o.x -= bg.bWidth;
|
||||
}
|
||||
}
|
||||
|
||||
public function select(option:UIContextMenuOption) {
|
||||
|
||||
@@ -87,6 +87,7 @@ class UIDropDown extends UISliceSprite {
|
||||
}
|
||||
|
||||
public function openContextMenu() {
|
||||
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_DROPDOWNAPPEAR_SOUND));
|
||||
var screenPos = getScreenPosition(null, __lastDrawCameras[0] == null ? FlxG.camera : __lastDrawCameras[0]);
|
||||
curMenu = UIState.state.openContextMenu([
|
||||
for(k=>o in items) {
|
||||
|
||||
@@ -15,16 +15,19 @@ class UIFileExplorer extends UISliceSprite {
|
||||
public var onFile:(String, Bytes)->Void;
|
||||
|
||||
public var uiElement:FlxSprite;
|
||||
|
||||
public var fileType:String = "txt";
|
||||
|
||||
public function new(x:Float, y:Float, ?w:Int, ?h:Int, fileType:String = "txt", ?onFile:(String, Bytes)->Void) {
|
||||
super(x, y, (w != null ? w : 320), (h != null ? h : 58), 'editors/ui/inputbox');
|
||||
this.fileType = fileType;
|
||||
|
||||
if (onFile != null) this.onFile = onFile;
|
||||
|
||||
uploadButton = new UIButton(x + 8, y+ 8, null, function () {
|
||||
var fileDialog = new FileDialog();
|
||||
fileDialog.onSelect.add(loadFile);
|
||||
fileDialog.browse(OPEN, fileType);
|
||||
fileDialog.browse(OPEN, this.fileType);
|
||||
}, bWidth - 16, bHeight - 16);
|
||||
members.push(uploadButton);
|
||||
|
||||
@@ -66,6 +69,7 @@ class UIFileExplorer extends UISliceSprite {
|
||||
}
|
||||
|
||||
public function removeFile() {
|
||||
if (!selectable) return;
|
||||
if (uiElement != null) {
|
||||
members.remove(uiElement);
|
||||
uiElement.destroy();
|
||||
|
||||
@@ -35,6 +35,9 @@ class UIImageExplorer extends UIFileExplorer {
|
||||
public var directoryTextBoxLabel:UIText;
|
||||
public var directoryTextBox:UITextBox;
|
||||
|
||||
inline function translate(id:String, ?args:Array<Dynamic>)
|
||||
return TU.translate("uiImageExplorer." + id, args);
|
||||
|
||||
public function new(x:Float, y:Float, image:String, ?w:Int, ?h:Int, ?onFile:(String, Bytes)->Void, ?directory:String = "images") {
|
||||
super(x, y, w, h, "png, jpg", function (filePath, file) {
|
||||
if (filePath != null && file != null) uploadImage(filePath, file);
|
||||
@@ -51,7 +54,7 @@ class UIImageExplorer extends UIFileExplorer {
|
||||
members.push(directoryIcon);
|
||||
|
||||
directoryBG = new UISliceSprite(0, 0, 210+16, 8+12+4+22+8, 'editors/ui/inputbox');
|
||||
directoryBG.alpha = 0.9; directoryBG.members.push(directoryTextBoxLabel = new UIText(8, 6, 0, 'Directory ($directory/)', 12));
|
||||
directoryBG.alpha = 0.9; directoryBG.members.push(directoryTextBoxLabel = new UIText(8, 6, 0, TU.getRaw('uiImageExplorer.directory').format([directory]), 12));
|
||||
|
||||
directoryTextBox = new UITextBox(8, 8+12+4, "", 210, 22, false, true);
|
||||
directoryBG.members.push(directoryTextBox);
|
||||
@@ -245,17 +248,18 @@ class UIImageExplorer extends UIFileExplorer {
|
||||
message.add('${imagePath.file}.${imagePath.ext}');
|
||||
message.add(' (${CoolUtil.getSizeString(size)}');
|
||||
|
||||
var typeOfFrame = isAtlas ? translate("symbol") : translate("animation");
|
||||
var shouldUsePlural = animationList.length != 1;
|
||||
if (animationList.length > 0) {
|
||||
if(animationList.length == 1) message.add(', ${animationList.length} ${isAtlas ? "Symbol" : "Animation"} Found');
|
||||
else message.add(', ${animationList.length} ${isAtlas ? "Symbol" : "Animation"}s Found');
|
||||
} else message.add(', #NO ${isAtlas ? "Symbol" : "Animation"}s Found#');
|
||||
message.add(', ${TU.getRaw("uiImageExplorer." + (shouldUsePlural ? "foundplural" : "found")).format([animationList.length, typeOfFrame])}');
|
||||
} else message.add(', ${TU.getRaw("uiImageExplorer.notFound").format([typeOfFrame])}');
|
||||
|
||||
if (isAtlas) {
|
||||
if(spritemaps.length == 1) message.add(', ${spritemaps.length} Spritemap Found');
|
||||
else message.add(', ${spritemaps.length} Spritemaps Found');
|
||||
shouldUsePlural = spritemaps.length != 1;
|
||||
message.add(', ${TU.getRaw("uiImageExplorer.foundAtlas" + (shouldUsePlural ? "plural" : "")).format([spritemaps.length])}');
|
||||
}
|
||||
message.add(')');
|
||||
message.add(isAtlas ? " - Atlas" : " - Spritemap");
|
||||
message.add(isAtlas ? ' - ${translate("atlas")}' : ' - ${translate("spritemap")}');
|
||||
|
||||
fileText = new UIText(x+20, y+16, bWidth-20-deleteButton.bWidth-16, "");
|
||||
fileText.applyMarkup(message.toString(), [new FlxTextFormatMarkerPair(new FlxTextFormat(0xFFAD1212), "#")]);
|
||||
@@ -337,10 +341,9 @@ class UIImageExplorer extends UIFileExplorer {
|
||||
}
|
||||
|
||||
if (alreadlyExistingFiles.length > 0 && checkExisting) @:privateAccess {
|
||||
(FlxG.state.subState != null && !FlxG.state._requestSubStateReset ? FlxG.state.subState : FlxG.state).openSubState(new UIWarningSubstate("Alreadly Existing Files!!!",
|
||||
"The following files alreadly exist: \n\n" + alreadlyExistingFiles.join('\n') + "\n\nIMPORTANT: OVERRIDING CAN NOT BE UNDONE!!!!!!!!",
|
||||
[ {
|
||||
label: "Override Files",
|
||||
var buttons = [
|
||||
{
|
||||
label: TU.translate("uiImageExplorer.override"),
|
||||
color: 0xFFFF0000,
|
||||
onClick: (_) -> {
|
||||
deleteExistingFiles();
|
||||
@@ -348,10 +351,12 @@ class UIImageExplorer extends UIFileExplorer {
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "Use Existing",
|
||||
label: TU.translate("uiImageExplorer.useExisting"),
|
||||
onClick: (_) -> {if (onFinishSaving != null) onFinishSaving();}
|
||||
}
|
||||
], false));
|
||||
];
|
||||
(FlxG.state.subState != null && !FlxG.state._requestSubStateReset ? FlxG.state.subState : FlxG.state).openSubState(new UIWarningSubstate("Alreadly Existing Files!!!",
|
||||
TU.getRaw("uiImageExplorer.fileAlreadyExists").format([alreadlyExistingFiles.join('\n')]), buttons, false));
|
||||
} else acuttalySaveFiles();
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import flixel.system.scaleModes.StageSizeScaleMode;
|
||||
|
||||
class UIScaleMode extends StageSizeScaleMode {
|
||||
override public function onMeasure(Width:Int, Height:Int):Void {
|
||||
if (Width < FlxG.initialWidth || Height < FlxG.initialHeight) {
|
||||
if ((Width < FlxG.initialWidth || Height < FlxG.initialHeight) && !Options.bypassEditorsResize) {
|
||||
@:privateAccess {
|
||||
FlxG.width = FlxG.initialWidth;
|
||||
FlxG.height = FlxG.initialHeight;
|
||||
|
||||
@@ -283,7 +283,7 @@ class UISoftcodedWindow extends UISubstateWindow {
|
||||
addLayout(getArr(layout.elements));
|
||||
|
||||
if(get("hasSaveButton")) {
|
||||
saveButton = new UIButton(windowSpr.x + windowSpr.bWidth - 20, windowSpr.y + windowSpr.bHeight - 20, "Save & Close", function() {
|
||||
saveButton = new UIButton(windowSpr.x + windowSpr.bWidth - 20, windowSpr.y + windowSpr.bHeight - 20, TU.translate("editor.saveClose"), function() {
|
||||
saveData();
|
||||
close();
|
||||
}, 125);
|
||||
@@ -295,7 +295,7 @@ class UISoftcodedWindow extends UISubstateWindow {
|
||||
if(get("hasCloseButton")) {
|
||||
var x = saveButton != null ? saveButton.x - 20 : windowSpr.x + windowSpr.bWidth - 20;
|
||||
var y = saveButton != null ? saveButton.y : windowSpr.y + windowSpr.bHeight - 20;
|
||||
closeButton = new UIButton(x, y, "Close", function() {
|
||||
closeButton = new UIButton(x, y, TU.translate("editor.close"), function() {
|
||||
close();
|
||||
}, 125);
|
||||
if(saveButton != null)
|
||||
|
||||
@@ -105,6 +105,10 @@ class UIState extends MusicBeatState {
|
||||
buttonHandler = null;
|
||||
}
|
||||
|
||||
if (FlxG.mouse.justPressed) {
|
||||
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_CLICK_SOUND));
|
||||
}
|
||||
|
||||
if (FlxG.mouse.justReleased)
|
||||
currentFocus = (hoveredSprite is IUIFocusable) ? (cast hoveredSprite) : null;
|
||||
|
||||
@@ -143,6 +147,7 @@ class UIState extends MusicBeatState {
|
||||
}
|
||||
|
||||
public function closeCurrentContextMenu() {
|
||||
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_WINDOWCLOSE_SOUND));
|
||||
if(curContextMenu != null) {
|
||||
curContextMenu.close();
|
||||
curContextMenu = null;
|
||||
@@ -150,6 +155,7 @@ class UIState extends MusicBeatState {
|
||||
}
|
||||
|
||||
public function openContextMenu(options:Array<UIContextMenuOption>, ?callback:UIContextMenuCallback, ?x:Float, ?y:Float, ?w:Int) {
|
||||
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_WINDOWAPPEAR_SOUND));
|
||||
var state = FlxG.state;
|
||||
while(state.subState != null && !(state._requestSubStateReset && state._requestedSubState == null))
|
||||
state = state.subState;
|
||||
|
||||
@@ -86,6 +86,7 @@ class UISubstateWindow extends MusicBeatSubstate {
|
||||
}
|
||||
|
||||
public override function destroy() {
|
||||
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_WINDOWCLOSE_SOUND));
|
||||
super.destroy();
|
||||
for(e in camShaders)
|
||||
e.removeShader(blurShader);
|
||||
|
||||
@@ -110,6 +110,7 @@ class UITextBox extends UISliceSprite implements IUIFocusable {
|
||||
case RIGHT:
|
||||
changeSelection(1);
|
||||
case BACKSPACE:
|
||||
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_TEXTREMOVE_SOUND));
|
||||
if (position > 0) {
|
||||
label.text = label.text.substr(0, position-1) + label.text.substr(position);
|
||||
changeSelection(-1);
|
||||
@@ -119,6 +120,7 @@ class UITextBox extends UISliceSprite implements IUIFocusable {
|
||||
case END:
|
||||
position = label.text.length;
|
||||
case V:
|
||||
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_TEXTTYPE_SOUND));
|
||||
// Hey lj here, fixed copying because before we checked if the modifier was left or right ctrl
|
||||
// but somehow it gave a int outside of the KeyModifier's range :sob:
|
||||
// apparently there is a boolean that just checks for you. yw :D
|
||||
@@ -129,13 +131,14 @@ class UITextBox extends UISliceSprite implements IUIFocusable {
|
||||
var data:String = Clipboard.generalClipboard.getData(TEXT_FORMAT);
|
||||
if (data != null) onTextInput(data);
|
||||
case C:
|
||||
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_TEXTTYPE_SOUND));
|
||||
// if we are not holding ctrl, ignore
|
||||
if (!modifier.ctrlKey) return;
|
||||
|
||||
// copying
|
||||
Clipboard.generalClipboard.setData(TEXT_FORMAT, label.text);
|
||||
default:
|
||||
// nothing
|
||||
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_TEXTTYPE_SOUND));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,8 @@ class UIWarningSubstate extends MusicBeatSubstate {
|
||||
_;
|
||||
};
|
||||
|
||||
public var bHeight:Int = 232;
|
||||
|
||||
var title:String;
|
||||
var message:String;
|
||||
var buttons:Array<WarningButton>;
|
||||
@@ -70,14 +72,14 @@ class UIWarningSubstate extends MusicBeatSubstate {
|
||||
FlxG.cameras.add(warnCam, false);
|
||||
|
||||
|
||||
var spr = new UISliceSprite(0, 0, CoolUtil.maxInt(560, 30 + (170 * buttons.length)), 232, 'editors/ui/${isError ? "normal" : "grayscale"}-popup');
|
||||
var spr = new UISliceSprite(0, 0, CoolUtil.maxInt(560, 30 + (170 * buttons.length)), bHeight, 'editors/ui/${isError ? "normal" : "grayscale"}-popup');
|
||||
|
||||
var sprIcon:FlxSprite = new FlxSprite(spr.x + 18, spr.y + 28 + 26).loadGraphic(Paths.image('editors/warnings/${isError ? "error" : "warning"}'));
|
||||
sprIcon.scale.set(1.4, 1.4);
|
||||
sprIcon.updateHitbox();
|
||||
|
||||
messageSpr = new UIText(0,0, spr.bWidth - 100 - (26 * 2), message);
|
||||
spr.bHeight = Std.int(232 + Math.abs(Math.min(sprIcon.height-messageSpr.height, 0)));
|
||||
spr.bHeight = Std.int(bHeight + Math.abs(Math.min(sprIcon.height-messageSpr.height, 0)));
|
||||
|
||||
spr.x = (FlxG.width - spr.bWidth) / 2;
|
||||
spr.y = (FlxG.height - spr.bHeight) / 2;
|
||||
@@ -96,7 +98,7 @@ class UIWarningSubstate extends MusicBeatSubstate {
|
||||
add(sprIcon);
|
||||
|
||||
messageSpr.x = sprIcon.x + 70 + 16 + 20;
|
||||
messageSpr.y = sprIcon.y + 16;
|
||||
messageSpr.y = sprIcon.y;
|
||||
add(messageSpr);
|
||||
|
||||
var xPos = (FlxG.width - (30 + (170 * buttons.length))) / 2;
|
||||
|
||||
@@ -15,6 +15,7 @@ class UIWindow extends UISliceSprite {
|
||||
|
||||
content = new FlxTypedGroup<FlxBasic>();
|
||||
members.push(content);
|
||||
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_WINDOWAPPEAR_SOUND));
|
||||
}
|
||||
|
||||
public override function update(elapsed:Float) {
|
||||
|
||||
Reference in New Issue
Block a user