Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
the stage editor shit
  • Loading branch information
KoloInDaCrib authored and ninjamuffin99 committed Oct 10, 2024
commit 27a0b4426f86f04362f97e16e2eff580c9402f34
30 changes: 30 additions & 0 deletions source/funkin/data/stage/StageData.hx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ class StageData
@:optional
public var cameraZoom:Null<Float>;

@:default("shared")
@:optional
public var directory:Null<String>;

public function new()
{
this.version = StageRegistry.STAGE_DATA_VERSION;
Expand Down Expand Up @@ -198,6 +202,32 @@ typedef StageDataProp =
@:default("sparrow")
@:optional
var animType:String;

/**
* The angle of the prop, as a float.
* @default 1.0
*/
@:optional
@:default(0.0)
var angle:Float;

/**
* The blend mode of the prop, as a string.
* Just like in photoshop.
* @default Nothing.
*/
@:default("")
@:optional
var blend:String;

/**
* The color of the prop overlay, as a hex string.
* White overlays, or the ones with the value #FFFFFF, do not appear.
* @default `#FFFFFF`
*/
@:default("#FFFFFF")
@:optional
var color:String;
};

typedef StageDataCharacter =
Expand Down
4 changes: 4 additions & 0 deletions source/funkin/play/stage/Stage.hx
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,10 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass implements
propSprite.scrollFactor.x = dataProp.scroll[0];
propSprite.scrollFactor.y = dataProp.scroll[1];

propSprite.angle = dataProp.angle;
propSprite.color = FlxColor.fromString(dataProp.color);
@:privateAccess if (!isSolidColor) propSprite.blend = BlendMode.fromString(dataProp.blend);

propSprite.zIndex = dataProp.zIndex;

propSprite.flipX = dataProp.flipX;
Expand Down
135 changes: 135 additions & 0 deletions source/funkin/save/Save.hx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import funkin.save.migrator.SaveDataMigrator;
import funkin.save.migrator.SaveDataMigrator;
import funkin.ui.debug.charting.ChartEditorState.ChartEditorLiveInputStyle;
import funkin.ui.debug.charting.ChartEditorState.ChartEditorTheme;
import funkin.ui.debug.stageeditor.StageEditorState.StageEditorTheme;
import funkin.util.SerializerUtil;
import thx.semver.Version;
import thx.semver.Version;
Expand Down Expand Up @@ -146,6 +147,14 @@ class Save
hitsoundVolumeOpponent: 1.0,
themeMusic: true
},

optionsStageEditor:
{
previousFiles: [],
moveStep: "1px",
angleStep: 5,
theme: StageEditorTheme.Light
}
};
}

Expand Down Expand Up @@ -428,6 +437,91 @@ class Save
return data.unlocks.oldChar;
}

public var stageEditorPreviousFiles(get, set):Array<String>;

function get_stageEditorPreviousFiles():Array<String>
{
if (data.optionsStageEditor.previousFiles == null) data.optionsStageEditor.previousFiles = [];

return data.optionsStageEditor.previousFiles;
}

function set_stageEditorPreviousFiles(value:Array<String>):Array<String>
{
// Set and apply.
data.optionsStageEditor.previousFiles = value;
flush();
return data.optionsStageEditor.previousFiles;
}

public var stageEditorHasBackup(get, set):Bool;

function get_stageEditorHasBackup():Bool
{
if (data.optionsStageEditor.hasBackup == null) data.optionsStageEditor.hasBackup = false;

return data.optionsStageEditor.hasBackup;
}

function set_stageEditorHasBackup(value:Bool):Bool
{
// Set and apply.
data.optionsStageEditor.hasBackup = value;
flush();
return data.optionsStageEditor.hasBackup;
}

public var stageEditorMoveStep(get, set):String;

function get_stageEditorMoveStep():String
{
if (data.optionsStageEditor.moveStep == null) data.optionsStageEditor.moveStep = "1px";

return data.optionsStageEditor.moveStep;
}

function set_stageEditorMoveStep(value:String):String
{
// Set and apply.
data.optionsStageEditor.moveStep = value;
flush();
return data.optionsStageEditor.moveStep;
}

public var stageEditorAngleStep(get, set):Float;

function get_stageEditorAngleStep():Float
{
if (data.optionsStageEditor.angleStep == null) data.optionsStageEditor.angleStep = 5;

return data.optionsStageEditor.angleStep;
}

function set_stageEditorAngleStep(value:Float):Float
{
// Set and apply.
data.optionsStageEditor.angleStep = value;
flush();
return data.optionsStageEditor.angleStep;
}

public var stageEditorTheme(get, set):StageEditorTheme;

function get_stageEditorTheme():StageEditorTheme
{
if (data.optionsStageEditor.theme == null) data.optionsStageEditor.theme = StageEditorTheme.Light;

return data.optionsStageEditor.theme;
}

function set_stageEditorTheme(value:StageEditorTheme):StageEditorTheme
{
// Set and apply.
data.optionsStageEditor.theme = value;
flush();
return data.optionsStageEditor.theme;
}

