diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 10ff1494f..cc470cdcc 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -7,6 +7,7 @@ on: - main - feat/* - beta-* + - wip-* workflow_dispatch: @@ -32,7 +33,7 @@ jobs: deploy: needs: test runs-on: ${{ matrix.os }} - if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop' || startsWith(github.ref, 'refs/heads/beta-') + if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop' || startsWith(github.ref, 'refs/heads/beta-') || startsWith(github.ref, 'refs/heads/wip-') strategy: matrix: diff --git a/CHANGELOG.md b/CHANGELOG.md index fe2e950ae..e458f2b15 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix typo with white tile reference in engine UI [@kevinfoley](https://github.com/kevinfoley) - Fix issue where text codes would only autocomplete only using localised names. `!Font`, `!Speed`, `!Instant` and `!Cursor` will no list results regardless of user's language setting - Fix issue where rename button wouldn't appear for variables used in script values +- Fix issue where actors referenced within scripts were not always being linked correctly ## [4.0.0] - 2024-06-19 diff --git a/src/lib/compiler/scriptBuilder.ts b/src/lib/compiler/scriptBuilder.ts index 88f1d3aba..0903409a9 100644 --- a/src/lib/compiler/scriptBuilder.ts +++ b/src/lib/compiler/scriptBuilder.ts @@ -2381,6 +2381,9 @@ extern void __mute_mask_${symbol}; if (typeof id === "number") { this.actorIndex = id; this._setConst(addr, this.actorIndex); + } else if (typeof id === "string" && id.startsWith(".")) { + this.actorIndex = -1; + this._set(addr, id); } else if (typeof id === "string") { const newIndex = this.getActorIndex(id); this.actorIndex = newIndex; @@ -3985,7 +3988,10 @@ extern void __mute_mask_${symbol}; // -------------------------------------------------------------------------- // Call Script - callScript = (scriptId: string, input: Dictionary) => { + callScript = ( + scriptId: string, + input: Dictionary + ) => { const { customEvents } = this.options; const customEvent = customEvents.find((ce) => ce.id === scriptId); @@ -4016,7 +4022,8 @@ extern void __mute_mask_${symbol}; if ( typeof variableValue !== "string" && variableValue.type !== "variable" && - variableValue.type !== "number" + variableValue.type !== "number" && + variableValue.type !== "argument" ) { const [rpnOps, fetchOps] = precompileScriptValue( optimiseScriptValue(variableValue) @@ -4057,6 +4064,8 @@ extern void __mute_mask_${symbol}; if (typeof actorValue === "string") { const actorIndex = this.getActorIndex(actorValue); this._stackPushConst(actorIndex, `Actor ${actorArg.id}`); + } else if (actorValue.type === "argument") { + this._stackPush(actorValue.symbol); } } } diff --git a/src/shared/lib/scripts/scriptDefHelpers.ts b/src/shared/lib/scripts/scriptDefHelpers.ts index b35e76791..013b62ba9 100644 --- a/src/shared/lib/scripts/scriptDefHelpers.ts +++ b/src/shared/lib/scripts/scriptDefHelpers.ts @@ -74,6 +74,10 @@ export const isActorField = ( args: ScriptEventArgs, scriptEventDefs: ScriptEventDefs ) => { + // Custom event calls + if (fieldName.startsWith("$actor[")) { + return true; + } const field = getField(cmd, fieldName, scriptEventDefs); return !!field && field.type === "actor" && isFieldVisible(field, args); }; diff --git a/test/data/compiler/scriptBuilder.test.ts b/test/data/compiler/scriptBuilder.test.ts index c07925dbe..13bde286a 100644 --- a/test/data/compiler/scriptBuilder.test.ts +++ b/test/data/compiler/scriptBuilder.test.ts @@ -1,3 +1,4 @@ +import { Dictionary } from "@reduxjs/toolkit"; import { PrecompiledScene } from "../../../src/lib/compiler/generateGBVMData"; import ScriptBuilder, { ScriptBuilderOptions, @@ -1085,3 +1086,151 @@ test("should support using variable to change font with %f$Var", async () => { ' .asciz "SetFont%fNewFont"', ]); }); + +test("should allow passing actors to custom event", async () => { + const output: string[] = []; + const additionalScripts: Dictionary<{ + symbol: string; + compiledScript: string; + }> = {}; + const scriptEventHandlers = await getTestScriptHandlers(); + const sb = new ScriptBuilder(output, { + scriptEventHandlers, + additionalScripts, + scene: { + id: "scene1", + actors: [{ ...dummyActorNormalized, id: "actorS0A0" }], + } as unknown as PrecompiledScene, + customEvents: [ + { + id: "script1", + name: "Script 1", + description: "", + variables: {}, + actors: { + "0": { + id: "0", + name: "Actor1", + }, + }, + symbol: "script_1", + script: [ + { + command: "EVENT_ACTOR_SET_POSITION", + args: { + actorId: "0", + x: { + type: "number", + value: 0, + }, + y: { + type: "number", + value: 0, + }, + }, + id: "event1", + }, + ], + }, + ], + } as unknown as ScriptBuilderOptions); + sb.callScript("script1", { + "$actor[0]$": "actorS0A0", + }); + expect(output).toEqual([ + " ; Call Script: Script 1", + " VM_PUSH_CONST 1 ; Actor 0", + " VM_CALL_FAR ___bank_script_1, _script_1", + "", + ]); + expect(additionalScripts["script_1"]?.compiledScript).toContain( + `VM_SET .LOCAL_ACTOR, .SCRIPT_ARG_0_ACTOR` + ); +}); + +test("should allow passing actors to nested custom event", async () => { + const output: string[] = []; + const additionalScripts: Dictionary<{ + symbol: string; + compiledScript: string; + }> = {}; + const scriptEventHandlers = await getTestScriptHandlers(); + const sb = new ScriptBuilder(output, { + scriptEventHandlers, + additionalScripts, + scene: { + id: "scene1", + actors: [{ ...dummyActorNormalized, id: "actorS0A0" }], + } as unknown as PrecompiledScene, + customEvents: [ + { + id: "script1", + name: "Script 1", + description: "", + variables: {}, + actors: { + "0": { + id: "0", + name: "Actor1", + }, + }, + symbol: "script_1", + script: [ + { + command: "EVENT_ACTOR_SET_POSITION", + args: { + actorId: "0", + x: { + type: "number", + value: 0, + }, + y: { + type: "number", + value: 0, + }, + }, + id: "event1", + }, + ], + }, + { + id: "script2", + name: "Script 2", + description: "", + variables: {}, + actors: { + "0": { + id: "0", + name: "Actor1", + }, + }, + symbol: "script_2", + script: [ + { + command: "EVENT_CALL_CUSTOM_EVENT", + args: { + customEventId: "script1", + "$actor[0]$": "0", + }, + id: "event2", + }, + ], + }, + ], + } as unknown as ScriptBuilderOptions); + sb.callScript("script2", { + "$actor[0]$": "actorS0A0", + }); + expect(output).toEqual([ + " ; Call Script: Script 2", + " VM_PUSH_CONST 1 ; Actor 0", + " VM_CALL_FAR ___bank_script_2, _script_2", + "", + ]); + expect(additionalScripts["script_1"]?.compiledScript).toContain( + `VM_SET .LOCAL_ACTOR, .SCRIPT_ARG_0_ACTOR` + ); + expect(additionalScripts["script_2"]?.compiledScript).toContain( + `VM_PUSH_VALUE .SCRIPT_ARG_0_ACTOR` + ); +}); diff --git a/test/helpers/scriptDefHelpers.test.ts b/test/helpers/scriptDefHelpers.test.ts new file mode 100644 index 000000000..c51893084 --- /dev/null +++ b/test/helpers/scriptDefHelpers.test.ts @@ -0,0 +1,40 @@ +import { + ScriptEventDefs, + isActorField, +} from "shared/lib/scripts/scriptDefHelpers"; + +test('should identify fields starting with "$actor[" as actor fields from custom events', () => { + expect(isActorField("EVENT_TEST", "$actor[0]", {}, {})).toEqual(true); +}); + +test('should identify fields with type "actor" as actor fields', () => { + expect( + isActorField("EVENT_TEST", "test", {}, { + EVENT_TEST: { + id: "EVENT_TEST", + fieldsLookup: { + test: { + key: "test", + type: "actor", + }, + }, + }, + } as unknown as ScriptEventDefs) + ).toEqual(true); +}); + +test('should not identify fields without type "actor" as actor fields', () => { + expect( + isActorField("EVENT_TEST", "test", {}, { + EVENT_TEST: { + id: "EVENT_TEST", + fieldsLookup: { + test: { + key: "test", + type: "number", + }, + }, + }, + } as unknown as ScriptEventDefs) + ).toEqual(false); +});