mirror of
https://github.com/tobspr/shapez.io.git
synced 2025-12-13 18:21:51 +00:00
Refactor how mods are loaded to resolve circular dependencies and prepare for future mod loading
This commit is contained in:
parent
01b9bf561c
commit
16ecbf9c6d
@ -1,7 +1,7 @@
|
||||
import { globalConfig } from "../../core/config";
|
||||
import { DrawParameters } from "../../core/draw_parameters";
|
||||
import { Signal } from "../../core/signal";
|
||||
import { MODS } from "../../mods/modloader";
|
||||
import { MOD_SIGNALS } from "../../mods/mod_signals";
|
||||
import { KEYMAPPINGS } from "../key_action_mapper";
|
||||
import { MetaBuilding } from "../meta_building";
|
||||
import { GameRoot } from "../root";
|
||||
@ -92,7 +92,7 @@ export class GameHUD {
|
||||
|
||||
const frag = document.createDocumentFragment();
|
||||
for (const key in this.parts) {
|
||||
MODS.signals.hudElementInitialized.dispatch(this.parts[key]);
|
||||
MOD_SIGNALS.hudElementInitialized.dispatch(this.parts[key]);
|
||||
this.parts[key].createElements(frag);
|
||||
}
|
||||
|
||||
@ -100,7 +100,7 @@ export class GameHUD {
|
||||
|
||||
for (const key in this.parts) {
|
||||
this.parts[key].initialize();
|
||||
MODS.signals.hudElementFinalized.dispatch(this.parts[key]);
|
||||
MOD_SIGNALS.hudElementFinalized.dispatch(this.parts[key]);
|
||||
}
|
||||
|
||||
this.root.keyMapper.getBinding(KEYMAPPINGS.ingame.toggleHud).add(this.toggleUi, this);
|
||||
|
||||
@ -10,10 +10,14 @@ import { COLOR_ITEM_SINGLETONS } from "./items/color_item";
|
||||
import { GameRoot } from "./root";
|
||||
import { enumSubShape } from "./shape_definition";
|
||||
import { Rectangle } from "../core/rectangle";
|
||||
import { MODS_ADDITIONAL_SHAPE_MAP_WEIGHTS } from "../mods/mod_interface";
|
||||
|
||||
const logger = createLogger("map_chunk");
|
||||
|
||||
/**
|
||||
* @type {Object<string, (distanceToOriginInChunks: number) => number>}
|
||||
*/
|
||||
export const MODS_ADDITIONAL_SHAPE_MAP_WEIGHTS = {};
|
||||
|
||||
export class MapChunk {
|
||||
/**
|
||||
*
|
||||
|
||||
@ -38,7 +38,7 @@ import { HUDSandboxController } from "../hud/parts/sandbox_controller";
|
||||
import { queryParamOptions } from "../../core/query_parameters";
|
||||
import { MetaBlockBuilding } from "../buildings/block";
|
||||
import { MetaItemProducerBuilding } from "../buildings/item_producer";
|
||||
import { MODS } from "../../mods/modloader";
|
||||
import { MOD_SIGNALS } from "../../mods/mod_signals";
|
||||
|
||||
/** @typedef {{
|
||||
* shape: string,
|
||||
@ -512,7 +512,7 @@ export function generateLevelDefinitions(limitedVersion = false) {
|
||||
]),
|
||||
];
|
||||
|
||||
MODS.signals.modifyLevelDefinitions.dispatch(levelDefinitions);
|
||||
MOD_SIGNALS.modifyLevelDefinitions.dispatch(levelDefinitions);
|
||||
|
||||
if (G_IS_DEV) {
|
||||
levelDefinitions.forEach(({ shape }) => {
|
||||
|
||||
@ -3,11 +3,23 @@ import { globalConfig } from "../core/config";
|
||||
import { smoothenDpi } from "../core/dpi_manager";
|
||||
import { DrawParameters } from "../core/draw_parameters";
|
||||
import { Vector } from "../core/vector";
|
||||
import { MODS_ADDITIONAL_SUB_SHAPE_DRAWERS } from "../mods/mod_interface";
|
||||
import { BasicSerializableObject, types } from "../savegame/serialization";
|
||||
import { enumColors, enumColorsToHexCode, enumColorToShortcode, enumShortcodeToColor } from "./colors";
|
||||
import { THEME } from "./theme";
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* context: CanvasRenderingContext2D,
|
||||
* quadrantSize: number,
|
||||
* layerScale: number,
|
||||
* }} SubShapeDrawOptions
|
||||
*/
|
||||
|
||||
/**
|
||||
* @type {Object<string, (options: SubShapeDrawOptions) => void>}
|
||||
*/
|
||||
export const MODS_ADDITIONAL_SUB_SHAPE_DRAWERS = {};
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* subShape: enumSubShape,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { MODS } from "../mods/modloader";
|
||||
import { MOD_SIGNALS } from "../mods/mod_signals";
|
||||
|
||||
export const THEMES = {
|
||||
dark: require("./themes/dark.json"),
|
||||
@ -9,5 +9,5 @@ export let THEME = THEMES.light;
|
||||
|
||||
export function applyGameTheme(id) {
|
||||
THEME = THEMES[id];
|
||||
MODS.signals.preprocessTheme.dispatch({ id, theme: THEME });
|
||||
MOD_SIGNALS.preprocessTheme.dispatch({ id, theme: THEME });
|
||||
}
|
||||
|
||||
@ -2,119 +2,118 @@
|
||||
import { Entity } from "../game/entity";
|
||||
/* typehints:end */
|
||||
|
||||
import { Mod } from "./mod";
|
||||
import { MetaBuilding } from "../game/meta_building";
|
||||
export default function ({ Mod, MetaBuilding }) {
|
||||
class MetaDemoModBuilding extends MetaBuilding {
|
||||
constructor() {
|
||||
super("demoModBuilding");
|
||||
}
|
||||
|
||||
export class MetaDemoModBuilding extends MetaBuilding {
|
||||
constructor() {
|
||||
super("demoModBuilding");
|
||||
getSilhouetteColor() {
|
||||
return "red";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the entity at the given location
|
||||
* @param {Entity} entity
|
||||
*/
|
||||
setupEntityComponents(entity) {}
|
||||
}
|
||||
|
||||
getSilhouetteColor() {
|
||||
return "red";
|
||||
}
|
||||
return class ModImpl extends Mod {
|
||||
constructor(modLoader) {
|
||||
super(
|
||||
{
|
||||
authorContact: "tobias@tobspr.io",
|
||||
authorName: "tobspr",
|
||||
name: "Demo Mod",
|
||||
version: "1",
|
||||
id: "demo-mod",
|
||||
},
|
||||
modLoader
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the entity at the given location
|
||||
* @param {Entity} entity
|
||||
*/
|
||||
setupEntityComponents(entity) {}
|
||||
}
|
||||
init() {
|
||||
// Add some custom css
|
||||
this.modInterface.registerCss(`
|
||||
* {
|
||||
color: red !important;
|
||||
}
|
||||
`);
|
||||
|
||||
export class DemoMod extends Mod {
|
||||
constructor(modLoader) {
|
||||
super(
|
||||
{
|
||||
authorContact: "tobias@tobspr.io",
|
||||
authorName: "tobspr",
|
||||
name: "Demo Mod",
|
||||
version: "1",
|
||||
id: "demo-mod",
|
||||
},
|
||||
modLoader
|
||||
);
|
||||
}
|
||||
// Replace a builtin sprite
|
||||
this.modInterface.registerSprite("sprites/colors/red.png", RESOURCES["red.png"]);
|
||||
|
||||
init() {
|
||||
// Add some custom css
|
||||
this.modLoader.modInterface.registerCss(`
|
||||
* {
|
||||
color: red !important;
|
||||
}
|
||||
`);
|
||||
// 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)),
|
||||
|
||||
// Replace a builtin sprite
|
||||
this.modLoader.modInterface.registerSprite("sprites/colors/red.png", RESOURCES["red.png"]);
|
||||
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();
|
||||
},
|
||||
});
|
||||
|
||||
// Add a new type of sub shape ("Line", short code "L")
|
||||
this.modLoader.modInterface.registerSubShapeType({
|
||||
id: "line",
|
||||
shortCode: "L",
|
||||
weightComputation: distanceToOriginInChunks =>
|
||||
Math.round(20 + Math.max(Math.min(distanceToOriginInChunks, 30), 0)),
|
||||
// Modify the theme colors
|
||||
this.signals.preprocessTheme.add(({ theme }) => {
|
||||
theme.map.background = "#eee";
|
||||
theme.items.outline = "#000";
|
||||
});
|
||||
|
||||
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 goal of the first level
|
||||
this.signals.modifyLevelDefinitions.add(definitions => {
|
||||
definitions[0].shape = "LuCuLuCu";
|
||||
});
|
||||
|
||||
// Modify the theme colors
|
||||
this.modLoader.signals.preprocessTheme.add(({ theme }) => {
|
||||
theme.map.background = "#eee";
|
||||
theme.items.outline = "#000";
|
||||
});
|
||||
|
||||
// Modify the goal of the first level
|
||||
this.modLoader.signals.modifyLevelDefinitions.add(definitions => {
|
||||
definitions[0].shape = "LuCuLuCu";
|
||||
});
|
||||
|
||||
this.modLoader.modInterface.registerTranslations("en", {
|
||||
ingame: {
|
||||
interactiveTutorial: {
|
||||
title: "Hello",
|
||||
hints: {
|
||||
"1_1_extractor": "World!",
|
||||
this.modInterface.registerTranslations("en", {
|
||||
ingame: {
|
||||
interactiveTutorial: {
|
||||
title: "Hello",
|
||||
hints: {
|
||||
"1_1_extractor": "World!",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
// Register the new building
|
||||
this.modLoader.modInterface.registerNewBuilding({
|
||||
metaClass: MetaDemoModBuilding,
|
||||
buildingIconBase64: RESOURCES["demoBuilding.png"],
|
||||
// Register the new building
|
||||
this.modInterface.registerNewBuilding({
|
||||
metaClass: MetaDemoModBuilding,
|
||||
buildingIconBase64: RESOURCES["demoBuilding.png"],
|
||||
|
||||
variantsAndRotations: [
|
||||
{
|
||||
description: "A test building",
|
||||
name: "A test name",
|
||||
variantsAndRotations: [
|
||||
{
|
||||
description: "A test building",
|
||||
name: "A test name",
|
||||
|
||||
regularImageBase64: RESOURCES["demoBuilding.png"],
|
||||
blueprintImageBase64: RESOURCES["demoBuildingBlueprint.png"],
|
||||
tutorialImageBase64: RESOURCES["demoBuildingBlueprint.png"],
|
||||
},
|
||||
],
|
||||
});
|
||||
regularImageBase64: RESOURCES["demoBuilding.png"],
|
||||
blueprintImageBase64: RESOURCES["demoBuildingBlueprint.png"],
|
||||
tutorialImageBase64: RESOURCES["demoBuildingBlueprint.png"],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// Add it to the regular toolbar
|
||||
this.modLoader.signals.hudElementInitialized.add(element => {
|
||||
if (element.constructor.name === "HUDBuildingsToolbar") {
|
||||
// @ts-ignore
|
||||
element.primaryBuildings.push(MetaDemoModBuilding);
|
||||
}
|
||||
});
|
||||
}
|
||||
// Add it to the regular toolbar
|
||||
this.signals.hudElementInitialized.add(element => {
|
||||
if (element.constructor.name === "HUDBuildingsToolbar") {
|
||||
// @ts-ignore
|
||||
element.primaryBuildings.push(MetaDemoModBuilding);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@ -2,6 +2,8 @@
|
||||
import { ModLoader } from "./modloader";
|
||||
/* typehints:end */
|
||||
|
||||
import { MOD_SIGNALS } from "./mod_signals";
|
||||
|
||||
export class Mod {
|
||||
/**
|
||||
*
|
||||
@ -17,6 +19,9 @@ export class Mod {
|
||||
constructor(metadata, modLoader) {
|
||||
this.metadata = metadata;
|
||||
this.modLoader = modLoader;
|
||||
|
||||
this.signals = MOD_SIGNALS;
|
||||
this.modInterface = modLoader.modInterface;
|
||||
}
|
||||
|
||||
init() {}
|
||||
|
||||
@ -6,33 +6,21 @@ import { MetaBuilding } from "../game/meta_building";
|
||||
import { defaultBuildingVariant } from "../game/meta_building";
|
||||
import { createLogger } from "../core/logging";
|
||||
import { AtlasSprite, SpriteAtlasLink } from "../core/sprites";
|
||||
import { enumShortcodeToSubShape, enumSubShape, enumSubShapeToShortcode } from "../game/shape_definition";
|
||||
import {
|
||||
enumShortcodeToSubShape,
|
||||
enumSubShape,
|
||||
enumSubShapeToShortcode,
|
||||
MODS_ADDITIONAL_SUB_SHAPE_DRAWERS,
|
||||
} from "../game/shape_definition";
|
||||
import { Loader } from "../core/loader";
|
||||
import { LANGUAGES } from "../languages";
|
||||
import { matchDataRecursive, T } from "../translations";
|
||||
import { registerBuildingVariant } from "../game/building_codes";
|
||||
import { gMetaBuildingRegistry } from "../core/global_registries";
|
||||
import { MODS_ADDITIONAL_SHAPE_MAP_WEIGHTS } from "../game/map_chunk";
|
||||
|
||||
const LOG = createLogger("mod-interface");
|
||||
|
||||
/**
|
||||
* @type {Object<string, (distanceToOriginInChunks: number) => number>}
|
||||
*/
|
||||
export const MODS_ADDITIONAL_SHAPE_MAP_WEIGHTS = {};
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* context: CanvasRenderingContext2D,
|
||||
* quadrantSize: number,
|
||||
* layerScale: number,
|
||||
* }} SubShapeDrawOptions
|
||||
*/
|
||||
|
||||
/**
|
||||
* @type {Object<string, (options: SubShapeDrawOptions) => void>}
|
||||
*/
|
||||
export const MODS_ADDITIONAL_SUB_SHAPE_DRAWERS = {};
|
||||
|
||||
export class ModInterface {
|
||||
/**
|
||||
*
|
||||
@ -91,7 +79,7 @@ export class ModInterface {
|
||||
* @param {string} param0.id
|
||||
* @param {string} param0.shortCode
|
||||
* @param {(distanceToOriginInChunks: number) => number} param0.weightComputation
|
||||
* @param {(options: SubShapeDrawOptions) => void} param0.draw
|
||||
* @param {(options: import("../game/shape_definition").SubShapeDrawOptions) => void} param0.draw
|
||||
*/
|
||||
registerSubShapeType({ id, shortCode, weightComputation, draw }) {
|
||||
if (shortCode.length !== 1) {
|
||||
|
||||
16
src/js/mods/mod_signals.js
Normal file
16
src/js/mods/mod_signals.js
Normal file
@ -0,0 +1,16 @@
|
||||
import { Signal } from "../core/signal";
|
||||
/* typehints:start */
|
||||
import { BaseHUDPart } from "../game/hud/base_hud_part";
|
||||
/* typehints:end */
|
||||
|
||||
// Single file to avoid circular deps
|
||||
|
||||
export const MOD_SIGNALS = {
|
||||
postInit: new Signal(),
|
||||
injectSprites: new Signal(),
|
||||
preprocessTheme: /** @type {TypedSignal<[Object]>} */ (new Signal()),
|
||||
modifyLevelDefinitions: /** @type {TypedSignal<[Array[Object]]>} */ (new Signal()),
|
||||
|
||||
hudElementInitialized: /** @type {TypedSignal<[BaseHUDPart]>} */ (new Signal()),
|
||||
hudElementFinalized: /** @type {TypedSignal<[BaseHUDPart]>} */ (new Signal()),
|
||||
};
|
||||
@ -1,9 +1,8 @@
|
||||
import { createLogger } from "../core/logging";
|
||||
import { Signal } from "../core/signal";
|
||||
import { DemoMod } from "./demo_mod";
|
||||
import { Mod } from "./mod";
|
||||
import { ModInterface } from "./mod_interface";
|
||||
import { BaseHUDPart } from "../game/hud/base_hud_part";
|
||||
import { MetaBuilding } from "../game/meta_building";
|
||||
import { MOD_SIGNALS } from "./mod_signals";
|
||||
|
||||
const LOG = createLogger("mods");
|
||||
|
||||
@ -21,17 +20,14 @@ export class ModLoader {
|
||||
|
||||
this.initialized = false;
|
||||
|
||||
this.signals = {
|
||||
postInit: new Signal(),
|
||||
injectSprites: new Signal(),
|
||||
preprocessTheme: /** @type {TypedSignal<[Object]>} */ (new Signal()),
|
||||
modifyLevelDefinitions: /** @type {TypedSignal<[Array[Object]]>} */ (new Signal()),
|
||||
this.signals = MOD_SIGNALS;
|
||||
|
||||
hudElementInitialized: /** @type {TypedSignal<[BaseHUDPart]>} */ (new Signal()),
|
||||
hudElementFinalized: /** @type {TypedSignal<[BaseHUDPart]>} */ (new Signal()),
|
||||
};
|
||||
|
||||
this.registerMod(DemoMod);
|
||||
this.registerMod(
|
||||
/** @type {any} */ (require("./demo_mod").default({
|
||||
Mod,
|
||||
MetaBuilding,
|
||||
}))
|
||||
);
|
||||
this.initMods();
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user