diff --git a/src/js/game/hud/hud.js b/src/js/game/hud/hud.js index cffc290d..9fe1ba2f 100644 --- a/src/js/game/hud/hud.js +++ b/src/js/game/hud/hud.js @@ -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); diff --git a/src/js/game/map_chunk.js b/src/js/game/map_chunk.js index 0c5390e4..ca2db36f 100644 --- a/src/js/game/map_chunk.js +++ b/src/js/game/map_chunk.js @@ -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 number>} + */ +export const MODS_ADDITIONAL_SHAPE_MAP_WEIGHTS = {}; + export class MapChunk { /** * diff --git a/src/js/game/modes/regular.js b/src/js/game/modes/regular.js index 0aad3669..699874b5 100644 --- a/src/js/game/modes/regular.js +++ b/src/js/game/modes/regular.js @@ -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 }) => { diff --git a/src/js/game/shape_definition.js b/src/js/game/shape_definition.js index acc0bd1c..9cf2d094 100644 --- a/src/js/game/shape_definition.js +++ b/src/js/game/shape_definition.js @@ -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 void>} + */ +export const MODS_ADDITIONAL_SUB_SHAPE_DRAWERS = {}; + /** * @typedef {{ * subShape: enumSubShape, diff --git a/src/js/game/theme.js b/src/js/game/theme.js index 023710dd..5e371740 100644 --- a/src/js/game/theme.js +++ b/src/js/game/theme.js @@ -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 }); } diff --git a/src/js/mods/demo_mod.js b/src/js/mods/demo_mod.js index 42f48fa3..944cae38 100644 --- a/src/js/mods/demo_mod.js +++ b/src/js/mods/demo_mod.js @@ -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); + } + }); + } + }; } //////////////////////////////////////////////////////////////////////// diff --git a/src/js/mods/mod.js b/src/js/mods/mod.js index 65cdd53a..12992999 100644 --- a/src/js/mods/mod.js +++ b/src/js/mods/mod.js @@ -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() {} diff --git a/src/js/mods/mod_interface.js b/src/js/mods/mod_interface.js index fae5f2ba..5d395819 100644 --- a/src/js/mods/mod_interface.js +++ b/src/js/mods/mod_interface.js @@ -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 number>} - */ -export const MODS_ADDITIONAL_SHAPE_MAP_WEIGHTS = {}; - -/** - * @typedef {{ - * context: CanvasRenderingContext2D, - * quadrantSize: number, - * layerScale: number, - * }} SubShapeDrawOptions - */ - -/** - * @type {Object 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) { diff --git a/src/js/mods/mod_signals.js b/src/js/mods/mod_signals.js new file mode 100644 index 00000000..a2bc0380 --- /dev/null +++ b/src/js/mods/mod_signals.js @@ -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()), +}; diff --git a/src/js/mods/modloader.js b/src/js/mods/modloader.js index 75115dd7..49e30662 100644 --- a/src/js/mods/modloader.js +++ b/src/js/mods/modloader.js @@ -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(); }