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:
parent
4176621b05
commit
ebda1c5024
@ -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");
|
||||
}
|
||||
|
||||
|
||||
@ -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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user