Scriptable Events (pls test)

This commit is contained in:
lunarcleint
2023-05-21 20:58:35 -07:00
parent d430b645f0
commit 59a7f95507
18 changed files with 152 additions and 180 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 436 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 413 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 421 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 413 B

@@ -55,7 +55,7 @@ class BaseGameParser {
if (camFocusedBF != (camFocusedBF = section.mustHitSection)) {
result.events.push({
time: curTime,
type: CAM_MOVEMENT,
name: "Camera Movement",
params: [camFocusedBF ? 1 : 0]
});
}
@@ -90,7 +90,7 @@ class BaseGameParser {
result.events.push({
time: curTime,
type: BPM_CHANGE,
name: "BPM Change",
params: [section.bpm]
});
}
+1 -40
View File
@@ -48,8 +48,8 @@ typedef ChartNote = {
}
typedef ChartEvent = {
var name:String;
var time:Float;
var type:ChartEventType;
var params:Array<Dynamic>;
}
@@ -67,43 +67,4 @@ abstract ChartStrumLineType(Int) from Int to Int {
* STRUMLINE IS MARKED AS ADDITIONAL - WILL BE PLAYED AS CPU EVEN IF OPPONENT MODE IS ENABLED
*/
var ADDITIONAL = 2;
}
@:enum
abstract ChartEventType(Int) from Int to Int {
/**
* CUSTOM EVENT
* Params:
* - Function Name (String)
* - Function Parameters...
*/
var CUSTOM = -1;
/**
* NO EVENT, MADE FOR UNKNOWN EVENTS / EVENTS THAT CANNOT BE PARSED
*/
var NONE = 0;
/**
* CAMERA MOVEMENT EVENT
* Params:
* - Target Strumline ID (Int)
*/
var CAM_MOVEMENT = 1;
/**
* BPM CHANGE EVENT
* Params:
* - Target BPM (Float)
*/
var BPM_CHANGE = 2;
/**
* ALT ANIM TOGGLE
* Params:
* - Strum Line which is going to be toggled (Int)
* - Whenever its going to be toggled or not (Bool)
*/
var ALT_ANIM_TOGGLE = 3;
/**
* Returns all usable event types.
*/
public static inline function getChartEventTypes():Array<ChartEventType>
return [CUSTOM, CAM_MOVEMENT, BPM_CHANGE, ALT_ANIM_TOGGLE];
}
@@ -0,0 +1,7 @@
package funkin.backend.scripting.events;
import funkin.backend.chart.ChartData;
final class EventGameEvent extends CancellableEvent {
public var event:ChartEvent;
}
+1 -1
View File
@@ -134,7 +134,7 @@ class Conductor
var songTime:Float = 0;
var stepTime:Float = 0;
for(e in song.events) if (e.type == BPM_CHANGE && e.params != null && e.params[0] is Float) {
for(e in song.events) if (e.name == "BPM Change" && e.params != null && e.params[0] is Float) {
if (e.params[0] == curBPM) continue;
var steps = (e.time - songTime) / ((60 / curBPM) * 1000 / 4);
stepTime += steps;
@@ -6,6 +6,7 @@ import sys.FileSystem;
import funkin.backend.assets.ModsFolder;
import funkin.menus.TitleState;
import funkin.menus.BetaWarningState;
import funkin.editors.charter.EventsData;
import flixel.FlxState;
/**
@@ -42,6 +43,7 @@ class MainState extends FlxState {
Main.refreshAssets();
ModsFolder.onModSwitch.dispatch(ModsFolder.currentModFolder);
DiscordUtil.reloadJsonData();
EventsData.reloadEvents();
TitleState.initialized = false;
+4 -1
View File
@@ -405,8 +405,11 @@ class Charter extends UIState {
var instPath:String;
public function loadSong() {
if (__reload)
if (__reload) {
EventsData.reloadEvents();
PlayState.loadSong(__song, __diff, false, false);
}
Conductor.setupSong(PlayState.SONG);
+10 -98
View File
@@ -1,76 +1,10 @@
package funkin.editors.charter;
import funkin.backend.system.Conductor;
import funkin.backend.chart.ChartData.ChartEventType;
import funkin.game.HealthIcon;
import funkin.backend.chart.ChartData.ChartEvent;
class CharterEvent extends UISliceSprite {
public static function getEventInfo(id:ChartEventType):EventDefineInfo {
return switch(id) {
case CUSTOM:
// hscript
{
name: "HScript Call",
params: [
{
name: "Function Name",
type: TString,
defValue: "myFunc"
},
{
name: "Function Parameters (String split with commas)",
type: TString,
defValue: ""
}
]
}
case CAM_MOVEMENT:
{
name: "Camera Movement",
params: [
{
name: "Camera Target",
type: TStrumLine,
defValue: 0
}
]
}
case BPM_CHANGE:
{
name: "BPM Change",
params: [
{
name: "Target BPM",
type: TFloat(1),
defValue: Conductor.bpm
}
]
}
case ALT_ANIM_TOGGLE:
{
name: "Alt Animation Toggle",
params: [
{
name: "Strumline",
type: TStrumLine,
defValue: 0
},
{
name: "Enable",
type: TBool,
defValue: true
}
]
}
default:
{
name: "Unknown",
params: []
}
};
}
public var events:Array<ChartEvent>;
public var step:Float;
@@ -92,17 +26,19 @@ class CharterEvent extends UISliceSprite {
}
}
private static function generateDefaultIcon(type:Int) {
var spr = new FlxSprite().loadGraphic(Paths.image('editors/charter/event-icons'), true, 16, 16);
spr.frame = spr.frames.frames[type+1];
return spr;
private static function generateDefaultIcon(name:String) {
var path:String = Paths.image('editors/charter/event-icons/$name');
if (!Assets.exists(path)) path = Paths.image('editors/charter/event-icons/Unknown');
var spr = new FlxSprite().loadGraphic(path);
return spr;
}
public static function generateEventIcon(event:ChartEvent) {
return switch(event.type) {
return switch(event.name) {
default:
generateDefaultIcon(event.type);
case CAM_MOVEMENT:
generateDefaultIcon(event.name);
case "Camera Movement":
// custom icon for camera movement
var state = cast(FlxG.state, Charter);
if (event.params[0] != null && event.params[0] >= 0 && event.params[0] < state.strumLines.length) {
@@ -111,7 +47,7 @@ class CharterEvent extends UISliceSprite {
healthIcon.setUnstretchedGraphicSize(32, 32, false);
healthIcon;
} else
generateDefaultIcon(event.type);
generateDefaultIcon(event.name);
}
}
@@ -136,28 +72,4 @@ class CharterEvent extends UISliceSprite {
x = -(bWidth = 37 + (icons.length * 22));
}
}
typedef EventDefineInfo = {
var name:String;
var params:Array<EventParamInfo>;
}
typedef EventInfo = {
> EventDefineInfo,
var paramValues:Array<Dynamic>;
}
typedef EventParamInfo = {
var name:String;
var type:EventParamType;
var defValue:Dynamic;
}
enum EventParamType {
TBool;
TInt(?min:Int, ?max:Int, ?step:Float);
TFloat(?min:Int, ?max:Int, ?step:Float, ?precision:Int);
TString;
TArrayOfString;
TStrumLine;
}
@@ -1,5 +1,3 @@
package funkin.editors.charter;
class CharterEventGroup extends FlxTypedGroup<CharterEvent> {
}
class CharterEventGroup extends FlxTypedGroup<CharterEvent> {}
@@ -2,6 +2,7 @@ package funkin.editors.charter;
import funkin.backend.system.Conductor;
import flixel.group.FlxGroup;
import funkin.editors.charter.EventsData;
class CharterEventScreen extends UISubstateWindow {
public var cam:FlxCamera;
@@ -40,11 +41,11 @@ class CharterEventScreen extends UISubstateWindow {
add(iconsPanel);
addButton = new UIButton(windowSpr.x + 1, windowSpr.y + 31, "", function() {
openSubState(new CharterEventTypeSelection(function(eventType) {
openSubState(new CharterEventTypeSelection(function(eventName) {
chartEvent.events.push({
time: Conductor.getTimeForStep(chartEvent.step),
params: [],
type: eventType
name: eventName
});
changeTab(chartEvent.events.length-1);
}));
@@ -105,11 +106,10 @@ class CharterEventScreen extends UISubstateWindow {
if (id >= 0 && id < chartEvent.events.length) {
curEvent = id;
var curEvent = chartEvent.events[curEvent];
var data = CharterEvent.getEventInfo(curEvent.type);
eventName.text = data.name;
eventName.text = curEvent.name;
// add new elements
var y:Float = eventName.y + eventName.height + 10;
for(k=>param in data.params) {
for(k=>param in EventsData.getEventParams(curEvent.name)) {
function addLabel() {
var label:UIText = new UIText(eventName.x, y, 0, param.name);
y += label.height + 4;
@@ -121,30 +121,30 @@ class CharterEventScreen extends UISubstateWindow {
case TString:
addLabel();
var textBox:UITextBox = new UITextBox(eventName.x, y, cast value);
y += textBox.height + 10;
y += textBox.height + 30;
paramsPanel.add(textBox);
paramsFields.push(textBox);
case TBool:
var checkbox = new UICheckbox(eventName.x, y, param.name, cast value);
y += checkbox.height + 10;
y += checkbox.height + 8;
paramsPanel.add(checkbox);
paramsFields.push(checkbox);
case TInt(min, max, step):
addLabel();
var numericStepper = new UINumericStepper(eventName.x, y, cast value, step.getDefault(1), 0, min, max);
y += numericStepper.height + 10;
y += numericStepper.height + 30;
paramsPanel.add(numericStepper);
paramsFields.push(numericStepper);
case TFloat(min, max, step, precision):
addLabel();
var numericStepper = new UINumericStepper(eventName.x, y, cast value, step.getDefault(1), precision, min, max);
y += numericStepper.height + 10;
y += numericStepper.height + 30;
paramsPanel.add(numericStepper);
paramsFields.push(numericStepper);
case TStrumLine:
addLabel();
var dropdown = new UIDropDown(eventName.x, y, 320, 32, [for(k=>s in cast(FlxG.state, Charter).strumLines.members) 'Strumline #${k+1} (${s.strumLine.characters[0]})'], cast value);
y += dropdown.height + 10;
y += dropdown.height + 30;
paramsPanel.add(dropdown);
paramsFields.push(dropdown);
default:
@@ -1,11 +1,11 @@
package funkin.editors.charter;
import funkin.backend.chart.ChartData.ChartEventType;
import funkin.editors.charter.EventsData;
class CharterEventTypeSelection extends UISubstateWindow {
var callback:ChartEventType->Void;
var callback:String->Void;
public function new(callback:ChartEventType->Void) {
public function new(callback:String->Void) {
super();
this.callback = callback;
}
@@ -16,11 +16,10 @@ class CharterEventTypeSelection extends UISubstateWindow {
var w:Int = winWidth - 20;
var lastIndex:Int = 0;
for(k=>eventType in ChartEventType.getChartEventTypes()) {
var type = CharterEvent.getEventInfo(eventType);
add(new UIButton(10, 41 + (32 * k), type.name, function() {
for(k=>eventName in EventsData.eventsList) {
add(new UIButton(10, 41 + (32 * k), eventName, function() {
close();
callback(eventType);
callback(eventName);
}, w));
lastIndex = k;
@@ -0,0 +1,88 @@
package funkin.editors.charter;
import hscript.Parser;
import hscript.Interp;
import haxe.Json;
import sys.io.File;
import openfl.Assets;
import haxe.io.Path;
import funkin.backend.assets.Paths;
using StringTools;
class EventsData {
public static var defaultEventsList:Array<String> = ["HScript Call", "Camera Movement", "BPM Change", "Alt Animation Toggle"];
public static var defaultEventsParams:Map<String, Array<EventParamInfo>> = [
"HScript Call" => [{name: "Function Name", type: TString, defValue: "myFunc"}, {name: "Function Parameters (String split with commas)", type: TString, defValue: ""}],
"Camera Movement" => [{name: "Camera Target", type: TStrumLine, defValue: 0}],
"BPM Change" => [{name: "Target BPM", type: TFloat(1), defValue: 100}],
"Alt Animation Toggle" => [{name: "Strumline", type: TStrumLine, defValue: 0}, {name: "Enable", type: TBool, defValue: true}],
];
public static var eventsList:Array<String> = defaultEventsList.copy();
public static var eventsParams:Map<String, Array<EventParamInfo>> = defaultEventsParams.copy();
public static function getEventParams(name:String):Array<EventParamInfo> {
return eventsParams.exists(name) ? eventsParams.get(name) : [];
}
public static function reloadEvents() {
eventsList = defaultEventsList.copy();
eventsParams = defaultEventsParams.copy();
var hscriptInterp:Interp = new Interp();
hscriptInterp.variables.set("Bool", TBool);
hscriptInterp.variables.set("Int", function (?min:Int, ?max:Int, ?step:Float):EventParamType {return TInt(min, max, step);});
hscriptInterp.variables.set("Float", function (?min:Int, ?max:Int, ?step:Float, ?precision:Int):EventParamType {return TFloat(min, max, step, precision);});
hscriptInterp.variables.set("String", TString);
hscriptInterp.variables.set("StrumLine", TStrumLine);
var hscriptParser:Parser = new Parser();
hscriptParser.allowJSON = hscriptParser.allowMetadata = false;
for (file in Paths.getFolderContent('data/events/', true, BOTH)) {
if (Path.extension(file) != "json") continue;
var eventName:String = Path.withoutExtension(Path.withoutDirectory(file));
eventsList.push(eventName);
eventsParams.set(eventName, []);
var fileTxt:String = Assets.getText(file);
if (fileTxt.trim() == "") {continue;}
try {
var data:Dynamic = Json.parse(fileTxt);
if (data == null || data.params == null) continue;
var finalParams:Array<EventParamInfo> = [];
for (paramData in cast(data.params, Array<Dynamic>)) {
try {
finalParams.push({name: paramData.name, type: hscriptInterp.expr(hscriptParser.parseString(paramData.type)), defValue: paramData.defaultValue});
} catch (e) {trace('Error parsing event param ${paramData.name} - ${eventName}: $e');}
}
eventsParams.set(eventName, finalParams);
} catch (e) {trace('Error parsing file $file: $e');}
}
hscriptInterp = null; hscriptParser = null;
}
}
typedef EventInfo = {
var params:Array<EventParamInfo>;
var paramValues:Array<Dynamic>;
}
typedef EventParamInfo = {
var name:String;
var type:EventParamType;
var defValue:Dynamic;
}
enum EventParamType {
TBool;
TInt(?min:Int, ?max:Int, ?step:Float);
TFloat(?min:Int, ?max:Int, ?step:Float, ?precision:Int);
TString;
TStrumLine;
}
+20 -18
View File
@@ -1,5 +1,6 @@
package funkin.game;
import funkin.editors.charter.EventsData;
import funkin.backend.system.RotatingSpriteGroup;
import funkin.editors.charter.Charter;
import funkin.savedata.FunkinSave;
@@ -550,21 +551,22 @@ class PlayState extends MusicBeatState
// dadMidpoint.put();
var camPos:FlxPoint = new FlxPoint(0, 0);
if (!chartingMode || Options.charterEnablePlaytestScripts) {
switch(SONG.meta.name) {
// case "":
// ADD YOUR HARDCODED SCRIPTS HERE!
default:
for(content in [
Paths.getFolderContent('songs/${SONG.meta.name.toLowerCase()}/scripts', true, fromMods ? MODS : BOTH),
Paths.getFolderContent('data/charts/', true, fromMods ? MODS : BOTH)]) {
for(file in content) {
var ext = Path.extension(file).toLowerCase();
if (Script.scriptExtensions.contains(ext))
scripts.add(Script.create(file));
}
function addScript(file:String) {
var ext = Path.extension(file).toLowerCase();
if (Script.scriptExtensions.contains(ext))
scripts.add(Script.create(file));
}
for(content in [Paths.getFolderContent('songs/${SONG.meta.name.toLowerCase()}/scripts', true, fromMods ? MODS : BOTH), Paths.getFolderContent('data/charts/', true, fromMods ? MODS : BOTH)])
for(file in content) addScript(file);
for (file in Paths.getFolderContent('data/events/', true, fromMods ? MODS : BOTH))
if (EventsData.eventsList.contains(Path.withoutExtension(Path.withoutDirectory(file)))) addScript(file);
}
}
@@ -897,7 +899,7 @@ class PlayState extends MusicBeatState
// get first camera focus
for(e in events) {
if (e.time > 10) break;
if (e.type == CAM_MOVEMENT) {
if (e.name == "Camera Movement") {
executeEvent(e);
break;
}
@@ -1170,24 +1172,24 @@ class PlayState extends MusicBeatState
if (event == null) return;
if (event.params == null) return;
switch(event.type) {
case CUSTOM:
if (scripts.event("onEvent", EventManager.get(EventGameEvent).recycle(event)).cancelled) return;
switch(event.name) {
case "HScript Call":
if (event.params[0] is String && event.params[1] is String) {
scripts.call(event.params[0], event.params[1].split(','));
}
case CAM_MOVEMENT:
case "Camera Movement":
if (event.params[0] is Int)
curCameraTarget = event.params[0];
case BPM_CHANGE:
// automatically handled by conductor
case ALT_ANIM_TOGGLE:
case "BPM Change": // automatically handled by conductor
case "Alt Animation Toggle":
if (event.params[0] is Int && event.params[1] is Bool) {
var strLine = strumLines.members[event.params[0]];
if (strLine != null)
strLine.altAnim = cast event.params[1];
}
// todo!!!
default:
case "Unknown":
}
}