1
0
mirror of https://github.com/tobspr/shapez.io.git synced 2025-12-14 02:31:51 +00:00

Make mod loading simpler

This commit is contained in:
tobspr 2022-01-16 17:05:13 +01:00
parent 41a8d4334c
commit 44c4807d74
15 changed files with 599 additions and 720 deletions

View File

@ -3,7 +3,16 @@
* If you are interested in adding more logic to the game, you should also check out * If you are interested in adding more logic to the game, you should also check out
* the advanced example * the advanced example
*/ */
registerMod(() => {
const METADATA = {
website: "https://tobspr.io",
author: "tobspr",
name: "Mod Example: Add new basic building",
version: "1",
id: "add-building-basic",
description: "Shows how to add a new basic building",
};
class MetaDemoModBuilding extends shapez.ModMetaBuilding { class MetaDemoModBuilding extends shapez.ModMetaBuilding {
constructor() { constructor() {
super("demoModBuilding"); super("demoModBuilding");
@ -34,22 +43,7 @@ registerMod(() => {
} }
} }
return class ModImpl extends shapez.Mod { class Mod extends shapez.Mod {
constructor(app, modLoader) {
super(
app,
{
website: "https://tobspr.io",
author: "tobspr",
name: "Mod Example: Add new basic building",
version: "1",
id: "add-building-basic",
description: "Shows how to add a new basic building",
},
modLoader
);
}
init() { init() {
// Register the new building // Register the new building
this.modInterface.registerNewBuilding({ this.modInterface.registerNewBuilding({
@ -64,8 +58,7 @@ registerMod(() => {
metaClass: MetaDemoModBuilding, metaClass: MetaDemoModBuilding,
}); });
} }
}; }
});
//////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////

View File

@ -2,7 +2,16 @@
* This shows how to add a new extended building (A building which flips shapes). * This shows how to add a new extended building (A building which flips shapes).
* Be sure to check out the "add_building_basic" example first * Be sure to check out the "add_building_basic" example first
*/ */
registerMod(() => { const METADATA = {
website: "https://tobspr.io",
author: "tobspr",
name: "Mod Example: Add a flipper building",
version: "1",
id: "add-building-extended",
description:
"Shows how to add a new building with logic, in this case it flips/mirrors shapez from top to down",
};
// Declare a new type of item processor // Declare a new type of item processor
shapez.enumItemProcessorTypes.flipper = "flipper"; shapez.enumItemProcessorTypes.flipper = "flipper";
@ -98,23 +107,7 @@ registerMod(() => {
} }
} }
return class ModImpl extends shapez.Mod { class Mod extends shapez.Mod {
constructor(app, modLoader) {
super(
app,
{
website: "https://tobspr.io",
author: "tobspr",
name: "Mod Example: Add a flipper building",
version: "1",
id: "add-building-extended",
description:
"Shows how to add a new building with logic, in this case it flips/mirrors shapez from top to down",
},
modLoader
);
}
init() { init() {
// Register the new building // Register the new building
this.modInterface.registerNewBuilding({ this.modInterface.registerNewBuilding({
@ -129,8 +122,7 @@ registerMod(() => {
metaClass: MetaModFlipperBuilding, metaClass: MetaModFlipperBuilding,
}); });
} }
}; }
});
//////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////

View File

@ -1,25 +1,18 @@
/** /**
* This is the minimal structure of a mod * This is the minimal structure of a mod
*/ */
registerMod(() => {
return class ModImpl extends shapez.Mod { const METADATA = {
constructor(app, modLoader) {
super(
app,
{
website: "https://tobspr.io", website: "https://tobspr.io",
author: "tobspr", author: "tobspr",
name: "Mod Example: Base", name: "Mod Example: Base",
version: "1", version: "1",
id: "base", id: "base",
description: "The most basic mod", description: "The most basic mod",
}, };
modLoader
);
}
class Mod extends shapez.Mod {
init() { init() {
// Start the modding here // Start the modding here
} }
}; }
});

View File

@ -1,24 +1,17 @@
/** /**
* This shows how to patch existing methods by making belts have a cost * This shows how to patch existing methods by making belts have a cost
*/ */
registerMod(() => {
return class ModImpl extends shapez.Mod { const METADATA = {
constructor(app, modLoader) {
super(
app,
{
website: "https://tobspr.io", website: "https://tobspr.io",
author: "tobspr", author: "tobspr",
name: "Mod Example: Patch Methods", name: "Mod Example: Patch Methods",
version: "1", version: "1",
id: "patch-methods", id: "patch-methods",
description: description: "Shows how to patch existing methods to change the game by making the belts cost shapes",
"Shows how to patch existing methods to change the game by making the belts cost shapes", };
},
modLoader
);
}
class Mod extends shapez.Mod {
init() { init() {
// Define our currency // Define our currency
const CURRENCY = "CyCyCyCy:--------:CuCuCuCu"; const CURRENCY = "CyCyCyCy:--------:CuCuCuCu";
@ -90,8 +83,7 @@ registerMod(() => {
return result; return result;
}); });
} }
}; }
});
const RESOURCES = { const RESOURCES = {
"currency.png": "currency.png":

View File

@ -1,4 +1,3 @@
registerMod(() => {
class DemoModComponent extends shapez.Component { class DemoModComponent extends shapez.Component {
static getId() { static getId() {
return "DemoMod"; return "DemoMod";
@ -80,7 +79,7 @@ registerMod(() => {
} }
} }
return class ModImpl extends shapez.Mod { class Mod extends shapez.Mod {
constructor(app, modLoader) { constructor(app, modLoader) {
super( super(
app, app,
@ -104,50 +103,9 @@ registerMod(() => {
// } // }
// `); // `);
// Replace a builtin sprite
["red", "green", "blue", "yellow", "purple", "cyan", "white"].forEach(color => {
this.modInterface.registerSprite(
"sprites/colors/" + color + ".png",
RESOURCES[color + ".png"]
);
});
// Add an atlas // Add an atlas
this.modInterface.registerAtlas(RESOURCES["demoAtlas.png"], RESOURCES["demoAtlas.json"]); this.modInterface.registerAtlas(RESOURCES["demoAtlas.png"], RESOURCES["demoAtlas.json"]);
// Add a new type of sub shape ("Line", short code "L")
this.modInterface.registerSubShapeType({
id: "line",
shortCode: "L",
weightComputation: distanceToOriginInChunks =>
Math.round(20 + Math.max(Math.min(distanceToOriginInChunks, 30), 0)),
draw: ({ context, quadrantSize, layerScale }) => {
const quadrantHalfSize = quadrantSize / 2;
context.beginPath();
context.moveTo(-quadrantHalfSize, quadrantHalfSize);
context.arc(
-quadrantHalfSize,
quadrantHalfSize,
quadrantSize * layerScale,
-Math.PI * 0.25,
0
);
context.closePath();
},
});
// Modify the theme colors
this.signals.preprocessTheme.add(({ theme }) => {
theme.map.background = "#eee";
theme.items.outline = "#000";
});
// Modify the goal of the first level
this.signals.modifyLevelDefinitions.add(definitions => {
definitions[0].shape = "LuCuLuCu";
});
this.modInterface.registerTranslations("en", { this.modInterface.registerTranslations("en", {
ingame: { ingame: {
interactiveTutorial: { interactiveTutorial: {
@ -236,8 +194,7 @@ registerMod(() => {
`); `);
} }
}; }
});
//////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////
// @notice: Later this part will be autogenerated // @notice: Later this part will be autogenerated

View File

@ -1,23 +1,16 @@
/** /**
* This example shows how to add custom css * This example shows how to add custom css
*/ */
registerMod(() => { const METADATA = {
return class ModImpl extends shapez.Mod {
constructor(app, modLoader) {
super(
app,
{
website: "https://tobspr.io", website: "https://tobspr.io",
author: "tobspr", author: "tobspr",
name: "Mod Example: Add custom CSS", name: "Mod Example: Add custom CSS",
version: "1", version: "1",
id: "custom-css", id: "custom-css",
description: "Shows how to add custom css", description: "Shows how to add custom css",
}, };
modLoader
);
}
class Mod extends shapez.Mod {
init() { init() {
// Notice that, since the UI is scaled dynamically, every pixel value // Notice that, since the UI is scaled dynamically, every pixel value
// should be wrapped in '$scaled()' (see below) // should be wrapped in '$scaled()' (see below)
@ -40,8 +33,7 @@ registerMod(() => {
} }
`); `);
} }
}; }
});
const RESOURCES = { const RESOURCES = {
"cat.png": "cat.png":

View File

@ -1,23 +1,17 @@
/** /**
* This shows how to add custom sub shapes * This shows how to add custom sub shapes
*/ */
registerMod(() => {
return class ModImpl extends shapez.Mod { const METADATA = {
constructor(app, modLoader) {
super(
app,
{
website: "https://tobspr.io", website: "https://tobspr.io",
author: "tobspr", author: "tobspr",
name: "Mod Example: Custom Sub Shapes", name: "Mod Example: Custom Sub Shapes",
version: "1", version: "1",
id: "custom-sub-shapes", id: "custom-sub-shapes",
description: "Shows how to add custom sub shapes", description: "Shows how to add custom sub shapes",
}, };
modLoader
);
}
class Mod extends shapez.Mod {
init() { init() {
// Add a new type of sub shape ("Line", short code "L") // Add a new type of sub shape ("Line", short code "L")
this.modInterface.registerSubShapeType({ this.modInterface.registerSubShapeType({
@ -49,5 +43,4 @@ registerMod(() => {
definitions[0].shape = "LuLuLuLu"; definitions[0].shape = "LuLuLuLu";
}); });
} }
}; }
});

View File

@ -1,23 +1,16 @@
/** /**
* This example shows how to add a new theme to the game * This example shows how to add a new theme to the game
*/ */
registerMod(() => { const METADATA = {
return class ModImpl extends shapez.Mod {
constructor(app, modLoader) {
super(
app,
{
website: "https://tobspr.io", website: "https://tobspr.io",
author: "tobspr", author: "tobspr",
name: "Mod Example: Custom Game Theme", name: "Mod Example: Custom Game Theme",
version: "1", version: "1",
id: "custom-theme", id: "custom-theme",
description: "Shows how to add a custom game theme", description: "Shows how to add a custom game theme",
}, };
modLoader
);
}
class Mod extends shapez.Mod {
init() { init() {
this.modInterface.registerGameTheme({ this.modInterface.registerGameTheme({
id: "my-theme", id: "my-theme",
@ -25,8 +18,7 @@ registerMod(() => {
theme: RESOURCES["my-theme.json"], theme: RESOURCES["my-theme.json"],
}); });
} }
}; }
});
const RESOURCES = { const RESOURCES = {
"my-theme.json": { "my-theme.json": {

View File

@ -1,23 +1,17 @@
/** /**
* This shows how to modify an existing building * This shows how to modify an existing building
*/ */
registerMod(() => {
return class ModImpl extends shapez.Mod { const METADATA = {
constructor(app, modLoader) {
super(
app,
{
website: "https://tobspr.io", website: "https://tobspr.io",
author: "tobspr", author: "tobspr",
name: "Mod Example: Modify existing building", name: "Mod Example: Modify existing building",
version: "1", version: "1",
id: "modify-existing-building", id: "modify-existing-building",
description: "Shows how to modify an existing building", description: "Shows how to modify an existing building",
}, };
modLoader
);
}
class Mod extends shapez.Mod {
init() { init() {
// Make Rotator always unlocked // Make Rotator always unlocked
this.modInterface.replaceMethod(shapez.MetaRotaterBuilding, "getIsUnlocked", function () { this.modInterface.replaceMethod(shapez.MetaRotaterBuilding, "getIsUnlocked", function () {
@ -32,5 +26,4 @@ registerMod(() => {
return [["Awesomeness", 5]]; return [["Awesomeness", 5]];
}); });
} }
}; }
});

View File

@ -2,23 +2,16 @@
* This example shows how to modify the builtin themes. If you want to create your own theme, * This example shows how to modify the builtin themes. If you want to create your own theme,
* be sure to check out the "custom_theme" example * be sure to check out the "custom_theme" example
*/ */
registerMod(() => { const METADATA = {
return class ModImpl extends shapez.Mod {
constructor(app, modLoader) {
super(
app,
{
website: "https://tobspr.io", website: "https://tobspr.io",
author: "tobspr", author: "tobspr",
name: "Mod Example: Modify Builtin Themes", name: "Mod Example: Modify Builtin Themes",
version: "1", version: "1",
id: "modify-theme", id: "modify-theme",
description: "Shows how to modify builtin themes", description: "Shows how to modify builtin themes",
}, };
modLoader
);
}
class Mod extends shapez.Mod {
init() { init() {
shapez.THEMES.light.map.background = "#eee"; shapez.THEMES.light.map.background = "#eee";
shapez.THEMES.light.items.outline = "#000"; shapez.THEMES.light.items.outline = "#000";
@ -26,5 +19,4 @@ registerMod(() => {
shapez.THEMES.dark.map.background = "#245"; shapez.THEMES.dark.map.background = "#245";
shapez.THEMES.dark.items.outline = "#fff"; shapez.THEMES.dark.items.outline = "#fff";
} }
}; }
});

View File

@ -2,34 +2,24 @@
* This example shows how to replace builtin sprites, in this case * This example shows how to replace builtin sprites, in this case
* the color sprites * the color sprites
*/ */
registerMod(() => {
return class ModImpl extends shapez.Mod { const METADATA = {
constructor(app, modLoader) {
super(
app,
{
website: "https://tobspr.io", website: "https://tobspr.io",
author: "tobspr", author: "tobspr",
name: "Mod Example: Replace builtin sprites", name: "Mod Example: Replace builtin sprites",
version: "1", version: "1",
id: "replace-builtin-sprites", id: "replace-builtin-sprites",
description: "Shows how to replace builtin sprites", description: "Shows how to replace builtin sprites",
}, };
modLoader
);
}
class Mod extends shapez.Mod {
init() { init() {
// Replace a builtin sprite // Replace a builtin sprite
["red", "green", "blue", "yellow", "purple", "cyan", "white"].forEach(color => { ["red", "green", "blue", "yellow", "purple", "cyan", "white"].forEach(color => {
this.modInterface.registerSprite( this.modInterface.registerSprite("sprites/colors/" + color + ".png", RESOURCES[color + ".png"]);
"sprites/colors/" + color + ".png",
RESOURCES[color + ".png"]
);
}); });
} }
}; }
});
//////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////

View File

@ -1,23 +1,17 @@
/** /**
* Shows to add new translations * Shows to add new translations
*/ */
registerMod(() => {
return class ModImpl extends shapez.Mod { const METADATA = {
constructor(app, modLoader) {
super(
app,
{
website: "https://tobspr.io", website: "https://tobspr.io",
author: "tobspr", author: "tobspr",
name: "Mod Example: Translations", name: "Mod Example: Translations",
version: "1", version: "1",
id: "translations", id: "translations",
description: "Shows how to add and modify translations", description: "Shows how to add and modify translations",
}, };
modLoader
);
}
class Mod extends shapez.Mod {
init() { init() {
// Replace an existing translation in the english language // Replace an existing translation in the english language
this.modInterface.registerTranslations("en", { this.modInterface.registerTranslations("en", {
@ -67,5 +61,4 @@ registerMod(() => {
} }
}); });
} }
}; }
});

4
src/js/globals.d.ts vendored
View File

@ -26,8 +26,6 @@ declare const shapez: any;
declare const ipcRenderer: any; declare const ipcRenderer: any;
declare const registerMod: any;
// Polyfills // Polyfills
declare interface String { declare interface String {
replaceAll(search: string, replacement: string): string; replaceAll(search: string, replacement: string): string;
@ -98,7 +96,7 @@ declare interface Window {
cpmstarAPI: any; cpmstarAPI: any;
// Mods // Mods
registerMod: any; $shapez_registerMod: any;
anyModLoaded: any; anyModLoaded: any;
shapez: any; shapez: any;

View File

@ -7,28 +7,22 @@ import { MOD_SIGNALS } from "./mod_signals";
export class Mod { export class Mod {
/** /**
*
* @param {Application} app * @param {Application} app
* @param {object} metadata
* @param {string} metadata.name
* @param {string} metadata.version
* @param {string} metadata.author
* @param {string} metadata.website
* @param {string} metadata.description
* @param {string} metadata.id
*
* @param {ModLoader} modLoader * @param {ModLoader} modLoader
* @param {import("./modloader").ModMetadata} meta
*/ */
constructor(app, metadata, modLoader) { constructor(app, modLoader, meta) {
this.app = app; this.app = app;
this.metadata = metadata;
this.modLoader = modLoader; this.modLoader = modLoader;
this.metadata = meta;
this.signals = MOD_SIGNALS; this.signals = MOD_SIGNALS;
this.modInterface = modLoader.modInterface; this.modInterface = modLoader.modInterface;
} }
init() {} init() {
// to be overridden
}
get dialogs() { get dialogs() {
return this.modInterface.dialogs; return this.modInterface.dialogs;

View File

@ -9,6 +9,17 @@ import { MOD_SIGNALS } from "./mod_signals";
const LOG = createLogger("mods"); const LOG = createLogger("mods");
/**
* @typedef {{
* name: string;
* version: string;
* author: string;
* website: string;
* description: string;
* id: string;
* }} ModMetadata
*/
export class ModLoader { export class ModLoader {
constructor() { constructor() {
LOG.log("modloader created"); LOG.log("modloader created");
@ -23,7 +34,7 @@ export class ModLoader {
this.modInterface = new ModInterface(this); this.modInterface = new ModInterface(this);
/** @type {((Object) => (new (Application, ModLoader) => Mod))[]} */ /** @type {({ meta: ModMetadata, modClass: typeof Mod})[]} */
this.modLoadQueue = []; this.modLoadQueue = [];
this.initialized = false; this.initialized = false;
@ -73,8 +84,11 @@ export class ModLoader {
this.exposeExports(); this.exposeExports();
window.registerMod = mod => { window.$shapez_registerMod = (modClass, meta) => {
this.modLoadQueue.push(mod); this.modLoadQueue.push({
modClass,
meta,
});
}; };
if (G_IS_STANDALONE || G_IS_DEV) { if (G_IS_STANDALONE || G_IS_DEV) {
@ -103,6 +117,7 @@ export class ModLoader {
mods.forEach(modCode => { mods.forEach(modCode => {
try { try {
modCode += "\n;window.$shapez_registerMod(Mod, METADATA);";
const func = new Function(modCode); const func = new Function(modCode);
func(); func();
} catch (ex) { } catch (ex) {
@ -116,9 +131,9 @@ export class ModLoader {
} }
this.initialized = true; this.initialized = true;
this.modLoadQueue.forEach(modClass => { this.modLoadQueue.forEach(({ modClass, meta }) => {
try { try {
const mod = new (modClass())(this.app, this); const mod = new modClass(this.app, this, meta);
mod.init(); mod.init();
this.mods.push(mod); this.mods.push(mod);
} catch (ex) { } catch (ex) {
@ -128,7 +143,7 @@ export class ModLoader {
}); });
this.modLoadQueue = []; this.modLoadQueue = [];
delete window.registerMod; delete window.$shapez_registerMod;
} }
} }