Scriptable Events (pls test)
This commit is contained in:
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]
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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":
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user