Merge branch 'stage-editor' into new-editors
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
<window width="550" height="570" title="Editing Character ${button.getName()}">
|
||||
<set name="XOFFSET" value="30" />
|
||||
<set name="YOFFSET" value="40" />
|
||||
<set name="TO" value="64" />
|
||||
|
||||
<exec>
|
||||
function col(c:Int) {
|
||||
return 20 + (150 + XOFFSET) * c;
|
||||
}
|
||||
previewSprite = null;
|
||||
if(char.animateAtlas == null) {
|
||||
previewSprite = new FunkinSprite(0, 0);
|
||||
previewSprite.frame = char.frame;
|
||||
|
||||
self.add(previewSprite);
|
||||
}
|
||||
</exec>
|
||||
|
||||
<title name="title" x="20" y="30 + 16" text="Character Data" />
|
||||
|
||||
<section>
|
||||
<set name="curY" value="title.y + TO" />
|
||||
|
||||
<stepper name="xStepper" label="X" value="charPos.x" x="col(0)" y="curY" step="1" width="100" />
|
||||
<stepper name="yStepper" label="Y" value="charPos.y" x="last.x + last.bWidth + XOFFSET - 20" y="curY" step="1" width="100" />
|
||||
|
||||
<stepper name="scrollXStepper" label="Scroll X" value="charPos.scrollFactor.x" x="col(1)" y="curY" precision="2" step="0.01" width="100" />
|
||||
<stepper name="scrollYStepper" label="Scroll Y" value="charPos.scrollFactor.y" x="last.x + last.bWidth + XOFFSET - 20" y="curY" precision="2" step="0.01" width="100" />
|
||||
|
||||
<stepper name="scaleXStepper" label="Scale X" value="charPos.scale.x" x="col(2)" y="curY" precision="2" step="0.01" width="100" />
|
||||
<stepper name="scaleYStepper" label="Scale Y" value="charPos.scale.y" x="last.x + last.bWidth + XOFFSET - 20" y="curY" precision="2" step="0.01" width="100" />
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<set name="curY" value="xStepper.y + xStepper.bHeight + YOFFSET" />
|
||||
|
||||
<stepper name="camxStepper" label="Cam X" value="charPos.camxoffset" x="col(0)" y="curY" step="1" width="100" />
|
||||
<stepper name="camyStepper" label="Cam Y" value="charPos.camyoffset" x="last.x + last.bWidth + XOFFSET - 20" y="curY" step="1" width="100" />
|
||||
|
||||
<label name="spacingLabel" text="Spacing (X, Y)" x="col(1)" y="curY" size="15" />
|
||||
<stepper name="spacingXStepper" value="charPos.charSpacingX" x="col(1)" y="curY" precision="2" step="0.01" width="100" />
|
||||
<stepper name="spacingYStepper" value="charPos.charSpacingY" x="last.x + last.bWidth + XOFFSET - 20" y="curY" precision="2" step="0.01" width="100" />
|
||||
|
||||
<stepper name="skewXStepper" label="Skew X" value="charPos.skewX" x="col(2)" y="curY" precision="2" step="0.01" width="100" />
|
||||
<stepper name="skewYStepper" label="Skew Y" value="charPos.skewY" x="last.x + last.bWidth + XOFFSET - 20" y="curY" precision="2" step="0.01" width="100" />
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<set name="curY" value="camxStepper.y + camxStepper.bHeight + YOFFSET" />
|
||||
|
||||
<stepper name="alphaStepper" label="Alpha" value="charPos.alpha" x="col(0)" y="curY" precision="2" step="0.01" width="100" />
|
||||
<stepper name="angleStepper" label="Angle" value="charPos.angle" x="last.x + last.bWidth + XOFFSET - 20" y="curY" precision="2" step="0.01" width="100" />
|
||||
|
||||
<stepper name="zoomFactorStepper" label="Zoom Factor" value="charPos.zoomFactor" x="col(1)" y="curY" precision="2" step="0.01" width="100" />
|
||||
|
||||
<section if="charPos.extra.exists('extraPos')">
|
||||
<textbox name="charName" label="Position Name" value="charPos.name" x="col(2)" y="curY" width="100"/>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<set name="curY" value="alphaStepper.y + alphaStepper.bHeight + YOFFSET - 20" />
|
||||
|
||||
<checkbox name="flipXCheckbox" text="Flip X" value="charPos.flipX" x="col(0)" y="curY" />
|
||||
|
||||
<radio for="memoryCheck" name="highMemoryRadio" text="High Memory Only" value="getEx('highMemory')" x="last.field.x + last.field.width + XOFFSET * 2" y="curY" />
|
||||
<radio for="memoryCheck" name="lowMemoryRadio" text="Low Memory Only" value="getEx('lowMemory')" x="last.field.x + last.field.width + XOFFSET * 2" y="curY" />
|
||||
</section>
|
||||
|
||||
<section if="previewSprite != null">
|
||||
<title name="previewTitle" x="20" y="self.winHeight - 220" text="Preview Transform:" size="15" />
|
||||
</section>
|
||||
<section if="previewSprite == null">
|
||||
<exec>
|
||||
self.windowSpr.bHeight -= 170;
|
||||
</exec>
|
||||
</section>
|
||||
|
||||
<exec>
|
||||
function onUpdate() {
|
||||
if(previewSprite == null) return;
|
||||
previewSprite.skew.x = skewXStepper.value;
|
||||
previewSprite.skew.y = skewYStepper.value;
|
||||
previewSprite.angle = angleStepper.value;
|
||||
previewSprite.antialiasing = char.antialiasing;
|
||||
var ratio = char.frameWidth / char.frameHeight;
|
||||
previewSprite.setGraphicSize(145 * scaleXStepper.value * ratio, 145 * scaleYStepper.value);
|
||||
previewSprite.updateHitbox();
|
||||
if(flipXCheckbox.checked != (charPos.name == "boyfriend"))
|
||||
previewSprite.scale.x *= -1;
|
||||
|
||||
previewSprite.x = 30;
|
||||
previewSprite.y = self.winHeight - previewSprite.height - 30;
|
||||
}
|
||||
|
||||
function onSave() {
|
||||
setEx("highMemory", highMemoryRadio.checked);
|
||||
setEx("lowMemory", lowMemoryRadio.checked);
|
||||
|
||||
charPos.x = xStepper.value;
|
||||
charPos.y = yStepper.value;
|
||||
charPos.scrollFactor.set(scrollXStepper.value, scrollYStepper.value);
|
||||
charPos.scale.set(scaleXStepper.value, scaleYStepper.value);
|
||||
charPos.camxoffset = camxStepper.value;
|
||||
charPos.camyoffset = camyStepper.value;
|
||||
charPos.skewX = skewXStepper.value;
|
||||
charPos.skewY = skewYStepper.value;
|
||||
charPos.alpha = alphaStepper.value;
|
||||
charPos.charSpacingX = spacingXStepper.value;
|
||||
charPos.charSpacingY = spacingYStepper.value;
|
||||
charPos.flipX = flipXCheckbox.checked;
|
||||
charPos.zoomFactor = zoomFactorStepper.value;
|
||||
charPos.angle = angleStepper.value;
|
||||
|
||||
charPos.revertCharacter(char);
|
||||
charPos.prepareCharacter(char, charPos._id);
|
||||
}
|
||||
</exec>
|
||||
</window>
|
||||
@@ -0,0 +1,185 @@
|
||||
<window width="1056" height="520" title="Editing Sprite ${button.getName()}">
|
||||
<set name="MARGIN" value="20" />
|
||||
<set name="XOFFSET" value="50" />
|
||||
<set name="YOFFSET" value="40" />
|
||||
<set name="TO" value="64" />
|
||||
|
||||
<exec>
|
||||
function col(c:Int) {
|
||||
return 20 + (200 + XOFFSET) * c;
|
||||
}
|
||||
|
||||
if(sprite.animateAtlas == null) {
|
||||
previewSprite = new FunkinSprite(50, self.winHeight - 100);
|
||||
previewSprite.frame = sprite.frame;
|
||||
|
||||
self.add(previewSprite);
|
||||
}
|
||||
</exec>
|
||||
|
||||
<title name="title" x="20" y="30 + 16" text="Sprite Data" />
|
||||
|
||||
<section> <!-- ROW 1 -->
|
||||
<set name="curY" value="title.y + TO" />
|
||||
|
||||
<textbox name="nameTextBox" label="Name Identifier" value="button.getName()" x="col(0)" y="curY" width="200" height="32" />
|
||||
<textbox name="spriteTextBox" value="getEx('imageFile')" x="last.x + last.bWidth + XOFFSET" y="curY" width="200" height="32" />
|
||||
<stepper name="alphaStepper" label="Alpha" value="sprite.alpha" x="spriteTextBox.x + spriteTextBox.bWidth + XOFFSET" y="curY" precision="2" step="0.01" width="100" />
|
||||
<label name="imageFile" text="Image File (WIP: needs reload) In (${stage.spritesParentFolder})" x="spriteTextBox.x" y="spriteTextBox.y - 14" size="15" />
|
||||
</section>
|
||||
|
||||
<section> <!-- ROW 2 -->
|
||||
<set name="curY" value="nameTextBox.y + nameTextBox.bHeight + YOFFSET" />
|
||||
|
||||
<stepper name="xStepper" label="X" value="sprite.x" x="col(0)" y="curY" step="1" width="100" />
|
||||
<stepper name="yStepper" label="Y" value="sprite.y" x="last.x + last.bWidth + XOFFSET - 20" y="curY" step="1" width="100" />
|
||||
|
||||
<stepper name="scrollXStepper" label="Scroll X" value="sprite.scrollFactor.x" x="spriteTextBox.x" y="curY" precision="2" step="0.01" width="100" />
|
||||
<stepper name="scrollYStepper" label="Scroll Y" value="sprite.scrollFactor.y" x="last.x + last.bWidth + XOFFSET - 20" y="curY" precision="2" step="0.01" width="100" />
|
||||
|
||||
<stepper name="scaleXStepper" label="Scale X" value="sprite.scale.x" x="col(2)" y="curY" precision="2" step="0.01" width="100" />
|
||||
<stepper name="scaleYStepper" label="Scale Y" value="sprite.scale.y" x="last.x + last.bWidth + XOFFSET - 20" y="curY" precision="2" step="0.01" width="100" />
|
||||
</section>
|
||||
|
||||
<section> <!-- ROW 3 -->
|
||||
<set name="curY" value="xStepper.y + xStepper.bHeight + YOFFSET" />
|
||||
|
||||
<stepper name="angleStepper" label="Angle" value="sprite.angle" x="col(0)" y="curY" precision="2" step="0.01" width="100" />
|
||||
<stepper name="zoomFactorStepper" label="Zoom Factor" value="sprite.zoomFactor" x="last.x + last.bWidth + XOFFSET - 20" y="curY" precision="2" step="0.01" width="100" />
|
||||
|
||||
<!-- TODO: Simplify this -->
|
||||
<dropdown name="animType" label="Animation Type" value="sprite.spriteAnimType" x="col(1)" y="curY" width="150" height="32">
|
||||
<item label="NONE" value="0" />
|
||||
<item label="BEAT" value="1" />
|
||||
<item label="LOOP" value="2" />
|
||||
</dropdown>
|
||||
|
||||
<stepper name="skewXStepper" label="Skew X" value="sprite.skew.x" x="col(2)" y="curY" precision="2" step="0.01" width="100" />
|
||||
<stepper name="skewYStepper" label="Skew Y" value="sprite.skew.y" x="last.x + last.bWidth + XOFFSET - 20" y="curY" precision="2" step="0.01" width="100" />
|
||||
</section>
|
||||
|
||||
<section> <!-- ROW 4 -->
|
||||
<set name="curY" value="angleStepper.y + angleStepper.bHeight + YOFFSET" />
|
||||
|
||||
<checkbox name="antialiasingCheckbox" text="Antialiasing" value="sprite.antialiasing" x="col(0)" y="curY" />
|
||||
<radio for="memoryCheck" name="highMemoryRadio" text="High Memory Only" value="getEx('highMemory')" x="col(1)" y="curY" />
|
||||
<radio for="memoryCheck" name="lowMemoryRadio" text="Low Memory Only" value="getEx('lowMemory')" x="col(2)" y="curY" />
|
||||
|
||||
<!-- TODO: Animation list -->
|
||||
</section>
|
||||
|
||||
<section> <!-- RIGHT SIDE -->
|
||||
<set name="curY" value="title.y + TO" />
|
||||
<set name="listWidth" value="250" />
|
||||
|
||||
<buttonlist name="animations" label="Animations (TODO)" x="col(3)" y="curY" width="listWidth" height="330" buttonSizeX="listWidth-10" buttonSizeY="35" buttonSpacing="5">
|
||||
<texture path="editors/ui/inputbox" />
|
||||
<camSpacing value="0" />
|
||||
|
||||
<code>
|
||||
buttonlist.addButton.exists = false;
|
||||
</code>
|
||||
|
||||
<!-- <code>
|
||||
import funkin.editors.extra.PropertyButton;
|
||||
|
||||
var buttonList = buttonlist; // important for now, since it gets reset after the code is run
|
||||
|
||||
var width = buttonList.buttonSize.x;
|
||||
var height = buttonList.buttonSize.y;
|
||||
|
||||
buttonList.addButton.callback = function() {
|
||||
buttonList.add(new PropertyButton("newProperty", "valueHere", buttonList, width, height, 150, 220, 25));
|
||||
}
|
||||
|
||||
//var defaultAttribs = Stage.DEFAULT_ATTRIBUTES;
|
||||
//for (i in stage.stageXML.attributes()) {
|
||||
// trace(i, defaultAttribs);
|
||||
// if (!defaultAttribs.contains(i))
|
||||
// buttonList.add(new PropertyButton(i, stage.stageXML.get(i), buttonList, width, height, 150, 220, 25));
|
||||
//}
|
||||
for(key => val in stage.extra)
|
||||
buttonList.add(new PropertyButton(key, val, buttonList, width, height, 150, 220, 25));
|
||||
</code> -->
|
||||
</buttonlist>
|
||||
|
||||
<text text="Tip: To access custom properties, hold shift when clicking edit" x="col(2) - 60" y="self.winHeight - 50" size="15" />
|
||||
</section>
|
||||
|
||||
|
||||
<!-- Preview Transform -->
|
||||
|
||||
<section if="previewSprite != null">
|
||||
<title name="previewTitle" x="20" y="self.winHeight - 150" text="Preview Transform:" size="15" />
|
||||
</section>
|
||||
|
||||
<exec>
|
||||
function onUpdate() {
|
||||
if(previewSprite == null) return;
|
||||
previewSprite.color = 0xFFFFFFFF; // TODO: Color Wheel
|
||||
previewSprite.skew.x = skewXStepper.value;
|
||||
previewSprite.skew.y = skewYStepper.value;
|
||||
previewSprite.angle = angleStepper.value;
|
||||
previewSprite.alpha = alphaStepper.value;
|
||||
previewSprite.antialiasing = antialiasingCheckbox.checked;
|
||||
var ratio = sprite.width / sprite.height;
|
||||
previewSprite.setGraphicSize(50 * scaleXStepper.value * ratio, 50 * scaleYStepper.value);
|
||||
previewSprite.updateHitbox();
|
||||
}
|
||||
|
||||
function onSave() {
|
||||
sprite.name = nameTextBox.label.text;
|
||||
setEx("imageFile", spriteTextBox.label.text);
|
||||
|
||||
setEx("highMemory", highMemoryRadio.checked);
|
||||
setEx("lowMemory", lowMemoryRadio.checked);
|
||||
|
||||
//sprite.x = xStepper.value;
|
||||
//sprite.y = yStepper.value;
|
||||
//sprite.zoomFactor = zoomFactorStepper.value;
|
||||
//sprite.antialiasing = antialiasingCheckbox.checked;
|
||||
//sprite.color = colorWheel.color;
|
||||
//sprite.spriteAnimType = animType.value;
|
||||
sprite.angle = angleStepper.value;
|
||||
sprite.alpha = alphaStepper.value;
|
||||
|
||||
function saveXY(obj:Dynamic, name:String, x:UINumericStepper, y:UINumericStepper) {
|
||||
button.xml.remove(name + "");
|
||||
button.xml.remove(name + "x");
|
||||
button.xml.remove(name + "y");
|
||||
|
||||
obj.x = x.value;
|
||||
obj.y = y.value;
|
||||
|
||||
if(x.value == y.value)
|
||||
button.xml.set(name, x.value);
|
||||
else {
|
||||
button.xml.set(name + "x", x.value);
|
||||
button.xml.set(name + "y", y.value);
|
||||
}
|
||||
}
|
||||
function save(name:String, value:Dynamic) {
|
||||
button.xml.remove(name);
|
||||
button.xml.set(name, value);
|
||||
}
|
||||
|
||||
// TODO: remove old attributes to prevent possible errors
|
||||
// button.xml.remove();
|
||||
|
||||
button.xml.set("name", nameTextBox.label.text);
|
||||
button.xml.set("sprite", spriteTextBox.label.text);
|
||||
button.xml.set("x", xStepper.value);
|
||||
button.xml.set("y", yStepper.value);
|
||||
button.xml.set("zoom", zoomFactorStepper.value);
|
||||
button.xml.set("type", animType.key);
|
||||
button.xml.set("antialiasing", antialiasingCheckbox.checked);
|
||||
button.xml.set("angle", angleStepper.value);
|
||||
button.xml.set("alpha", alphaStepper.value);
|
||||
|
||||
saveXY(sprite.scrollFactor, "scroll", scrollXStepper, scrollYStepper);
|
||||
saveXY(sprite.scale, "scale", scaleXStepper, scaleYStepper);
|
||||
saveXY(sprite.skew, "skew", skewXStepper, skewYStepper);
|
||||
XMLUtil.loadSpriteFromXML(sprite, button.xml, stage.spritesParentFolder);
|
||||
}
|
||||
</exec>
|
||||
</window>
|
||||
@@ -0,0 +1,110 @@
|
||||
<window width="470" height="620" title="Editing Stage Info '${stage.stageName}'">
|
||||
<set name="MARGIN" value="20" />
|
||||
<set name="XOFFSET" value="50" />
|
||||
<set name="YOFFSET" value="40" />
|
||||
<set name="TO" value="64" />
|
||||
<set name="storedProps" value="[]" />
|
||||
|
||||
<exec>
|
||||
import haxe.ds.StringMap;
|
||||
var oldInfo = {
|
||||
name: stage.stageName,
|
||||
folder: stage.spritesParentFolder,
|
||||
startCamX: stage.startCam.x,
|
||||
startCamY: stage.startCam.y,
|
||||
zoom: stage.defaultZoom,
|
||||
attrib: new StringMap()
|
||||
};
|
||||
|
||||
function col(c:Int) {
|
||||
return MARGIN + (170 + XOFFSET) * c;
|
||||
}
|
||||
|
||||
for(prop => val in stage.extra) {
|
||||
if(!StringTools.startsWith(prop, "stageEditor.")) {
|
||||
storedProps.push(prop);
|
||||
oldInfo.attrib.set(prop, val);
|
||||
}
|
||||
}
|
||||
</exec>
|
||||
|
||||
<title name="title" x="MARGIN" y="30 + 16" text="Stage Info" />
|
||||
|
||||
<section> <!-- ROW 1 -->
|
||||
<set name="curY" value="title.y + TO" />
|
||||
|
||||
<textbox name="nameTextBox" label="Stage Name" value="stage.stageName" x="col(0)" y="curY" width="200" height="32" />
|
||||
<textbox name="spritePathTextBox" value="stage.spritesParentFolder" x="col(1)" y="curY" width="200" height="32" />
|
||||
<label text="Sprite Path (Needs Refresh)" x="spritePathTextBox.x" y="spritePathTextBox.y - 14" size="15" />
|
||||
</section>
|
||||
|
||||
<section> <!-- ROW 2 -->
|
||||
<set name="curY" value="nameTextBox.y + nameTextBox.bHeight + YOFFSET" />
|
||||
|
||||
<label text="Start Cam Pos (X, Y)" x="col(0)" y="curY" />
|
||||
<stepper name="camxStepper" value="stage.startCam.x" x="col(0)" y="curY" step="1" width="120" />
|
||||
<stepper name="camyStepper" value="stage.startCam.y" x="last.x + last.bWidth + XOFFSET - 30" y="curY" step="1" width="120" />
|
||||
|
||||
<stepper name="zoomStepper" label="Zoom" value="stage.defaultZoom" x="col(1)" y="curY" precision="2" step="0.01" width="100" />
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<set name="curY" value="camxStepper.y + camxStepper.bHeight + YOFFSET" />
|
||||
<set name="listWidth" value="self.winWidth - MARGIN * 2" />
|
||||
|
||||
<buttonlist name="attributesList" label="Custom Attributes" x="col(0)" y="curY" width="listWidth" height="290" buttonSizeX="listWidth-10" buttonSizeY="35" buttonSpacing="5">
|
||||
<texture path="editors/ui/inputbox" />
|
||||
<camSpacing value="0" />
|
||||
|
||||
<code>
|
||||
import funkin.editors.extra.PropertyButton;
|
||||
|
||||
var buttonList = buttonlist; // important for now, since it gets reset after the code is run
|
||||
|
||||
var width = buttonList.buttonSize.x;
|
||||
var height = buttonList.buttonSize.y;
|
||||
|
||||
buttonList.addButton.callback = function() {
|
||||
buttonList.add(new PropertyButton("newProperty", "valueHere", buttonList, width, height, 150, 220, 25));
|
||||
}
|
||||
|
||||
//var defaultAttribs = Stage.DEFAULT_ATTRIBUTES;
|
||||
//for (i in stage.stageXML.attributes()) {
|
||||
// trace(i, defaultAttribs);
|
||||
// if (!defaultAttribs.contains(i))
|
||||
// buttonList.add(new PropertyButton(i, stage.stageXML.get(i), buttonList, width, height, 150, 220, 25));
|
||||
//}
|
||||
for(key => val in stage.extra)
|
||||
buttonList.add(new PropertyButton(key, val, buttonList, width, height, 150, 220, 25));
|
||||
</code>
|
||||
</buttonlist>
|
||||
</section>
|
||||
|
||||
<exec>
|
||||
function onSave() {
|
||||
stage.startCam.x = camxStepper.value;
|
||||
stage.startCam.y = camyStepper.value;
|
||||
stage.defaultZoom = zoomStepper.value;
|
||||
stage.stageName = nameTextBox.label.text;
|
||||
stage.spritesParentFolder = spritePathTextBox.label.text;
|
||||
|
||||
for(prop in storedProps)
|
||||
stage.extra.remove(prop);
|
||||
|
||||
var newInfo = {
|
||||
name: stage.stageName,
|
||||
folder: stage.spritesParentFolder,
|
||||
startCamX: stage.startCam.x,
|
||||
startCamY: stage.startCam.y,
|
||||
zoom: stage.defaultZoom,
|
||||
attrib: new StringMap()
|
||||
};
|
||||
for(prop in attributesList.buttons.members) {
|
||||
stage.extra.set(prop.propertyText.label.text, prop.valueText.label.text);
|
||||
newInfo.attrib.set(prop.propertyText.label.text, prop.valueText.label.text);
|
||||
}
|
||||
|
||||
FlxG.state.undos.addToUndo(CEditInfo(oldInfo, newInfo));
|
||||
}
|
||||
</exec>
|
||||
</window>
|
||||
@@ -0,0 +1,66 @@
|
||||
<window width="350" height="570" title="Editing XML <${button.getName()} />">
|
||||
<set name="MARGIN" value="20" />
|
||||
<set name="XOFFSET" value="30" />
|
||||
<set name="YOFFSET" value="40" />
|
||||
<set name="TO" value="64" />
|
||||
|
||||
<exec>
|
||||
function col(c:Int) {
|
||||
return MARGIN + (150 + XOFFSET) * c;
|
||||
}
|
||||
</exec>
|
||||
|
||||
<title name="title" x="MARGIN" y="30 + 16" text="Xml Data" />
|
||||
|
||||
<section>
|
||||
<set name="curY" value="title.y + TO" />
|
||||
|
||||
<textbox name="nodeNameTextBox" label="Element Name" value="button.getName()" x="col(0)" y="curY" width="280" height="32" />
|
||||
</section>
|
||||
<section>
|
||||
<set name="curY" value="nodeNameTextBox.y + nodeNameTextBox.bHeight + YOFFSET - 30" />
|
||||
|
||||
<radio for="memoryCheck" name="highMemoryRadio" text="High Memory Only" value="button.highMemory" x="col(0)" y="curY" />
|
||||
<radio for="memoryCheck" name="lowMemoryRadio" text="Low Memory Only" value="button.lowMemory" x="col(0)" y="last.y + last.check.height + YOFFSET - 30" />
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<set name="curY" value="lowMemoryRadio.y + lowMemoryRadio.check.height + YOFFSET" />
|
||||
<set name="listWidth" value="self.winWidth - MARGIN * 2" />
|
||||
|
||||
<buttonlist name="attributesList" label="Attributes" x="col(0)" y="curY" width="listWidth" height="260" buttonSizeX="listWidth-10" buttonSizeY="35" buttonSpacing="5">
|
||||
<texture path="editors/ui/inputbox" />
|
||||
<camSpacing value="0" />
|
||||
|
||||
<code>
|
||||
import funkin.editors.extra.PropertyButton;
|
||||
|
||||
var buttonList = buttonlist; // important for now, since it gets reset after the code is run
|
||||
|
||||
var width = buttonList.buttonSize.x;
|
||||
var height = buttonList.buttonSize.y;
|
||||
|
||||
buttonList.addButton.callback = function() {
|
||||
buttonList.add(new PropertyButton("newProperty", "valueHere", buttonList, width, height, 110, 145, 25));
|
||||
}
|
||||
|
||||
for (i in xml.attributes())
|
||||
buttonList.add(new PropertyButton(i, xml.get(i), buttonList, width, height, 110, 145, 25));
|
||||
</code>
|
||||
</buttonlist>
|
||||
</section>
|
||||
|
||||
<exec>
|
||||
function onSave() {
|
||||
button.xml.nodeName = nodeNameTextBox.label.text;
|
||||
button.highMemory = highMemoryRadio.checked;
|
||||
button.lowMemory = lowMemoryRadio.checked;
|
||||
|
||||
for (i in xml.attributes())
|
||||
xml.remove(i);
|
||||
|
||||
for (val in attributesList.buttons.members)
|
||||
xml.set(val.propertyText.label.text, val.valueText.label.text);
|
||||
}
|
||||
</exec>
|
||||
</window>
|
||||
@@ -0,0 +1,51 @@
|
||||
<window width="450" height="570" title="Editing XML ${type}">
|
||||
<!-- BASED ON unknownEditScreen.xml -->
|
||||
<set name="MARGIN" value="20" />
|
||||
<set name="XOFFSET" value="30" />
|
||||
<set name="YOFFSET" value="40" />
|
||||
<set name="TO" value="64" />
|
||||
|
||||
<exec>
|
||||
function col(c:Int) {
|
||||
return MARGIN + (150 + XOFFSET) * c;
|
||||
}
|
||||
</exec>
|
||||
|
||||
<title name="title" x="MARGIN" y="30 + 16" text="Xml Data" />
|
||||
|
||||
<section>
|
||||
<set name="curY" value="title.y + TO" />
|
||||
<set name="listWidth" value="self.winWidth - MARGIN * 2" />
|
||||
|
||||
<buttonlist name="attributesList" label="Attributes" x="col(0)" y="curY" width="listWidth" height="400" buttonSizeX="listWidth-10" buttonSizeY="35" buttonSpacing="5">
|
||||
<texture path="editors/ui/inputbox" />
|
||||
<camSpacing value="0" />
|
||||
|
||||
<code>
|
||||
import funkin.editors.extra.PropertyButton;
|
||||
|
||||
var buttonList = buttonlist; // important for now, since it gets reset after the code is run
|
||||
|
||||
var width = buttonList.buttonSize.x;
|
||||
var height = buttonList.buttonSize.y;
|
||||
|
||||
buttonList.addButton.callback = function() {
|
||||
buttonList.add(new PropertyButton("newProperty", "valueHere", buttonList, width, height, 160, 195, 25));
|
||||
}
|
||||
|
||||
for (i in xml.attributes())
|
||||
buttonList.add(new PropertyButton(i, xml.get(i), buttonList, width, height, 160, 195, 25));
|
||||
</code>
|
||||
</buttonlist>
|
||||
</section>
|
||||
|
||||
<exec>
|
||||
function onSave() {
|
||||
for (i in xml.attributes())
|
||||
xml.remove(i);
|
||||
|
||||
for (val in attributesList.buttons.members)
|
||||
xml.set(val.propertyText.label.text, val.valueText.label.text);
|
||||
}
|
||||
</exec>
|
||||
</window>
|
||||
@@ -0,0 +1,12 @@
|
||||
<!-- praying to god we remove this file later on -->
|
||||
|
||||
<stage zoom="0.9" name="stage" folder="stages/default/" startCamPosY="600" startCamPosX="1000" example="value">
|
||||
<sprite name="normBG" sprite="stagefront" x="-500" y="100" scale="0.75" updateHitbox="true" />
|
||||
<sprite name="noUpdateHitbox" sprite="stagefront" x="-500" y="300" scale="0.75" />
|
||||
<sprite name="ro-tat-ay" sprite="stagefront" x="-500" y="700" scale="0.75" updateHitbox="true" angle="37" />
|
||||
<box name="box" x="0" y="0" width="200" height="200" color="0xFF0000" />
|
||||
<solid name="box2" x="0" y="300" width="200" height="200" color="0x00FF00" />
|
||||
<unknown-sprite />
|
||||
|
||||
<unknown-sprite-1-attr name="test" />
|
||||
</stage>
|
||||
@@ -0,0 +1,317 @@
|
||||
/**
|
||||
* THIS FILE IS TEMPORARY UNTIL TRANSFORMS ARE AT A USEABLE STATE.
|
||||
* TODO:
|
||||
* - !!!! MOVE BACK TO SOURCE !!!!
|
||||
* - Rotation
|
||||
* - Skewing
|
||||
* - Angle + Scale (like already at a angle)
|
||||
*/
|
||||
|
||||
import funkin.editors.stage.StageEditor;
|
||||
import funkin.editors.ui.UIWarningSubstate;
|
||||
import funkin.backend.utils.WindowUtils;
|
||||
import flixel.math.FlxAngle;
|
||||
import openfl.Lib;
|
||||
|
||||
var exID = StageEditor.exID;
|
||||
|
||||
function tryUpdateHitbox(sprite) {
|
||||
var spriteNode = sprite.extra.get(exID("node"));
|
||||
if (spriteNode.exists("updateHitbox") && spriteNode.get("updateHitbox") == "true") {
|
||||
sprite.updateHitbox();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!FlxG.keys.pressed.ALT) {
|
||||
sprite.x = storedPos.x - (sprite.frameWidth * (storedScale.x - sprite.scale.x) * 0.5);
|
||||
sprite.y = storedPos.y - (sprite.frameHeight * (storedScale.y - sprite.scale.y) * 0.5);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
var allowClose = false;
|
||||
function create() {
|
||||
WindowUtils.preventClosing = true;
|
||||
WindowUtils.resetClosing();
|
||||
WindowUtils.onClosing = function() {
|
||||
if(!allowClose)
|
||||
Lib.application.window.onClose.cancel();
|
||||
|
||||
var substate;
|
||||
substate = new UIWarningSubstate("nuh uh", "you dont need to close goofus doofus", [
|
||||
{
|
||||
label: "Actually Exit",
|
||||
onClick: function(t) {
|
||||
allowClose = true;
|
||||
Lib.application.window.close();
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "Reload State",
|
||||
onClick: function(t) {
|
||||
FlxG.switchState(new StageEditor(StageEditor.__stage));
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "oh whoops",
|
||||
onClick: function(t) {
|
||||
substate.close();
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
openSubState(substate);
|
||||
}
|
||||
}
|
||||
|
||||
function destroy() {
|
||||
WindowUtils.preventClosing = false;
|
||||
WindowUtils.resetClosing();
|
||||
WindowUtils.onClosing = null;
|
||||
}
|
||||
|
||||
function update() {
|
||||
if (FlxG.mouse.justPressed)
|
||||
lastRelative.set();
|
||||
|
||||
if(FlxG.keys.justPressed.R) {
|
||||
trace("reloading");
|
||||
FlxG.switchState(new StageEditor(StageEditor.__stage));
|
||||
}
|
||||
}
|
||||
|
||||
function genericScale(sprite, relative, doX, doY) {
|
||||
|
||||
var relativeMult = 1 / (FlxMath.lerp(1, stageCamera.zoom, sprite.zoomFactor) / stageCamera.zoom) * (FlxG.keys.pressed.ALT ? 2 : 1);
|
||||
relative.x *= relativeMult;
|
||||
relative.y *= relativeMult;
|
||||
|
||||
var width = sprite.frameWidth * storedScale.x;
|
||||
var height = sprite.frameHeight * storedScale.y;
|
||||
if(doX) width -= relative.x;
|
||||
if(doY) height -= relative.y;
|
||||
CoolUtil.setGraphicSizeFloat(sprite, width, height);
|
||||
|
||||
if(FlxG.keys.pressed.SHIFT) {
|
||||
var nscale = Math.max(sprite.scale.x, sprite.scale.y);
|
||||
sprite.scale.set(nscale, nscale);
|
||||
}
|
||||
|
||||
var updatedHitbox = tryUpdateHitbox(sprite);
|
||||
if (FlxG.keys.pressed.ALT) {
|
||||
sprite.x = storedPos.x;
|
||||
sprite.y = storedPos.y;
|
||||
if(updatedHitbox) {
|
||||
sprite.x += (sprite.frameWidth * (storedScale.x - sprite.scale.x) * 0.5);
|
||||
sprite.y += (sprite.frameHeight * (storedScale.y - sprite.scale.y) * 0.5);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return !updatedHitbox;
|
||||
}
|
||||
|
||||
function genericOppositeScale(sprite, relative, scaleX, scaleY, repositionX, repositionY) {
|
||||
if(repositionX) relative.x *= -1;
|
||||
if(repositionY) relative.y *= -1;
|
||||
var repositioned = genericScale(sprite, relative, scaleX, scaleY);
|
||||
if (!repositioned) {
|
||||
if(repositionX) sprite.x = storedPos.x + (sprite.frameWidth * (storedScale.x - sprite.scale.x));
|
||||
if(repositionY) sprite.y = storedPos.y + (sprite.frameHeight * (storedScale.y - sprite.scale.y));
|
||||
} else if (!FlxG.keys.pressed.ALT) {
|
||||
if(repositionX) sprite.x += (sprite.frameWidth * (storedScale.x - sprite.scale.x));
|
||||
if(repositionY) sprite.y += (sprite.frameHeight * (storedScale.y - sprite.scale.y));
|
||||
}
|
||||
}
|
||||
|
||||
var oldSpritePos = FlxPoint.get();
|
||||
function SCALE_BOTTOM_RIGHT(sprite, relative) {
|
||||
preRotBullshit(sprite, relative);
|
||||
genericScale(sprite, relative, true, true);
|
||||
postRotBullshit(sprite, relative);
|
||||
}
|
||||
|
||||
/**
|
||||
ROTATION
|
||||
**/
|
||||
function preRotBullshit(sprite, relative) {
|
||||
if (sprite.angle != 0)
|
||||
relative = rotateAround(relative, FlxPoint.weak(0, 0), -sprite.angle);
|
||||
|
||||
if (FlxG.mouse.justPressed) {
|
||||
oldSpritePos.x = sprite.x;
|
||||
oldSpritePos.y = sprite.y;
|
||||
}
|
||||
}
|
||||
|
||||
function postRotBullshit(sprite, relative) {
|
||||
if (sprite.angle != 0) {
|
||||
var p = rotateAround(FlxPoint.get(sprite.x, sprite.y), oldSpritePos, sprite.angle);
|
||||
sprite.x = p.x;
|
||||
sprite.y = p.y;
|
||||
}
|
||||
}
|
||||
|
||||
function rotateAround(p, origin, angle) {
|
||||
var rel = FlxPoint.get(p.x - origin.x, p.y - origin.y);
|
||||
rotateByDegrees(rel, angle);
|
||||
p.x = origin.x + rel.x;
|
||||
p.y = origin.y + rel.y;
|
||||
rel.put();
|
||||
return p;
|
||||
}
|
||||
function rotateByDegrees(p, angle) {
|
||||
var rads = angle * FlxAngle.TO_RAD;
|
||||
var s:Float = Math.sin(rads);
|
||||
var c:Float = Math.cos(rads);
|
||||
var tempX:Float = p.x;
|
||||
|
||||
p.x = tempX * c - p.y * s;
|
||||
p.y = tempX * s + p.y * c;
|
||||
}
|
||||
/**
|
||||
END OF ROTATION
|
||||
**/
|
||||
|
||||
|
||||
function SCALE_TOP_RIGHT(sprite, relative) {
|
||||
preRotBullshit(sprite, relative);
|
||||
genericOppositeScale(sprite, relative, true, true, false, true);
|
||||
postRotBullshit(sprite, relative);
|
||||
}
|
||||
|
||||
function MOVE_CENTER(sprite, relative) {
|
||||
preRotBullshit(sprite, relative);
|
||||
sprite.x = storedPos.x-relative.x;
|
||||
sprite.y = storedPos.y-relative.y;
|
||||
postRotBullshit(sprite, relative);
|
||||
}
|
||||
|
||||
function SCALE_TOP_LEFT(sprite, relative) {
|
||||
preRotBullshit(sprite, relative);
|
||||
genericOppositeScale(sprite, relative, true, true, true, true);
|
||||
postRotBullshit(sprite, relative);
|
||||
}
|
||||
|
||||
function SCALE_BOTTOM_LEFT(sprite, relative) {
|
||||
preRotBullshit(sprite, relative);
|
||||
genericOppositeScale(sprite, relative, true, true, true, false);
|
||||
postRotBullshit(sprite, relative);
|
||||
}
|
||||
|
||||
function SCALE_LEFT(sprite, relative) {
|
||||
preRotBullshit(sprite, relative);
|
||||
genericOppositeScale(sprite, relative, true, false, true, false);
|
||||
postRotBullshit(sprite, relative);
|
||||
}
|
||||
|
||||
function SCALE_RIGHT(sprite, relative) {
|
||||
preRotBullshit(sprite, relative);
|
||||
genericScale(sprite, relative, true, false);
|
||||
postRotBullshit(sprite, relative);
|
||||
}
|
||||
|
||||
function SCALE_TOP(sprite, relative) {
|
||||
preRotBullshit(sprite, relative);
|
||||
genericOppositeScale(sprite, relative, false, true, false, true);
|
||||
postRotBullshit(sprite, relative);
|
||||
}
|
||||
|
||||
function SCALE_BOTTOM(sprite, relative) {
|
||||
preRotBullshit(sprite, relative);
|
||||
genericScale(sprite, relative, false, true);
|
||||
postRotBullshit(sprite, relative);
|
||||
}
|
||||
|
||||
var lastRelative = FlxPoint.get();
|
||||
|
||||
function SKEW_LEFT(sprite, relative) {
|
||||
preRotBullshit(sprite, relative);
|
||||
if (!FlxG.keys.pressed.SHIFT)
|
||||
genericOppositeScale(sprite, relative, true, false, true, false);
|
||||
|
||||
var topLeft = sprite.extra.get(exID("buttonBoxes"))[0];
|
||||
var topRight = sprite.extra.get(exID("buttonBoxes"))[1];
|
||||
|
||||
sprite.y = storedPos.y - relative.y * 0.5;
|
||||
sprite.skew.y = Math.atan2(
|
||||
topRight.y - (topLeft.y + (lastRelative.y - relative.y)),
|
||||
topRight.x - (topLeft.x + (FlxG.keys.pressed.SHIFT ? 0 : relative.x - lastRelative.x))
|
||||
) * FlxAngle.TO_DEG;
|
||||
|
||||
lastRelative.set((FlxG.keys.pressed.SHIFT ? lastRelative.x : relative.x), relative.y);
|
||||
postRotBullshit(sprite, relative);
|
||||
}
|
||||
|
||||
function SKEW_BOTTOM(sprite, relative) {
|
||||
preRotBullshit(sprite, relative);
|
||||
if (!FlxG.keys.pressed.SHIFT)
|
||||
genericScale(sprite, relative, false, true);
|
||||
|
||||
var topLeft = sprite.extra.get(exID("buttonBoxes"))[0];
|
||||
var bottomLeft = sprite.extra.get(exID("buttonBoxes"))[2];
|
||||
|
||||
sprite.x = storedPos.x - relative.x * 0.5;
|
||||
sprite.skew.x = Math.atan2(
|
||||
topLeft.x - (bottomLeft.x + (lastRelative.x - relative.x)),
|
||||
topLeft.y - (bottomLeft.y + (FlxG.keys.pressed.SHIFT ? 0 : lastRelative.y - relative.y))
|
||||
) * FlxAngle.TO_DEG;
|
||||
|
||||
lastRelative.set(relative.x, (FlxG.keys.pressed.SHIFT ? lastRelative.y : relative.y));
|
||||
postRotBullshit(sprite, relative);
|
||||
}
|
||||
|
||||
function SKEW_TOP(sprite, relative) {
|
||||
preRotBullshit(sprite, relative);
|
||||
if (!FlxG.keys.pressed.SHIFT)
|
||||
genericOppositeScale(sprite, relative, false, true, false, true);
|
||||
|
||||
var topLeft = sprite.extra.get(exID("buttonBoxes"))[0];
|
||||
var bottomLeft = sprite.extra.get(exID("buttonBoxes"))[2];
|
||||
|
||||
sprite.x = storedPos.x - relative.x * 0.5;
|
||||
sprite.skew.x = Math.atan2(
|
||||
bottomLeft.x - (topLeft.x + (lastRelative.x - relative.x)),
|
||||
bottomLeft.y - (topLeft.y + (FlxG.keys.pressed.SHIFT ? 0 : relative.y - lastRelative.y))
|
||||
) * FlxAngle.TO_DEG;
|
||||
|
||||
lastRelative.set(relative.x, (FlxG.keys.pressed.SHIFT ? lastRelative.y : relative.y));
|
||||
postRotBullshit(sprite, relative);
|
||||
}
|
||||
|
||||
function SKEW_RIGHT(sprite, relative) {
|
||||
preRotBullshit(sprite, relative);
|
||||
if (!FlxG.keys.pressed.SHIFT)
|
||||
genericScale(sprite, relative, true, false);
|
||||
|
||||
var topLeft = sprite.extra.get(exID("buttonBoxes"))[0];
|
||||
var topRight = sprite.extra.get(exID("buttonBoxes"))[1];
|
||||
|
||||
sprite.y = storedPos.y - relative.y * 0.5;
|
||||
sprite.skew.y = Math.atan2(
|
||||
topLeft.y - (topRight.y + (lastRelative.y - relative.y)),
|
||||
topLeft.x - (topRight.x + (FlxG.keys.pressed.SHIFT ? 0 : lastRelative.x - relative.x))
|
||||
) * FlxAngle.TO_DEG;
|
||||
|
||||
lastRelative.set((FlxG.keys.pressed.SHIFT ? lastRelative.x : relative.x), relative.y);
|
||||
postRotBullshit(sprite, relative);
|
||||
}
|
||||
|
||||
function ROTATE(sprite, relative) {
|
||||
var buttonBoxes:Array<FlxPoint> = sprite.extra.get(exID("buttonBoxes"));
|
||||
var p:FlxPoint = buttonBoxes[8];
|
||||
|
||||
FlxG.mouse.getWorldPosition(stageCamera, _point);
|
||||
|
||||
var dx:Float = _point.x - p.x;
|
||||
var dy:Float = _point.y - p.y;
|
||||
var angle = FlxAngle.angleFromOrigin(dx, dy, true) + angleOffset;
|
||||
if(FlxG.keys.pressed.SHIFT) angle = Std.int(angle / 45) * 45;
|
||||
sprite.angle = angle;
|
||||
}
|
||||
|
||||
|
||||
/*function postDraw() {
|
||||
if(storedCenter.x != 0 || storedCenter.y != 0) {
|
||||
drawLine(storedCenter, storedRelative, 0.3);
|
||||
}
|
||||
}*/
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 335 B After Width: | Height: | Size: 213 B |
Binary file not shown.
|
After Width: | Height: | Size: 793 B |
Binary file not shown.
|
After Width: | Height: | Size: 277 B |
Binary file not shown.
|
After Width: | Height: | Size: 133 B |
Binary file not shown.
|
After Width: | Height: | Size: 1.0 KiB |
+22
-4
@@ -49,7 +49,8 @@ class Main {
|
||||
"If you want a full build which contains all assets, run \"cne release\" or \"cne test-release\"",
|
||||
"Additional arguments will be sent to the lime compiler.",
|
||||
"",
|
||||
"-debug : Builds a debug build."
|
||||
"-debug : Builds a debug build.",
|
||||
"-clean : Compiled files will be deleted before compiling."
|
||||
].join("\n")
|
||||
},
|
||||
{
|
||||
@@ -67,7 +68,20 @@ class Main {
|
||||
"If you want a full build which contains all assets, run \"cne release\" or \"cne test-release\"",
|
||||
"Additional arguments will be sent to the lime compiler.",
|
||||
"",
|
||||
"-debug : Builds a debug build."
|
||||
"-debug : Builds a debug build.",
|
||||
"-clean : Compiled files will be deleted before compiling."
|
||||
].join("\n")
|
||||
},
|
||||
{
|
||||
names: ["run"],
|
||||
doc: "Runs the last build that was created.",
|
||||
func: Compiler.run,
|
||||
dDoc: [
|
||||
"Usage: run <optional arguments>",
|
||||
"",
|
||||
"This will run the last build that was created.",
|
||||
"If the last build was a debug build, you need to pass the -debug argument to run it.",
|
||||
"Additional arguments will be sent to the lime compiler."
|
||||
].join("\n")
|
||||
},
|
||||
{
|
||||
@@ -79,7 +93,9 @@ class Main {
|
||||
"",
|
||||
"This will create and run a final ready-for-release build,",
|
||||
"which means this build will be able to be release on websites such as GameBanana without worrying about source-dependant stuff.",
|
||||
"Additional arguments will be sent to the lime compiler."
|
||||
"Additional arguments will be sent to the lime compiler.",
|
||||
"",
|
||||
"-clean : Compiled files will be deleted before compiling."
|
||||
].join("\n")
|
||||
},
|
||||
{
|
||||
@@ -91,7 +107,9 @@ class Main {
|
||||
"",
|
||||
"This will create and run a final ready-for-release build,",
|
||||
"which means this build will be able to be release on websites such as GameBanana without worrying about source-dependant stuff.",
|
||||
"Additional arguments will be sent to the lime compiler."
|
||||
"Additional arguments will be sent to the lime compiler.",
|
||||
"",
|
||||
"-clean : Compiled files will be deleted before compiling."
|
||||
].join("\n")
|
||||
},
|
||||
{
|
||||
|
||||
@@ -2,19 +2,23 @@ package commands;
|
||||
|
||||
class Compiler {
|
||||
public static function test(args:Array<String>) {
|
||||
__build(args, ["test", getBuildTarget(), "-DTEST_BUILD"]);
|
||||
__runLime(args, ["test", getBuildTarget(), "-DTEST_BUILD"]);
|
||||
}
|
||||
public static function build(args:Array<String>) {
|
||||
__build(args, ["build", getBuildTarget(), "-DTEST_BUILD"]);
|
||||
__runLime(args, ["build", getBuildTarget(), "-DTEST_BUILD"]);
|
||||
}
|
||||
public static function release(args:Array<String>) {
|
||||
__build(args, ["build", getBuildTarget()]);
|
||||
__runLime(args, ["build", getBuildTarget()]);
|
||||
}
|
||||
public static function testRelease(args:Array<String>) {
|
||||
__build(args, ["test", getBuildTarget()]);
|
||||
__runLime(args, ["test", getBuildTarget()]);
|
||||
}
|
||||
|
||||
private static function __build(args:Array<String>, arg:Array<String>) {
|
||||
public static function run(args:Array<String>) {
|
||||
__runLime(args, ["run", getBuildTarget()]);
|
||||
}
|
||||
|
||||
private static function __runLime(args:Array<String>, arg:Array<String>) {
|
||||
for(a in args)
|
||||
arg.push(a);
|
||||
Sys.command("lime", arg);
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<git name="flixel-addons" url="https://github.com/FNF-CNE-Devs/flixel-addons" />
|
||||
|
||||
<!-- Other Libraries -->
|
||||
<git name="hscript-improved" url="https://github.com/FNF-CNE-Devs/hscript-improved" ref="custom-classes" />
|
||||
<git name="hscript-improved" url="https://github.com/FNF-CNE-Devs/hscript-improved" />
|
||||
<git name="flxanimate" url="https://github.com/FNF-CNE-Devs/flxanimate" />
|
||||
<git name="hxdiscord_rpc" url="https://github.com/FNF-CNE-Devs/hxdiscord_rpc" />
|
||||
<lib name="hxvlc" />
|
||||
|
||||
Vendored
+12
@@ -0,0 +1,12 @@
|
||||
package external;
|
||||
|
||||
#if mac
|
||||
@:build(external.LinkerMacro.xml('external_code.xml'))
|
||||
@:include('Mac.h')
|
||||
@:native('ExternalMac')
|
||||
extern class ExternalMac
|
||||
{
|
||||
@:native('ExternalMac::setCursorIcon')
|
||||
public static function setCursorIcon(icon:Int, customCursor:cpp.ConstCharStar, customX:Single, customY:Single):Bool;
|
||||
}
|
||||
#end
|
||||
Vendored
+40
@@ -0,0 +1,40 @@
|
||||
package external;
|
||||
|
||||
import haxe.io.Path;
|
||||
import haxe.macro.Expr;
|
||||
import haxe.macro.Context;
|
||||
import haxe.xml.Printer;
|
||||
import sys.FileSystem;
|
||||
|
||||
class LinkerMacro
|
||||
{
|
||||
public static macro function xml(filename:String = 'Build.xml'):Array<Field>
|
||||
{
|
||||
var pos:Position = Context.currentPos();
|
||||
|
||||
var sourcePath:String = Path.directory(Context.getPosInfos(pos).file);
|
||||
|
||||
if (!Path.isAbsolute(sourcePath))
|
||||
sourcePath = FileSystem.absolutePath(sourcePath);
|
||||
|
||||
sourcePath = Path.removeTrailingSlashes(Path.normalize(sourcePath));
|
||||
|
||||
var includeElement:Xml = Xml.createElement('include');
|
||||
|
||||
includeElement.set('name', Path.join([sourcePath, filename]));
|
||||
|
||||
@:privateAccess
|
||||
{
|
||||
var printer:Printer = new Printer(true);
|
||||
|
||||
printer.writeNode(includeElement, '\n');
|
||||
|
||||
Context.getLocalClass().get().meta.add(':buildXml', [{
|
||||
expr: EConst(CString(printer.output.toString())),
|
||||
pos: pos
|
||||
}], pos);
|
||||
}
|
||||
|
||||
return Context.getBuildFields();
|
||||
}
|
||||
}
|
||||
Vendored
+32
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<xml>
|
||||
<pragma once="true" />
|
||||
|
||||
<files id="haxe">
|
||||
<compilerflag value="-I${this_dir}/include/mac" if="macos" />
|
||||
</files>
|
||||
|
||||
<files id="__main__">
|
||||
<compilerflag value="-I${this_dir}/include/mac" if="macos" />
|
||||
</files>
|
||||
|
||||
<files id="external-code">
|
||||
<section if="macos">
|
||||
<compilerflag value="-I${this_dir}/include/mac" />
|
||||
|
||||
<file name="${this_dir}/src/mac/CursorHelper.mm" />
|
||||
<file name="${this_dir}/src/mac/Mac.mm" />
|
||||
</section>
|
||||
</files>
|
||||
|
||||
<target id="haxe">
|
||||
<section if="macos">
|
||||
<vflag name="-framework" value="IOKit" />
|
||||
<vflag name="-framework" value="AppKit" />
|
||||
<vflag name="-framework" value="Cocoa" />
|
||||
<vflag name="-framework" value="Foundation" />
|
||||
</section>
|
||||
|
||||
<files id="external-code" />
|
||||
</target>
|
||||
</xml>
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface CursorHelper : NSObject
|
||||
|
||||
+ (NSCursor *)getCursorForSelector:(SEL)selector defaultCursor:(NSCursor *)defaultCursor;
|
||||
|
||||
@end
|
||||
Vendored
+13
@@ -0,0 +1,13 @@
|
||||
#ifndef MAC_H
|
||||
#define MAC_H
|
||||
|
||||
#if defined(__APPLE__)
|
||||
|
||||
namespace ExternalMac
|
||||
{
|
||||
bool setCursorIcon(int icon, const char *customCursor, float customX, float customY);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "CursorHelper.h"
|
||||
|
||||
@implementation CursorHelper
|
||||
|
||||
+ (NSCursor *)getCursorForSelector:(SEL)selector defaultCursor:(NSCursor *)defaultCursor
|
||||
{
|
||||
NSCursor *cursor = nil;
|
||||
|
||||
if ([NSCursor respondsToSelector:selector])
|
||||
{
|
||||
cursor = [NSCursor performSelector:selector];
|
||||
}
|
||||
else
|
||||
{
|
||||
NSLog(@"CursorHelper: selector %@ not found", NSStringFromSelector(selector));
|
||||
|
||||
cursor = defaultCursor;
|
||||
}
|
||||
|
||||
return cursor;
|
||||
}
|
||||
|
||||
@end
|
||||
Vendored
+140
@@ -0,0 +1,140 @@
|
||||
#include <unordered_map>
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "CursorHelper.h"
|
||||
|
||||
#define ARROW 0
|
||||
#define CROSSHAIR 1
|
||||
#define CLICK 2
|
||||
#define IBEAM 3
|
||||
#define MOVE 4
|
||||
#define HAND 5
|
||||
#define DRAG 6
|
||||
#define DRAG_OPEN 7
|
||||
#define WAIT 8
|
||||
#define WAIT_ARROW 9
|
||||
#define DISABLED 10
|
||||
#define RESIZE_TR 11
|
||||
#define RESIZE_BL 12
|
||||
#define RESIZE_TL 13
|
||||
#define RESIZE_BR 14
|
||||
#define RESIZE_H 15
|
||||
#define RESIZE_V 16
|
||||
#define RESIZE_T 17
|
||||
#define RESIZE_B 18
|
||||
#define RESIZE_L 19
|
||||
#define RESIZE_R 20
|
||||
#define RESIZE_TLBR 21
|
||||
#define RESIZE_TRBL 22
|
||||
#define CUSTOM -1
|
||||
|
||||
namespace ExternalMac
|
||||
{
|
||||
std::unordered_map<const char *, NSCursor *> customCursorCache;
|
||||
|
||||
bool setCursorIcon(int icon, const char *customCursor, float customX, float customY)
|
||||
{
|
||||
NSCursor *cursor = nil;
|
||||
|
||||
switch (icon)
|
||||
{
|
||||
case ARROW:
|
||||
cursor = [NSCursor arrowCursor];
|
||||
break;
|
||||
case CLICK:
|
||||
cursor = [NSCursor pointingHandCursor];
|
||||
break;
|
||||
case CROSSHAIR:
|
||||
cursor = [NSCursor crosshairCursor];
|
||||
break;
|
||||
case HAND:
|
||||
cursor = [NSCursor closedHandCursor];
|
||||
break;
|
||||
case IBEAM:
|
||||
cursor = [NSCursor IBeamCursor];
|
||||
break;
|
||||
case RESIZE_H:
|
||||
cursor = [NSCursor resizeLeftRightCursor];
|
||||
break;
|
||||
case RESIZE_V:
|
||||
cursor = [NSCursor resizeUpDownCursor];
|
||||
break;
|
||||
// TODO: DEFAULTS
|
||||
case RESIZE_TL:
|
||||
cursor = [CursorHelper getCursorForSelector:@selector(_windowResizeNorthWestCursor)defaultCursor:nil];
|
||||
break;
|
||||
case RESIZE_TR:
|
||||
cursor = [CursorHelper getCursorForSelector:@selector(_windowResizeNorthEastCursor)defaultCursor:nil];
|
||||
break;
|
||||
case RESIZE_BL:
|
||||
cursor = [CursorHelper getCursorForSelector:@selector(_windowResizeSouthWestCursor)defaultCursor:nil];
|
||||
break;
|
||||
case RESIZE_BR:
|
||||
cursor = [CursorHelper getCursorForSelector:@selector(_windowResizeSouthEastCursor)defaultCursor:nil];
|
||||
break;
|
||||
case RESIZE_T:
|
||||
cursor = [CursorHelper getCursorForSelector:@selector(_windowResizeNorthCursor)defaultCursor:nil];
|
||||
break;
|
||||
case RESIZE_B:
|
||||
cursor = [CursorHelper getCursorForSelector:@selector(_windowResizeSouthCursor)defaultCursor:nil];
|
||||
break;
|
||||
case RESIZE_L:
|
||||
cursor = [CursorHelper getCursorForSelector:@selector(_windowResizeWestCursor)defaultCursor:nil];
|
||||
break;
|
||||
case RESIZE_R:
|
||||
cursor = [CursorHelper getCursorForSelector:@selector(_windowResizeEastCursor)defaultCursor:nil];
|
||||
break;
|
||||
case RESIZE_TLBR:
|
||||
cursor = [CursorHelper getCursorForSelector:@selector(_windowResizeNorthWestSouthEastCursor)defaultCursor:nil];
|
||||
break;
|
||||
case RESIZE_TRBL:
|
||||
cursor = [CursorHelper getCursorForSelector:@selector(_windowResizeNorthEastSouthWestCursor)defaultCursor:nil];
|
||||
break;
|
||||
case WAIT:
|
||||
cursor = [CursorHelper getCursorForSelector:@selector(_waitCursor)defaultCursor:nil];
|
||||
break;
|
||||
case WAIT_ARROW:
|
||||
cursor = [CursorHelper getCursorForSelector:@selector(_waitCursor)defaultCursor:nil];
|
||||
break;
|
||||
case DISABLED:
|
||||
cursor = [NSCursor operationNotAllowedCursor];
|
||||
break;
|
||||
case DRAG:
|
||||
cursor = [NSCursor closedHandCursor];
|
||||
break;
|
||||
case DRAG_OPEN:
|
||||
cursor = [NSCursor openHandCursor];
|
||||
break;
|
||||
case CUSTOM:
|
||||
if (customCursorCache.find(customCursor) != customCursorCache.end())
|
||||
cursor = customCursorCache[customCursor];
|
||||
else
|
||||
{
|
||||
NSString *cursorImageName = [NSString stringWithUTF8String:customCursor];
|
||||
NSImage *tmpImage = [[NSImage alloc] initWithContentsOfFile:cursorImageName];
|
||||
|
||||
if (tmpImage != nil)
|
||||
{
|
||||
cursor = [[NSCursor alloc] initWithImage:tmpImage hotSpot:NSMakePoint(customX, customY)];
|
||||
customCursorCache[customCursor] = cursor;
|
||||
[tmpImage release];
|
||||
}
|
||||
else
|
||||
NSLog(@"Failed to load image %@", cursorImageName);
|
||||
|
||||
[cursorImageName release];
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (cursor != nil)
|
||||
{
|
||||
[cursor set];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,15 @@ enum abstract XMLAnimType(Int)
|
||||
default: def;
|
||||
}
|
||||
}
|
||||
|
||||
@:to public function toString():String {
|
||||
return switch (cast this)
|
||||
{
|
||||
case NONE: "none";
|
||||
case BEAT: "beat";
|
||||
case LOOP: "loop";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements IOffsetCompatible implements IXMLEvents
|
||||
@@ -39,9 +48,10 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I
|
||||
public var beatAnims:Array<BeatAnim> = [];
|
||||
public var name:String;
|
||||
public var zoomFactor:Float = 1;
|
||||
public var initialZoom:Float = 1;
|
||||
public var debugMode:Bool = false;
|
||||
public var animDatas:Map<String, AnimData> = [];
|
||||
public var animEnabled:Bool = true;
|
||||
public var zoomFactorEnabled:Bool = true;
|
||||
|
||||
/**
|
||||
* ODD interval -> asynced; EVEN interval -> synced
|
||||
@@ -86,6 +96,7 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I
|
||||
spr.skew.set(source.skew.x, source.skew.y);
|
||||
spr.transformMatrix = source.transformMatrix;
|
||||
spr.matrixExposed = source.matrixExposed;
|
||||
spr.zoomFactor = source.zoomFactor;
|
||||
spr.animOffsets = source.animOffsets.copy();
|
||||
}
|
||||
return spr;
|
||||
@@ -120,6 +131,7 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I
|
||||
private var countedBeat = 0;
|
||||
public function beatHit(curBeat:Int)
|
||||
{
|
||||
if(!animEnabled) return;
|
||||
if (beatAnims.length > 0 && (curBeat + beatOffset) % beatInterval == 0)
|
||||
{
|
||||
if(skipNegativeBeats && curBeat < 0) return;
|
||||
@@ -138,49 +150,6 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I
|
||||
{
|
||||
}
|
||||
|
||||
public override function getScreenBounds(?newRect:FlxRect, ?camera:FlxCamera):FlxRect
|
||||
{
|
||||
__doPreZoomScaleProcedure(camera);
|
||||
var r = super.getScreenBounds(newRect, camera);
|
||||
__doPostZoomScaleProcedure();
|
||||
return r;
|
||||
}
|
||||
|
||||
public override function drawComplex(camera:FlxCamera)
|
||||
{
|
||||
super.drawComplex(camera);
|
||||
}
|
||||
|
||||
public override function doAdditionalMatrixStuff(matrix:FlxMatrix, camera:FlxCamera)
|
||||
{
|
||||
super.doAdditionalMatrixStuff(matrix, camera);
|
||||
matrix.translate(-camera.width / 2, -camera.height / 2);
|
||||
|
||||
var requestedZoom = FlxMath.lerp(1, camera.zoom, zoomFactor);
|
||||
var diff = requestedZoom / camera.zoom;
|
||||
matrix.scale(diff, diff);
|
||||
matrix.translate(camera.width / 2, camera.height / 2);
|
||||
}
|
||||
|
||||
public override function getScreenPosition(?point:FlxPoint, ?Camera:FlxCamera):FlxPoint
|
||||
{
|
||||
if (__shouldDoScaleProcedure())
|
||||
{
|
||||
__oldScrollFactor.set(scrollFactor.x, scrollFactor.y);
|
||||
var requestedZoom = FlxMath.lerp(initialZoom, camera.zoom, zoomFactor);
|
||||
var diff = requestedZoom / camera.zoom;
|
||||
|
||||
scrollFactor.scale(1 / diff);
|
||||
|
||||
var r = super.getScreenPosition(point, Camera);
|
||||
|
||||
scrollFactor.set(__oldScrollFactor.x, __oldScrollFactor.y);
|
||||
|
||||
return r;
|
||||
}
|
||||
return super.getScreenPosition(point, Camera);
|
||||
}
|
||||
|
||||
// ANIMATE ATLAS DRAWING
|
||||
#if REGION
|
||||
public override function draw()
|
||||
@@ -236,33 +205,60 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I
|
||||
}
|
||||
#end
|
||||
|
||||
// SCALING FUNCS
|
||||
#if REGION
|
||||
private inline function __shouldDoScaleProcedure()
|
||||
return zoomFactor != 1;
|
||||
// ZOOM FACTOR
|
||||
private inline function __shouldDoZoomFactor()
|
||||
return zoomFactorEnabled && zoomFactor != 1;
|
||||
|
||||
static var __oldScrollFactor:FlxPoint = new FlxPoint();
|
||||
static var __oldScale:FlxPoint = new FlxPoint();
|
||||
var __skipZoomProcedure:Bool = false;
|
||||
|
||||
private function __doPreZoomScaleProcedure(camera:FlxCamera)
|
||||
public override function getScreenBounds(?newRect:FlxRect, ?camera:FlxCamera):FlxRect
|
||||
{
|
||||
if (__skipZoomProcedure = !__shouldDoScaleProcedure())
|
||||
return;
|
||||
__oldScale.set(scale.x, scale.y);
|
||||
var requestedZoom = FlxMath.lerp(initialZoom, camera.zoom, zoomFactor);
|
||||
var diff = requestedZoom * camera.zoom;
|
||||
if (camera == null)
|
||||
camera = FlxG.camera;
|
||||
|
||||
scale.scale(diff);
|
||||
var r = super.getScreenBounds(newRect, camera);
|
||||
|
||||
if(__shouldDoZoomFactor()) {
|
||||
r.x -= camera.width / 2;
|
||||
r.y -= camera.height / 2;
|
||||
|
||||
var ratio = (camera.zoom > 0 ? Math.max : Math.min)(0, FlxMath.lerp(1 / camera.zoom, 1, zoomFactor));
|
||||
r.x *= ratio;
|
||||
r.y *= ratio;
|
||||
r.width *= ratio;
|
||||
r.height *= ratio;
|
||||
|
||||
r.x += camera.width / 2;
|
||||
r.y += camera.height / 2;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
private function __doPostZoomScaleProcedure()
|
||||
override public function isOnScreen(?camera:FlxCamera):Bool
|
||||
{
|
||||
if (__skipZoomProcedure)
|
||||
return;
|
||||
scale.set(__oldScale.x, __oldScale.y);
|
||||
if (forceIsOnScreen)
|
||||
return true;
|
||||
|
||||
if (camera == null)
|
||||
camera = FlxG.camera;
|
||||
|
||||
var bounds = getScreenBounds(_rect, camera);
|
||||
if (bounds.width == 0 && bounds.height == 0)
|
||||
return false;
|
||||
return camera.containsRect(bounds);
|
||||
}
|
||||
|
||||
// ZOOM FACTOR RENDERING
|
||||
public override function doAdditionalMatrixStuff(matrix:FlxMatrix, camera:FlxCamera)
|
||||
{
|
||||
super.doAdditionalMatrixStuff(matrix, camera);
|
||||
if(__shouldDoZoomFactor()) {
|
||||
matrix.translate(-camera.width / 2, -camera.height / 2);
|
||||
|
||||
var requestedZoom = (camera.zoom >= 0 ? Math.max : Math.min)(FlxMath.lerp(1, camera.zoom, zoomFactor), 0);
|
||||
var diff = requestedZoom / camera.zoom;
|
||||
matrix.scale(diff, diff);
|
||||
matrix.translate(camera.width / 2, camera.height / 2);
|
||||
}
|
||||
}
|
||||
#end
|
||||
|
||||
// OFFSETTING
|
||||
#if REGION
|
||||
@@ -350,6 +346,11 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I
|
||||
public inline function isAnimFinished() {
|
||||
return animateAtlas != null ? (animateAtlas.anim.finished) : (animation.curAnim != null ? animation.curAnim.finished : true);
|
||||
}
|
||||
|
||||
override function updateAnimation(elapsed:Float) {
|
||||
if (animEnabled)
|
||||
super.updateAnimation(elapsed);
|
||||
}
|
||||
#end
|
||||
|
||||
// Getter / Setters
|
||||
|
||||
@@ -97,13 +97,12 @@ class Paths
|
||||
return getPath('images/$key.$ext', library);
|
||||
}
|
||||
|
||||
inline static public function script(key:String, ?library:String, isAssetsPath:Bool = false) {
|
||||
static public function script(key:String, ?library:String, isAssetsPath:Bool = false) {
|
||||
var scriptPath = isAssetsPath ? key : getPath(key, library);
|
||||
if (!OpenFlAssets.exists(scriptPath)) {
|
||||
var p:String;
|
||||
for(ex in Script.scriptExtensions) {
|
||||
p = '$scriptPath.$ex';
|
||||
if (OpenFlAssets.exists(p)) {
|
||||
if (OpenFlAssets.exists(p = scriptPath + '.' + ex)) {
|
||||
scriptPath = p;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ class EventsData {
|
||||
|
||||
for (file in Paths.getFolderContent('data/events/', true, BOTH)) {
|
||||
if (Path.extension(file) != "json" && Path.extension(file) != "pack") continue;
|
||||
var eventName:String = Path.withoutExtension(Path.withoutDirectory(file));
|
||||
var eventName:String = CoolUtil.getFilename(file);
|
||||
var fileTxt:String = Assets.getText(file);
|
||||
|
||||
if (Path.extension(file) == "pack") {
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
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 FakeCamera extends FlxCamera {
|
||||
public static final instance = new FakeCamera();
|
||||
|
||||
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) {}
|
||||
|
||||
// override function set_zoom(Zoom:Float):Float return zoom = (Zoom == 0) ? FlxCamera.defaultZoom : Zoom;
|
||||
|
||||
public override function setScale(X:Float, Y:Float):Void {}
|
||||
}
|
||||
@@ -17,7 +17,7 @@ class SystemInfo extends FramerateCategory {
|
||||
|
||||
static var __formattedSysText:String = "";
|
||||
|
||||
public static inline function init() {
|
||||
public static function init() {
|
||||
#if linux
|
||||
var process = new HiddenProcess("cat", ["/etc/os-release"]);
|
||||
if (process.exitCode() != 0) Logs.trace('Unable to grab OS Label', ERROR, RED);
|
||||
|
||||
@@ -133,10 +133,8 @@ class CoolUtil
|
||||
* Whenever a value is NaN or not.
|
||||
* @param v Value
|
||||
*/
|
||||
public static inline function isNaN(v:Dynamic) {
|
||||
if (v is Float || v is Int)
|
||||
return Math.isNaN(cast(v, Float));
|
||||
return false;
|
||||
public static inline function isNaN(v:Dynamic):Bool {
|
||||
return (v is Float) ? Math.isNaN(cast(v, Float)) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -185,20 +183,21 @@ class CoolUtil
|
||||
return str;
|
||||
}
|
||||
|
||||
private static var sizeLabels:Array<String> = ["B", "KB", "MB", "GB", "TB"];
|
||||
|
||||
/**
|
||||
* Returns a string representation of a size, following this format: `1.02 GB`, `134.00 MB`
|
||||
* @param size Size to convert to string
|
||||
* @return String Result string representation
|
||||
*/
|
||||
public static function getSizeString(size:Float):String {
|
||||
var labels = ["B", "KB", "MB", "GB", "TB"];
|
||||
var rSize:Float = size;
|
||||
var label:Int = 0;
|
||||
while(rSize > 1024 && label < labels.length-1) {
|
||||
while(rSize > 1024 && label < sizeLabels.length-1) {
|
||||
label++;
|
||||
rSize /= 1024;
|
||||
}
|
||||
return '${Std.int(rSize) + "." + addZeros(Std.string(Std.int((rSize % 1) * 100)), 2)}${labels[label]}';
|
||||
return Std.int(rSize) + "." + addZeros(Std.string(Std.int((rSize % 1) * 100)), 2) + sizeLabels[label];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -416,6 +415,21 @@ class CoolUtil
|
||||
sprite.scale.set(nScale, nScale);
|
||||
}
|
||||
|
||||
public static function setGraphicSizeFloat(sprite:FlxSprite, Width:Float = 0, Height:Float = 0):Void
|
||||
{
|
||||
if (Width <= 0 && Height <= 0)
|
||||
return;
|
||||
|
||||
var newScaleX:Float = Width / sprite.frameWidth;
|
||||
var newScaleY:Float = Height / sprite.frameHeight;
|
||||
sprite.scale.set(newScaleX, newScaleY);
|
||||
|
||||
if (Width <= 0)
|
||||
sprite.scale.x = newScaleY;
|
||||
else if (Height <= 0)
|
||||
sprite.scale.y = newScaleX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a simple string representation of a FlxKey. Used in Controls options.
|
||||
* @param key Key
|
||||
@@ -705,6 +719,16 @@ class CoolUtil
|
||||
@:noUsing public static inline function flxeaseFromString(mainEase:String, suffix:String)
|
||||
return Reflect.field(FlxEase, mainEase + (mainEase == "linear" ? "" : suffix));
|
||||
|
||||
/*
|
||||
* Returns the filename of a path, without the extension.
|
||||
* @param path Path to get the filename from
|
||||
* @return Filename
|
||||
*/
|
||||
@:noUsing public static inline function getFilename(file:String) {
|
||||
var file = new haxe.io.Path(file);
|
||||
return file.file;
|
||||
}
|
||||
|
||||
public static function sortAlphabetically(array:Array<String>, ?lowercase:Bool=false) {
|
||||
array.sort(function(a1, a2):Int {
|
||||
if(lowercase) {
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
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.FakeCamera.instance;
|
||||
nc.zoom = camera.zoom;
|
||||
nc.scroll.set(camera.scroll.x, camera.scroll.y);
|
||||
nc.pixelPerfectRender = camera.pixelPerfectRender;
|
||||
@:privateAccess sprite.drawComplex(nc);
|
||||
|
||||
var isFunkinSprite = sprite is FunkinSprite;
|
||||
|
||||
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;
|
||||
|
||||
if(isFunkinSprite) {
|
||||
var sprite:FunkinSprite = cast sprite;
|
||||
|
||||
var ratio = 1 - FlxMath.lerp(1 / camera.zoom, 1, sprite.zoomFactor);
|
||||
x += camera.width / 2 * ratio;
|
||||
y += camera.height / 2 * ratio;
|
||||
}
|
||||
point.set(x, y);
|
||||
}
|
||||
return points;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
package funkin.backend.utils;
|
||||
|
||||
import haxe.macro.Expr.Case;
|
||||
import openfl.ui.Mouse;
|
||||
import openfl.ui.MouseCursor;
|
||||
import funkin.backend.utils.native.*;
|
||||
|
||||
/**
|
||||
@@ -72,6 +75,17 @@ class NativeAPI {
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
* Set cursor icon.
|
||||
**/
|
||||
public static function setCursorIcon(icon:CodeCursor) {
|
||||
#if mac
|
||||
Mac.setMouseCursorIcon(cast icon);
|
||||
#else
|
||||
Mouse.cursor = icon.toOpenFL();
|
||||
#end
|
||||
}
|
||||
|
||||
public static function consoleColorToANSI(color:ConsoleColor) {
|
||||
return switch(color) {
|
||||
case BLACK: 30;
|
||||
@@ -141,4 +155,102 @@ enum abstract MessageBoxIcon(Int) {
|
||||
var MSG_QUESTION = 0x00000020;
|
||||
var MSG_WARNING = 0x00000030;
|
||||
var MSG_INFORMATION = 0x00000040;
|
||||
}
|
||||
|
||||
enum abstract CodeCursor(String) {
|
||||
var CUSTOM;// = "arrow";
|
||||
var ARROW;// = "arrow";
|
||||
var CLICK;// = "click";
|
||||
var CROSSHAIR;// = "crosshair";
|
||||
var HAND;// = "hand";
|
||||
var IBEAM;// = "ibeam";
|
||||
var MOVE;// = "move";
|
||||
|
||||
var RESIZE_H;// = "resize_we";
|
||||
var RESIZE_V;// = "resize_ns";
|
||||
var RESIZE_TL;// = "resize_nw";
|
||||
var RESIZE_TR;// = "resize_ne";
|
||||
var RESIZE_BL;// = "resize_sw";
|
||||
var RESIZE_BR;// = "resize_se";
|
||||
var RESIZE_T;// = "resize_n";
|
||||
var RESIZE_B;// = "resize_b";
|
||||
var RESIZE_L;// = "resize_w";
|
||||
var RESIZE_R;// = "resize_e";
|
||||
|
||||
var RESIZE_TLBR;// = "resize_nw_se";
|
||||
var RESIZE_TRBL;// = "resize_ne_sw";
|
||||
|
||||
var WAIT;// = "wait";
|
||||
var WAIT_ARROW;// = "waitarrow";
|
||||
var DISABLED;// = "disabled";
|
||||
var DRAG;// = "drag";
|
||||
var DRAG_OPEN;// = "dragopen";
|
||||
|
||||
@:to public function toOpenFL():MouseCursor {
|
||||
return @:privateAccess switch(cast this) {
|
||||
case ARROW: MouseCursor.ARROW;
|
||||
case CROSSHAIR: MouseCursor.__CROSSHAIR;
|
||||
case CLICK: MouseCursor.BUTTON;
|
||||
case IBEAM: MouseCursor.IBEAM;
|
||||
case MOVE: MouseCursor.__MOVE;
|
||||
case HAND: MouseCursor.HAND;
|
||||
case DRAG: MouseCursor.HAND;
|
||||
case DRAG_OPEN: MouseCursor.ARROW; // Could be HAND, but it might be better to use ARROW since on windows it would be weird to have the dragging cursor
|
||||
case WAIT: MouseCursor.__WAIT;
|
||||
case WAIT_ARROW: MouseCursor.__WAIT_ARROW;
|
||||
|
||||
case DISABLED: MouseCursor.ARROW;
|
||||
|
||||
case RESIZE_TR: MouseCursor.__RESIZE_NESW;
|
||||
case RESIZE_BL: MouseCursor.__RESIZE_NESW;
|
||||
case RESIZE_TL: MouseCursor.__RESIZE_NWSE;
|
||||
case RESIZE_BR: MouseCursor.__RESIZE_NWSE;
|
||||
case RESIZE_H: MouseCursor.__RESIZE_WE;
|
||||
case RESIZE_V: MouseCursor.__RESIZE_NS;
|
||||
|
||||
case RESIZE_T: MouseCursor.__RESIZE_NS;
|
||||
case RESIZE_B: MouseCursor.__RESIZE_NS;
|
||||
case RESIZE_L: MouseCursor.__RESIZE_WE;
|
||||
case RESIZE_R: MouseCursor.__RESIZE_WE;
|
||||
|
||||
case RESIZE_TLBR: MouseCursor.__RESIZE_NWSE;
|
||||
case RESIZE_TRBL: MouseCursor.__RESIZE_NESW;
|
||||
case CUSTOM: MouseCursor.__CUSTOM;
|
||||
//default: ARROW;
|
||||
}
|
||||
}
|
||||
|
||||
@:to public function toInt():Int {
|
||||
return switch(cast this) {
|
||||
case ARROW: 0;
|
||||
case CROSSHAIR: 1;
|
||||
case CLICK: 2;
|
||||
case IBEAM: 3;
|
||||
case MOVE: 4;
|
||||
case HAND: 5;
|
||||
case DRAG: 6;
|
||||
case DRAG_OPEN: 7;
|
||||
case WAIT: 8;
|
||||
case WAIT_ARROW: 9;
|
||||
|
||||
case DISABLED: 10;
|
||||
|
||||
case RESIZE_TR: 11;
|
||||
case RESIZE_BL: 12;
|
||||
case RESIZE_TL: 13;
|
||||
case RESIZE_BR: 14;
|
||||
case RESIZE_H: 15;
|
||||
case RESIZE_V: 16;
|
||||
|
||||
case RESIZE_T: 17;
|
||||
case RESIZE_B: 18;
|
||||
case RESIZE_L: 19;
|
||||
case RESIZE_R: 20;
|
||||
|
||||
case RESIZE_TLBR: 21;
|
||||
case RESIZE_TRBL: 22;
|
||||
|
||||
case CUSTOM: -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@ class WindowUtils {
|
||||
static var __triedClosing:Bool = false;
|
||||
public static inline function resetClosing() __triedClosing = false;
|
||||
|
||||
public static inline function init() {
|
||||
public static function init() {
|
||||
resetTitle();
|
||||
resetClosing();
|
||||
|
||||
|
||||
@@ -150,10 +150,15 @@ class XMLUtil {
|
||||
if(node.has.color)
|
||||
spr.color = FlxColor.fromString(node.getAtt("color")).getDefault(0xFFFFFFFF);
|
||||
|
||||
if(node.has.angle)
|
||||
spr.angle = Std.parseFloat(node.getAtt("angle")).getDefault(spr.angle);
|
||||
|
||||
if (node.has.playOnCountdown)
|
||||
spr.skipNegativeBeats = node.att.playOnCountdown == "true";
|
||||
if (node.has.beatInterval)
|
||||
spr.beatInterval = Std.parseInt(node.att.beatInterval);
|
||||
if (node.has.interval)
|
||||
spr.beatInterval = Std.parseInt(node.att.interval);
|
||||
if (node.has.beatOffset)
|
||||
spr.beatOffset = Std.parseInt(node.att.beatOffset);
|
||||
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
package funkin.backend.utils.native;
|
||||
|
||||
#if mac
|
||||
@:cppFileCode("#include <sys/sysctl.h>")
|
||||
class Mac {
|
||||
import funkin.backend.utils.NativeAPI.CodeCursor;
|
||||
import openfl.ui.Mouse;
|
||||
|
||||
@:headerInclude('sys/sysctl.h')
|
||||
class Mac
|
||||
{
|
||||
@:functionCode('
|
||||
int mib [] = { CTL_HW, HW_MEMSIZE };
|
||||
int64_t value = 0;
|
||||
@@ -17,5 +21,39 @@ class Mac {
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static function setMouseCursorIcon(icon:CodeCursor):Void
|
||||
{
|
||||
final valid:Bool = external.ExternalMac.setCursorIcon(icon.toInt(), null, 0, 0);
|
||||
|
||||
if (!valid)
|
||||
Mouse.cursor = icon.toOpenFL();
|
||||
}
|
||||
}
|
||||
#end
|
||||
#end
|
||||
|
||||
|
||||
/*
|
||||
|
||||
Cursor
|
||||
|
||||
Description
|
||||
The arrow cursor (arrow)
|
||||
The I-beam cursor for indicating insertion points (iBeam)
|
||||
The cross-hair cursor (crosshair)
|
||||
The closed-hand cursor (closedHand)
|
||||
The open-hand cursor (openHand)
|
||||
The pointing-hand cursor (pointingHand)
|
||||
The resize-left cursor (resizeLeft)
|
||||
The resize-right cursor (resizeRight)
|
||||
The resize-left-and-right cursor (resizeLeftRight)
|
||||
The resize-up cursor (resizeUp)
|
||||
The resize-down cursor (resizeDown)
|
||||
The resize-up-and-down cursor (resizeUpDown)
|
||||
The disappearing item cursor (disappearingItem)
|
||||
The I-Beam text cursor for vertical layout (iBeamCursorForVerticalLayout).
|
||||
The not allowed cursor (operationNotAllowed).
|
||||
The drag link cursor (dragLink).
|
||||
The drag copy cursor (dragCopy).
|
||||
The contextual menu cursor (contextualMenu).
|
||||
*/
|
||||
|
||||
@@ -20,7 +20,7 @@ class EditorPicker extends MusicBeatSubstate {
|
||||
{
|
||||
name: "Stage Editor",
|
||||
iconID: 2,
|
||||
state: null
|
||||
state: funkin.editors.stage.StageSelection
|
||||
},
|
||||
{
|
||||
name: "UI Debug State",
|
||||
|
||||
@@ -44,7 +44,10 @@ class CharacterAnimButtons extends UIButton {
|
||||
editButton.autoAlpha = false;
|
||||
members.push(editButton);
|
||||
|
||||
editIcon = new FlxSprite(editButton.x + 8, editButton.y + 8).loadGraphic(Paths.image('editors/character/edit-button'));
|
||||
editIcon = new FlxSprite(editButton.x + 8, editButton.y + 8).loadGraphic(Paths.image('editors/character/edit-button'), true, 16, 16);
|
||||
editIcon.animation.add("edit", [0]);
|
||||
editIcon.animation.add("advanced", [1]);
|
||||
editIcon.animation.play("edit");
|
||||
editIcon.antialiasing = false;
|
||||
members.push(editIcon);
|
||||
|
||||
|
||||
@@ -804,7 +804,7 @@ class Charter extends UIState {
|
||||
}
|
||||
|
||||
if (s is CharterNote) cast(s, CharterNote).snappedToStrumline = true;
|
||||
if (s is UISprite) cast(s, UISprite).cursor = BUTTON;
|
||||
if (s is UISprite) cast(s, UISprite).cursor = CLICK;
|
||||
}
|
||||
if (!(verticalChange == 0 && horizontalChange == 0)) {
|
||||
notesGroup.sortNotes(); eventsGroup.sortEvents();
|
||||
@@ -866,7 +866,7 @@ class Charter extends UIState {
|
||||
case SUSTAIN_DRAG:
|
||||
selectionDragging = FlxG.mouse.pressed;
|
||||
if (selectionDragging) {
|
||||
currentCursor = BUTTON;
|
||||
currentCursor = CLICK;
|
||||
selection.loop(function (n:CharterNote) {
|
||||
var change:Float = Math.max((mousePos.y-(FlxG.keys.pressed.SHIFT ? dragStartPos.y : quantStep(dragStartPos.y))) / 40, -n.susLength);
|
||||
n.tempSusLength = change;
|
||||
|
||||
@@ -23,7 +23,7 @@ class CharterEvent extends UISliceSprite implements ICharterSelectable {
|
||||
this.step = step;
|
||||
this.events = events.getDefault([]);
|
||||
|
||||
cursor = BUTTON;
|
||||
cursor = CLICK;
|
||||
}
|
||||
|
||||
public override function update(elapsed:Float) {
|
||||
|
||||
@@ -13,7 +13,7 @@ class CharterEventAdd extends UISliceSprite {
|
||||
text = new UIText(0, 0, 0, "");
|
||||
members.push(text);
|
||||
|
||||
cursor = BUTTON;
|
||||
cursor = CLICK;
|
||||
}
|
||||
|
||||
public override function onHovered() {
|
||||
|
||||
@@ -44,7 +44,7 @@ class CharterNote extends UISprite implements ICharterSelectable {
|
||||
|
||||
typeText = new UIText(x, y, 0, Std.string(type));
|
||||
|
||||
cursor = sustainSpr.cursor = BUTTON;
|
||||
cursor = sustainSpr.cursor = CLICK;
|
||||
moves = false;
|
||||
}
|
||||
|
||||
@@ -139,7 +139,7 @@ class CharterNote extends UISprite implements ICharterSelectable {
|
||||
|
||||
sustainDraggable = false;
|
||||
if (!hovered && susLength != 0) {
|
||||
UIState.state.updateSpriteRect(sustainSpr);
|
||||
sustainSpr.updateSpriteRect();
|
||||
sustainDraggable = UIState.state.isOverlapping(sustainSpr, @:privateAccess sustainSpr.__rect);
|
||||
}
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ class CharterStrumline extends UISprite {
|
||||
draggingSprite.alpha = 0.4;
|
||||
draggingSprite.y = 9;
|
||||
draggingSprite.antialiasing = true;
|
||||
draggingSprite.cursor = BUTTON;
|
||||
draggingSprite.cursor = CLICK;
|
||||
members.push(draggingSprite);
|
||||
|
||||
button = new CharterStrumlineOptions(this);
|
||||
@@ -85,7 +85,7 @@ class CharterStrumline extends UISprite {
|
||||
healthIcons.follow(this, ((40 * 4) - healthIcons.width) / 2, 7 + (__healthYOffset = FlxMath.lerp(__healthYOffset, draggable ? 8 : 0, 1/20)));
|
||||
|
||||
draggingSprite.selectable = draggable;
|
||||
UIState.state.updateSpriteRect(draggingSprite);
|
||||
draggingSprite.updateSpriteRect();
|
||||
|
||||
var dragScale:Float = FlxMath.lerp(draggingSprite.scale.x, draggable ? 1 : 0.8, 1/16);
|
||||
draggingSprite.scale.set(dragScale, dragScale);
|
||||
|
||||
@@ -24,7 +24,7 @@ class CharterStrumlineButton extends UISprite {
|
||||
button.scale.set(0.85,0.85);
|
||||
button.updateHitbox();
|
||||
button.antialiasing = true;
|
||||
button.cursor = BUTTON;
|
||||
button.cursor = CLICK;
|
||||
members.push(button);
|
||||
|
||||
this.text = new UIText(0,0, 160, text);
|
||||
@@ -58,7 +58,7 @@ class CharterStrumlineButton extends UISprite {
|
||||
|
||||
super.update(elapsed);
|
||||
|
||||
UIState.state.updateSpriteRect(button);
|
||||
button.updateSpriteRect();
|
||||
if(UIState.state.curContextMenu == null && UIState.state.isOverlapping(button, button.__rect)) {
|
||||
buttonScale.set(0.95, 0.95);
|
||||
if (FlxG.mouse.justPressed && onClick != null) {
|
||||
|
||||
@@ -6,28 +6,36 @@ class PropertyButton extends UIButton {
|
||||
public var deleteButton:UIButton;
|
||||
public var deleteIcon:FlxSprite;
|
||||
|
||||
public function new(property, value, parent) {
|
||||
super(0, 0, '', function () {}, 280, 35);
|
||||
members.push(propertyText = new UITextBox(5, 5, property, 100, 25));
|
||||
members.push(valueText = new UITextBox(propertyText.x + propertyText.bWidth + 10, 5, value, 135, 25));
|
||||
public function new(property, value, parent, width:Int = 280, height:Int = 35, nameWidth:Int = 100, valueWidth:Int = 135, inputHeight:Int = 25) {
|
||||
super(0, 0, '', function () {}, width, height);
|
||||
members.push(propertyText = new UITextBox(5, 5, property, nameWidth, inputHeight));
|
||||
members.push(valueText = new UITextBox(propertyText.x + propertyText.bWidth + 10, 5, value, valueWidth, inputHeight));
|
||||
|
||||
deleteButton = new UIButton(valueText.x + 135, bHeight/2 - (25/2), "", function () {
|
||||
var deleteSize = height - 5 * 2;
|
||||
deleteButton = new UIButton(valueText.x + 135, bHeight/2 - (deleteSize/2), "", function () {
|
||||
parent.remove(this);
|
||||
}, 25, 25);
|
||||
}, deleteSize, deleteSize);
|
||||
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 = new FlxSprite(deleteButton.x, deleteButton.y).loadGraphic(Paths.image('editors/delete-button'));
|
||||
deleteIcon.antialiasing = false;
|
||||
members.push(deleteIcon);
|
||||
|
||||
updatePos();
|
||||
}
|
||||
|
||||
public override function update(elapsed) {
|
||||
super.update(elapsed);
|
||||
propertyText.follow(this, 5, 5);
|
||||
valueText.follow(this, propertyText.x + propertyText.bWidth, 5);
|
||||
deleteButton.follow(this, valueText.x + 135, bHeight/2 - (25/2));
|
||||
deleteIcon.follow(deleteButton, 5, 5);
|
||||
updatePos();
|
||||
}
|
||||
|
||||
public function updatePos() {
|
||||
propertyText.follow(this, 5, bHeight/2 - (propertyText.bHeight/2));
|
||||
valueText.follow(this, propertyText.x + propertyText.bWidth, bHeight/2 - (valueText.bHeight/2));
|
||||
//deleteButton.follow(this, valueText.x + 135, bHeight/2 - (25/2));
|
||||
deleteButton.follow(this, bWidth - deleteButton.bWidth - 5, bHeight/2 - deleteButton.bHeight/2);
|
||||
deleteIcon.follow(deleteButton, deleteButton.bWidth / 2 - deleteIcon.width / 2, deleteButton.bHeight / 2 - deleteIcon.height / 2);
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package funkin.editors;
|
||||
|
||||
import flixel.math.FlxPoint;
|
||||
import funkin.editors.ui.*;
|
||||
import funkin.editors.ui.notifications.*;
|
||||
|
||||
using funkin.editors.ui.UIUtil;
|
||||
#end
|
||||
@@ -0,0 +1,62 @@
|
||||
package funkin.editors.stage;
|
||||
|
||||
typedef StageCreationData = {
|
||||
var name:String;
|
||||
var path:String;
|
||||
}
|
||||
|
||||
class StageCreationScreen extends UISubstateWindow {
|
||||
private var onSave:Null<StageCreationData> -> Void = null;
|
||||
|
||||
public var stageNameTextBox:UITextBox;
|
||||
public var stagePathTextBox:UITextBox;
|
||||
public var voicesExplorer:UIFileExplorer;
|
||||
|
||||
public var saveButton:UIButton;
|
||||
public var closeButton:UIButton;
|
||||
|
||||
public function new(?onSave:StageCreationData->Void) {
|
||||
super();
|
||||
if (onSave != null) this.onSave = onSave;
|
||||
}
|
||||
|
||||
public override function create() {
|
||||
winTitle = "Creating New Stage";
|
||||
|
||||
winWidth = 748 - 32 + 40;
|
||||
winHeight = 220;
|
||||
|
||||
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 stageInfo:UIText;
|
||||
add(stageInfo = new UIText(windowSpr.x + 20, windowSpr.y + 30 + 16, 0, "Stage Info", 28));
|
||||
|
||||
add(stageNameTextBox = new UITextBox(stageInfo.x, stageInfo.y + stageInfo.height + 36, "Stage Name"));
|
||||
addLabelOn(stageNameTextBox, "Stage Name");
|
||||
|
||||
add(stagePathTextBox = new UITextBox(stageNameTextBox.x + 320 + 26, stageNameTextBox.y, "Stage Path"));
|
||||
addLabelOn(stagePathTextBox, "Stage Path");
|
||||
|
||||
add(saveButton = new UIButton(windowSpr.x + windowSpr.bWidth - 20 - 125, windowSpr.y + windowSpr.bHeight - 16 - 32, "Save & Close", function() {
|
||||
saveStageInfo();
|
||||
close();
|
||||
}, 125));
|
||||
|
||||
add(closeButton = new UIButton(saveButton.x - 20 - saveButton.bWidth, saveButton.y, "Cancel", close, 125));
|
||||
closeButton.color = 0xFFFF0000;
|
||||
}
|
||||
|
||||
function saveStageInfo() {
|
||||
if (onSave != null) onSave({
|
||||
name: stageNameTextBox.label.text,
|
||||
path: stagePathTextBox.label.text
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,65 @@
|
||||
package funkin.editors.stage;
|
||||
|
||||
import funkin.editors.stage.StageCreationScreen.StageCreationData;
|
||||
import funkin.game.Stage;
|
||||
import funkin.options.OptionsScreen;
|
||||
import funkin.options.type.NewOption;
|
||||
import funkin.options.type.OptionType;
|
||||
import funkin.options.type.TextOption;
|
||||
|
||||
class StageSelection extends EditorTreeMenu
|
||||
{
|
||||
public var stages:Array<String> = [];
|
||||
|
||||
public override function create()
|
||||
{
|
||||
bgType = "charter";
|
||||
super.create();
|
||||
|
||||
var modsList:Array<String> = Stage.getList(true, true);
|
||||
|
||||
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.",
|
||||
function() {
|
||||
FlxG.switchState(new StageEditor(stage));
|
||||
});
|
||||
}
|
||||
];
|
||||
|
||||
list.insert(0, new NewOption("New Stage", "New Stage", function() {
|
||||
FlxG.state.openSubState(new StageCreationScreen(saveStage));
|
||||
}));
|
||||
|
||||
main = new OptionsScreen("Stage Editor", "Select a stage to edit. HSCRIPT only stages are not supported.", list);
|
||||
|
||||
//DiscordUtil.call("onEditorTreeLoaded", ["Stage Editor"]);
|
||||
}
|
||||
|
||||
public function saveStage(creation:StageCreationData) {
|
||||
if ([for (s in stages) s.toLowerCase()].contains(creation.name.toLowerCase())) {
|
||||
openSubState(new UIWarningSubstate("Creating Stage: Error!", "The stage you are trying to create alreadly exists, if you would like to override it delete the stage first!", [
|
||||
{label: "Ok", color: 0xFFFF0000, onClick: function(t) {}}
|
||||
]));
|
||||
return;
|
||||
}
|
||||
|
||||
#if sys
|
||||
// Save File
|
||||
CoolUtil.safeSaveFile('${Paths.getAssetsRoot()}/data/stages/${creation.name}.xml', '<!DOCTYPE codename-engine-stage>\n<stage folder="${creation.path}">\n</stage>');
|
||||
#end
|
||||
|
||||
// Add to List
|
||||
var option = new TextOption(creation.name, "Press ACCEPT to edit this stage.", function() {
|
||||
FlxG.switchState(new StageEditor(creation.name));
|
||||
});
|
||||
optionsTree.members[optionsTree.members.length-1].insert(1, option);
|
||||
}
|
||||
|
||||
override function createPost() {
|
||||
super.createPost();
|
||||
|
||||
main.changeSelection(1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
package funkin.editors.stage.elements;
|
||||
|
||||
import funkin.editors.stage.StageEditor.StageXMLEditScreen;
|
||||
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 = 0xff7aa8ff;
|
||||
|
||||
hasAdvancedEdit = true;
|
||||
|
||||
updateInfo();
|
||||
}
|
||||
|
||||
public override function update(elapsed:Float) {
|
||||
super.update(elapsed);
|
||||
}
|
||||
|
||||
public override function updateInfo() {
|
||||
char.visible = !isHidden;
|
||||
char.alpha = 0.5 * charPos.alpha;
|
||||
var charScale = (char.xml.has.scale ? Std.parseFloat(char.xml.att.scale).getDefault(1) : 1);
|
||||
charPos.scale.x = char.scale.x / charScale;
|
||||
charPos.scale.y = char.scale.y / charScale;
|
||||
charPos.x = char.x;
|
||||
charPos.y = char.y;
|
||||
char.scale.copyFrom(charPos.scale);
|
||||
super.updateInfo();
|
||||
char.scale.scale(charScale);
|
||||
}
|
||||
|
||||
public override function getSprite():FunkinSprite {
|
||||
return char;
|
||||
}
|
||||
|
||||
public override function onSelect() {
|
||||
StageEditor.instance.selectSprite(char);
|
||||
}
|
||||
|
||||
/*public override function onSelect() {
|
||||
UIState.state.displayNotification(new UIBaseNotification("Selecting a character isnt implemented yet!", 2, BOTTOM_LEFT));
|
||||
CoolUtil.playMenuSFX(WARNING, 0.45);
|
||||
}*/
|
||||
|
||||
public override function onEdit() {
|
||||
if(!FlxG.keys.pressed.SHIFT) {
|
||||
FlxG.state.openSubState(new StageCharacterEditScreen(this));
|
||||
} else {
|
||||
FlxG.state.openSubState(new StageXMLEditScreen(this.xml, updateInfo, "Character"));
|
||||
}
|
||||
}
|
||||
|
||||
//public override function onEdit() {
|
||||
// UIState.state.displayNotification(new UIBaseNotification("Editing a character isnt implemented yet!", 2, BOTTOM_LEFT));
|
||||
// CoolUtil.playMenuSFX(WARNING, 0.45);
|
||||
//}
|
||||
|
||||
public override function onVisiblityToggle() {
|
||||
isHidden = !isHidden;
|
||||
updateInfo();
|
||||
}
|
||||
|
||||
public override function getName():String {
|
||||
return charPos.name;
|
||||
}
|
||||
|
||||
public override function getPos():FlxPoint {
|
||||
return FlxPoint.get(charPos.x, charPos.y);
|
||||
}
|
||||
|
||||
public override function updatePos() {
|
||||
super.updatePos();
|
||||
}
|
||||
}
|
||||
|
||||
class StageCharacterEditScreen extends UISoftcodedWindow {
|
||||
public var button:StageCharacterButton;
|
||||
public var char:Character;
|
||||
public var charPos:StageCharPos;
|
||||
|
||||
public function new(button:StageCharacterButton) {
|
||||
this.button = button;
|
||||
this.char = button.char;
|
||||
this.charPos = button.charPos;
|
||||
super("layouts/stage/characterEditScreen", [
|
||||
"stage" => StageEditor.instance.stage,
|
||||
"char" => char,
|
||||
"charPos" => charPos,
|
||||
"button" => button,
|
||||
"xml" => button.xml,
|
||||
"exID" => StageEditor.exID,
|
||||
"getEx" => function(name:String):Dynamic {
|
||||
return char.extra.get(StageEditor.exID(name));
|
||||
},
|
||||
"setEx" => function(name:String, value:Dynamic) {
|
||||
char.extra.set(StageEditor.exID(name), value);
|
||||
charPos.extra.set(StageEditor.exID(name), value);
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
public override function create() {
|
||||
super.create();
|
||||
}
|
||||
|
||||
public override function saveData() {
|
||||
super.saveData();
|
||||
button.updateInfo();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,218 @@
|
||||
package funkin.editors.stage.elements;
|
||||
|
||||
import flixel.util.FlxColor;
|
||||
import haxe.xml.Access;
|
||||
|
||||
using flixel.util.FlxColorTransformUtil;
|
||||
|
||||
class StageElementButton extends UIButton {
|
||||
public var xml:Access;
|
||||
|
||||
public var editButton:UIButton;
|
||||
public var editIcon:FlxSprite;
|
||||
|
||||
public var visibilityButton:UIButton;
|
||||
public var visibilityIcon:FlxSprite;
|
||||
|
||||
public var deleteButton:UIButton;
|
||||
public var deleteIcon:FlxSprite;
|
||||
|
||||
public var isHidden:Bool = false;
|
||||
public var hasAdvancedEdit:Bool = false;
|
||||
|
||||
public var selected:Bool = false;
|
||||
|
||||
public var tagColor:UISliceSprite;
|
||||
|
||||
public function new(x:Float,y:Float, xml:Access) {
|
||||
this.xml = xml;
|
||||
super(x,y, getInfoText(), function () {
|
||||
onSelect();
|
||||
}, StageEditor.SPRITE_WINDOW_WIDTH, StageEditor.SPRITE_WINDOW_BUTTON_HEIGHT);
|
||||
autoAlpha = false;
|
||||
|
||||
tagColor = new UISliceSprite(x, y, 10, StageEditor.SPRITE_WINDOW_BUTTON_HEIGHT, 'editors/ui/button');
|
||||
tagColor.alpha = 1; // Make entire sprite transparent
|
||||
tagColor.selectable = false;
|
||||
tagColor.active = false;
|
||||
//members.push(tagColor);
|
||||
|
||||
topAlpha = middleAlpha = bottomAlpha = 0.7;
|
||||
|
||||
field.alignment = LEFT;
|
||||
|
||||
visibilityButton = new UIButton(x+282+17, y, "", function () {
|
||||
onVisiblityToggle();
|
||||
}, 32);
|
||||
visibilityButton.autoAlpha = false;
|
||||
members.push(visibilityButton);
|
||||
|
||||
visibilityIcon = new FlxSprite(visibilityButton.x + 8, visibilityButton.y + 8).loadGraphic(Paths.image('editors/stage/visible-icon'), true, 16, 16);
|
||||
visibilityIcon.animation.add("visible", [0]);
|
||||
visibilityIcon.animation.add("invisible", [1]);
|
||||
visibilityIcon.animation.play("visible");
|
||||
visibilityIcon.antialiasing = false;
|
||||
visibilityIcon.updateHitbox();
|
||||
members.push(visibilityIcon);
|
||||
|
||||
editButton = new UIButton(visibilityButton.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'), true, 16, 16);
|
||||
editIcon.animation.add("edit", [0]);
|
||||
editIcon.animation.add("advanced", [1]);
|
||||
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 + 8, deleteButton.y + 8).loadGraphic(Paths.image('editors/stage/trash-icon'));
|
||||
deleteIcon.antialiasing = false;
|
||||
members.push(deleteIcon);
|
||||
|
||||
setEditNormal();
|
||||
}
|
||||
|
||||
public function setEditNormal() {
|
||||
editIcon.animation.play("edit");
|
||||
editButton.color = FlxColor.YELLOW;
|
||||
}
|
||||
|
||||
public function setEditAdvanced() {
|
||||
editIcon.animation.play("advanced");
|
||||
editButton.color = 0xFFFF5B0F;
|
||||
}
|
||||
|
||||
|
||||
var _lastSelected:Bool = false;
|
||||
|
||||
public override function update(elapsed:Float) {
|
||||
editButton.selectable = visibilityButton.selectable = deleteButton.selectable = selectable;
|
||||
editButton.shouldPress = visibilityButton.shouldPress = deleteButton.shouldPress = shouldPress;
|
||||
|
||||
hovered = !deleteButton.hovered;
|
||||
updatePos();
|
||||
super.update(elapsed);
|
||||
field.x += 12;
|
||||
|
||||
if(hasAdvancedEdit) {
|
||||
if(FlxG.keys.pressed.SHIFT) {
|
||||
setEditAdvanced();
|
||||
} else {
|
||||
setEditNormal();
|
||||
}
|
||||
}
|
||||
|
||||
tagColor.color = color;
|
||||
}
|
||||
|
||||
//private var _lastHovered:Bool = false;
|
||||
|
||||
public override function draw() {
|
||||
//if(_lastHovered != (_lastHovered = hovered && !pressed)) {
|
||||
if(_lastSelected != (_lastSelected = selected)) {
|
||||
updateColorTransform();
|
||||
}
|
||||
super.draw();
|
||||
}
|
||||
|
||||
/*public override function setFrameOffset() {
|
||||
var _frameOffset = 0;
|
||||
if(selected) _frameOffset = 9;
|
||||
if(hovered && pressed) _frameOffset = 18;
|
||||
framesOffset = _frameOffset;
|
||||
}*/
|
||||
|
||||
public override function updateColorTransform() {
|
||||
super.updateColorTransform();
|
||||
|
||||
if(selected) {
|
||||
useColorTransform = true;
|
||||
colorTransform.setOffsets(70, 70, 70, 0);
|
||||
} else {
|
||||
colorTransform.setOffsets(0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public function updateInfo() {
|
||||
field.text = getInfoText();
|
||||
|
||||
visibilityIcon.animation.play(isHidden ? "invisible" : "visible");
|
||||
visibilityIcon.alpha = !isHidden ? 1 : 0.5;
|
||||
}
|
||||
|
||||
public function updatePos() {
|
||||
// buttons
|
||||
var spacing = 8;
|
||||
var buttonY = y + (bHeight - 32) / 2;
|
||||
deleteButton.x = bWidth - deleteButton.bWidth - spacing;
|
||||
deleteButton.y = buttonY;
|
||||
editButton.x = deleteButton.x - editButton.bWidth - spacing;
|
||||
editButton.y = buttonY;
|
||||
visibilityButton.x = editButton.x - visibilityButton.bWidth - spacing;
|
||||
visibilityButton.y = buttonY;
|
||||
//deleteButton.x = (editButton.x = (visibilityButton.x = (x+282+17))+32+17)+32+17;
|
||||
//deleteButton.y = editButton.y = visibilityButton.y = y;
|
||||
// icons
|
||||
visibilityIcon.x = visibilityButton.x + 8; visibilityIcon.y = visibilityButton.y + 8;
|
||||
editIcon.x = editButton.x + 8; editIcon.y = editButton.y + 8;
|
||||
deleteIcon.x = deleteButton.x + 8; deleteIcon.y = deleteButton.y + 8;
|
||||
|
||||
tagColor.x = x;// + bWidth - tagColor.bWidth;
|
||||
tagColor.y = y;
|
||||
}
|
||||
|
||||
public function getSprite():FunkinSprite {
|
||||
return null;
|
||||
}
|
||||
|
||||
public function canRender() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getName():String {
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
public function onSelect() {
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
public function onVisiblityToggle() {
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
public function onEdit() {
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
public function onDelete() {
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
public function getPos():FlxPoint {
|
||||
return FlxPoint.get(-1, -1);
|
||||
}
|
||||
|
||||
public function getInfoText():String {
|
||||
var pos = getPos();
|
||||
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)})';
|
||||
}
|
||||
pos.put();
|
||||
return text;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package funkin.editors.stage.elements;
|
||||
|
||||
import flixel.util.FlxColor;
|
||||
import haxe.xml.Access;
|
||||
|
||||
class StageSolidButton extends StageSpriteButton {
|
||||
|
||||
public function new(x:Float,y:Float, sprite:FunkinSprite, xml:Access) {
|
||||
super(x,y, sprite, xml);
|
||||
color = 0xFFD9FF50;
|
||||
}
|
||||
|
||||
public override function onEdit() {
|
||||
// TODO: implement
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
package funkin.editors.stage.elements;
|
||||
|
||||
import funkin.editors.stage.StageEditor.StageXMLEditScreen;
|
||||
import funkin.editors.stage.elements.StageElementButton;
|
||||
import funkin.editors.ui.UISoftcodedWindow;
|
||||
import haxe.xml.Access;
|
||||
|
||||
class StageSpriteButton extends StageElementButton {
|
||||
public var sprite:FunkinSprite;
|
||||
|
||||
public function new(x:Float,y:Float, sprite:FunkinSprite, xml:Access) {
|
||||
this.sprite = sprite;
|
||||
super(x,y, xml);
|
||||
|
||||
//color = 0xFFD9FF50;
|
||||
|
||||
hasAdvancedEdit = true;
|
||||
|
||||
updateInfo();
|
||||
}
|
||||
|
||||
public override function update(elapsed:Float) {
|
||||
super.update(elapsed);
|
||||
}
|
||||
|
||||
public override function updateInfo() {
|
||||
sprite.visible = !isHidden;
|
||||
super.updateInfo();
|
||||
}
|
||||
|
||||
public override function getSprite():FunkinSprite {
|
||||
return sprite;
|
||||
}
|
||||
|
||||
public override function onSelect() {
|
||||
StageEditor.instance.selectSprite(sprite);
|
||||
}
|
||||
|
||||
public override function onVisiblityToggle() {
|
||||
isHidden = !isHidden;
|
||||
updateInfo();
|
||||
}
|
||||
|
||||
public override function onEdit() {
|
||||
if(!FlxG.keys.pressed.SHIFT) {
|
||||
FlxG.state.openSubState(new StageSpriteEditScreen(this));
|
||||
} else {
|
||||
FlxG.state.openSubState(new StageXMLEditScreen(this.xml, updateInfo, "Sprite"));
|
||||
}
|
||||
}
|
||||
|
||||
public override function onDelete() {
|
||||
sprite.destroy();
|
||||
xml.x.parent.removeChild(xml.x);
|
||||
StageEditor.instance.stage.stageSprites.remove(sprite.name);
|
||||
StageEditor.instance.xmlMap.remove(sprite);
|
||||
StageEditor.instance.stageSpritesWindow.remove(this);
|
||||
}
|
||||
|
||||
public override function getName():String {
|
||||
return xml.att.name;
|
||||
}
|
||||
|
||||
public override function getPos():FlxPoint {
|
||||
return FlxPoint.get(sprite.x, sprite.y);
|
||||
}
|
||||
|
||||
public override function updatePos() {
|
||||
super.updatePos();
|
||||
}
|
||||
}
|
||||
|
||||
class StageSpriteEditScreen extends UISoftcodedWindow {
|
||||
public var newSprite:Bool = false;
|
||||
public var button:StageSpriteButton;
|
||||
public var sprite:FunkinSprite;
|
||||
var isSaving:Bool = false;
|
||||
|
||||
public function new(button:StageSpriteButton) {
|
||||
this.button = button;
|
||||
this.sprite = button.getSprite();
|
||||
super("layouts/stage/spriteEditScreen", [
|
||||
"stage" => StageEditor.instance.stage,
|
||||
"sprite" => sprite,
|
||||
"button" => button,
|
||||
"xml" => button.xml,
|
||||
"exID" => StageEditor.exID,
|
||||
"getEx" => function(name:String):Dynamic {
|
||||
return sprite.extra.get(StageEditor.exID(name));
|
||||
},
|
||||
"setEx" => function(name:String, value:Dynamic) {
|
||||
sprite.extra.set(StageEditor.exID(name), value);
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
public override function create() {
|
||||
super.create();
|
||||
}
|
||||
|
||||
public override function saveData() {
|
||||
isSaving = true;
|
||||
super.saveData();
|
||||
button.updateInfo();
|
||||
}
|
||||
|
||||
public override function close() {
|
||||
if (!isSaving && newSprite) {
|
||||
button.onDelete();
|
||||
trace("deleting sprite");
|
||||
}
|
||||
super.close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package funkin.editors.stage.elements;
|
||||
|
||||
import funkin.game.Character;
|
||||
import funkin.game.Stage.StageCharPos;
|
||||
import haxe.xml.Access;
|
||||
|
||||
class StageUnknownButton extends StageElementButton {
|
||||
public var lowMemory:Bool = false;
|
||||
public var highMemory:Bool = false;
|
||||
|
||||
public function new(x:Float,y:Float, xml:Access) {
|
||||
super(x,y, xml);
|
||||
|
||||
if(xml.x.parent != null) {
|
||||
lowMemory = xml.x.parent.nodeName == "low-memory";
|
||||
highMemory = xml.x.parent.nodeName == "high-memory";
|
||||
}
|
||||
|
||||
color = 0xff000000;
|
||||
|
||||
visibilityButton.exists = false;
|
||||
members.remove(visibilityButton);
|
||||
|
||||
visibilityIcon.exists = false;
|
||||
members.remove(visibilityIcon);
|
||||
|
||||
setEditAdvanced();
|
||||
|
||||
updateInfo();
|
||||
}
|
||||
|
||||
public override function update(elapsed:Float) {
|
||||
super.update(elapsed);
|
||||
}
|
||||
|
||||
public override function updateInfo() {
|
||||
super.updateInfo();
|
||||
}
|
||||
|
||||
public override function getSprite():FunkinSprite {
|
||||
var sprite = new FunkinSprite(); // to allow it to be saved
|
||||
sprite.extra.set(StageEditor.exID("lowMemory"), lowMemory);
|
||||
sprite.extra.set(StageEditor.exID("highMemory"), highMemory);
|
||||
sprite.extra.set(StageEditor.exID("button"), this);
|
||||
return sprite;
|
||||
}
|
||||
|
||||
public override function canRender() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public override function onSelect() {
|
||||
}
|
||||
|
||||
public override function onEdit() {
|
||||
FlxG.state.openSubState(new StageUnknownEditScreen(this));
|
||||
}
|
||||
|
||||
//public override function onEdit() {
|
||||
// UIState.state.displayNotification(new UIBaseNotification("Editing a character isnt implemented yet!", 2, BOTTOM_LEFT));
|
||||
// CoolUtil.playMenuSFX(WARNING, 0.45);
|
||||
//}
|
||||
|
||||
public override function onVisiblityToggle() {
|
||||
}
|
||||
|
||||
public override function getName():String {
|
||||
return xml.name;
|
||||
}
|
||||
|
||||
public override function getPos():FlxPoint {
|
||||
return FlxPoint.get(0, 0);
|
||||
}
|
||||
|
||||
public override function getInfoText():String {
|
||||
return 'Unknown\n<${getName()} />';
|
||||
}
|
||||
|
||||
public override function updatePos() {
|
||||
super.updatePos();
|
||||
}
|
||||
}
|
||||
|
||||
class StageUnknownEditScreen extends UISoftcodedWindow {
|
||||
public var button:StageUnknownButton;
|
||||
|
||||
public function new(button:StageUnknownButton) {
|
||||
this.button = button;
|
||||
super("layouts/stage/unknownEditScreen", [
|
||||
"stage" => StageEditor.instance.stage,
|
||||
"button" => button,
|
||||
"xml" => button.xml,
|
||||
"exID" => StageEditor.exID
|
||||
]);
|
||||
}
|
||||
|
||||
public override function create() {
|
||||
super.create();
|
||||
}
|
||||
|
||||
public override function saveData() {
|
||||
super.saveData();
|
||||
button.updateInfo();
|
||||
}
|
||||
}
|
||||
@@ -57,7 +57,7 @@ class UIAudioPlayer extends UIButton {
|
||||
members.push(timeBarPlayer);
|
||||
|
||||
timeBarSpr = cast new UISprite(timeBar.x, timeBar.y).makeSolid(timeBar.barWidth, timeBar.barHeight, 0x00FFFFFF);
|
||||
timeBarSpr.cursor = BUTTON;
|
||||
timeBarSpr.cursor = CLICK;
|
||||
members.push(timeBarSpr);
|
||||
|
||||
volumeBar = new FlxBar(timeBar.x + timeBar.barWidth - 56, y + 6, LEFT_TO_RIGHT, 56, 10, sound, "volume", 0, 1);
|
||||
@@ -67,7 +67,7 @@ class UIAudioPlayer extends UIButton {
|
||||
members.push(volumeBar);
|
||||
|
||||
volumeBarSpr = cast new UISprite(volumeBar.x, volumeBar.y).makeSolid(volumeBar.barWidth, volumeBar.barHeight, 0x00FFFFFF);
|
||||
volumeBarSpr.cursor = BUTTON;
|
||||
volumeBarSpr.cursor = CLICK;
|
||||
members.push(volumeBarSpr);
|
||||
|
||||
volumeIcon = new FlxSprite(volumeBar.x - 12 - 8, volumeBar.y-1).loadGraphic(Paths.image('editors/ui/audio-icon'));
|
||||
|
||||
@@ -13,7 +13,7 @@ class UIButton extends UISliceSprite {
|
||||
field.alignment = CENTER;
|
||||
field.fieldWidth = w;
|
||||
|
||||
cursor = BUTTON;
|
||||
cursor = CLICK;
|
||||
}
|
||||
|
||||
public override function resize(w:Int, h:Int) {
|
||||
@@ -38,7 +38,11 @@ class UIButton extends UISliceSprite {
|
||||
}
|
||||
|
||||
public override function draw() {
|
||||
framesOffset = hovered ? (pressed ? 18 : 9) : 0;
|
||||
setFrameOffset();
|
||||
super.draw();
|
||||
}
|
||||
|
||||
public function setFrameOffset() {
|
||||
framesOffset = hovered ? (pressed ? 18 : 9) : 0;
|
||||
}
|
||||
}
|
||||
@@ -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,14 +41,14 @@ 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;
|
||||
}
|
||||
|
||||
public inline function add(button:T) {
|
||||
button.ID = buttons.members.length-1;
|
||||
button.ID = buttons.members.length;
|
||||
buttons.add(button);
|
||||
curMovingInterval = 0;
|
||||
nextscrollY += button.bHeight;
|
||||
@@ -95,7 +95,8 @@ class UIButtonList<T:UIButton> extends UIWindow {
|
||||
curMovingInterval = 0;
|
||||
}
|
||||
}
|
||||
addIcon.x = addButton.x + addButton.bWidth / 2 - addIcon.width / 2; addIcon.y = addButton.y + addButton.bHeight / 2 - addIcon.height / 2;
|
||||
addIcon.x = addButton.x + addButton.bWidth / 2 - addIcon.width / 2;
|
||||
addIcon.y = addButton.y + addButton.bHeight / 2 - addIcon.height / 2;
|
||||
}
|
||||
var nextscrollY:Float = 0;
|
||||
public override function update(elapsed:Float) {
|
||||
|
||||
@@ -10,19 +10,19 @@ class UICheckbox extends UISprite {
|
||||
public function new(x:Float, y:Float, text:String, checked:Bool = false, w:Int = 0) {
|
||||
super(x, y);
|
||||
loadGraphic(Paths.image('editors/ui/checkbox'), true, 20, 20);
|
||||
for(frame=>name in ["normal", "hover", "pressed", "checkmark"])
|
||||
for(frame=>name in ["normal", "hover", "pressed", "selected"])
|
||||
animation.add(name, [frame], 0, false);
|
||||
|
||||
this.checked = checked;
|
||||
|
||||
field = new UIText(x, y, w, text);
|
||||
check = new FlxSprite().loadGraphicFromSprite(this);
|
||||
check.animation.play("checkmark");
|
||||
check.animation.play("selected");
|
||||
|
||||
members.push(check);
|
||||
members.push(field);
|
||||
|
||||
cursor = BUTTON;
|
||||
cursor = CLICK;
|
||||
}
|
||||
|
||||
public override function update(elapsed:Float) {
|
||||
|
||||
@@ -7,6 +7,7 @@ class UIContextMenu extends MusicBeatSubstate {
|
||||
public var options:Array<UIContextMenuOption>;
|
||||
var x:Float;
|
||||
var y:Float;
|
||||
var w:Int = 100;
|
||||
var contextCam:FlxCamera;
|
||||
|
||||
var bg:UISliceSprite;
|
||||
@@ -21,11 +22,12 @@ class UIContextMenu extends MusicBeatSubstate {
|
||||
__oobDeletion = false;
|
||||
}
|
||||
|
||||
public function new(options:Array<UIContextMenuOption>, callback:UIContextMenuCallback, x:Float, y:Float) {
|
||||
public function new(options:Array<UIContextMenuOption>, callback:UIContextMenuCallback, x:Float, y:Float, ?w:Int = 100) {
|
||||
super();
|
||||
this.options = options.getDefault([]);
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.w = w;
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
@@ -37,7 +39,7 @@ class UIContextMenu extends MusicBeatSubstate {
|
||||
contextCam.scroll.set(0, 7.5);
|
||||
FlxG.cameras.add(contextCam, false);
|
||||
|
||||
bg = new UISliceSprite(x, y, 100, 100, 'editors/ui/context-bg');
|
||||
bg = new UISliceSprite(x, y, w, 100, 'editors/ui/context-bg');
|
||||
bg.cameras = [contextCam];
|
||||
add(bg);
|
||||
|
||||
|
||||
@@ -1,34 +1,68 @@
|
||||
package funkin.editors.ui;
|
||||
|
||||
import flixel.util.typeLimit.OneOfTwo;
|
||||
|
||||
class UIDropDown extends UISliceSprite {
|
||||
public var dropButton:UIButton;
|
||||
public var label:UIText;
|
||||
|
||||
public var index:Int = 0;
|
||||
public var options:Array<String>;
|
||||
public var items:Array<DropDownItem>;
|
||||
public var key(get, never):String;
|
||||
inline function get_key():String {
|
||||
return items[index].label;
|
||||
}
|
||||
public var value(get, never):Dynamic;
|
||||
inline function get_value():Dynamic {
|
||||
return items[index].value;
|
||||
}
|
||||
|
||||
public var onChange:Int->Void;
|
||||
|
||||
var curMenu:UIContextMenu = null;
|
||||
|
||||
public function new(x:Float, y:Float, width:Int = 320, height:Int = 32, options:Array<String>, index:Int = 0) {
|
||||
public static function getItems(options:Array<String>):Array<DropDownItem> {
|
||||
return [for(i=>o in options) {label: o, value: i}];
|
||||
}
|
||||
|
||||
public function new(x:Float, y:Float, width:Int = 320, height:Int = 32, _options:Array<OneOfTwo<DropDownItem, String>>, index:Int = 0) {
|
||||
super(x, y, width - height, height, 'editors/ui/inputbox'); // using same sprite cause fuck you
|
||||
|
||||
this.options = options;
|
||||
var _items:Array<DropDownItem> = [];
|
||||
|
||||
if(_options.length > 0) {
|
||||
if(_options[0] is String) {
|
||||
_items = getItems(_options);
|
||||
} else {
|
||||
_items = cast _options;
|
||||
}
|
||||
}
|
||||
|
||||
this.items = _items;
|
||||
this.options = [for(o in _items) o.label]; // backwards compatibility
|
||||
this.index = index;
|
||||
|
||||
cursor = BUTTON;
|
||||
cursor = CLICK;
|
||||
|
||||
label = new UIText(0, 0, width - height, options[index]);
|
||||
label = new UIText(0, 0, width - height, items[index].label);
|
||||
members.push(label);
|
||||
|
||||
dropButton = new UIButton(0, 0, "V", null, height, height);
|
||||
members.push(dropButton);
|
||||
}
|
||||
|
||||
public static function indexOfItemValue(items:Array<DropDownItem>, value:Dynamic):Int {
|
||||
return [for(o in items) o.value].indexOf(value);
|
||||
}
|
||||
|
||||
public static function indexOfItemLabel(items:Array<DropDownItem>, str:String):Int {
|
||||
return [for(o in items) o.label].indexOf(str);
|
||||
}
|
||||
|
||||
public function setOption(newIndex:Int) {
|
||||
if (index != (index = newIndex)) {
|
||||
label.text = options[index];
|
||||
label.text = items[index].label;
|
||||
if (onChange != null)
|
||||
onChange(index);
|
||||
}
|
||||
@@ -53,12 +87,17 @@ class UIDropDown extends UISliceSprite {
|
||||
public function openContextMenu() {
|
||||
var screenPos = getScreenPosition(null, __lastDrawCameras[0] == null ? FlxG.camera : __lastDrawCameras[0]);
|
||||
curMenu = UIState.state.openContextMenu([
|
||||
for(k=>o in options) {
|
||||
for(k=>o in items) {
|
||||
icon: (k == index) ? 1 : 0,
|
||||
label: o
|
||||
label: o.label
|
||||
}
|
||||
], function(_, i, _) {
|
||||
setOption(i);
|
||||
}, __lastDrawCameras[0].x + screenPos.x, __lastDrawCameras[0].y + screenPos.y + bHeight);
|
||||
}
|
||||
}
|
||||
|
||||
typedef DropDownItem = {
|
||||
var label:String;
|
||||
var value:Dynamic;
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package funkin.editors.ui;
|
||||
|
||||
import flixel.util.typeLimit.OneOfTwo;
|
||||
|
||||
class UIRadioButton extends UISprite {
|
||||
public var checked:Bool = false;
|
||||
public var onChecked:Bool->Void = null;
|
||||
|
||||
public var field:UIText;
|
||||
public var check:FlxSprite;
|
||||
|
||||
public var parent:OneOfTwo<UIWindow, UISubstateWindow> = null;
|
||||
public var forID:String = null;
|
||||
|
||||
public function new(x:Float, y:Float, text:String, checked:Bool = false, forID:String = null, w:Int = 0) {
|
||||
super(x, y);
|
||||
this.forID = forID;
|
||||
loadGraphic(Paths.image('editors/ui/radiobutton'), true, 20, 20);
|
||||
for(frame=>name in ["normal", "hover", "pressed", "selected"])
|
||||
animation.add(name, [frame], 0, false);
|
||||
|
||||
this.checked = checked;
|
||||
this.targetScale = checked ? 1 : 0;
|
||||
|
||||
field = new UIText(x, y, w, text);
|
||||
check = new FlxSprite().loadGraphicFromSprite(this);
|
||||
check.animation.play("selected");
|
||||
check.scale.set(targetScale, targetScale);
|
||||
|
||||
members.push(check);
|
||||
members.push(field);
|
||||
|
||||
cursor = CLICK;
|
||||
|
||||
autoAlpha = false;
|
||||
}
|
||||
|
||||
public var targetScale:Float = 1;
|
||||
|
||||
public override function update(elapsed:Float) {
|
||||
// ANIMATION HANDLING
|
||||
animation.play(hovered ? (pressed ? "pressed" : "hover") : "normal");
|
||||
|
||||
// CHECKMARK HANDLING
|
||||
if(autoAlpha)
|
||||
check.alpha = checked ? 1 : 0;
|
||||
check.scale.x = CoolUtil.fpsLerp(check.scale.x, targetScale, 0.25);
|
||||
check.scale.y = CoolUtil.fpsLerp(check.scale.y, targetScale, 0.25);
|
||||
|
||||
// POSITION HANDLING
|
||||
updatePositions();
|
||||
|
||||
super.update(elapsed);
|
||||
}
|
||||
|
||||
public inline function updatePositions() {
|
||||
check.follow(this);
|
||||
field.follow(this, 25, 0);
|
||||
}
|
||||
|
||||
public override function draw() {
|
||||
updatePositions();
|
||||
super.draw();
|
||||
}
|
||||
|
||||
public override function onHovered() {
|
||||
super.onHovered();
|
||||
if (FlxG.mouse.justReleased) {
|
||||
// clicked
|
||||
checked = !checked;
|
||||
check.scale.set(1.25, 1.25);
|
||||
targetScale = checked ? 1 : 0;
|
||||
|
||||
if (Options.editorSFX)
|
||||
CoolUtil.playMenuSFX(checked ? CHECKED : UNCHECKED, 0.5);
|
||||
|
||||
if(parent != null) {
|
||||
var members = (parent is UIWindow) ? (cast(parent, UIWindow)).members : (cast(parent, UISubstateWindow)).members;
|
||||
var radios:Array<UIRadioButton> = cast members.filter((o) -> o is UIRadioButton && o != this);
|
||||
//trace(radios);
|
||||
for(radio in radios) {
|
||||
if(radio.forID != null && radio.forID == forID) {
|
||||
radio.checked = false;
|
||||
radio.targetScale = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (onChecked != null)
|
||||
onChecked(checked);
|
||||
}
|
||||
}
|
||||
|
||||
public override function updateButton() {
|
||||
__rect.x = x;
|
||||
__rect.y = y;
|
||||
__rect.width = field.width + 30;
|
||||
__rect.height = field.height > height ? field.height : height;
|
||||
UIState.state.updateRectButtonHandler(this, __rect, onHovered);
|
||||
}
|
||||
|
||||
public override function toString() {
|
||||
return "RadioButton(" + field.text + ", " + checked + ", " + forID + ")";
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,7 @@ class UIScrollBar extends UISprite {
|
||||
this.length = length;
|
||||
|
||||
thumb = new UISliceSprite(0, 0, w, h, 'editors/ui/scrollbar');
|
||||
thumb.cursor = BUTTON;
|
||||
thumb.cursor = CLICK;
|
||||
members.push(thumb);
|
||||
|
||||
thumbIcon = new FlxSprite(0, 0, Paths.image('editors/ui/scrollbar-icon'));
|
||||
|
||||
@@ -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() {
|
||||
@@ -30,28 +31,71 @@ class UISliceSprite extends UISprite {
|
||||
bHeight = h;
|
||||
}
|
||||
|
||||
public var topAlpha:Null<Float> = null;
|
||||
public var middleAlpha:Null<Float> = null;
|
||||
public var bottomAlpha:Null<Float> = null;
|
||||
|
||||
public var drawTop:Bool = true;
|
||||
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;
|
||||
|
||||
public var topHeight:Int = 0;
|
||||
public var bottomHeight:Int = 0;
|
||||
public var leftWidth:Int = 0;
|
||||
public var rightWidth:Int = 0;
|
||||
|
||||
override function set_frames(val) {
|
||||
super.set_frames(val);
|
||||
calculateFrames();
|
||||
return val;
|
||||
}
|
||||
|
||||
function set_framesOffset(value:Int) {
|
||||
if(value != framesOffset) {
|
||||
framesOffset = value;
|
||||
calculateFrames();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function calculateFrames() {
|
||||
if(frames == null) return;
|
||||
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];
|
||||
|
||||
leftWidth = Std.int(Math.max(topleft.frame.width, Math.max(middleleft.frame.width, bottomleft.frame.width)));
|
||||
rightWidth = Std.int(Math.max(topright.frame.width, Math.max(middleright.frame.width, bottomright.frame.width)));
|
||||
topHeight = Std.int(Math.max(topleft.frame.height, Math.max(top.frame.height, topright.frame.height)));
|
||||
bottomHeight = Std.int(Math.max(topleft.frame.height, Math.max(top.frame.height, topright.frame.height)));
|
||||
}
|
||||
|
||||
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];
|
||||
|
||||
var oldAlpha = alpha;
|
||||
// TOP
|
||||
if (drawTop) {
|
||||
// TOP LEFT
|
||||
if(topAlpha != null) alpha = topAlpha;
|
||||
frame = topleft;
|
||||
setPosition(x, y);
|
||||
__setSize(
|
||||
@@ -80,6 +124,7 @@ class UISliceSprite extends UISprite {
|
||||
|
||||
// MIDDLE
|
||||
if (drawMiddle && bHeight > top.frame.height + bottom.frame.height) {
|
||||
if(middleAlpha != null) alpha = middleAlpha;
|
||||
var middleHeight:Float = bHeight - (topleft.frame.height * Math.min(bHeight/(topleft.frame.height*2), 1)) -
|
||||
bottomleft.frame.height * Math.min(bHeight/(bottomleft.frame.height*2), 1);
|
||||
|
||||
@@ -106,6 +151,7 @@ class UISliceSprite extends UISprite {
|
||||
|
||||
// BOTTOM
|
||||
if (drawBottom) {
|
||||
if(bottomAlpha != null) alpha = bottomAlpha;
|
||||
// BOTTOM LEFT
|
||||
frame = bottomleft;
|
||||
setPosition(x, y + bHeight - (bottomleft.frame.height * Math.min(bHeight/(bottomleft.frame.height*2), 1)));
|
||||
@@ -134,7 +180,9 @@ class UISliceSprite extends UISprite {
|
||||
bottomright.frame.height * Math.min(bHeight/(bottomright.frame.height*2), 1)
|
||||
);
|
||||
super.drawSuper();
|
||||
|
||||
}
|
||||
alpha = oldAlpha;
|
||||
}
|
||||
|
||||
setPosition(x, y);
|
||||
|
||||
@@ -45,7 +45,7 @@ class UISlider extends UISprite {
|
||||
makeGraphic(barWidth, 12, 0x00000000, true);
|
||||
this.drawRoundRect(0, 0, barWidth, 12, 8, 6, 0xFFC0C0C0);
|
||||
this.drawRoundRect(1, 1, barWidth-2, 12-2, 8, 5, 0xFF140013);
|
||||
cursor = BUTTON;
|
||||
cursor = CLICK;
|
||||
|
||||
progressbar = new UISprite(centered ? barWidth/2 : 0);
|
||||
progressbar.makeGraphic(barWidth, 8, 0x00000000, true);
|
||||
@@ -63,7 +63,7 @@ class UISlider extends UISprite {
|
||||
var selectableBar:UISprite = new UISprite(x,y);
|
||||
selectableBar.loadGraphic(Paths.image("editors/ui/slider"));
|
||||
selectableBar.antialiasing = true;
|
||||
selectableBar.cursor = BUTTON;
|
||||
selectableBar.cursor = CLICK;
|
||||
members.push(selectableBar);
|
||||
|
||||
switch (i) {
|
||||
@@ -74,7 +74,7 @@ class UISlider extends UISprite {
|
||||
|
||||
selectableHitbox = new UISprite(x,y);
|
||||
selectableHitbox.makeSolid(barWidth, 18, -1);
|
||||
selectableHitbox.cursor = BUTTON;
|
||||
selectableHitbox.cursor = CLICK;
|
||||
selectableHitbox.alpha = 0;
|
||||
members.push(selectableHitbox);
|
||||
|
||||
|
||||
@@ -0,0 +1,340 @@
|
||||
package funkin.editors.ui;
|
||||
|
||||
import flixel.math.FlxPoint;
|
||||
import funkin.backend.chart.ChartData.ChartMetaData;
|
||||
import funkin.backend.scripting.HScript;
|
||||
import funkin.backend.scripting.Script;
|
||||
import funkin.editors.extra.PropertyButton;
|
||||
import funkin.editors.stage.elements.StageElementButton;
|
||||
import funkin.editors.ui.UIDropDown;
|
||||
import haxe.xml.Access;
|
||||
import hscript.*;
|
||||
import hscript.Expr.Error;
|
||||
import hscript.Interp;
|
||||
import hscript.Parser;
|
||||
import haxe.io.Path;
|
||||
|
||||
using StringTools;
|
||||
|
||||
class UISoftcodedWindow extends UISubstateWindow {
|
||||
public var saveButton:UIButton;
|
||||
public var closeButton:UIButton;
|
||||
|
||||
var elementMap:Map<String, UISprite> = [];
|
||||
|
||||
var xmlPath:String;
|
||||
var filename:String;
|
||||
var customVariables:Map<String, Dynamic>;
|
||||
|
||||
public function new(xmlPath:String, customVariables:Map<String, Dynamic> = null) {
|
||||
super();
|
||||
this.xmlPath = xmlPath;
|
||||
this.filename = Path.withoutDirectory(xmlPath);
|
||||
this.customVariables = customVariables;
|
||||
}
|
||||
|
||||
var parser = HScript.initParser();
|
||||
var interp = new Interp();
|
||||
|
||||
function set(name:String, value:Dynamic) {
|
||||
interp.variables.set(name, value);
|
||||
}
|
||||
|
||||
function get(name:String):Dynamic {
|
||||
return interp.variables.get(name);
|
||||
}
|
||||
function varExists(name:String):Bool {
|
||||
return interp.variables.exists(name);
|
||||
}
|
||||
|
||||
function execString(input:String):String {
|
||||
if(input == null) return null;
|
||||
if(input.indexOf("${") == -1) return input;
|
||||
var text = "";
|
||||
while(input.length > 0) {
|
||||
var idx = input.indexOf("${");
|
||||
if(idx == -1) {
|
||||
text += input;
|
||||
break;
|
||||
}
|
||||
text += input.substring(0, idx);
|
||||
var ei = input.indexOf("}", idx);
|
||||
if(ei == -1) {
|
||||
text += input.substring(idx);
|
||||
break;
|
||||
}
|
||||
var block = input.substring(idx+2, ei);
|
||||
text += exec(block);
|
||||
input = input.substring(ei + 1);
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
function execDefault(code:String, defaultValue:Dynamic):Dynamic {
|
||||
return CoolUtil.getDefault(exec(code), defaultValue);
|
||||
}
|
||||
|
||||
function execAtt(el:Access, key:String, ?defaultValue:Dynamic):Dynamic {
|
||||
return execDefault(el.getAtt(key), defaultValue);
|
||||
}
|
||||
|
||||
function exec(code:String):Dynamic {
|
||||
try {
|
||||
if (code != null && code.trim() != "")
|
||||
return interp.execute(parser.parseString(code, filename));
|
||||
} catch(e:Error) {
|
||||
_errorHandler(e);
|
||||
} catch(e) {
|
||||
_errorHandler(new Error(ECustom(e.toString()), 0, 0, filename, 0));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function _errorHandler(error:Error) {
|
||||
var fileName = error.origin;
|
||||
var fn = '$fileName:${error.line}: ';
|
||||
var err = error.toString();
|
||||
if (err.startsWith(fn)) err = err.substr(fn.length);
|
||||
|
||||
Logs.traceColored([
|
||||
Logs.logText(fn, GREEN),
|
||||
Logs.logText(err, RED)
|
||||
], ERROR);
|
||||
}
|
||||
|
||||
public override function create() {
|
||||
var layout = new Access(Xml.parse(Assets.getText(Paths.xml("editors/" + xmlPath))).firstElement());
|
||||
|
||||
for(k=>e in Script.getDefaultVariables()) {
|
||||
set(k, e);
|
||||
}
|
||||
set("self", this);
|
||||
set("hasSaveButton", true);
|
||||
set("hasCloseButton", true);
|
||||
set("getRadioButtons", function(forID:String) {
|
||||
var radios:Array<UIRadioButton> = cast members.filter((o) -> o is UIRadioButton);
|
||||
return cast radios.filter((o) -> o.forID == forID);
|
||||
});
|
||||
for(k=>v in customVariables) {
|
||||
set(k, v);
|
||||
}
|
||||
set("last", null);
|
||||
|
||||
if(varExists("winTitle")) winTitle = get("winTitle");
|
||||
if(varExists("winWidth")) winWidth = get("winWidth");
|
||||
if(varExists("winHeight")) winHeight = get("winHeight");
|
||||
|
||||
winTitle = execString(layout.getAtt("title").getDefault(winTitle));
|
||||
winWidth = Std.parseInt(layout.getAtt("width")).getDefault(winWidth);
|
||||
winHeight = Std.parseInt(layout.getAtt("height")).getDefault(winHeight);
|
||||
|
||||
super.create();
|
||||
|
||||
function addLabelOn(ui:UISprite, text:String)
|
||||
add(new UIText(ui.x, ui.y - 24, 0, text));
|
||||
|
||||
function getArr(i:Iterator<Access>) {
|
||||
return [for(e in i) e];
|
||||
}
|
||||
|
||||
function addLayout(elements:Array<Access>) {
|
||||
for(el in elements) {
|
||||
var type = el.name;
|
||||
|
||||
switch(type) {
|
||||
case "section":
|
||||
if(execAtt(el, "if", true) != true)
|
||||
continue;
|
||||
if(execAtt(el, "unless", false) != false)
|
||||
continue;
|
||||
addLayout(getArr(el.elements));
|
||||
continue;
|
||||
case "set":
|
||||
set(execString(el.getAtt("name")), execAtt(el, "value"));
|
||||
continue;
|
||||
case "exec":
|
||||
exec(el.innerData);
|
||||
continue;
|
||||
}
|
||||
|
||||
var name:String = execString(el.getAtt("name"));
|
||||
var label:String = execString(el.getAtt("label"));
|
||||
var x:Float = execAtt(el, "x", 0);
|
||||
var y:Float = execAtt(el, "y", 0);
|
||||
|
||||
var sprite:FlxSprite = switch(type) {
|
||||
case "image":
|
||||
var spr = XMLUtil.createSpriteFromXML(el, "", LOOP);
|
||||
spr.x = x;
|
||||
spr.y = y;
|
||||
cast add(spr);
|
||||
case "solid":
|
||||
var spr = new FunkinSprite(x, y).makeSolid(
|
||||
execAtt(el, "width", 100),
|
||||
execAtt(el, "height", 100),
|
||||
execAtt(el, "color", 0xFFFFFFFF)
|
||||
);
|
||||
cast add(spr);
|
||||
case "textbox":
|
||||
var text:String = execString(el.getAtt("text"));
|
||||
text = Std.string(execAtt(el, "value", text));
|
||||
var textBox = new UITextBox(x, y, text, execAtt(el, "width"), execAtt(el, "height"), execAtt(el, "multiline", false));//(el.getAtt("multiline").getDefault("false") == "true"));
|
||||
add(textBox);
|
||||
addLabelOn(textBox, label);
|
||||
textBox;
|
||||
case "title":
|
||||
cast add(new UIText(x, y, execAtt(el, "width", 0), execString(el.getAtt("text")), execAtt(el, "size", 28)));
|
||||
case "label":
|
||||
cast add(new UIText(x, y - 24, execAtt(el, "width", 0), execString(el.getAtt("text")), execAtt(el, "size", 15)));
|
||||
case "text":
|
||||
cast add(new UIText(x, y, execAtt(el, "width", 0), execString(el.getAtt("text")), execAtt(el, "size", 15)));
|
||||
case "stepper":
|
||||
// x:Float, y:Float, value:Float = 0, step:Float = 1, precision:Int = 0, ?min:Float, ?max:Float, w:Int = 180, h:Int = 32
|
||||
var stepper = new UINumericStepper(
|
||||
x,
|
||||
y,
|
||||
execAtt(el, "value", 0),
|
||||
execAtt(el, "step", 1),
|
||||
execAtt(el, "precision", 0),
|
||||
execAtt(el, "min"),
|
||||
execAtt(el, "max"),
|
||||
execAtt(el, "width"),
|
||||
execAtt(el, "height")
|
||||
);
|
||||
add(stepper);
|
||||
addLabelOn(stepper, label);
|
||||
stepper;
|
||||
case "checkbox":
|
||||
var checkbox = new UICheckbox(x, y, execString(el.getAtt("text")), execAtt(el, "value", false));
|
||||
add(checkbox);
|
||||
addLabelOn(checkbox, label);
|
||||
checkbox;
|
||||
case "radio":
|
||||
var radio = new UIRadioButton(x, y, execString(el.getAtt("text")), execAtt(el, "value", false), execString(el.getAtt("for")));
|
||||
radio.parent = this;
|
||||
add(radio);
|
||||
addLabelOn(radio, label);
|
||||
radio;
|
||||
case "slider":
|
||||
var slider = new UISlider(x, y, execAtt(el, "width"), execAtt(el, "value", 0), execAtt(el, "segments", []), execAtt(el, "centered", false));
|
||||
add(slider);
|
||||
addLabelOn(slider, label);
|
||||
slider;
|
||||
case "dropdown":
|
||||
var items:Array<DropDownItem> = [];
|
||||
for(i=>node in el.nodes.item)
|
||||
items.push({label: execString(node.getAtt("label")), value: execAtt(node, "value", i)});
|
||||
var curValue = execAtt(el, "value");
|
||||
var index = UIDropDown.indexOfItemValue(items, curValue);
|
||||
if (index == -1) index = 0;
|
||||
|
||||
var dropdown = new UIDropDown(x, y, execAtt(el, "width"), execAtt(el, "height"), items, index);
|
||||
add(dropdown);
|
||||
addLabelOn(dropdown, label);
|
||||
dropdown;
|
||||
case "buttonlist":
|
||||
//x:Float, y:Float, width:Int, height:Int, windowName:String, buttonSize:FlxPoint, ?buttonOffset:FlxPoint, ?buttonSpacing:Float
|
||||
var buttonlist = new UIButtonList<UIButton>(
|
||||
x,
|
||||
y,
|
||||
execAtt(el, "width"),
|
||||
execAtt(el, "height"),
|
||||
execString(el.getAtt("title")).getDefault(""),
|
||||
FlxPoint.get(execAtt(el, "buttonSizeX", 0), execAtt(el, "buttonSizeY", 0)),
|
||||
FlxPoint.get(execAtt(el, "buttonOffsetX", 0), execAtt(el, "buttonOffsetY", 0)),
|
||||
execAtt(el, "buttonSpacing")
|
||||
);
|
||||
var old = get("buttonlist");
|
||||
set("buttonlist", buttonlist);
|
||||
var texture = "editors/ui/inputbox";
|
||||
var code = "";
|
||||
for(node in getArr(el.elements)) {
|
||||
switch(node.name) {
|
||||
case "camSpacing": buttonlist.cameraSpacing = execAtt(node, "value", 0);
|
||||
case "texture": texture = execString(node.getAtt("path")).getDefault(texture);
|
||||
case "code": code = node.innerData;
|
||||
}
|
||||
}
|
||||
buttonlist.frames = Paths.getFrames(texture);
|
||||
if(code != "")
|
||||
exec(code);
|
||||
add(buttonlist);
|
||||
addLabelOn(buttonlist, label);
|
||||
set("buttonlist", old);
|
||||
buttonlist;
|
||||
// UIColorwheel
|
||||
// UIAudioPlayer
|
||||
// UIButtonList
|
||||
// UIFileExplorer
|
||||
// UIAutoCompleteTextbox
|
||||
default: {
|
||||
Logs.trace("Unknown element type: " + type, ERROR, RED);
|
||||
Logs.trace("Skipping element: " + el.x.toString(), ERROR, RED);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
set("last", sprite);
|
||||
if(name != null) set(name, sprite);
|
||||
if(sprite is UISprite) elementMap.set(name, cast sprite);
|
||||
}
|
||||
}
|
||||
|
||||
addLayout(getArr(layout.elements));
|
||||
|
||||
if(get("hasSaveButton")) {
|
||||
saveButton = new UIButton(windowSpr.x + windowSpr.bWidth - 20, windowSpr.y + windowSpr.bHeight - 20, "Save & Close", function() {
|
||||
saveData();
|
||||
close();
|
||||
}, 125);
|
||||
saveButton.x -= saveButton.bWidth;
|
||||
saveButton.y -= saveButton.bHeight;
|
||||
add(saveButton);
|
||||
}
|
||||
|
||||
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() {
|
||||
close();
|
||||
}, 125);
|
||||
if(saveButton != null)
|
||||
closeButton.color = 0xFFFF0000;
|
||||
closeButton.x -= closeButton.bWidth;
|
||||
if(saveButton == null)
|
||||
closeButton.y -= closeButton.bHeight;
|
||||
add(closeButton);
|
||||
}
|
||||
}
|
||||
|
||||
function getElement(name:String):UISprite {
|
||||
return elementMap.get(name);
|
||||
}
|
||||
|
||||
function callFunc(name:String, args:Array<Dynamic>) {
|
||||
if (interp == null) return null;
|
||||
if (!interp.variables.exists(name)) return null;
|
||||
|
||||
var func = interp.variables.get(name);
|
||||
if (func != null && Reflect.isFunction(func))
|
||||
return Reflect.callMethod(null, func, args);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
override function update(elapsed:Float) {
|
||||
super.update(elapsed);
|
||||
callFunc("onUpdate", [elapsed]);
|
||||
}
|
||||
|
||||
public function saveData() {
|
||||
for (el in elementMap) {
|
||||
if(el is UINumericStepper) {
|
||||
var el:UINumericStepper = cast el;
|
||||
@:privateAccess el.__onChange(el.label.text);
|
||||
}
|
||||
}
|
||||
|
||||
callFunc("onSave", []);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package funkin.editors.ui;
|
||||
|
||||
import flixel.math.FlxRect;
|
||||
import funkin.backend.utils.NativeAPI.CodeCursor;
|
||||
import openfl.ui.MouseCursor;
|
||||
|
||||
@:allow(funkin.editors.ui.UIState)
|
||||
@@ -21,7 +22,7 @@ class UISprite extends FlxSprite {
|
||||
|
||||
public var hoverCallback:Void->Void = null;
|
||||
|
||||
public var cursor:MouseCursor = ARROW;
|
||||
public var cursor:CodeCursor = ARROW;
|
||||
|
||||
public var focused(get, set):Bool;
|
||||
public var selectable:Bool = true;
|
||||
@@ -55,7 +56,8 @@ class UISprite extends FlxSprite {
|
||||
FlxCamera._defaultCameras = cameras;
|
||||
|
||||
for(m in members)
|
||||
m.update(elapsed);
|
||||
if(m.exists && m.active)
|
||||
m.update(elapsed);
|
||||
|
||||
FlxCamera._defaultCameras = __oldDefCams;
|
||||
}
|
||||
@@ -114,4 +116,11 @@ class UISprite extends FlxSprite {
|
||||
if (hoverCallback != null)
|
||||
hoverCallback();
|
||||
}
|
||||
|
||||
public function updateSpriteRect() {
|
||||
__rect.x = x;
|
||||
__rect.y = y;
|
||||
__rect.width = width;
|
||||
__rect.height = height;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
package funkin.editors.ui;
|
||||
|
||||
import funkin.editors.ui.notifications.UIBaseNotification;
|
||||
import flixel.math.FlxPoint;
|
||||
import flixel.math.FlxRect;
|
||||
import funkin.backend.system.framerate.Framerate;
|
||||
import funkin.backend.utils.NativeAPI.CodeCursor;
|
||||
import funkin.editors.ui.UIContextMenu.UIContextMenuCallback;
|
||||
import funkin.editors.ui.UIContextMenu.UIContextMenuOption;
|
||||
import lime.ui.KeyCode;
|
||||
@@ -19,7 +21,9 @@ class UIState extends MusicBeatState {
|
||||
public var hoveredSprite:UISprite = null;
|
||||
public var currentFocus:IUIFocusable = null;
|
||||
|
||||
public var currentCursor:MouseCursor = ARROW;
|
||||
public var currentCursor:CodeCursor = ARROW;
|
||||
|
||||
public var uiCameras:Array<FlxCamera> = [];
|
||||
|
||||
private var __rect:FlxRect;
|
||||
private var __mousePos:FlxPoint;
|
||||
@@ -59,28 +63,15 @@ class UIState extends MusicBeatState {
|
||||
currentFocus.onTextEdit(str, start, end);
|
||||
}
|
||||
|
||||
public inline function updateSpriteRect(spr:UISprite) {
|
||||
spr.__rect.x = spr.x;
|
||||
spr.__rect.y = spr.y;
|
||||
spr.__rect.width = spr.width;
|
||||
spr.__rect.height = spr.height;
|
||||
}
|
||||
|
||||
public function updateButtonHandler(spr:UISprite, buttonHandler:Void->Void) {
|
||||
spr.__rect.x = spr.x;
|
||||
spr.__rect.y = spr.y;
|
||||
spr.__rect.width = spr.width;
|
||||
spr.__rect.height = spr.height;
|
||||
spr.updateSpriteRect();
|
||||
updateRectButtonHandler(spr, spr.__rect, buttonHandler);
|
||||
}
|
||||
|
||||
public function isOverlapping(spr:UISprite, rect:FlxRect) {
|
||||
for(camera in spr.__lastDrawCameras) {
|
||||
var pos = FlxG.mouse.getScreenPosition(camera, FlxPoint.get());
|
||||
__rect.x = rect.x;
|
||||
__rect.y = rect.y;
|
||||
__rect.width = rect.width;
|
||||
__rect.height = rect.height;
|
||||
__rect.copyFrom(rect);
|
||||
|
||||
__rect.x -= camera.scroll.x * spr.scrollFactor.x;
|
||||
__rect.y -= camera.scroll.y * spr.scrollFactor.y;
|
||||
@@ -117,12 +108,12 @@ class UIState extends MusicBeatState {
|
||||
|
||||
FlxG.sound.keysAllowed = currentFocus != null ? !(currentFocus is UITextBox) : true;
|
||||
|
||||
if (hoveredSprite != null) {
|
||||
Mouse.cursor = hoveredSprite.cursor;
|
||||
hoveredSprite = null;
|
||||
if (hoveredSprite != null && hoveredSprite.cursor != null) {
|
||||
NativeAPI.setCursorIcon(hoveredSprite.cursor);
|
||||
} else {
|
||||
Mouse.cursor = currentCursor;
|
||||
NativeAPI.setCursorIcon(currentCursor);
|
||||
}
|
||||
hoveredSprite = null;
|
||||
}
|
||||
|
||||
public override function destroy() {
|
||||
@@ -145,7 +136,7 @@ class UIState extends MusicBeatState {
|
||||
}
|
||||
}
|
||||
|
||||
public function openContextMenu(options:Array<UIContextMenuOption>, ?callback:UIContextMenuCallback, ?x:Float, ?y:Float) {
|
||||
public function openContextMenu(options:Array<UIContextMenuOption>, ?callback:UIContextMenuCallback, ?x:Float, ?y:Float, ?w:Int) {
|
||||
var state = FlxG.state;
|
||||
while(state.subState != null && !(state._requestSubStateReset && state._requestedSubState == null))
|
||||
state = state.subState;
|
||||
@@ -153,7 +144,24 @@ class UIState extends MusicBeatState {
|
||||
state.persistentDraw = true;
|
||||
state.persistentUpdate = true;
|
||||
|
||||
state.openSubState(curContextMenu = new UIContextMenu(options, callback, x.getDefault(__mousePos.x), y.getDefault(__mousePos.y)));
|
||||
state.openSubState(curContextMenu = new UIContextMenu(options, callback, x.getDefault(__mousePos.x), y.getDefault(__mousePos.y), w));
|
||||
return curContextMenu;
|
||||
}
|
||||
|
||||
public function displayNotification(notification:UIBaseNotification) {
|
||||
notification.cameras = uiCameras;
|
||||
notification.onRemove = (notif) -> {
|
||||
notification.onRemove = null;
|
||||
remove(notif, true);
|
||||
};
|
||||
add(notification);
|
||||
notification.update(0);
|
||||
notification.appearAnimation();
|
||||
// TODO: future tooltips
|
||||
//notification.x = __mousePos.x;
|
||||
//notification.y = __mousePos.y;
|
||||
//notification.alpha = 0;
|
||||
//notification.appearAnimation();
|
||||
//FlxTween.tween(notification, {x: __mousePos.x, y: __mousePos.y, alpha: 1}, .3, {ease: FlxEase.circInOut});
|
||||
}
|
||||
}
|
||||
@@ -43,7 +43,7 @@ class UITopMenuButton extends UISliceSprite {
|
||||
super(x, y, 0, 23, "editors/ui/menu-item");
|
||||
this.contextMenu = contextMenu;
|
||||
this.parent = parent;
|
||||
cursor = BUTTON;
|
||||
cursor = CLICK;
|
||||
|
||||
this.label = new UIText(4, 0, 0, label);
|
||||
this.label.alignment = CENTER;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package funkin.editors.ui;
|
||||
|
||||
import flixel.input.FlxInput.FlxInputState;
|
||||
import flixel.input.keyboard.FlxKey;
|
||||
import funkin.editors.ui.UIContextMenu.UIContextMenuOption;
|
||||
|
||||
@@ -14,6 +15,19 @@ class UIUtil {
|
||||
return contextMenu != null && UIState.state.curContextMenu == contextMenu;
|
||||
}
|
||||
|
||||
@:noUsing public static function fixKey(key:FlxKey):FlxKey {
|
||||
return switch(key) {
|
||||
#if mac
|
||||
case CONTROL: WINDOWS; // Remap control to the meta key
|
||||
#end
|
||||
default: key;
|
||||
}
|
||||
}
|
||||
|
||||
public static function getKeyState(key:FlxKey, Status:FlxInputState):Bool {
|
||||
return FlxG.keys.checkStatus(fixKey(key), Status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process all options with shortcuts present in a `Array<UIContextMenuOption>`. Also checks childrens.
|
||||
* @param topMenuOptions
|
||||
@@ -41,14 +55,7 @@ class UIUtil {
|
||||
var shouldPress = Std.int(key) > 0;
|
||||
if(!shouldPress) key = -key;
|
||||
|
||||
var k = switch(key) {
|
||||
#if mac
|
||||
case CONTROL:
|
||||
WINDOWS;
|
||||
#end
|
||||
default:
|
||||
key;
|
||||
}
|
||||
var k = fixKey(key);
|
||||
if (FlxG.keys.checkStatus(k, shouldPress ? JUST_PRESSED : JUST_RELEASED)) {
|
||||
justPressed = true;
|
||||
} else if (!FlxG.keys.checkStatus(k, shouldPress ? PRESSED : RELEASED)) {
|
||||
|
||||
@@ -2,19 +2,48 @@ 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);
|
||||
|
||||
__rect.x = x; __rect.y = y+23;
|
||||
__rect.width = bWidth; __rect.height = bHeight-23;
|
||||
cursor = ARROW;
|
||||
|
||||
if(collapsable) {
|
||||
__rect.x = x; __rect.y = y;
|
||||
__rect.width = bWidth; __rect.height = topHeight;
|
||||
|
||||
if(UIState.state.isOverlapping(this, __rect)) {
|
||||
cursor = CLICK;
|
||||
if(FlxG.mouse.justPressed) {
|
||||
content.exists = !content.exists;
|
||||
drawMiddle = !drawMiddle;
|
||||
drawBottom = !drawBottom;
|
||||
}
|
||||
} else {
|
||||
if(!content.exists) {
|
||||
cursor = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
__rect.x = x;
|
||||
__rect.width = bWidth;
|
||||
if(content.exists) {
|
||||
__rect.y = y+topHeight;
|
||||
__rect.height = bHeight-topHeight;
|
||||
}
|
||||
hovered = UIState.state.isOverlapping(this, __rect);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
package funkin.editors.ui.notifications;
|
||||
|
||||
import flixel.group.FlxGroup;
|
||||
import flixel.math.FlxRect;
|
||||
import flixel.tweens.FlxTween;
|
||||
import flixel.util.FlxTimer;
|
||||
|
||||
using flixel.util.FlxSpriteUtil;
|
||||
|
||||
class UIBaseNotification extends UISliceSprite {
|
||||
public var textField:UIText;
|
||||
public var closeButton:UIButton;
|
||||
|
||||
public var onRemove:(notif:UIBaseNotification)->Void = null;
|
||||
|
||||
public var showedAnimation:Bool = false;
|
||||
public var closed:Bool = false;
|
||||
|
||||
public function new(text:String, showTimer:Float = 5, corner:Corner = BOTTOM_LEFT) {
|
||||
super(x, y, 340, 46, "editors/ui/inputbox");
|
||||
|
||||
closeButton = new UIButton(0, 0, "X", () -> {
|
||||
if (closed) return;
|
||||
closed = true;
|
||||
|
||||
(new FlxTimer()).start(0.2, (_) -> {disappearAnimation();});
|
||||
}, 32, 32);
|
||||
closeButton.hoverCallback = () -> {
|
||||
closeButton.color = 0xFFAC3D3D;
|
||||
closeButton.field.color = 0xFFffffff;
|
||||
//closeButton.field.color = closeButton.hovered ? 0xFFffffff : 0xFF7f7f7f;
|
||||
}
|
||||
closeButton.autoAlpha = false;
|
||||
closeButton.alpha = 0.0;
|
||||
|
||||
textField = new UIText(0, 0, 0, text, 14);
|
||||
textField.alignment = LEFT;
|
||||
textField.fieldWidth = bWidth - 32 - 8;
|
||||
members.push(textField);
|
||||
|
||||
if(textField.height > bHeight)
|
||||
bHeight = Std.int(textField.height);
|
||||
|
||||
alpha = 0;
|
||||
textField.alpha = 0;
|
||||
|
||||
showingTimer = showTimer;
|
||||
|
||||
members.push(closeButton);
|
||||
|
||||
var margin = 20;
|
||||
|
||||
switch(corner) {
|
||||
case TOP_LEFT:
|
||||
x = margin;
|
||||
y = margin;
|
||||
case TOP_RIGHT:
|
||||
x = FlxG.width - bWidth - margin;
|
||||
y = margin;
|
||||
case BOTTOM_LEFT:
|
||||
x = margin;
|
||||
y = FlxG.height - bHeight - margin;
|
||||
case BOTTOM_RIGHT:
|
||||
x = FlxG.width - bWidth - margin;
|
||||
y = FlxG.height - bHeight - margin;
|
||||
}
|
||||
|
||||
defaultX = x;
|
||||
defaultY = y;
|
||||
|
||||
switch(corner) {
|
||||
case TOP_LEFT | BOTTOM_LEFT:
|
||||
x -= bWidth - margin - 20;
|
||||
case TOP_RIGHT | BOTTOM_RIGHT:
|
||||
x += bWidth - margin - 20;
|
||||
}
|
||||
|
||||
closingX = x;
|
||||
closingY = y;
|
||||
|
||||
alpha = 0;
|
||||
}
|
||||
|
||||
public var defaultX:Float = 0;
|
||||
public var defaultY:Float = 0;
|
||||
|
||||
public var closingX:Float = 0;
|
||||
public var closingY:Float = 0;
|
||||
|
||||
public var showingTimer:Float = 5;
|
||||
public var timerActive:Bool = false;
|
||||
|
||||
public var progress:Float = 0;
|
||||
public override function update(elapsed:Float) {
|
||||
closeButton.color = 0xFFFFFFFF;
|
||||
closeButton.field.color = 0xFF7f7f7f;
|
||||
super.update(elapsed);
|
||||
|
||||
if(timerActive) {
|
||||
showingTimer -= elapsed;
|
||||
if(showingTimer <= 0) {
|
||||
timerActive = false;
|
||||
idleDisappearAnimation();
|
||||
}
|
||||
}
|
||||
|
||||
textField.follow(this, 5, bHeight / 2 - textField.height / 2);
|
||||
closeButton.follow(this, bWidth-closeButton.bWidth - 7, bHeight / 2 - closeButton.bHeight / 2);
|
||||
closeButton.field.alpha = alpha * .7;
|
||||
//closeButton.alpha = alpha;
|
||||
textField.alpha = alpha;
|
||||
}
|
||||
|
||||
public function appearAnimation() {
|
||||
x = closingX; alpha=0; FlxTween.cancelTweensOf(this, ["x", "alpha"]);
|
||||
FlxTween.tween(this, {x: defaultX}, .4, {ease: FlxEase.circInOut});
|
||||
FlxTween.tween(this, {alpha: 1}, .3, {ease: FlxEase.sineOut, startDelay: .1, onComplete: (_) -> {
|
||||
if(showingTimer > 0) timerActive = true;
|
||||
}});
|
||||
|
||||
showedAnimation = true;
|
||||
}
|
||||
|
||||
public var isClosing:Bool = false;
|
||||
|
||||
public function disappearAnimation() {
|
||||
if (isClosing) return;
|
||||
isClosing = true;
|
||||
|
||||
// TODO: make it so you can click through the notification
|
||||
closeButton.active = false;
|
||||
closeButton.selectable = false;
|
||||
closeButton.canBeHovered = false;
|
||||
selectable = false;
|
||||
canBeHovered = false;
|
||||
|
||||
x = defaultX; alpha = 1; FlxTween.cancelTweensOf(this, ["x", "alpha"]);
|
||||
FlxTween.tween(this, {alpha: 0}, .3, {ease: FlxEase.sineOut, onComplete: (_) -> {
|
||||
if(onRemove != null) onRemove(this);
|
||||
destroy();
|
||||
}});
|
||||
|
||||
showedAnimation = false;
|
||||
}
|
||||
|
||||
public function idleDisappearAnimation() {
|
||||
if (isClosing) return;
|
||||
isClosing = true;
|
||||
|
||||
x = defaultX; alpha = 1; FlxTween.cancelTweensOf(this, ["x", "alpha"]);
|
||||
FlxTween.tween(this, {alpha: 0}, .3, {ease: FlxEase.sineOut});
|
||||
FlxTween.tween(this, {x: closingX}, .4, {ease: FlxEase.circInOut, onComplete: (_) -> {
|
||||
if(onRemove != null) onRemove(this);
|
||||
destroy();
|
||||
}});
|
||||
|
||||
showedAnimation = false;
|
||||
}
|
||||
}
|
||||
|
||||
enum abstract Corner(Int) {
|
||||
var TOP_LEFT = 0;
|
||||
var TOP_RIGHT = 1;
|
||||
var BOTTOM_LEFT = 2;
|
||||
var BOTTOM_RIGHT = 3;
|
||||
}
|
||||
@@ -78,7 +78,7 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
|
||||
playAnim(event.animName, event.force, event.context, event.reversed, event.frame);
|
||||
}
|
||||
|
||||
public function new(x:Float, y:Float, ?character:String = "bf", isPlayer:Bool = false, switchAnims:Bool = true)
|
||||
public function new(x:Float, y:Float, ?character:String = "bf", isPlayer:Bool = false, switchAnims:Bool = true, disableScripts:Bool = false)
|
||||
{
|
||||
super(x, y);
|
||||
|
||||
@@ -114,7 +114,12 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
|
||||
continue;
|
||||
}
|
||||
// Loads the script
|
||||
script = Script.create(Paths.script(Path.withoutExtension(xmlPath), null, true));
|
||||
if(!disableScripts) {
|
||||
script = Script.create(Paths.script(Path.withoutExtension(xmlPath), null, true));
|
||||
} else {
|
||||
script = new DummyScript(curCharacter);
|
||||
}
|
||||
|
||||
script.setParent(this);
|
||||
script.load();
|
||||
applyXML(xml);
|
||||
@@ -539,7 +544,7 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
|
||||
public static function getList(?mods:Bool = false):Array<String> {
|
||||
return [
|
||||
for (path in Paths.getFolderContent('data/characters/', true, mods ? MODS : BOTH))
|
||||
if (Path.extension(path) == "xml") Path.withoutDirectory(Path.withoutExtension(path))
|
||||
if (Path.extension(path) == "xml") CoolUtil.getFilename(path)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -587,7 +587,7 @@ class PlayState extends MusicBeatState
|
||||
for (event in SONG.events) songEvents.pushOnce(event.name);
|
||||
|
||||
for (file in Paths.getFolderContent('data/events/', true, fromMods ? MODS : BOTH)) {
|
||||
var fileName:String = Path.withoutExtension(Path.withoutDirectory(file));
|
||||
var fileName:String = CoolUtil.getFilename(file);
|
||||
if (EventsData.eventsList.contains(fileName) && songEvents.contains(fileName)) {
|
||||
addScript(file);
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ class SplashGroup extends FlxTypedGroup<FunkinSprite> {
|
||||
|
||||
// immediatly draw once and put image in GPU to prevent freezes
|
||||
// TODO: change to graphics cache
|
||||
@:privateAccess
|
||||
splash.drawComplex(FlxG.camera);
|
||||
} catch(e:Dynamic) {
|
||||
Logs.trace('Couldn\'t parse splash data for "${path}": ${e.toString()}', ERROR);
|
||||
|
||||
+211
-86
@@ -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,103 @@ 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> = [];
|
||||
|
||||
private var spritesParentFolder = "";
|
||||
public var defaultZoom:Float = 1.05;
|
||||
public var startCam = new FlxPoint();
|
||||
|
||||
public function getSprite(name:String) {
|
||||
public var onXMLLoaded:(Access, Array<Access>)->Array<Access> = null;
|
||||
public var onNodeLoaded:(Access, Dynamic)->Dynamic = null;
|
||||
public var onNodeFinished:(Access, Dynamic)->Void = null;
|
||||
public var onAddSprite:(FlxObject)->Void = null;
|
||||
|
||||
public var spritesParentFolder = "";
|
||||
|
||||
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 static var DEFAULT_ATTRIBUTES:Array<String> = ["name", "startCamPosX", "startCamPosY", "zoom", "folder"];
|
||||
|
||||
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) {
|
||||
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 (xml != null) {
|
||||
var parsed:Null<Float>;
|
||||
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)
|
||||
// Load custom attributes
|
||||
for(att in xml.x.attributes())
|
||||
if(!DEFAULT_ATTRIBUTES.contains(att))
|
||||
extra.set(att, xml.x.get(att));
|
||||
|
||||
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,19 +118,16 @@ 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;
|
||||
|
||||
stageSprites.set(spr.name, spr);
|
||||
state.add(spr);
|
||||
spr;
|
||||
addSprite(spr);
|
||||
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,35 +135,19 @@ class Stage extends FlxBasic implements IBeatReceiver {
|
||||
(node.has.color) ? CoolUtil.getColorFromDynamic(node.att.color) : -1
|
||||
);
|
||||
|
||||
stageSprites.set(node.getAtt("name"), spr);
|
||||
state.add(spr);
|
||||
spr;
|
||||
stageSprites.set(spr.name, spr);
|
||||
addSprite(spr);
|
||||
case "boyfriend" | "bf" | "player":
|
||||
addCharPos("boyfriend", node, {
|
||||
x: 770,
|
||||
y: 100,
|
||||
scroll: 1,
|
||||
flip: true
|
||||
});
|
||||
addCharPos("boyfriend", node, getDefaultPos("boyfriend"));
|
||||
case "girlfriend" | "gf":
|
||||
addCharPos("girlfriend", node, {
|
||||
x: 400,
|
||||
y: 130,
|
||||
scroll: 0.95,
|
||||
flip: false
|
||||
});
|
||||
addCharPos("girlfriend", node, getDefaultPos("girlfriend"));
|
||||
case "dad" | "opponent":
|
||||
addCharPos("dad", node, {
|
||||
x: 100,
|
||||
y: 100,
|
||||
scroll: 1,
|
||||
flip: false
|
||||
});
|
||||
addCharPos("dad", node, getDefaultPos("dad"));
|
||||
case "character" | "char":
|
||||
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,50 +157,72 @@ 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)
|
||||
XMLUtil.applyXMLProperty(sprite, e);
|
||||
}
|
||||
|
||||
if(onNodeFinished != null) {
|
||||
onNodeFinished(node, sprite);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (characterPoses["girlfriend"] == null)
|
||||
addCharPos("girlfriend", null, {
|
||||
x: 400,
|
||||
y: 130,
|
||||
scroll: 0.95,
|
||||
flip: false
|
||||
});
|
||||
addCharPos("girlfriend", null, getDefaultPos("girlfriend"));
|
||||
|
||||
if (characterPoses["dad"] == null)
|
||||
addCharPos("dad", null, {
|
||||
x: 100,
|
||||
y: 100,
|
||||
scroll: 1,
|
||||
flip: false
|
||||
});
|
||||
addCharPos("dad", null, getDefaultPos("dad"));
|
||||
|
||||
if (characterPoses["boyfriend"] == null)
|
||||
addCharPos("boyfriend", null, {
|
||||
addCharPos("boyfriend", null, getDefaultPos("boyfriend"));
|
||||
|
||||
if (PlayState.instance != state) return;
|
||||
for(k=>e in stageSprites) {
|
||||
stageScript.set(k, e);
|
||||
}
|
||||
}
|
||||
|
||||
public static function getDefaultPos(name:String):StageCharPosInfo {
|
||||
return switch(name) {
|
||||
case "boyfriend" | "bf" | "player": {
|
||||
x: 770,
|
||||
y: 100,
|
||||
scroll: 1,
|
||||
flip: true
|
||||
});
|
||||
|
||||
if (PlayState.instance == null) return;
|
||||
for(k=>e in stageSprites) {
|
||||
stageScript.set(k, e);
|
||||
};
|
||||
case "girlfriend" | "gf": {
|
||||
x: 400,
|
||||
y: 130,
|
||||
scroll: 0.95,
|
||||
flip: false
|
||||
};
|
||||
case "dad" | "opponent": {
|
||||
x: 100,
|
||||
y: 100,
|
||||
scroll: 1,
|
||||
flip: false
|
||||
};
|
||||
default: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
scroll: 1,
|
||||
flip: false
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -191,33 +233,51 @@ class Stage extends FlxBasic implements IBeatReceiver {
|
||||
if (node != null) {
|
||||
charPos.x = Std.parseFloat(node.getAtt("x")).getDefault(charPos.x);
|
||||
charPos.y = Std.parseFloat(node.getAtt("y")).getDefault(charPos.y);
|
||||
charPos.charSpacingX = Std.parseFloat(node.getAtt("spacingx")).getDefault(charPos.charSpacingX);
|
||||
charPos.charSpacingY = Std.parseFloat(node.getAtt("spacingy")).getDefault(charPos.charSpacingY);
|
||||
charPos.camxoffset = Std.parseFloat(node.getAtt("camxoffset")).getDefault(charPos.camxoffset);
|
||||
charPos.camyoffset = Std.parseFloat(node.getAtt("camyoffset")).getDefault(charPos.camyoffset);
|
||||
charPos.skewX = Std.parseFloat(node.getAtt("skewx")).getDefault(charPos.skewX);
|
||||
charPos.skewY = Std.parseFloat(node.getAtt("skewy")).getDefault(charPos.skewY);
|
||||
charPos.alpha = Std.parseFloat(node.getAtt("alpha")).getDefault(charPos.alpha);
|
||||
charPos.angle = Std.parseFloat(node.getAtt("angle")).getDefault(charPos.angle);
|
||||
charPos.flipX = (node.has.flip || node.has.flipX) ? (node.getAtt("flip") == "true" || node.getAtt("flipX") == "true") : charPos.flipX;
|
||||
charPos.zoomFactor = Std.parseFloat(node.getAtt("zoomfactor")).getDefault(charPos.zoomFactor);
|
||||
|
||||
var scale = Std.parseFloat(node.getAtt("scale")).getDefault(charPos.scale.x);
|
||||
charPos.scale.set(scale, scale);
|
||||
if (node.has.scale) {
|
||||
var scale:Null<Float> = Std.parseFloat(node.att.scale);
|
||||
if (scale.isNotNull()) charPos.scale.set(scale, scale);
|
||||
}
|
||||
if (node.has.scalex) {
|
||||
var scale:Null<Float> = Std.parseFloat(node.att.scalex);
|
||||
if (scale.isNotNull()) charPos.scale.x = scale;
|
||||
}
|
||||
if (node.has.scaley) {
|
||||
var scale:Null<Float> = Std.parseFloat(node.att.scaley);
|
||||
if (scale.isNotNull()) charPos.scale.y = scale;
|
||||
}
|
||||
|
||||
if (node.has.scroll) {
|
||||
var scroll:Null<Float> = Std.parseFloat(node.att.scroll);
|
||||
if (scroll != null) charPos.scrollFactor.set(scroll, scroll);
|
||||
} else {
|
||||
if (node.has.scrollx) {
|
||||
var scroll:Null<Float> = Std.parseFloat(node.att.scrollx);
|
||||
if (scroll != null) charPos.scrollFactor.x = scroll;
|
||||
}
|
||||
if (node.has.scrolly) {
|
||||
var scroll:Null<Float> = Std.parseFloat(node.att.scrolly);
|
||||
if (scroll != null) charPos.scrollFactor.y = scroll;
|
||||
}
|
||||
}
|
||||
if (node.has.scrollx) {
|
||||
var scroll:Null<Float> = Std.parseFloat(node.att.scrollx);
|
||||
if (scroll != null) charPos.scrollFactor.x = scroll;
|
||||
}
|
||||
if (node.has.scrolly) {
|
||||
var scroll:Null<Float> = Std.parseFloat(node.att.scrolly);
|
||||
if (scroll != null) charPos.scrollFactor.y = scroll;
|
||||
}
|
||||
}
|
||||
|
||||
state.add(charPos);
|
||||
return characterPoses[name] = charPos;
|
||||
return addSprite(characterPoses[name] = charPos);
|
||||
}
|
||||
|
||||
function addSprite<T:FlxObject>(sprite:T):T {
|
||||
state.add(sprite);
|
||||
if(onAddSprite != null) onAddSprite(sprite);
|
||||
return sprite;
|
||||
}
|
||||
|
||||
public inline function isCharFlipped(posName:String, def:Bool = false)
|
||||
@@ -239,19 +299,29 @@ 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("test");
|
||||
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;
|
||||
@@ -261,6 +331,7 @@ class StageCharPos extends FlxObject {
|
||||
public var alpha:Float = 1;
|
||||
public var flipX:Bool = false;
|
||||
public var scale:FlxPoint = FlxPoint.get(1, 1);
|
||||
public var zoomFactor:Float = 1;
|
||||
|
||||
public function new() {
|
||||
super();
|
||||
@@ -273,13 +344,51 @@ class StageCharPos extends FlxObject {
|
||||
super.destroy();
|
||||
}
|
||||
|
||||
private var _id:Float = -1;
|
||||
|
||||
private var oldInfo:OldCharInfo = null;
|
||||
|
||||
public function prepareCharacter(char:Character, id:Float = 0) {
|
||||
_id = id;
|
||||
oldInfo = getOldInfo(char);
|
||||
char.setPosition(x + (id * charSpacingX), y + (id * charSpacingY));
|
||||
char.scrollFactor.set(scrollFactor.x, scrollFactor.y);
|
||||
char.scale.x *= scale.x; char.scale.y *= scale.y;
|
||||
char.cameraOffset += FlxPoint.weak(camxoffset, camyoffset);
|
||||
char.skew.x += skewX; char.skew.y += skewY;
|
||||
char.alpha *= alpha;
|
||||
char.angle += angle;
|
||||
char.zoomFactor *= zoomFactor;
|
||||
}
|
||||
|
||||
public function getOldInfo(char:Character) {
|
||||
return {
|
||||
x: char.x, y: char.y,
|
||||
scrollX: char.scrollFactor.x, scrollY: char.scrollFactor.y,
|
||||
scaleX: char.scale.x, scaleY: char.scale.y,
|
||||
camxoffset: char.cameraOffset.x, camyoffset: char.cameraOffset.y,
|
||||
skewX: char.skew.x, skewY: char.skew.y,
|
||||
alpha: char.alpha, zoomFactor: char.zoomFactor,
|
||||
angle: char.angle
|
||||
}
|
||||
}
|
||||
|
||||
public function revertCharacter(char:Character) {
|
||||
if(oldInfo == null) return;
|
||||
for(field in Reflect.fields(oldInfo)) {
|
||||
switch(field) {
|
||||
case "scrollX": char.scrollFactor.x = oldInfo.scrollX;
|
||||
case "scrollY": char.scrollFactor.y = oldInfo.scrollY;
|
||||
case "scaleX": char.scale.x = oldInfo.scaleX;
|
||||
case "scaleY": char.scale.y = oldInfo.scaleY;
|
||||
case "camxoffset": char.cameraOffset.x = oldInfo.camxoffset;
|
||||
case "camyoffset": char.cameraOffset.y = oldInfo.camyoffset;
|
||||
case "skewX": char.skew.x = oldInfo.skewX;
|
||||
case "skewY": char.skew.y = oldInfo.skewY;
|
||||
default: Reflect.setProperty(char, field, Reflect.field(oldInfo, field));
|
||||
}
|
||||
}
|
||||
oldInfo = null;
|
||||
}
|
||||
}
|
||||
typedef StageCharPosInfo = {
|
||||
@@ -287,4 +396,20 @@ typedef StageCharPosInfo = {
|
||||
var y:Float;
|
||||
var flip:Bool;
|
||||
var scroll:Float;
|
||||
}
|
||||
|
||||
typedef OldCharInfo = {
|
||||
var x:Float;
|
||||
var y:Float;
|
||||
var scrollX:Float;
|
||||
var scrollY:Float;
|
||||
var scaleX:Float;
|
||||
var scaleY:Float;
|
||||
var camxoffset:Float;
|
||||
var camyoffset:Float;
|
||||
var skewX:Float;
|
||||
var skewY:Float;
|
||||
var alpha:Float;
|
||||
var zoomFactor:Float;
|
||||
var angle:Float;
|
||||
}
|
||||
Reference in New Issue
Block a user