Initial Stage Editor Commit

Co-Authored-By: ⍚~Nex <87421482+NexIsDumb@users.noreply.github.com>
This commit is contained in:
NeeEoo
2024-04-13 17:22:44 +02:00
co-authored by ⍚~Nex
parent 59164cbb1d
commit 008cfd0748
14 changed files with 1295 additions and 47 deletions
@@ -0,0 +1,34 @@
package funkin.backend.system;
import flixel.graphics.FlxGraphic;
import flixel.graphics.frames.FlxFrame;
import flixel.graphics.tile.FlxDrawTrianglesItem;
import flixel.math.FlxMatrix;
import flixel.math.FlxPoint;
import flixel.system.FlxAssets.FlxShader;
import openfl.display.BitmapData;
import openfl.display.BlendMode;
import openfl.geom.ColorTransform;
import openfl.geom.Point;
import openfl.geom.Rectangle;
class NothingCamera extends FlxCamera {
public static final instance = new NothingCamera();
public function new() {
super();
visible = false;
}
override function startTrianglesBatch(graphic:FlxGraphic, smoothing:Bool = false, isColored:Bool = false, ?blend:BlendMode, ?hasColorOffsets:Bool, ?shader:FlxShader) { return null;}
override function startQuadBatch(graphic:FlxGraphic, colored:Bool, hasColorOffsets:Bool = false, ?blend:BlendMode, smooth:Bool = false, ?shader:FlxShader) { return null;}
override function clearDrawStack() {}
override function render() {}
public override function drawPixels(?frame:FlxFrame, ?pixels:BitmapData, matrix:FlxMatrix, ?transform:ColorTransform, ?blend:BlendMode, ?smoothing:Bool = false, ?shader:FlxShader) {}
public override function copyPixels(?frame:FlxFrame, ?pixels:BitmapData, ?sourceRect:Rectangle, destPoint:Point, ?transform:ColorTransform, ?blend:BlendMode, ?smoothing:Bool = false, ?shader:FlxShader) {}
public override function drawTriangles(graphic:FlxGraphic, vertices:DrawData<Float>, indices:DrawData<Int>, uvtData:DrawData<Float>, ?colors:DrawData<Int>, ?position:FlxPoint, ?blend:BlendMode, repeat:Bool = false, smoothing:Bool = false, ?transform:ColorTransform, ?shader:FlxShader):Void {}
public override function update(elapsed:Float) {}
}
+33
View File
@@ -0,0 +1,33 @@
package funkin.backend.utils;
import flixel.FlxCamera;
import flixel.math.FlxPoint;
import flixel.util.typeLimit.OneOfTwo;
class MatrixUtil {
public static function getMatrixPosition(sprite:FlxSprite, points:OneOfTwo<FlxPoint, Array<FlxPoint>>, ?camera:FlxCamera, _width:Float = 1, _height:Float = 1):Array<FlxPoint>
{
//if(_width == -1) _width = sprite.width;
//if(_height == -1) _height = sprite.height;
if(camera == null) camera = sprite.camera;
if(points is FlxBasePoint) points = [points];
var nc = funkin.backend.system.NothingCamera.instance;
nc.scroll.set(camera.scroll.x, camera.scroll.y);
//nc.zoom = camera.zoom; // might not be needed
nc.pixelPerfectRender = camera.pixelPerfectRender;
@:privateAccess sprite.drawComplex(nc);
var points:Array<FlxPoint> = cast points;
@:privateAccess for(point in points) {
var x = sprite._matrix.__transformX(point.x * _width, point.y * _height);
var y = sprite._matrix.__transformY(point.x * _width, point.y * _height);
// reset to ingame coords
x += camera.scroll.x;
y += camera.scroll.y;
point.set(x, y);
}
return points;
}
}
+1 -1
View File
@@ -20,7 +20,7 @@ class EditorPicker extends MusicBeatSubstate {
{
name: "Stage Editor",
iconID: 2,
state: null
state: funkin.editors.stager.StageSelection
},
{
name: "UI Debug State",
+648
View File
@@ -0,0 +1,648 @@
package funkin.editors.stager;
import flixel.math.FlxAngle;
import flixel.util.FlxColor;
import flixel.math.FlxPoint;
import funkin.backend.system.framerate.Framerate;
import funkin.backend.utils.XMLUtil.AnimData;
import funkin.editors.stager.elements.*;
import funkin.editors.ui.UIContextMenu.UIContextMenuOption;
import funkin.game.Character;
import funkin.game.Stage;
import haxe.xml.Access;
import haxe.xml.Printer;
using funkin.backend.utils.MatrixUtil;
class StageEditor extends UIState {
static var __stage:String;
public var stage:Stage;
public static var instance(get, null):StageEditor;
private static inline function get_instance()
return FlxG.state is StageEditor ? cast FlxG.state : null;
public var topMenu:Array<UIContextMenuOption>;
public var topMenuSpr:UITopMenu;
public var uiGroup:FlxTypedGroup<FlxSprite> = new FlxTypedGroup<FlxSprite>();
public var stageSpritesWindow:UIButtonList<StageElementButton>;
public var xmlMap:Map<FlxBasic, Access> = new Map<FlxBasic, Access>();
public var chars:Array<Character> = [];
public var stageCamera:FlxCamera;
public var guideCamera:FlxCamera;
public var uiCamera:FlxCamera;
public var undos:UndoList<StageChange> = new UndoList<StageChange>();
public inline static function exID(id:String) {
return "stageEditor." + id;
}
public function new(stage:String) {
super();
if (stage != null) __stage = stage;
}
public override function create() {
super.create();
WindowUtils.suffix = " (Stage Editor)";
SaveWarning.selectionClass = StageSelection;
SaveWarning.saveFunc = () -> {_file_save(null);};
topMenu = [
{
label: "File",
childs: [
{
label: "New",
onSelect: _file_new,
},
null,
{
label: "Save",
keybind: [CONTROL, S],
onSelect: _file_save,
},
{
label: "Save As...",
keybind: [CONTROL, SHIFT, S],
onSelect: _file_saveas,
},
null,
{
label: "Exit",
onSelect: _file_exit
}
]
},
/*{
label: "Edit",
childs: [
{
label: "Undo",
keybind: [CONTROL, Z],
onSelect: _edit_undo
},
{
label: "Redo",
keybinds: [[CONTROL, Y], [CONTROL, SHIFT, Z]],
onSelect: _edit_redo
}
]
},*/
/*{
label: "Character",
childs: [
{
label: "New Animation",
keybind: [CONTROL, N],
onSelect: _char_add_anim,
},
{
label: "Edit Animation",
onSelect: _char_edit_anim,
},
{
label: "Delete Animation",
keybind: [DELETE],
onSelect: _char_remove_anim,
},
null,
{
label: "Edit Info",
onSelect: _char_edit_info,
}
]
},
{
label: "Playback",
childs: [
{
label: "Play Animation",
keybind: [SPACE],
onSelect: _playback_play_anim,
},
{
label: "Stop Animation",
onSelect: _playback_stop_anim
}
]
},
{
label: "Offsets",
childs: [
{
label: "Move Left",
keybind: [LEFT],
onSelect: _offsets_left,
},
{
label: "Move Up",
keybind: [UP],
onSelect: _offsets_up,
},
{
label: "Move Down",
keybind: [DOWN],
onSelect: _offsets_down,
},
{
label: "Move Right",
keybind: [RIGHT],
onSelect: _offsets_right,
},
null,
{
label: "Move Extra Left",
keybind: [SHIFT, LEFT],
onSelect: _offsets_extra_left,
},
{
label: "Move Extra Up",
keybind: [SHIFT, UP],
onSelect: _offsets_extra_up,
},
{
label: "Move Extra Down",
keybind: [SHIFT, DOWN],
onSelect: _offsets_extra_down,
},
{
label: "Move Extra Right",
keybind: [SHIFT, RIGHT],
onSelect: _offsets_extra_right,
},
null,
{
label: "Clear Offsets",
keybind: [CONTROL, R],
onSelect: _offsets_clear,
}
]
},*/
{
label: "View",
childs: [
{
label: "Zoom in",
keybind: [CONTROL, NUMPADPLUS],
onSelect: _view_zoomin
},
{
label: "Zoom out",
keybind: [CONTROL, NUMPADMINUS],
onSelect: _view_zoomout
},
{
label: "Reset zoom",
keybind: [CONTROL, NUMPADZERO],
onSelect: _view_zoomreset
},
]
},
];
stageCamera = FlxG.camera;
guideCamera = new FlxCamera();
guideCamera.bgColor = 0;
uiCamera = new FlxCamera();
uiCamera.bgColor = 0;
FlxG.cameras.add(guideCamera, false);
FlxG.cameras.add(uiCamera, false);
// Load from xml
var order:Array<Dynamic> = [];
stage = new Stage(__stage, this, false);
stage.onXMLLoaded = function(xml:Access, elems:Array<Access>) {
return elems;
}
stage.onNodeLoaded = function(node:Access, sprite:Dynamic):Dynamic {
if(sprite is FunkinSprite) {
sprite.forceIsOnScreen = true; // hack
sprite.extra.set(exID("type"), node.name);
//sprite.active = false;
}
if(sprite is StageCharPos) {
var charPos:StageCharPos = cast sprite;
var charName = switch(charPos.name) {
case "boyfriend": "bf";
case "girlfriend": "gf";
default: charPos.name;
}
var char = new Character(0,0, charName, stage.isCharFlipped(charPos.name, charName == "bf"));
char.debugMode = true;
// Play first anim, and make it the last frame
var animToPlay = char.getAnimOrder()[0];
char.playAnim(animToPlay, true, NONE);
var lastIndx = (char.animateAtlas != null) ?
char.animateAtlas.anim.length - 1 :
char.animation.curAnim.numFrames - 1;
char.playAnim(animToPlay, true, NONE, false, lastIndx);
char.stopAnimation();
// Add it to the stage
char.visible = true;
char.alpha = 0.5;
char.extra.set(exID("node"), node);
char.extra.set(exID("pos"), charPos);
charPos.extra.set(exID("char"), char);
chars.push(char);
}
order.push(sprite);
xmlMap.set(sprite, node);
return sprite;
}
stage.loadXml(stage.stageXML, true);
add(stage);
for(char in chars) {
var charPos = char.extra.get(exID("pos"));
var node = char.extra.get(exID("node"));
stage.applyCharStuff(char, charPos.name, 0);
}
zoom = calculateZoom(stage.defaultZoom);
topMenuSpr = new UITopMenu(topMenu);
topMenuSpr.cameras = uiGroup.cameras = [uiCamera];
final margin = 22;
final width = 470;
final TOP_MENU_HEIGHT = 25;
var buttonSize:FlxPoint = FlxPoint.get(width-margin*2, 32);
stageSpritesWindow = new UIButtonList<StageElementButton>(Std.int(FlxG.width - width), TOP_MENU_HEIGHT, width, Std.int(FlxG.height - TOP_MENU_HEIGHT), "Stage Sprites", buttonSize);
stageSpritesWindow.collapsable = true;
stageSpritesWindow.addButton.callback = () -> {
// TODO: implement this
}
for (i=>sprite in order) {
trace(sprite, Type.getClassName(Type.getClass(sprite)));
var xml = xmlMap.get(sprite);
if(xml != null) {
if(sprite is FunkinSprite) {
var type = sprite.extra.get(exID("type"));
stageSpritesWindow.add((type == "box" || type == "solid") ? new StageSolidButton(0,0, sprite, xml) : new StageSpriteButton(0,0, sprite, xml));
}
else if(sprite is StageCharPos) {
stageSpritesWindow.add(new StageCharacterButton(0,0, sprite, xml));
}
}
}
uiGroup.add(stageSpritesWindow);
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", ["Stage Editor", __stage]);
}
override function destroy() {
super.destroy();
nextScroll = FlxDestroyUtil.destroy(nextScroll);
if(Framerate.isLoaded) {
Framerate.fpsCounter.alpha = 1;
Framerate.memoryCounter.alpha = 1;
Framerate.codenameBuildField.alpha = 1;
}
}
//private var movingCam:Bool = false;
//private var camDragSpeed:Float = 1.2;
private var nextScroll:FlxPoint = FlxPoint.get(0,0);
public override function update(elapsed:Float) {
super.update(elapsed);
if (true) {
if(FlxG.keys.justPressed.ANY)
UIUtil.processShortcuts(topMenu);
}
/*if (character != null)
characterPropertiresWindow.characterInfo.text = '${character.getNameList().length} Animations\nFlipped: ${character.flipX}\nSprite: ${character.sprite}\nAnim: ${character.getAnimName()}\nOffset: (${character.frameOffset.x}, ${character.frameOffset.y})';
if (!(characterPropertiresWindow.hovered || characterAnimsWindow.hovered) && !characterAnimsWindow.dragging) {
if (FlxG.mouse.wheel != 0) {
zoom += 0.25 * FlxG.mouse.wheel;
__camZoom = Math.pow(2, zoom);
}
if (FlxG.mouse.justReleasedRight) {
closeCurrentContextMenu();
openContextMenu(topMenu[2].childs);
}
if (FlxG.mouse.pressed) {
nextScroll.set(nextScroll.x - FlxG.mouse.deltaScreenX, nextScroll.y - FlxG.mouse.deltaScreenY);
currentCursor = HAND;
} else
currentCursor = ARROW;
} else if (!FlxG.mouse.pressed)
currentCursor = ARROW;
characterBG.scale.set(FlxG.width/characterBG.width, FlxG.height/characterBG.height);
characterBG.scale.set(characterBG.scale.x / charCamera.zoom, characterBG.scale.y / charCamera.zoom);*/
if (true/*!(characterPropertiresWindow.hovered || characterAnimsWindow.hovered) && !characterAnimsWindow.dragging*/) {
if (FlxG.mouse.wheel != 0) {
zoom += 0.25 * FlxG.mouse.wheel;
updateZoom();
}
//if (FlxG.mouse.justReleasedRight) {
// closeCurrentContextMenu();
// openContextMenu(topMenu[2].childs);
//}
if (FlxG.mouse.pressed) {
nextScroll.set(nextScroll.x - FlxG.mouse.deltaScreenX, nextScroll.y - FlxG.mouse.deltaScreenY);
currentCursor = HAND;
} else
currentCursor = ARROW;
} else if (!FlxG.mouse.pressed)
currentCursor = ARROW;
stageCamera.scroll.set(
lerp(stageCamera.scroll.x, nextScroll.x, 0.35),
lerp(stageCamera.scroll.y, nextScroll.y, 0.35)
);
stageCamera.zoom = lerp(stageCamera.zoom, __camZoom, 0.125);
WindowUtils.prefix = undos.unsaved ? "* " : "";
SaveWarning.showWarning = undos.unsaved;
}
// TOP MENU OPTIONS
#if REGION
function _file_exit(_) {
if (undos.unsaved) SaveWarning.triggerWarning();
else FlxG.switchState(new StageSelection());
}
function _file_new(_) {
}
function _file_save(_) {
#if sys
CoolUtil.safeSaveFile(
'${Paths.getAssetsRoot()}/data/stages/${stage.stageName}.xml',
buildStage()
);
undos.save();
#else
_file_saveas(_);
#end
}
function _file_saveas(_) {
openSubState(new SaveSubstate(buildStage(), {
defaultSaveFile: '${stage.stageName}.xml'
}));
undos.save();
}
function buildStage():String {
/*if (character.isPlayer != character.playerOffsets) {
character.switchOffset('singLEFT', 'singRIGHT');
character.switchOffset('singLEFTmiss', 'singRIGHTmiss');
}
var charXML:Xml = character.buildXML([
for (button in characterAnimsWindow.buttons.members)
button.anim
]);
// clean
if (charXML.exists("gameOverChar") && character.gameOverCharacter == "bf-dead") charXML.remove("gameOverChar");
if (charXML.exists("camx") && character.cameraOffset.x == 0) charXML.remove("camx");
if (charXML.exists("camy") && character.cameraOffset.y == 0) charXML.remove("camy");
if (charXML.exists("holdTime") && character.holdTime == 4) charXML.remove("holdTime");
if (charXML.exists("flipX") && !character.flipX) charXML.remove("flipX");
if (charXML.exists("scale") && character.scale.x == 1) charXML.remove("scale");
if (charXML.exists("antialiasing") && character.antialiasing) charXML.remove("antialiasing");*/
return "<!DOCTYPE codename-engine-character>\n";// + Printer.print(charXML, true);
}
/*function _edit_undo(_) {
var undo = undos.undo();
switch(undo) {
case null:
// do nothing
case CEditInfo(oldInfo, newInfo):
editInfo(oldInfo, false);
case CCreateAnim(animID, animData):
deleteAnim(animData.name, false);
case CEditAnim(name, oldData, animData):
editAnim(name, oldData, false);
case CDeleteAnim(animID, animData):
createAnim(animData, animID, false);
case CChangeOffset(name, change):
changeOffset(name, change * -1, false);
case CResetOffsets(oldOffsets):
for (anim => offsets in oldOffsets) {
character.animOffsets.set(anim, offsets.clone());
ghosts.setOffsets(anim, offsets.clone());
}
for (charButton in characterAnimsWindow.buttons.members)
charButton.updateInfo(charButton.anim, character.getAnimOffset(charButton.anim), ghosts.animGhosts[charButton.anim].visible);
changeOffset(character.getAnimName(), FlxPoint.get(0, 0), false); // apply da new offsets
}
}
function _edit_redo(_) {
var redo = undos.redo();
switch(redo) {
case null:
// do nothing
case CEditInfo(oldInfo, newInfo):
editInfo(newInfo, false);
case CCreateAnim(animID, animData):
createAnim(animData, animID, false);
case CEditAnim(name, oldData, animData):
editAnim(oldData.name, animData, false);
case CDeleteAnim(animID, animData):
deleteAnim(animData.name, false);
case CChangeOffset(name, change):
changeOffset(name, change, false);
case CResetOffsets(oldOffsets):
clearOffsets(false);
}
}*/
public function editInfoWithUI() {
FlxG.state.openSubState(new StageInfoScreen(stage, (_) -> {
if (_ != null) editInfo(_);
}));
}
public function editInfo(newInfo:Xml, addtoUndo:Bool = true) {
/*var oldInfo = stage.buildXML();
stage.applyXML(new Access(newInfo));
if (addtoUndo)
undos.addToUndo(CEditInfo(oldInfo, newInfo));*/
}
var zoom(default, set):Float = 0;
var __camZoom(default, set):Float = 1;
function set_zoom(val:Float) {
return zoom = FlxMath.bound(val, -3.5, 1.75); // makes zooming not lag behind when continuing scrolling
}
function set___camZoom(val:Float) {
return __camZoom = FlxMath.bound(val, 0.1, 3);
}
function _view_zoomin(_) {
zoom += 0.25;
updateZoom();
}
function _view_zoomout(_) {
zoom -= 0.25;
updateZoom();
}
function _view_zoomreset(_) {
zoom = 0;
updateZoom();
}
inline function updateZoom() {
__camZoom = Math.pow(2, zoom);
}
inline function calculateZoom(zoom:Float) {
return Math.log(zoom) / Math.log(2);
}
#end
override function draw() {
super.draw();
if(dot == null) {
dot = new FlxSprite().makeGraphic(10, 10, FlxColor.RED);
dot.camera = stageCamera;
dot.forceIsOnScreen = true;
}
//guideCamera.zoom = stageCamera.zoom;
//dot.scrollFactor.set(0, 0);
/*for(member in members) {
if(member is FlxSprite) {
if(camera == null || camera == stageCamera) {
var sprite:FlxSprite = cast member;
@:privateAccess if(sprite._frame == null) continue;
if(sprite.visible) {
//trace("Drawing Guides for " + sprite);
drawGuides(sprite);
}
}
}
}*/
for(sprite in stage.stageSprites) {
if(sprite.visible) {
@:privateAccess if(sprite._frame == null) continue;
drawGuides(sprite);
}
}
for(char in chars) {
if(char.visible) {
@:privateAccess if(char._frame == null) continue;
drawGuides(char);
}
}
}
function drawGuides(sprite:FlxSprite) {
var oldWidth = sprite.width;
var oldHeight = sprite.height;
var oldOffset = sprite.offset.clone(FlxPoint.weak());
var oldOrigin = sprite.origin.clone(FlxPoint.weak());
sprite.updateHitbox();
sprite.offset.copyFrom(oldOffset);
sprite.origin.copyFrom(oldOrigin);
var corners = sprite.getMatrixPosition([
// corners
FlxPoint.get(0, 0),
FlxPoint.get(1, 0),
FlxPoint.get(0, 1),
FlxPoint.get(1, 1),
// edges
FlxPoint.get(0, 0.5),
FlxPoint.get(1, 0.5),
FlxPoint.get(0.5, 1),
FlxPoint.get(0.5, 0),
// center
//FlxPoint.get(0.5, 0.5)
], sprite.camera, sprite.width, sprite.height);
//Logs.trace("Guide at " + corners[0].x + ", " + corners[0].y + " sprite at " + sprite.x + ", " + sprite.y);
for(corner in corners) {
drawDot(corner.x, corner.y);
corner.put();
}
drawLine(corners[0], corners[1]); // tl - tr
drawLine(corners[0], corners[2]); // tl - bl
drawLine(corners[1], corners[3]); // tr - br
drawLine(corners[2], corners[3]); // bl - br
// reset hitbox to old values
sprite.width = oldWidth;
sprite.height = oldHeight;
}
inline function drawDot(x:Float, y:Float) {
dot.setPosition(x, y);
dot.scale.set(0.7/stageCamera.zoom, 0.7/stageCamera.zoom);
dot.x -= dot.width / 2;
dot.y -= dot.height / 2;
dot.draw();
}
function drawLine(point1:FlxPoint, point2:FlxPoint) {
var dx:Float = point2.x - point1.x;
var dy:Float = point2.y - point1.y;
var angle:Float = Math.atan2(dy, dx);
var distance:Float = Math.sqrt(dx * dx + dy * dy);
dot.setPosition(point1.x, point1.y);
dot.angle = angle * FlxAngle.TO_DEG;
dot.origin.set(0, dot.frameHeight / 2);
dot.scale.x = distance / dot.frameWidth;
dot.scale.y = 0.20/stageCamera.zoom;
//dot.x -= dot.width / 2;
dot.y -= dot.height / 2;
dot.draw();
// Reset the angle and scale
dot.angle = 0;
dot.scale.x = dot.scale.y = 1;
dot.updateHitbox();
}
var dot:FlxSprite = null;
}
enum StageChange {
CEditInfo(oldInfo:Xml, newInfo:Xml);
}
@@ -0,0 +1,169 @@
package funkin.editors.stager;
import flixel.math.FlxPoint;
import funkin.editors.extra.PropertyButton;
import funkin.game.Stage;
import haxe.xml.Access;
class StageInfoScreen extends UISubstateWindow {
public var stage:Stage;
public var saveButton:UIButton;
public var closeButton:UIButton;
public var onSave:(xml:Xml) -> Void = null;
public function new(stage:Stage, ?onSave:(xml:Xml) -> Void) {
this.stage = stage;
this.onSave = onSave;
super();
}
public override function create() {
winTitle = "Editing Stage";
winWidth = 1014;
winHeight = 600;
super.create();
function addLabelOn(ui:UISprite, text:String)
add(new UIText(ui.x, ui.y - 24, 0, text));
/*var title:UIText;
add(title = new UIText(windowSpr.x + 20, windowSpr.y + 30 + 16, 0, "Sprite Data", 28));
spriteTextBox = new UITextBox(title.x, title.y + title.height + 38, character.sprite, 200);
add(spriteTextBox);
addLabelOn(spriteTextBox, "Sprite");
iconTextBox = new UITextBox(spriteTextBox.x + 200 + 26, spriteTextBox.y, character.getIcon(), 150);
iconTextBox.onChange = (newIcon:String) -> {updateIcon(newIcon);}
add(iconTextBox);
addLabelOn(iconTextBox, "Icon");
updateIcon(character.getIcon());
gameOverCharTextBox = new UITextBox(iconTextBox.x + 150 + (75 + 12), iconTextBox.y, character.gameOverCharacter, 200);
add(gameOverCharTextBox);
addLabelOn(gameOverCharTextBox, "Game Over Character");
antialiasingCheckbox = new UICheckbox(spriteTextBox.x, spriteTextBox.y + 10 + 32 + 28, "Antialiasing", character.antialiasing);
add(antialiasingCheckbox);
addLabelOn(antialiasingCheckbox, "Antialiased");
flipXCheckbox = new UICheckbox(antialiasingCheckbox.x + 172, spriteTextBox.y + 10 + 32 + 28, "FlipX", character.flipX);
add(flipXCheckbox);
addLabelOn(flipXCheckbox, "Flipped");
iconColorWheel = new UIColorwheel(gameOverCharTextBox.x + 200 + 20, gameOverCharTextBox.y, character.iconColor);
add(iconColorWheel);
addLabelOn(iconColorWheel, "Icon Color");
add(title = new UIText(spriteTextBox.x, spriteTextBox.y + 10 + 46 + 84, 0, "Character Data", 28));
positionXStepper = new UINumericStepper(title.x, title.y + title.height + 36, character.globalOffset.x, 0.001, 2, null, null, 84);
add(positionXStepper);
addLabelOn(positionXStepper, "Position (X,Y)");
add(new UIText(positionXStepper.x + 84 - 32 + 0, positionXStepper.y + 9, 0, ",", 22));
positionYStepper = new UINumericStepper(positionXStepper.x + 84 - 32 + 26, positionXStepper.y, character.globalOffset.y, 0.001, 2, null, null, 84);
add(positionYStepper);
cameraXStepper = new UINumericStepper(positionYStepper.x + 36 + 84 - 32, positionYStepper.y, character.cameraOffset.x, 0.001, 2, null, null, 84);
add(cameraXStepper);
addLabelOn(cameraXStepper, "Camera Position (X,Y)");
add(new UIText(cameraXStepper.x + 84 - 32 + 0, cameraXStepper.y + 9, 0, ",", 22));
cameraYStepper = new UINumericStepper(cameraXStepper.x + 84 - 32 + 26, cameraXStepper.y, character.cameraOffset.y, 0.001, 2, null, null, 84);
add(cameraYStepper);
scaleStepper = new UINumericStepper(cameraYStepper.x + 84 - 32 + 90, cameraYStepper.y, character.scale.x, 0.001, 2, null, null, 74);
add(scaleStepper);
addLabelOn(scaleStepper, "Scale");
singTimeStepper = new UINumericStepper(scaleStepper.x + 74 - 32 + 36, scaleStepper.y, character.holdTime, 0.001, 2, null, null, 74);
add(singTimeStepper);
addLabelOn(singTimeStepper, "Sing Duration (Steps)");
customPropertiesButtonList = new UIButtonList<PropertyButton>(singTimeStepper.x + singTimeStepper.width + 200, singTimeStepper.y, 290, 200, '', FlxPoint.get(280, 35), null, 5);
customPropertiesButtonList.frames = Paths.getFrames('editors/ui/inputbox');
customPropertiesButtonList.cameraSpacing = 0;
customPropertiesButtonList.addButton.callback = function() {
customPropertiesButtonList.add(new PropertyButton("newProperty", "valueHere", customPropertiesButtonList));
}
for (prop=>val in character.extra)
customPropertiesButtonList.add(new PropertyButton(prop, val, customPropertiesButtonList));
add(customPropertiesButtonList);
addLabelOn(customPropertiesButtonList, "Custom Values (Advanced)");
isPlayerCheckbox = new UICheckbox(positionXStepper.x, positionXStepper.y + 10 + 32 + 28, "isPlayer", character.playerOffsets);
add(isPlayerCheckbox);
addLabelOn(isPlayerCheckbox, "Is Player");
isGFCheckbox = new UICheckbox(isPlayerCheckbox.x + 128, positionXStepper.y + 10 + 32 + 28, "isGF", character.isGF);
add(isGFCheckbox);
addLabelOn(isGFCheckbox, "Is GF");
for (checkbox in [isPlayerCheckbox, isGFCheckbox, antialiasingCheckbox, flipXCheckbox])
{checkbox.y += 4; checkbox.x += 6;}*/
saveButton = new UIButton(windowSpr.x + windowSpr.bWidth - 20, windowSpr.y + windowSpr.bHeight- 20, "Save & Close", function() {
//saveCharacterInfo();
close();
}, 125);
saveButton.x -= saveButton.bWidth;
saveButton.y -= saveButton.bHeight;
closeButton = new UIButton(saveButton.x - 20, saveButton.y, "Close", function() {
if (onSave != null) onSave(null);
close();
}, 125);
closeButton.x -= closeButton.bWidth;
closeButton.color = 0xFFFF0000;
add(closeButton);
add(saveButton);
}
/*function saveCharacterInfo() {
for (stepper in [positionXStepper, positionYStepper, cameraXStepper, cameraYStepper, singTimeStepper, scaleStepper])
@:privateAccess stepper.__onChange(stepper.label.text);
var xml = Xml.createElement("character");
xml.set("isPlayer", isPlayerCheckbox.checked ? "true" : "false");
xml.set("isGF", isGFCheckbox.checked ? "true" : "false");
xml.set("x", Std.string(positionXStepper.value));
xml.set("y", Std.string(positionYStepper.value));
xml.set("gameOverChar", gameOverCharTextBox.label.text);
xml.set("camx", Std.string(cameraXStepper.value));
xml.set("camy", Std.string(cameraYStepper.value));
xml.set("holdTime", Std.string(singTimeStepper.value));
xml.set("flipX", Std.string(flipXCheckbox.checked));
xml.set("icon", iconTextBox.label.text);
xml.set("scale", Std.string(scaleStepper.value));
xml.set("antialiasing", antialiasingCheckbox.checked ? "true" : "false");
xml.set("sprite", spriteTextBox.label.text);
if (iconColorWheel.colorChanged)
xml.set("color", iconColorWheel.curColor.toWebString());
for (val in customPropertiesButtonList.buttons.members)
xml.set(val.propertyText.label.text, val.valueText.label.text);
for (anim in character.animDatas)
{
var animXml:Xml = Xml.createElement('anim');
animXml.set("name", anim.name);
animXml.set("anim", anim.anim);
animXml.set("loop", Std.string(anim.loop));
animXml.set("fps", Std.string(anim.fps));
var offset:FlxPoint = character.getAnimOffset(anim.name);
animXml.set("x", Std.string(offset.x));
animXml.set("y", Std.string(offset.y));
if (anim.indices.length > 0)
animXml.set("indices", anim.indices.join(","));
xml.addChild(animXml);
}
if (onSave != null) onSave(xml);
}*/
}
@@ -0,0 +1,43 @@
package funkin.editors.stager;
import funkin.options.OptionsScreen;
import funkin.game.Stage;
import funkin.options.type.IconOption;
import funkin.options.type.NewOption;
import funkin.options.type.OptionType;
import funkin.options.type.TextOption;
class StageSelection extends EditorTreeMenu
{
public override function create()
{
bgType = "charter";
super.create();
var modsList:Array<String> = Stage.getList(true, true);
var list:Array<OptionType> = [
for (char in (modsList.length == 0 ? Stage.getList(false, true) : modsList))
new TextOption(char, "Press ACCEPT to edit this stage.",
function() {
FlxG.switchState(new StageEditor(char));
})
];
list.insert(0, new NewOption("New Stage", "New Stage", function() {
openSubState(new UIWarningSubstate("New Stage: Feature Not Implemented!", "This feature isnt implemented yet. Please wait for more cne updates to have this functional.\n\n\n- Codename Devs", [
{label: "Ok", color: 0xFFFF0000, onClick: function(t) {}}
]));
}));
main = new OptionsScreen("Stage Editor", "Select a stage to edit. HSCRIPT only stages are not supported.", list);
DiscordUtil.call("onEditorTreeLoaded", ["Stage Editor"]);
}
override function createPost() {
super.createPost();
main.changeSelection(1);
}
}
@@ -0,0 +1,55 @@
package funkin.editors.stager.elements;
import funkin.game.Character;
import funkin.game.Stage.StageCharPos;
import haxe.xml.Access;
class StageCharacterButton extends StageElementButton {
public var charPos:StageCharPos;
public var char:Character;
public function new(x:Float,y:Float, charPos:StageCharPos, xml:Access) {
this.charPos = charPos;
this.char = charPos.extra.get(StageEditor.exID("char"));
super(x,y, xml);
color = 0xffa5c4ff;
updateInfo(charPos);
}
public override function update(elapsed:Float) {
editButton.selectable = ghostButton.selectable = deleteButton.selectable = selectable;
editButton.shouldPress = ghostButton.shouldPress = deleteButton.shouldPress = shouldPress;
hovered = !deleteButton.hovered;
updatePos();
super.update(elapsed);
}
public override function updateInfo(charPos:Dynamic) {
if(charPos is StageCharPos) {
var charPos:StageCharPos = cast charPos;
this.charPos = charPos;
char.visible = !isHidden;
}
super.updateInfo(charPos);
}
public override function onSelect() {
}
public override function onGhostClick() {
isHidden = !isHidden;
updateInfo(this.charPos);
}
public override function getInfoText():String {
return '${charPos.name} (${charPos.x}, ${charPos.y})';
}
public override function updatePos() {
super.updatePos();
}
}
@@ -0,0 +1,112 @@
package funkin.editors.stager.elements;
import haxe.xml.Access;
import flixel.util.FlxColor;
class StageElementButton extends UIButton {
public var xml:Access;
public var editButton:UIButton;
public var editIcon:FlxSprite;
public var ghostButton:UIButton;
public var ghostIcon:FlxSprite;
public var deleteButton:UIButton;
public var deleteIcon:FlxSprite;
public var isHidden:Bool = false;
public function new(x:Float,y:Float, xml:Access) {
this.xml = xml;
super(x,y, getInfoText(), function () {
onSelect();
//CharacterEditor.instance.playAnimation(this.sprite.name);
}, 282);
autoAlpha = false;
ghostButton = new UIButton(x+282+17, y, "", function () {
onGhostClick();
//CharacterEditor.instance.ghostAnim(this.anim);
}, 32);
ghostButton.autoAlpha = false;
members.push(ghostButton);
ghostIcon = new FlxSprite(ghostButton.x + 8, ghostButton.y + 8).loadGraphic(Paths.image('editors/character/ghost-button'), true, 16, 16);
ghostIcon.animation.add("alive", [0]);
ghostIcon.animation.add("dead", [1]);
ghostIcon.animation.play("dead"); ghostIcon.alpha = 0.5;
ghostIcon.antialiasing = false;
ghostIcon.updateHitbox();
members.push(ghostIcon);
editButton = new UIButton(ghostButton.x+32+17, y, "", function () {
onEdit();
}, 32);
editButton.frames = Paths.getFrames("editors/ui/grayscale-button");
editButton.color = FlxColor.YELLOW;
editButton.autoAlpha = false;
members.push(editButton);
editIcon = new FlxSprite(editButton.x + 8, editButton.y + 8).loadGraphic(Paths.image('editors/character/edit-button'));
editIcon.antialiasing = false;
members.push(editIcon);
deleteButton = new UIButton(editButton.x+32+17, y, "", function () {
onDelete();
}, 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);
}
public override function update(elapsed:Float) {
editButton.selectable = ghostButton.selectable = deleteButton.selectable = selectable;
editButton.shouldPress = ghostButton.shouldPress = deleteButton.shouldPress = shouldPress;
hovered = !deleteButton.hovered;
updatePos();
super.update(elapsed);
}
public function updateInfo(sprite:Dynamic) {
field.text = getInfoText();
ghostIcon.animation.play(!isHidden ? "alive" : "dead");
ghostIcon.alpha = !isHidden ? 1 : 0.5;
}
public function updatePos() {
// buttons
deleteButton.x = (editButton.x = (ghostButton.x = (x+282+17))+32+17)+32+17;
deleteButton.y = editButton.y = ghostButton.y = y;
// icons
ghostIcon.x = ghostButton.x + 8; ghostIcon.y = ghostButton.y + 8;
editIcon.x = editButton.x + 8; editIcon.y = editButton.y + 8;
deleteIcon.x = deleteButton.x + (15/2); deleteIcon.y = deleteButton.y + 8;
}
public function onSelect() {
// TODO: implement
}
public function onGhostClick() {
// TODO: implement
}
public function onEdit() {
// TODO: implement
}
public function onDelete() {
// TODO: implement
}
public function getInfoText():String {
return 'NOT IMPLEMENTED';
}
}
@@ -0,0 +1,16 @@
package funkin.editors.stager.elements;
import haxe.xml.Access;
import flixel.util.FlxColor;
class StageSolidButton extends StageSpriteButton {
public function new(x:Float,y:Float, sprite:FlxSprite, xml:Access) {
super(x,y, sprite, xml);
color = 0xFFD9FF50;
}
public override function onEdit() {
// TODO: implement
}
}
@@ -0,0 +1,54 @@
package funkin.editors.stager.elements;
import haxe.xml.Access;
import flixel.util.FlxColor;
class StageSpriteButton extends StageElementButton {
public var sprite:FlxSprite;
public function new(x:Float,y:Float, sprite:FlxSprite, xml:Access) {
this.sprite = sprite;
super(x,y, xml);
//color = 0xFFD9FF50;
updateInfo(sprite);
}
public override function update(elapsed:Float) {
super.update(elapsed);
}
public override function updateInfo(sprite:Dynamic) {
if(sprite is FlxSprite) {
var sprite:FlxSprite = cast sprite;
this.sprite = sprite;
sprite.visible = !isHidden;
}
super.updateInfo(sprite);
}
public override function onGhostClick() {
isHidden = !isHidden;
updateInfo(this.sprite);
}
public override function onEdit() {
// TODO: implement
}
public override function onDelete() {
sprite.destroy();
xml.x.parent.removeChild(xml.x);
StageEditor.instance.xmlMap.remove(sprite);
StageEditor.instance.stageSpritesWindow.remove(this);
}
public override function getInfoText():String {
return '${xml.att.name} (${sprite.x}, ${sprite.y})';
}
public override function updatePos() {
super.updatePos();
}
}
+3 -3
View File
@@ -14,7 +14,7 @@ class UIButtonList<T:UIButton> extends UIWindow {
public var cameraSpacing = 30;
public var buttonSpacing:Float = 16;
public var buttonSize:FlxPoint = FlxPoint.get();
public var buttonSize:FlxPoint = null;
public var buttonOffset:FlxPoint = FlxPoint.get();
public var dragging:Bool = false;
@@ -41,9 +41,9 @@ class UIButtonList<T:UIButton> extends UIWindow {
addIcon = new FlxSprite(addButton.x + addButton.bHeight / 2, addButton.y + (32/2) - 8).loadGraphic(Paths.image('editors/charter/add-button'));
addIcon.antialiasing = false;
addButton.members.push(addIcon);
members.push(addButton);
content.add(addButton);
members.push(buttons);
content.add(buttons);
nextscrollY = buttonCameras.scroll.y = -this.buttonSpacing;
}
+32 -11
View File
@@ -5,7 +5,7 @@ import flixel.graphics.frames.FlxFrame;
class UISliceSprite extends UISprite {
public var bWidth:Int = 120;
public var bHeight:Int = 20;
public var framesOffset:Int = 0;
public var framesOffset(default, set):Int = 0;
public var incorporeal:Bool = false;
@@ -14,6 +14,7 @@ class UISliceSprite extends UISprite {
frames = Paths.getFrames(path);
resize(w, h);
calculateFrames();
}
public override function updateButton() {
@@ -34,21 +35,41 @@ class UISliceSprite extends UISprite {
public var drawMiddle:Bool = true;
public var drawBottom:Bool = true;
var topleft:FlxFrame = null;
var top:FlxFrame = null;
var topright:FlxFrame = null;
var middleleft:FlxFrame = null;
var middle:FlxFrame = null;
var middleright:FlxFrame = null;
var bottomleft:FlxFrame = null;
var bottom:FlxFrame = null;
var bottomright:FlxFrame = null;
function set_framesOffset(value:Int) {
if(value != framesOffset) {
framesOffset = value;
calculateFrames();
}
return value;
}
function calculateFrames() {
topleft = frames.frames[framesOffset];
top = frames.frames[framesOffset + 1];
topright = frames.frames[framesOffset + 2];
middleleft = frames.frames[framesOffset + 3];
middle = frames.frames[framesOffset + 4];
middleright = frames.frames[framesOffset + 5];
bottomleft = frames.frames[framesOffset + 6];
bottom = frames.frames[framesOffset + 7];
bottomright = frames.frames[framesOffset + 8];
}
public override function draw() @:privateAccess {
var x:Float = this.x;
var y:Float = this.y;
if (visible && !(bWidth == 0 || bHeight == 0)) {
var topleft:FlxFrame = frames.frames[framesOffset];
var top:FlxFrame = frames.frames[framesOffset + 1];
var topright:FlxFrame = frames.frames[framesOffset + 2];
var middleleft:FlxFrame = frames.frames[framesOffset + 3];
var middle:FlxFrame = frames.frames[framesOffset + 4];
var middleright:FlxFrame = frames.frames[framesOffset + 5];
var bottomleft:FlxFrame = frames.frames[framesOffset + 6];
var bottom:FlxFrame = frames.frames[framesOffset + 7];
var bottomright:FlxFrame = frames.frames[framesOffset + 8];
// TOP
if (drawTop) {
// TOP LEFT
+16
View File
@@ -2,17 +2,33 @@ package funkin.editors.ui;
class UIWindow extends UISliceSprite {
public var titleSpr:UIText;
public var collapsable:Bool = false;
public var content:FlxTypedGroup<FlxBasic>;
public override function new(x:Float, y:Float, w:Int, h:Int, title:String) {
super(x, y, w, h, "editors/ui/normal-popup");
members.push(titleSpr = new UIText(x + 25, y, bWidth - 50, title, 15, -1));
titleSpr.y = y + ((30 - titleSpr.height) / 2);
content = new FlxTypedGroup<FlxBasic>();
members.push(content);
}
public override function update(elapsed:Float) {
super.update(elapsed);
if(collapsable && FlxG.mouse.justPressed) {
// TOOLBAR
__rect.x = x; __rect.y = y;
__rect.width = bWidth; __rect.height = 23;
if(UIState.state.isOverlapping(this, __rect)) {
content.exists = !content.exists;
drawMiddle = !drawMiddle;
drawBottom = !drawBottom;
}
}
__rect.x = x; __rect.y = y+23;
__rect.width = bWidth; __rect.height = bHeight-23;
hovered = UIState.state.isOverlapping(this, __rect);
+78 -31
View File
@@ -1,5 +1,6 @@
package funkin.game;
import flixel.util.typeLimit.OneOfTwo;
import flixel.FlxState;
import flixel.math.FlxPoint;
import funkin.backend.scripting.Script;
@@ -12,65 +13,94 @@ import haxe.xml.Access;
using StringTools;
class Stage extends FlxBasic implements IBeatReceiver {
public var extra:Map<String, Dynamic> = [];
public var stageXML:Access;
public var stagePath:String;
public var stageFile:String;
public var stageName:String;
public var stageSprites:Map<String, FlxSprite> = [];
public var stageScript:Script;
public var state:FlxState;
public var characterPoses:Map<String, StageCharPos> = [];
public var defaultZoom:Float = 1.05;
public var startCam = new FlxPoint();
public var onXMLLoaded:(Access, Array<Access>)->Array<Access> = null;
public var onNodeLoaded:(Access, Dynamic)->Dynamic = null;
private var spritesParentFolder = "";
public function getSprite(name:String) {
public inline function getSprite(name:String) {
return stageSprites[name];
}
public function new(stage:String, ?state:FlxState) {
public function new(stage:String, ?state:FlxState, autoLoad:Bool = true) {
super();
if (state == null) state = PlayState.instance;
if (state == null) state = FlxG.state;
this.state = state;
stagePath = Paths.xml('stages/$stage');
stageFile = stage;
stagePath = Paths.xml('stages/$stageFile');
try {
if (Assets.exists(stagePath)) stageXML = new Access(Xml.parse(Assets.getText(stagePath)).firstElement());
} catch(e) {
Logs.trace('Couldn\'t load stage "$stage": ${e.message}', ERROR);
Logs.trace('Couldn\'t load stage "$stageFile": ${e.message}', ERROR);
}
if (PlayState.instance != null) {
stageScript = Script.create(Paths.script('data/stages/$stage'));
if (autoLoad) loadXml(stageXML);
}
public function loadXml(xml:Access, forceLoadAll:Bool = false) {
if (PlayState.instance == state) {
stageScript = Script.create(Paths.script('data/stages/$stageFile'));
PlayState.instance.scripts.add(stageScript);
stageScript.load();
}
if (stageXML != null) {
if (PlayState.instance != null) {
if (xml != null) {
var parsed:Null<Float>;
if(stageXML.has.startCamPosX && (parsed = Std.parseFloat(stageXML.att.startCamPosX)) != null) PlayState.instance.camFollow.x = parsed;
if(stageXML.has.startCamPosY && (parsed = Std.parseFloat(stageXML.att.startCamPosY)) != null) PlayState.instance.camFollow.y = parsed;
if(stageXML.has.zoom && (parsed = Std.parseFloat(stageXML.att.zoom)) != null) PlayState.instance.defaultCamZoom = parsed;
PlayState.instance.curStage = stageXML.has.name ? stageXML.att.name : stage;
if((parsed = Std.parseFloat(xml.getAtt("startCamPosX"))).isNotNull()) startCam.x = parsed;
if((parsed = Std.parseFloat(xml.getAtt("startCamPosY"))).isNotNull()) startCam.y = parsed;
if((parsed = Std.parseFloat(xml.getAtt("zoom"))).isNotNull()) defaultZoom = parsed;
stageName = xml.getAtt("name").getDefault(stageFile);
if (PlayState.instance == state) {
if(xml.has.startCamPosX) PlayState.instance.camFollow.x = startCam.x;
if(xml.has.startCamPosY) PlayState.instance.camFollow.y = startCam.y;
if(xml.has.zoom) PlayState.instance.defaultCamZoom = defaultZoom;
PlayState.instance.curStage = stageName;
}
if (stageXML.has.folder) {
spritesParentFolder = stageXML.att.folder;
if (xml.has.folder) {
spritesParentFolder = xml.att.folder;
if (!spritesParentFolder.endsWith("/")) spritesParentFolder += "/";
}
var elems = [];
for(node in stageXML.elements) {
if (node.name == "high-memory" && !Options.lowMemoryMode)
var elems:Array<Access> = [];
// some way to tag that the sprites are from the group
for(node in xml.elements) {
if (node.name == "high-memory" && (!Options.lowMemoryMode || forceLoadAll))
for(e in node.elements)
elems.push(e);
else if (node.name == "low-memory" && (Options.lowMemoryMode || forceLoadAll))
for(e in node.elements)
elems.push(e);
else
elems.push(node);
}
if (PlayState.instance != null) {
var event = PlayState.instance.scripts.event("onStageXMLParsed", EventManager.get(StageXMLEvent).recycle(this, stageXML, elems));
if (PlayState.instance == state) {
var event = PlayState.instance.scripts.event("onStageXMLParsed", EventManager.get(StageXMLEvent).recycle(this, xml, elems));
elems = event.elems;
}
if(onXMLLoaded != null) {
elems = onXMLLoaded(xml, elems);
}
for(node in elems) {
var sprite:Dynamic = switch(node.name) {
@@ -79,8 +109,8 @@ class Stage extends FlxBasic implements IBeatReceiver {
var spr = XMLUtil.createSpriteFromXML(node, spritesParentFolder, LOOP);
if (!node.has.zoomfactor && PlayState.instance != null)
spr.initialZoom = PlayState.instance.defaultCamZoom;
if (!node.has.zoomfactor/* && PlayState.instance == state*/)
spr.initialZoom = defaultZoom;//PlayState.instance.defaultCamZoom;
stageSprites.set(spr.name, spr);
state.add(spr);
@@ -88,10 +118,11 @@ class Stage extends FlxBasic implements IBeatReceiver {
case "box" | "solid":
if ( !node.has.name || !node.has.width || !node.has.height) continue;
var spr = new FlxSprite(
var spr = new FunkinSprite(
(node.has.x) ? Std.parseFloat(node.att.x).getDefault(0) : 0,
(node.has.y) ? Std.parseFloat(node.att.y).getDefault(0) : 0
);
spr.name = node.getAtt("name");
(node.name == "solid" ? spr.makeSolid : spr.makeGraphic)(
Std.parseInt(node.att.width),
@@ -99,7 +130,10 @@ class Stage extends FlxBasic implements IBeatReceiver {
(node.has.color) ? CoolUtil.getColorFromDynamic(node.att.color) : -1
);
stageSprites.set(node.getAtt("name"), spr);
if (!node.has.zoomfactor/* && PlayState.instance == state*/)
spr.initialZoom = defaultZoom;//PlayState.instance.defaultCamZoom;
stageSprites.set(spr.name, spr);
state.add(spr);
spr;
case "boyfriend" | "bf" | "player":
@@ -127,7 +161,7 @@ class Stage extends FlxBasic implements IBeatReceiver {
if (!node.has.name) continue;
addCharPos(node.att.name, node);
case "ratings" | "combo":
if (PlayState.instance == null) continue;
if (PlayState.instance != state) continue;
PlayState.instance.comboGroup.setPosition(
Std.parseFloat(node.getAtt("x")).getDefault(PlayState.instance.comboGroup.x),
Std.parseFloat(node.getAtt("y")).getDefault(PlayState.instance.comboGroup.y)
@@ -137,9 +171,12 @@ class Stage extends FlxBasic implements IBeatReceiver {
default: null;
}
if(PlayState.instance != null) {
if(PlayState.instance == state) {
sprite = PlayState.instance.scripts.event("onStageNodeParsed", EventManager.get(StageNodeEvent).recycle(this, node, sprite, node.name)).sprite;
}
if(onNodeLoaded != null) {
sprite = onNodeLoaded(node, sprite);
}
if (sprite != null) {
for(e in node.nodes.property)
@@ -172,7 +209,7 @@ class Stage extends FlxBasic implements IBeatReceiver {
flip: true
});
if (PlayState.instance == null) return;
if (PlayState.instance != state) return;
for(k=>e in stageSprites) {
stageScript.set(k, e);
}
@@ -181,6 +218,7 @@ class Stage extends FlxBasic implements IBeatReceiver {
public function addCharPos(name:String, node:Access, ?nonXMLInfo:StageCharPosInfo):StageCharPos {
var charPos = new StageCharPos();
charPos.visible = charPos.active = false;
charPos.name = name;
if (nonXMLInfo != null) {
charPos.setPosition(nonXMLInfo.x, nonXMLInfo.y);
@@ -239,19 +277,28 @@ class Stage extends FlxBasic implements IBeatReceiver {
public function measureHit(curMeasure:Int) {}
public static function getList(?mods:Bool = false):Array<String> {
public static function getList(?mods:Bool = false, ?xmlOnly:Bool = false):Array<String> {
var list:Array<String> = [];
for (path in Paths.getFolderContent('data/stages/', true, mods ? MODS : BOTH))
if (Path.extension(path) == "xml" || Path.extension(path) == "hx") {
var file:String = Path.withoutDirectory(Path.withoutExtension(path));
list.pushOnce(file);
var extensions:Array<String> = ["xml"];
if (!xmlOnly) extensions.push("hx");
for (path in Paths.getFolderContent("data/stages/", false, mods ? MODS : BOTH)) {
var extension = Path.extension(path);
if (extensions.contains(extension)) {
list.pushOnce(Path.withoutExtension(path));
}
}
trace(list);
return list;
}
}
class StageCharPos extends FlxObject {
public var extra:Map<String, Dynamic> = [];
public var name:String;
public var charSpacingX:Float = 20;
public var charSpacingY:Float = 0;
public var camxoffset:Float = 0;