1
0
mirror of https://github.com/tobspr/shapez.io.git synced 2025-12-13 18:21:51 +00:00

Allow mods to register game systems and draw stuff

This commit is contained in:
tobspr 2022-01-14 13:09:05 +01:00
parent 4176621b05
commit ebda1c5024
4 changed files with 172 additions and 2 deletions

View File

@ -1,4 +1,5 @@
/* typehints:start */
import { GameSystem } from "./game_system";
import { GameRoot } from "./root";
/* typehints:end */
@ -30,6 +31,11 @@ import { ZoneSystem } from "./systems/zone";
const logger = createLogger("game_system_manager");
/**
* @type {Object<string, Array<{ id: string; systemClass: new (any) => GameSystem}>>}
*/
export const MODS_ADDITIONAL_SYSTEMS = {};
export class GameSystemManager {
/**
*
@ -123,7 +129,15 @@ export class GameSystemManager {
* Initializes all systems
*/
internalInitSystems() {
const addBefore = id => {
const systems = MODS_ADDITIONAL_SYSTEMS[id];
if (systems) {
systems.forEach(({ id, systemClass }) => add(id, systemClass));
}
};
const add = (id, systemClass) => {
addBefore(id);
this.systems[id] = new systemClass(this.root);
this.systemUpdateOrder.push(id);
};
@ -187,6 +201,14 @@ export class GameSystemManager {
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");
}

View File

@ -5,10 +5,20 @@ import { Entity } from "./entity";
import { MapChunk } from "./map_chunk";
import { GameRoot } from "./root";
import { THEME } from "./theme";
import { drawSpriteClipped } from "../core/draw_utils";
export const CHUNK_OVERLAY_RES = 3;
export const MOD_CHUNK_DRAW_HOOKS = {
backgroundLayerBefore: [],
backgroundLayerAfter: [],
foregroundDynamicBefore: [],
foregroundDynamicAfter: [],
staticBefore: [],
staticAfter: [],
};
export class MapChunkView extends MapChunk {
/**
*
@ -42,6 +52,11 @@ export class MapChunkView extends MapChunk {
*/
drawBackgroundLayer(parameters) {
const systems = this.root.systemMgr.systems;
MOD_CHUNK_DRAW_HOOKS.backgroundLayerBefore.forEach(systemId =>
systems[systemId].drawChunk(parameters, this)
);
if (systems.zone) {
systems.zone.drawChunk(parameters, this);
}
@ -52,6 +67,10 @@ export class MapChunkView extends MapChunk {
systems.beltUnderlays.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) {
const systems = this.root.systemMgr.systems;
MOD_CHUNK_DRAW_HOOKS.foregroundDynamicBefore.forEach(systemId =>
systems[systemId].drawChunk(parameters, this)
);
systems.itemEjector.drawChunk(parameters, this);
systems.itemAcceptor.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) {
const systems = this.root.systemMgr.systems;
MOD_CHUNK_DRAW_HOOKS.staticBefore.forEach(systemId => systems[systemId].drawChunk(parameters, this));
systems.staticMapEntities.drawChunk(parameters, this);
systems.lever.drawChunk(parameters, this);
systems.display.drawChunk(parameters, this);
@ -80,6 +109,8 @@ export class MapChunkView extends MapChunk {
systems.constantProducer.drawChunk(parameters, this);
systems.goalAcceptor.drawChunk(parameters, this);
systems.itemProcessorOverlays.drawChunk(parameters, this);
MOD_CHUNK_DRAW_HOOKS.staticAfter.forEach(systemId => systems[systemId].drawChunk(parameters, this));
}
/**

View File

@ -1,4 +1,22 @@
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 {
constructor() {
super("demoModBuilding");
@ -8,7 +26,58 @@ registerMod(shapez => {
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 {
@ -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
this.modInterface.registerNewBuilding({
metaClass: MetaDemoModBuilding,

View File

@ -1,5 +1,6 @@
/* typehints:start */
import { ModLoader } from "./modloader";
import { GameSystem } from "../game/game_system";
import { Component } from "../game/component";
import { MetaBuilding } from "../game/meta_building";
/* typehints:end */
@ -19,6 +20,8 @@ import { matchDataRecursive, T } from "../translations";
import { gBuildingVariants, registerBuildingVariant } from "../game/building_codes";
import { gComponentRegistry, gMetaBuildingRegistry } from "../core/global_registries";
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");
@ -115,6 +118,40 @@ export class ModInterface {
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