mirror of
https://github.com/tobspr/shapez.io.git
synced 2025-12-14 10:41:52 +00:00
Allow mods to register game systems and draw stuff
This commit is contained in:
parent
4176621b05
commit
ebda1c5024
@ -1,4 +1,5 @@
|
|||||||
/* typehints:start */
|
/* typehints:start */
|
||||||
|
import { GameSystem } from "./game_system";
|
||||||
import { GameRoot } from "./root";
|
import { GameRoot } from "./root";
|
||||||
/* typehints:end */
|
/* typehints:end */
|
||||||
|
|
||||||
@ -30,6 +31,11 @@ import { ZoneSystem } from "./systems/zone";
|
|||||||
|
|
||||||
const logger = createLogger("game_system_manager");
|
const logger = createLogger("game_system_manager");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {Object<string, Array<{ id: string; systemClass: new (any) => GameSystem}>>}
|
||||||
|
*/
|
||||||
|
export const MODS_ADDITIONAL_SYSTEMS = {};
|
||||||
|
|
||||||
export class GameSystemManager {
|
export class GameSystemManager {
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@ -123,7 +129,15 @@ export class GameSystemManager {
|
|||||||
* Initializes all systems
|
* Initializes all systems
|
||||||
*/
|
*/
|
||||||
internalInitSystems() {
|
internalInitSystems() {
|
||||||
|
const addBefore = id => {
|
||||||
|
const systems = MODS_ADDITIONAL_SYSTEMS[id];
|
||||||
|
if (systems) {
|
||||||
|
systems.forEach(({ id, systemClass }) => add(id, systemClass));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const add = (id, systemClass) => {
|
const add = (id, systemClass) => {
|
||||||
|
addBefore(id);
|
||||||
this.systems[id] = new systemClass(this.root);
|
this.systems[id] = new systemClass(this.root);
|
||||||
this.systemUpdateOrder.push(id);
|
this.systemUpdateOrder.push(id);
|
||||||
};
|
};
|
||||||
@ -187,6 +201,14 @@ export class GameSystemManager {
|
|||||||
add("zone", ZoneSystem);
|
add("zone", ZoneSystem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addBefore("end");
|
||||||
|
|
||||||
|
for (const key in MODS_ADDITIONAL_SYSTEMS) {
|
||||||
|
if (!this.systems[key]) {
|
||||||
|
logger.error("Mod system not attached due to invalid 'before': ", key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
logger.log("📦 There are", this.systemUpdateOrder.length, "game systems");
|
logger.log("📦 There are", this.systemUpdateOrder.length, "game systems");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -5,10 +5,20 @@ import { Entity } from "./entity";
|
|||||||
import { MapChunk } from "./map_chunk";
|
import { MapChunk } from "./map_chunk";
|
||||||
import { GameRoot } from "./root";
|
import { GameRoot } from "./root";
|
||||||
import { THEME } from "./theme";
|
import { THEME } from "./theme";
|
||||||
import { drawSpriteClipped } from "../core/draw_utils";
|
|
||||||
|
|
||||||
export const CHUNK_OVERLAY_RES = 3;
|
export const CHUNK_OVERLAY_RES = 3;
|
||||||
|
|
||||||
|
export const MOD_CHUNK_DRAW_HOOKS = {
|
||||||
|
backgroundLayerBefore: [],
|
||||||
|
backgroundLayerAfter: [],
|
||||||
|
|
||||||
|
foregroundDynamicBefore: [],
|
||||||
|
foregroundDynamicAfter: [],
|
||||||
|
|
||||||
|
staticBefore: [],
|
||||||
|
staticAfter: [],
|
||||||
|
};
|
||||||
|
|
||||||
export class MapChunkView extends MapChunk {
|
export class MapChunkView extends MapChunk {
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@ -42,6 +52,11 @@ export class MapChunkView extends MapChunk {
|
|||||||
*/
|
*/
|
||||||
drawBackgroundLayer(parameters) {
|
drawBackgroundLayer(parameters) {
|
||||||
const systems = this.root.systemMgr.systems;
|
const systems = this.root.systemMgr.systems;
|
||||||
|
|
||||||
|
MOD_CHUNK_DRAW_HOOKS.backgroundLayerBefore.forEach(systemId =>
|
||||||
|
systems[systemId].drawChunk(parameters, this)
|
||||||
|
);
|
||||||
|
|
||||||
if (systems.zone) {
|
if (systems.zone) {
|
||||||
systems.zone.drawChunk(parameters, this);
|
systems.zone.drawChunk(parameters, this);
|
||||||
}
|
}
|
||||||
@ -52,6 +67,10 @@ export class MapChunkView extends MapChunk {
|
|||||||
|
|
||||||
systems.beltUnderlays.drawChunk(parameters, this);
|
systems.beltUnderlays.drawChunk(parameters, this);
|
||||||
systems.belt.drawChunk(parameters, this);
|
systems.belt.drawChunk(parameters, this);
|
||||||
|
|
||||||
|
MOD_CHUNK_DRAW_HOOKS.backgroundLayerAfter.forEach(systemId =>
|
||||||
|
systems[systemId].drawChunk(parameters, this)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -61,9 +80,17 @@ export class MapChunkView extends MapChunk {
|
|||||||
drawForegroundDynamicLayer(parameters) {
|
drawForegroundDynamicLayer(parameters) {
|
||||||
const systems = this.root.systemMgr.systems;
|
const systems = this.root.systemMgr.systems;
|
||||||
|
|
||||||
|
MOD_CHUNK_DRAW_HOOKS.foregroundDynamicBefore.forEach(systemId =>
|
||||||
|
systems[systemId].drawChunk(parameters, this)
|
||||||
|
);
|
||||||
|
|
||||||
systems.itemEjector.drawChunk(parameters, this);
|
systems.itemEjector.drawChunk(parameters, this);
|
||||||
systems.itemAcceptor.drawChunk(parameters, this);
|
systems.itemAcceptor.drawChunk(parameters, this);
|
||||||
systems.miner.drawChunk(parameters, this);
|
systems.miner.drawChunk(parameters, this);
|
||||||
|
|
||||||
|
MOD_CHUNK_DRAW_HOOKS.foregroundDynamicAfter.forEach(systemId =>
|
||||||
|
systems[systemId].drawChunk(parameters, this)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -73,6 +100,8 @@ export class MapChunkView extends MapChunk {
|
|||||||
drawForegroundStaticLayer(parameters) {
|
drawForegroundStaticLayer(parameters) {
|
||||||
const systems = this.root.systemMgr.systems;
|
const systems = this.root.systemMgr.systems;
|
||||||
|
|
||||||
|
MOD_CHUNK_DRAW_HOOKS.staticBefore.forEach(systemId => systems[systemId].drawChunk(parameters, this));
|
||||||
|
|
||||||
systems.staticMapEntities.drawChunk(parameters, this);
|
systems.staticMapEntities.drawChunk(parameters, this);
|
||||||
systems.lever.drawChunk(parameters, this);
|
systems.lever.drawChunk(parameters, this);
|
||||||
systems.display.drawChunk(parameters, this);
|
systems.display.drawChunk(parameters, this);
|
||||||
@ -80,6 +109,8 @@ export class MapChunkView extends MapChunk {
|
|||||||
systems.constantProducer.drawChunk(parameters, this);
|
systems.constantProducer.drawChunk(parameters, this);
|
||||||
systems.goalAcceptor.drawChunk(parameters, this);
|
systems.goalAcceptor.drawChunk(parameters, this);
|
||||||
systems.itemProcessorOverlays.drawChunk(parameters, this);
|
systems.itemProcessorOverlays.drawChunk(parameters, this);
|
||||||
|
|
||||||
|
MOD_CHUNK_DRAW_HOOKS.staticAfter.forEach(systemId => systems[systemId].drawChunk(parameters, this));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -1,4 +1,22 @@
|
|||||||
registerMod(shapez => {
|
registerMod(shapez => {
|
||||||
|
class DemoModComponent extends shapez.Component {
|
||||||
|
static getId() {
|
||||||
|
return "DemoMod";
|
||||||
|
}
|
||||||
|
|
||||||
|
static getSchema() {
|
||||||
|
return {
|
||||||
|
magicNumber: shapez.types.uint,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(magicNumber) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.magicNumber = magicNumber;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class MetaDemoModBuilding extends shapez.MetaBuilding {
|
class MetaDemoModBuilding extends shapez.MetaBuilding {
|
||||||
constructor() {
|
constructor() {
|
||||||
super("demoModBuilding");
|
super("demoModBuilding");
|
||||||
@ -8,7 +26,58 @@ registerMod(shapez => {
|
|||||||
return "red";
|
return "red";
|
||||||
}
|
}
|
||||||
|
|
||||||
setupEntityComponents(entity) {}
|
setupEntityComponents(entity) {
|
||||||
|
entity.addComponent(new DemoModComponent(Math.floor(Math.random() * 100.0)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DemoModSystem extends shapez.GameSystemWithFilter {
|
||||||
|
constructor(root) {
|
||||||
|
super(root, [DemoModComponent]);
|
||||||
|
}
|
||||||
|
|
||||||
|
update() {
|
||||||
|
// nothing to do here
|
||||||
|
}
|
||||||
|
|
||||||
|
drawChunk(parameters, chunk) {
|
||||||
|
const contents = chunk.containedEntitiesByLayer.regular;
|
||||||
|
for (let i = 0; i < contents.length; ++i) {
|
||||||
|
const entity = contents[i];
|
||||||
|
const demoComp = entity.components.DemoMod;
|
||||||
|
if (!demoComp) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const staticComp = entity.components.StaticMapEntity;
|
||||||
|
|
||||||
|
const context = parameters.context;
|
||||||
|
const center = staticComp.getTileSpaceBounds().getCenter().toWorldSpace();
|
||||||
|
|
||||||
|
// Culling for better performance
|
||||||
|
if (parameters.visibleRect.containsCircle(center.x, center.y, 40)) {
|
||||||
|
// Circle
|
||||||
|
context.fillStyle = "#53cf47";
|
||||||
|
context.strokeStyle = "#000";
|
||||||
|
context.lineWidth = 2;
|
||||||
|
|
||||||
|
const timeFactor = 5.23 * this.root.time.now();
|
||||||
|
context.beginCircle(
|
||||||
|
center.x + Math.cos(timeFactor) * 10,
|
||||||
|
center.y + Math.sin(timeFactor) * 10,
|
||||||
|
7
|
||||||
|
);
|
||||||
|
context.fill();
|
||||||
|
context.stroke();
|
||||||
|
|
||||||
|
// Text
|
||||||
|
context.fillStyle = "#fff";
|
||||||
|
context.textAlign = "center";
|
||||||
|
context.font = "12px GameFont";
|
||||||
|
context.fillText(demoComp.magicNumber, center.x, center.y + 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return class ModImpl extends shapez.Mod {
|
return class ModImpl extends shapez.Mod {
|
||||||
@ -87,6 +156,17 @@ registerMod(shapez => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Register a new component
|
||||||
|
this.modInterface.registerComponent(DemoModComponent);
|
||||||
|
|
||||||
|
// Register a new game system which can update and draw stuff
|
||||||
|
this.modInterface.registerGameSystem({
|
||||||
|
id: "demo_mod",
|
||||||
|
systemClass: DemoModSystem,
|
||||||
|
before: "belt",
|
||||||
|
drawHooks: ["staticAfter"],
|
||||||
|
});
|
||||||
|
|
||||||
// Register the new building
|
// Register the new building
|
||||||
this.modInterface.registerNewBuilding({
|
this.modInterface.registerNewBuilding({
|
||||||
metaClass: MetaDemoModBuilding,
|
metaClass: MetaDemoModBuilding,
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
/* typehints:start */
|
/* typehints:start */
|
||||||
import { ModLoader } from "./modloader";
|
import { ModLoader } from "./modloader";
|
||||||
|
import { GameSystem } from "../game/game_system";
|
||||||
import { Component } from "../game/component";
|
import { Component } from "../game/component";
|
||||||
import { MetaBuilding } from "../game/meta_building";
|
import { MetaBuilding } from "../game/meta_building";
|
||||||
/* typehints:end */
|
/* typehints:end */
|
||||||
@ -19,6 +20,8 @@ import { matchDataRecursive, T } from "../translations";
|
|||||||
import { gBuildingVariants, registerBuildingVariant } from "../game/building_codes";
|
import { gBuildingVariants, registerBuildingVariant } from "../game/building_codes";
|
||||||
import { gComponentRegistry, gMetaBuildingRegistry } from "../core/global_registries";
|
import { gComponentRegistry, gMetaBuildingRegistry } from "../core/global_registries";
|
||||||
import { MODS_ADDITIONAL_SHAPE_MAP_WEIGHTS } from "../game/map_chunk";
|
import { MODS_ADDITIONAL_SHAPE_MAP_WEIGHTS } from "../game/map_chunk";
|
||||||
|
import { MODS_ADDITIONAL_SYSTEMS } from "../game/game_system_manager";
|
||||||
|
import { MOD_CHUNK_DRAW_HOOKS } from "../game/map_chunk_view";
|
||||||
|
|
||||||
const LOG = createLogger("mod-interface");
|
const LOG = createLogger("mod-interface");
|
||||||
|
|
||||||
@ -115,6 +118,40 @@ export class ModInterface {
|
|||||||
gComponentRegistry.register(component);
|
gComponentRegistry.register(component);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {Object} param0
|
||||||
|
* @param {string} param0.id
|
||||||
|
* @param {new (any) => GameSystem} param0.systemClass
|
||||||
|
* @param {string=} param0.before
|
||||||
|
* @param {string[]=} param0.drawHooks
|
||||||
|
*/
|
||||||
|
registerGameSystem({ id, systemClass, before, drawHooks }) {
|
||||||
|
const key = before || "key";
|
||||||
|
const payload = { id, systemClass };
|
||||||
|
|
||||||
|
if (MODS_ADDITIONAL_SYSTEMS[key]) {
|
||||||
|
MODS_ADDITIONAL_SYSTEMS[key].push(payload);
|
||||||
|
} else {
|
||||||
|
MODS_ADDITIONAL_SYSTEMS[key] = [payload];
|
||||||
|
}
|
||||||
|
if (drawHooks) {
|
||||||
|
drawHooks.forEach(hookId => this.registerGameSystemDrawHook(hookId, id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {string} hookId
|
||||||
|
* @param {string} systemId
|
||||||
|
*/
|
||||||
|
registerGameSystemDrawHook(hookId, systemId) {
|
||||||
|
if (!MOD_CHUNK_DRAW_HOOKS[hookId]) {
|
||||||
|
throw new Error("bad game system draw hook: " + hookId);
|
||||||
|
}
|
||||||
|
MOD_CHUNK_DRAW_HOOKS[hookId].push(systemId);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {object} param0
|
* @param {object} param0
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user