Skip to content

Commit

Permalink
Add concept of scene temporary variables that are reset to 0 on scene…
Browse files Browse the repository at this point in the history
… init, allows "Replace Tile From Sequence" to not need a state variable to be set explicitly
  • Loading branch information
chrismaltby committed Apr 5, 2024
1 parent c658567 commit e541d59
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 40 deletions.
26 changes: 19 additions & 7 deletions src/lib/compiler/compileData.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import keyBy from "lodash/keyBy";
import { eventHasArg } from "lib/helpers/eventSystem";
import compileImages from "./compileImages";
import compileEntityEvents from "./compileEntityEvents";
import compileEntityEvents, {
isEmptyCompiledScript,
} from "./compileEntityEvents";
import {
projectTemplatesRoot,
MAX_ACTORS,
Expand Down Expand Up @@ -1485,11 +1487,9 @@ const compile = async (
filename: string;
data: string;
}> = {};
const compiledCustomEventScriptCache: Dictionary<{
scriptRef: string;
argsLen: number;
}> = {};
const compiledSubScriptCache: Dictionary<string> = {};
const compiledAssetsCache: Dictionary<string> = {};
const entityTmpVarCount: Dictionary<number> = {};

const eventPtrs: PrecompiledSceneEventPtrs[] = precompiled.sceneData.map(
(scene, sceneIndex) => {
Expand Down Expand Up @@ -1572,13 +1572,18 @@ const compile = async (
additionalScripts,
additionalOutput,
symbols,
compiledCustomEventScriptCache,
compiledSubScriptCache,
compiledAssetsCache,
entityTmpVarCount,
branch: false,
isFunction: false,
debugEnabled,
});

if (isEmptyCompiledScript(compiledScript)) {
return null;
}

output[`${scriptName}.s`] = compiledScript;
output[`${scriptName}.h`] = compileScriptHeader(scriptName);

Expand All @@ -1588,6 +1593,13 @@ const compile = async (
const bankSceneEvents = (scene: PrecompiledScene, sceneIndex: number) => {
// Merge start scripts for actors with scene start script
const initScript = ([] as ScriptEvent[]).concat(
{
id: "",
command: "INTERNAL_RESET_SCENE_TMP",
args: {
sceneId: scene.id,
},
},
scene.actors
.map((actor) => {
const actorStartScript = actor.startScript || [];
Expand Down Expand Up @@ -1714,7 +1726,6 @@ VM_ACTOR_SET_SPRITESHEET_BY_REF .ARG2, .ARG1`,
);

return {
start: bankSceneEvents(scene, sceneIndex),
playerHit1: compileScript(
combinedPlayerHitScript,
"scene",
Expand Down Expand Up @@ -1788,6 +1799,7 @@ VM_ACTOR_SET_SPRITESHEET_BY_REF .ARG2, .ARG1`,
"script"
);
}),
start: bankSceneEvents(scene, sceneIndex), // Need to compile scene start script last to make sure all scene temporary variables have been found
};
}
);
Expand Down
14 changes: 14 additions & 0 deletions src/lib/compiler/compileEntityEvents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ const compileEntityEvents = (
args?.value as number,
subInput[i]?.children?.true
);
} else if (command === "INTERNAL_RESET_SCENE_TMP") {
scriptBuilder.insertSceneTemporaryVariablesResetPlaceholder();
} else if (command !== "EVENT_END") {
warnings(
`No compiler for command "${command}". Are you missing a plugin? ${JSON.stringify(
Expand Down Expand Up @@ -164,6 +166,18 @@ const compileEntityEvents = (
}
};

export const isEmptyCompiledScript = (script: string): boolean => {
return (
script
.replace(/;.*/g, "") // Strip comments
.replace(/[\s\S]*::/, "") // Strip head
.split("\n")
.map((l) => l.trim())
.filter((i) => i)
.join() === "VM_LOCK,VM_STOP"
);
};

export default compileEntityEvents;

export { STRING_NOT_FOUND, VARIABLE_NOT_FOUND };
8 changes: 7 additions & 1 deletion src/lib/compiler/generateGBVMData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1147,9 +1147,15 @@ export const compileGameGlobalsInclude = (
variableAliasLookup: Dictionary<{ symbol: string }>,
stateReferences: string[]
) => {
const variables = Object.values(variableAliasLookup).map(
const allVariables = Object.values(variableAliasLookup).map(
(v) => v?.symbol
) as string[];
const tmpSorted = allVariables
.filter((item) => item.startsWith("TMP_"))
.sort();
const globals = allVariables.filter((item) => !item.startsWith("TMP_"));
const variables = [...globals, ...tmpSorted];

return (
variables
.map((string, stringIndex) => {
Expand Down
92 changes: 74 additions & 18 deletions src/lib/compiler/scriptBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ import { encodeString } from "shared/lib/helpers/fonts";
import { mapScript } from "shared/lib/scripts/walk";
import { ScriptEventHandlers } from "lib/project/loadScriptEventHandlers";
import { VariableMapData } from "lib/compiler/compileData";
import { checksumString } from "lib/helpers/checksum";

export type ScriptOutput = string[];

Expand Down Expand Up @@ -144,11 +145,9 @@ export interface ScriptBuilderOptions {
symbols: Dictionary<string>;
argLookup: ScriptBuilderFunctionArgLookup;
maxDepth: number;
compiledCustomEventScriptCache: Dictionary<{
scriptRef: string;
argsLen: number;
}>;
compiledSubScriptCache: Dictionary<string>;
debugEnabled: boolean;
entityTmpVarCount: Dictionary<number>;
compiledAssetsCache: Dictionary<string>;
compileEvents: (self: ScriptBuilder, events: ScriptEvent[]) => void;
}
Expand Down Expand Up @@ -537,8 +536,8 @@ class ScriptBuilder {
argLookup: options.argLookup || { actor: new Map(), variable: new Map() },
maxDepth: options.maxDepth ?? 5,
debugEnabled: options.debugEnabled ?? false,
compiledCustomEventScriptCache:
options.compiledCustomEventScriptCache ?? {},
entityTmpVarCount: options.entityTmpVarCount ?? {},
compiledSubScriptCache: options.compiledSubScriptCache ?? {},
compiledAssetsCache: options.compiledAssetsCache ?? {},
compileEvents: options.compileEvents || ((_self, _e) => {}),
settings: options.settings || defaultProjectSettings,
Expand Down Expand Up @@ -3415,19 +3414,14 @@ extern void __mute_mask_${symbol};
};

compileCustomEventScript = (customEventId: string) => {
const { customEvents, compiledCustomEventScriptCache } = this.options;
const { customEvents } = this.options;
const customEvent = customEvents.find((ce) => ce.id === customEventId);

if (!customEvent) {
console.warn("Script not found", customEventId);
return;
}

const cachedResult = compiledCustomEventScriptCache[customEventId];
if (cachedResult) {
return cachedResult;
}

const argLookup: {
actor: Map<string, ScriptBuilderFunctionArg>;
variable: Map<string, ScriptBuilderFunctionArg>;
Expand Down Expand Up @@ -3575,17 +3569,15 @@ extern void __mute_mask_${symbol};
customEvent.symbol ? customEvent.symbol : `script_custom_0`,
false
);
const result = { scriptRef: symbol, argsLen };
compiledCustomEventScriptCache[customEventId] = result;

this._compileSubScript("custom", script, symbol, {
const scriptRef = this._compileSubScript("custom", script, symbol, {
argLookup,
entity: customEvent,
entityType: "customEvent",
entityScriptKey: "script",
});

return result;
return { scriptRef, argsLen };
};

returnFar = () => {
Expand Down Expand Up @@ -3708,7 +3700,12 @@ extern void __mute_mask_${symbol};
return variable.symbol;
}

if (typeof variable === "string" && variable.startsWith(".LOCAL")) {
if (
typeof variable === "string" &&
(variable.startsWith(".LOCAL") ||
variable.startsWith("VAR_") ||
variable.startsWith("TMP_"))
) {
return variable;
}

Expand Down Expand Up @@ -4113,6 +4110,37 @@ extern void __mute_mask_${symbol};
this._memSet(0, 0, "MAX_GLOBAL_VARS");
};

// --------------------------------------------------------------------------
// Scene Temporary Variables

declareSceneTemporaryVariable = () => {
const {
variableAliasLookup,
scene,
entityTmpVarCount,
entity,
entityType,
argLookup,
} = this.options;
const counter = entityTmpVarCount[scene.id] ?? 0;
const symbol = `TMP_S_${counter}`;
variableAliasLookup[symbol] = {
symbol,
name: symbol,
id: symbol,
isLocal: false,
entityType,
entityId: "",
sceneId: scene.id,
};
entityTmpVarCount[scene.id] = counter + 1;
return symbol;
};

insertSceneTemporaryVariablesResetPlaceholder = () => {
this.output.push("||RESET_SCENE_TMP_VARS||");
};

// --------------------------------------------------------------------------
// Engine Fields

Expand Down Expand Up @@ -5303,10 +5331,27 @@ extern void __mute_mask_${symbol};
},
}
);

// Cache identical scripts to reuse symbols where possible
const cacheKey = checksumString(
compiledSubScript
// Strip script symbol
.replace(new RegExp(symbol, "g"), "SYMBOL")
// Strip Debug symbols
.replace(/^GBVM\$.*/gm, "")
.replace(/^.globl GBVM\$.*/gm, "")
);
const cachedSymbol = this.options.compiledSubScriptCache[cacheKey];
if (cachedSymbol) {
return cachedSymbol;
}
this.options.compiledSubScriptCache[cacheKey] = symbol;

this.options.additionalScripts[symbol] = {
symbol,
compiledScript: compiledSubScript,
};

return symbol;
};

Expand Down Expand Up @@ -5437,7 +5482,18 @@ ${lock ? this._padCmd("VM_LOCK", "", 8, 24) + "\n\n" : ""}${
reserveMem > 0
? this._padCmd("VM_RESERVE", String(reserveMem), 8, 24) + "\n\n"
: ""
}${this.output.join("\n")}
}${this.output.join("\n").replace(/\|\|RESET_SCENE_TMP_VARS\|\|\n/, () => {
const { scene, entityTmpVarCount } = this.options;
const tmpCount = entityTmpVarCount[scene.id] ?? 0;
if (tmpCount >= 1) {
return (
` ; Reset Scene Tmp Vars\n` +
this._padCmd("VM_MEMSET", `TMP_S_0, 0, ${tmpCount}`, 8, 24) +
"\n\n"
);
}
return "";
})}
`;
};
}
Expand Down
23 changes: 9 additions & 14 deletions src/lib/events/eventReplaceTileXYSequence.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,13 +81,6 @@ const fields = [
},
],
},
{
key: "variable",
label: l10n("FIELD_STATE_VARIABLE"),
description: l10n("FIELD_STATE_VARIABLE_DESC"),
type: "variable",
defaultValue: "LAST_VARIABLE",
},
];