/**
* When we've seen a character unlock, add it to the list of characters seen.
* @param character
Expand Down Expand Up @@ -1068,6 +1162,11 @@ typedef RawSaveData =
* The user's preferences specific to the Chart Editor.
*/
var optionsChartEditor:SaveDataChartEditorOptions;

/**
* The user's preferences specific to the Stage Editor.
*/
var optionsStageEditor:SaveDataStageEditorOptions;
};

typedef SaveApiData =
Expand Down Expand Up @@ -1441,3 +1540,39 @@ typedef SaveDataChartEditorOptions =
*/
var ?playbackSpeed:Float;
};

typedef SaveDataStageEditorOptions =
{
// a lot of these things were copied from savedatacharteditoroptions

/**
* Whether the Stage Editor created a backup the last time it closed.
* Prompt the user to load it, then set this back to `false`.
* @default `false`
*/
var ?hasBackup:Bool;

/**
* Previous files opened in the Stage Editor.
* @default `[]`
*/
var ?previousFiles:Array<String>;

/**
* The Step at which an Object or Character is moved.
* @default `1px`
*/
var ?moveStep:String;

/**
* The Step at which an Object is rotated.
* @default `5`
*/
var ?angleStep:Float;

/**
* Theme in the Stage Editor.
* @default `StageEditorTheme.Light`
*/
var ?theme:StageEditorTheme;
};
3 changes: 2 additions & 1 deletion source/funkin/ui/debug/DebugMenuSubState.hx
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ class DebugMenuSubState extends MusicBeatSubState
// createItem("Input Offset Testing", openInputOffsetTesting);
createItem("CHARACTER SELECT", openCharSelect, true);
createItem("ANIMATION EDITOR", openAnimationEditor);
// createItem("STAGE EDITOR", openStageEditor);
createItem("STAGE EDITOR", openStageEditor);
// createItem("TEST STICKERS", testStickers);
#if sys
createItem("OPEN CRASH LOG FOLDER", openLogFolder);
Expand Down Expand Up @@ -125,6 +125,7 @@ class DebugMenuSubState extends MusicBeatSubState
function openStageEditor()
{
trace('Stage Editor');
FlxG.switchState(() -> new funkin.ui.debug.stageeditor.StageEditorState());
}

#if sys
Expand Down
119 changes: 119 additions & 0 deletions source/funkin/ui/debug/stageeditor/StageEditorObject.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package funkin.ui.debug.stageeditor;

import funkin.data.animation.AnimationData;
import funkin.graphics.FunkinSprite;
import funkin.modding.events.ScriptEvent;

/**
* Contains all the Logic needed for Stage Editor. Only for Stage Editor, as in the gameplay StageProps and Boppers will be used.
*/
class StageEditorObject extends FunkinSprite
{
/**
* The internal Name of the Object.
*/
public var name:String = "Unnamed";

/**
* What animation to play upon starting.
*/
public var startingAnimation:String = "";

public var animDatas:Map<String, AnimationData> = [];

override public function new()
{
super();
}

/**
* Whether the Object is currently being modified in the Stage Editor.
*/
public var isDebugged(default, set):Bool = true;

function set_isDebugged(value:Bool)
{
this.isDebugged = value;

if (value == false) // plays upon starting yippee!!!
playAnim(startingAnimation, true);
else
{
if (animation.curAnim != null)
{
animation.stop();
offset.set();
updateHitbox();
}
}

return value;
}

public function playAnim(name:String, restart:Bool = false, reversed:Bool = false)
{
if (!animation.getNameList().contains(name)) return;

animation.play(name, restart, reversed, 0);

if (animDatas.exists(name)) offset.set(animDatas[name].offsets[0], animDatas[name].offsets[1]);
else
offset.set();
}

/**
* On which beat should it dance?
*/
public var danceEvery:Float = 0;

/**
* Internal, handles danceLeft and danceRight.
*/
var _danced:Bool = true;

public function dance(restart:Bool = false)
{
if (isDebugged) return;

var idle = animation.getNameList().contains("idle");
var dancing = animation.getNameList().contains("danceLeft") && animation.getNameList().contains("danceRight");

if (!idle && !dancing) return;

if (dancing)
{
if (_danced) playAnim("danceRight", restart);
else
playAnim("danceLeft", restart);

_danced = !_danced;
}
else if (idle)
{
playAnim("idle", restart);
}
}

public function addAnim(name:String, prefix:String, offsets:Array<Float>, indices:Array<Int>, frameRate:Int = 24, looped:Bool = true, flipX:Bool = false,
flipY:Bool = false)
{
if (indices.length > 0) animation.addByIndices(name, prefix, indices, "", frameRate, looped, flipX, flipY);
else
animation.addByPrefix(name, prefix, frameRate, looped, flipX, flipY);

if (animation.getNameList().contains(name)) // sometimes the animation doesnt add
{
animDatas.set(name,
{
name: name,
prefix: prefix,
offsets: offsets,
looped: looped,
frameRate: frameRate,
flipX: flipX,
flipY: flipY,
frameIndices: indices
});
}
}
}
Loading