const compile = (input, helpers) => {
Expand All @@ -99,6 +92,7 @@ const compile = (input, helpers) => {
variableAdd,
variableSetToUnionValue,
markLocalsUsed,
declareSceneTemporaryVariable,
_declareLocal,
_rpn,
_addComment,
Expand All @@ -108,6 +102,7 @@ const compile = (input, helpers) => {
const fromVar = localVariableFromUnion(input.tileIndex);
const framesVar = localVariableFromUnion(input.frames);
const toVar = _declareLocal("to_var", 1, true);
const counterVar = declareSceneTemporaryVariable();

// Calculate max frame
_addComment("Calculate max frame");
Expand All @@ -134,26 +129,26 @@ const compile = (input, helpers) => {
}
_addNL();

ifVariableCompare(input.variable, ".LT", fromVar, () => {
variableSetToUnionValue(input.variable, input.tileIndex);
ifVariableCompare(counterVar, ".LT", fromVar, () => {
variableSetToUnionValue(counterVar, input.tileIndex);
});

replaceTileXYVariable(
input.x,
input.y,
input.tilesetId,
input.variable,
counterVar,
input.tileSize
);

if (input.tileSize === "16px") {
variableAdd(input.variable, 2);
variableAdd(counterVar, 2);
} else {
variableInc(input.variable);
variableInc(counterVar);
}

ifVariableCompare(input.variable, ".GT", toVar, () => {
variableSetToUnionValue(input.variable, input.tileIndex);
ifVariableCompare(counterVar, ".GT", toVar, () => {
variableSetToUnionValue(counterVar, input.tileIndex);
});

markLocalsUsed(fromVar, framesVar, toVar);
Expand Down

0 comments on commit e541d59

Please sign in to comment.