1
0
mirror of https://github.com/tobspr/shapez.io.git synced 2025-12-11 09:11:50 +00:00

remove mod_examples

This commit is contained in:
EmeraldBlock 2024-04-24 16:14:33 -05:00
parent 7aeb687d14
commit b4bbfcb4f0
26 changed files with 1 additions and 1796 deletions

View File

@ -118,7 +118,7 @@ function webserver() {
*/
async function serveHTML({ version = "web-dev" }) {
browserSync.init({
server: [buildFolder, path.join(baseDir, "mod_examples")],
server: buildFolder,
port: 3005,
ghostMode: false,
logLevel: "info",

View File

@ -1,58 +0,0 @@
# shapez.io Modding
## General Instructions
Currently there are two options to develop mods for shapez.io:
1. Writing single file mods, which doesn't require any additional tools and can be loaded directly in the game
2. Using the [create-shapezio-mod](https://www.npmjs.com/package/create-shapezio-mod) package. This package is still in development but allows you to pack multiple files and images into a single mod file, so you don't have to base64 encode your images etc.
## Mod Developer Discord
A great place to get help with mod development is the official [shapez.io modloader discord](https://discord.gg/xq5v8uyMue).
## Setting up your development environment
The simplest way of developing mods is by just creating a `mymod.js` file and putting it in the `mods/` folder of the standalone (You can find the `mods/` folder by clicking "Open Mods Folder" in the shapez Standalone, be sure to select the 1.5.0-modloader branch on Steam).
You can then add `--dev` to the launch options on Steam. This adds an application menu where you can click "Restart" to reload your mod, and will also show the developer console where you can see any potential errors.
## Getting started
To get into shapez.io modding, I highly recommend checking out all of the examples in this folder. Here's a list of examples and what features of the modloader they show:
| Example | Description | Demonstrates |
| ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
| [base.js](base.js) | The most basic mod | Base structure of a mod |
| [class_extensions.js](class_extensions.js) | Shows how to extend multiple methods of one class at once, useful for overriding a lot of methods | Overriding and extending builtin methods |
| [custom_css.js](custom_css.js) | Modifies the Main Menu State look | Modifying the UI styles with CSS |
| [replace_builtin_sprites.js](replace_builtin_sprites.js) | Replaces all color sprites with icons | Replacing builtin sprites |
| [translations.js](translations.js) | Shows how to replace and add new translations in multiple languages | Adding and replacing translations |
| [add_building_basic.js](add_building_basic.js) | Shows how to add a new building | Registering a new building |
| [add_building_flipper.js](add_building_flipper.js) | Adds a "flipper" building which mirrors shapes from top to bottom | Registering a new building, Adding a custom shape and item processing operation (flip) |
| [custom_drawing.js](custom_drawing.js) | Displays a a small indicator on every item processing building whether it is currently working | Adding a new GameSystem and drawing overlays |
| [custom_keybinding.js](custom_keybinding.js) | Adds a new customizable ingame keybinding (Shift+F) | Adding a new keybinding |
| [custom_sub_shapes.js](custom_sub_shapes.js) | Adds a new type of sub-shape (Line) | Adding a new sub shape and drawing it, making it spawn on the map, modifying the builtin levels |
| [modify_theme.js](modify_theme.js) | Modifies the default game themes | Modifying the builtin themes |
| [custom_theme.js](custom_theme.js) | Adds a new UI and map theme | Adding a new game theme |
| [mod_settings.js](mod_settings.js) | Shows a dialog counting how often the mod has been launched | Reading and storing mod settings |
| [storing_data_in_savegame.js](storing_data_in_savegame.js) | Shows how to store custom (structured) data in the savegame | Storing custom data in savegame |
| [modify_existing_building.js](modify_existing_building.js) | Makes the rotator building always unlocked and adds a new statistic to the building panel | Modifying a builtin building, replacing builtin methods |
| [modify_ui.js](modify_ui.js) | Shows how to add custom UI elements to builtin game states (the Main Menu in this case) | Extending builtin UI states, Adding CSS |
| [pasting.js](pasting.js) | Shows a dialog when pasting text in the game | Listening to paste events |
| [smooth_zooming.js](smooth_zooming.js) | Allows to smoothly zoom in and out | Keybindings, overriding methods |
| [sandbox.js](sandbox.js) | Makes blueprints free and always unlocked | Overriding builtin methods |
### Advanced Examples
| Example | Description | Demonstrates |
| ------------------------------------------------ | ---------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [notification_blocks.js](notification_blocks.js) | Adds a notification block building, which shows a user defined notification when receiving a truthy signal | Adding a new Component, Adding a new GameSystem, Working with wire networks, Adding a new building, Adding a new HUD part, Using Input Dialogs, Adding Translations |
| [usage_statistics.js](usage_statistics.js) | Displays a percentage on every building showing its utilization | Adding a new component, Adding a new GameSystem, Drawing within a GameSystem, Modifying builtin buildings, Adding custom game logic |
| [new_item_type.js](new_item_type.js) | Adds a new type of items to the map (fluids) | Adding a new item type, modifying map generation |
| [buildings_have_cost.js](buildings_have_cost.js) | Adds a new currency, and belts cost 1 of that currency | Extending and replacing builtin methods, Adding CSS and custom sprites |
| [mirrored_cutter.js](mirrored_cutter.js) | Adds a mirrored variant of the cutter | Adding a new variant to existing buildings |
### Creating new sprites
If you want to add new buildings and create sprites for them, you can download the original Photoshop PSD files here: https://static.shapez.io/building-psds.zip

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,20 +0,0 @@
// @ts-nocheck
const METADATA = {
website: "https://tobspr.io",
author: "tobspr",
name: "Mod Example: Base",
version: "1",
id: "base",
description: "The most basic mod",
minimumGameVersion: ">=1.5.0",
// You can specify this parameter if savegames will still work
// after your mod has been uninstalled
doesNotAffectSavegame: true,
};
class Mod extends shapez.Mod {
init() {
// Start the modding here
}
}

File diff suppressed because one or more lines are too long

View File

@ -1,32 +0,0 @@
// @ts-nocheck
const METADATA = {
website: "https://tobspr.io",
author: "tobspr",
name: "Mod Example: Class Extensions",
version: "1",
id: "class-extensions",
description: "Shows how to extend builtin classes",
minimumGameVersion: ">=1.5.0",
};
const BeltExtension = ({ $super, $old }) => ({
getShowWiresLayerPreview() {
// Access the old method
return !$old.getShowWiresLayerPreview();
},
getIsReplaceable(variant, rotationVariant) {
// Instead of super, use $super
return $super.getIsReplaceable.call(this, variant, rotationVariant);
},
getIsRemoveable() {
return false;
},
});
class Mod extends shapez.Mod {
init() {
this.modInterface.extendClass(shapez.MetaBeltBuilding, BeltExtension);
}
}

File diff suppressed because one or more lines are too long

View File

@ -1,63 +0,0 @@
// @ts-nocheck
const METADATA = {
website: "https://tobspr.io",
author: "tobspr",
name: "Mod Example: custom drawing",
version: "1",
id: "base",
description: "Displays an indicator on every item processing building when its working",
minimumGameVersion: ">=1.5.0",
// You can specify this parameter if savegames will still work
// after your mod has been uninstalled
doesNotAffectSavegame: true,
};
class ItemProcessorStatusGameSystem extends shapez.GameSystem {
drawChunk(parameters, chunk) {
const contents = chunk.containedEntitiesByLayer.regular;
for (let i = 0; i < contents.length; ++i) {
const entity = contents[i];
const processorComp = entity.components.ItemProcessor;
if (!processorComp) {
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 = processorComp.ongoingCharges.length === 0 ? "#aaa" : "#53cf47";
context.strokeStyle = "#000";
context.lineWidth = 1;
context.beginCircle(center.x + 5, center.y + 5, 4);
context.fill();
context.stroke();
}
}
}
}
class Mod extends shapez.Mod {
init() {
// Register our game system
this.modInterface.registerGameSystem({
id: "item_processor_status",
systemClass: ItemProcessorStatusGameSystem,
// Specify at which point the update method will be called,
// in this case directly before the belt system. You can use
// before: "end" to make it the last system
before: "belt",
// Specify where our drawChunk method should be called, check out
// map_chunk_view
drawHooks: ["staticAfter"],
});
}
}

View File

@ -1,32 +0,0 @@
// @ts-nocheck
const METADATA = {
website: "https://tobspr.io",
author: "tobspr",
name: "Mod Example: Custom Keybindings",
version: "1",
id: "base",
description: "Shows how to add a new keybinding",
minimumGameVersion: ">=1.5.0",
// You can specify this parameter if savegames will still work
// after your mod has been uninstalled
doesNotAffectSavegame: true,
};
class Mod extends shapez.Mod {
init() {
// Register keybinding
this.modInterface.registerIngameKeybinding({
id: "demo_mod_binding",
keyCode: shapez.keyToKeyCode("F"),
translation: "Do something (always with SHIFT)",
modifiers: {
shift: true,
},
handler: root => {
this.dialogs.showInfo("Mod Message", "It worked!");
return shapez.STOP_PROPAGATION;
},
});
}
}

View File

@ -1,46 +0,0 @@
// @ts-nocheck
const METADATA = {
website: "https://tobspr.io",
author: "tobspr",
name: "Mod Example: Custom Sub Shapes",
version: "1",
id: "custom-sub-shapes",
description: "Shows how to add custom sub shapes",
minimumGameVersion: ">=1.5.0",
};
class Mod extends shapez.Mod {
init() {
// Add a new type of sub shape ("Line", short code "L")
this.modInterface.registerSubShapeType({
id: "line",
shortCode: "L",
// Make it spawn on the map
weightComputation: distanceToOriginInChunks =>
Math.round(20 + Math.max(Math.min(distanceToOriginInChunks, 30), 0)),
// This defines how to draw it
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();
context.fill();
context.stroke();
},
});
// Modify the goal of the first level to add our goal
this.signals.modifyLevelDefinitions.add(definitions => {
definitions[0].shape = "LuLuLuLu";
});
}
}

View File

@ -1,99 +0,0 @@
// @ts-nocheck
const METADATA = {
website: "https://tobspr.io",
author: "tobspr",
name: "Mod Example: Custom Game Theme",
version: "1",
id: "custom-theme",
description: "Shows how to add a custom game theme",
minimumGameVersion: ">=1.5.0",
// You can specify this parameter if savegames will still work
// after your mod has been uninstalled
doesNotAffectSavegame: true,
};
class Mod extends shapez.Mod {
init() {
this.modInterface.registerGameTheme({
id: "my-theme",
name: "My fancy theme",
theme: RESOURCES["my-theme.json"],
});
}
}
const RESOURCES = {
"my-theme.json": {
map: {
background: "#abc",
grid: "#ccc",
gridLineWidth: 1,
selectionOverlay: "rgba(74, 163, 223, 0.7)",
selectionOutline: "rgba(74, 163, 223, 0.5)",
selectionBackground: "rgba(74, 163, 223, 0.2)",
chunkBorders: "rgba(0, 30, 50, 0.03)",
directionLock: {
regular: {
color: "rgb(74, 237, 134)",
background: "rgba(74, 237, 134, 0.2)",
},
wires: {
color: "rgb(74, 237, 134)",
background: "rgba(74, 237, 134, 0.2)",
},
error: {
color: "rgb(255, 137, 137)",
background: "rgba(255, 137, 137, 0.2)",
},
},
colorBlindPickerTile: "rgba(50, 50, 50, 0.4)",
resources: {
shape: "#eaebec",
red: "#ffbfc1",
green: "#cbffc4",
blue: "#bfdaff",
},
chunkOverview: {
empty: "#a6afbb",
filled: "#c5ccd6",
beltColor: "#777",
},
wires: {
overlayColor: "rgba(97, 161, 152, 0.75)",
previewColor: "rgb(97, 161, 152, 0.4)",
highlightColor: "rgba(72, 137, 255, 1)",
},
connectedMiners: {
overlay: "rgba(40, 50, 60, 0.5)",
textColor: "#fff",
textColorCapped: "#ef5072",
background: "rgba(40, 50, 60, 0.8)",
},
zone: {
borderSolid: "rgba(23, 192, 255, 1)",
outerColor: "rgba(240, 240, 255, 0.5)",
},
},
items: {
outline: "#55575a",
outlineWidth: 0.75,
circleBackground: "rgba(40, 50, 65, 0.1)",
},
shapeTooltip: {
background: "#dee1ea",
outline: "#54565e",
},
},
};

File diff suppressed because one or more lines are too long

View File

@ -1,32 +0,0 @@
// @ts-nocheck
const METADATA = {
website: "https://tobspr.io",
author: "tobspr",
name: "Mod Example: Mod Settings",
version: "1",
id: "mod-settings",
description: "Shows how to add settings to your mod",
minimumGameVersion: ">=1.5.0",
settings: {
timesLaunched: 0,
},
};
class Mod extends shapez.Mod {
init() {
// Increment the setting every time we launch the mod
this.settings.timesLaunched++;
this.saveSettings();
// Show a dialog in the main menu with the settings
this.signals.stateEntered.add(state => {
if (state instanceof shapez.MainMenuState) {
this.dialogs.showInfo(
"Welcome back",
`You have launched this mod ${this.settings.timesLaunched} times`
);
}
});
}
}

View File

@ -1,27 +0,0 @@
// @ts-nocheck
const METADATA = {
website: "https://tobspr.io",
author: "tobspr",
name: "Mod Example: Modify existing building",
version: "1",
id: "modify-existing-building",
description: "Shows how to modify an existing building",
minimumGameVersion: ">=1.5.0",
};
class Mod extends shapez.Mod {
init() {
// Make Rotator always unlocked
this.modInterface.replaceMethod(shapez.MetaRotaterBuilding, "getIsUnlocked", function () {
return true;
});
// Add some custom stats to the info panel when selecting the building
this.modInterface.replaceMethod(shapez.MetaRotaterBuilding, "getAdditionalStatistics", function (
root,
variant
) {
return [["Awesomeness", 5]];
});
}
}

View File

@ -1,24 +0,0 @@
// @ts-nocheck
const METADATA = {
website: "https://tobspr.io",
author: "tobspr",
name: "Mod Example: Modify Builtin Themes",
version: "1",
id: "modify-theme",
description: "Shows how to modify builtin themes",
minimumGameVersion: ">=1.5.0",
// You can specify this parameter if savegames will still work
// after your mod has been uninstalled
doesNotAffectSavegame: true,
};
class Mod extends shapez.Mod {
init() {
shapez.THEMES.light.map.background = "#eee";
shapez.THEMES.light.items.outline = "#000";
shapez.THEMES.dark.map.background = "#245";
shapez.THEMES.dark.items.outline = "#fff";
}
}

View File

@ -1,46 +0,0 @@
// @ts-nocheck
const METADATA = {
website: "https://tobspr.io",
author: "tobspr",
name: "Mod Example: Modify UI",
version: "1",
id: "modify-ui",
description: "Shows how to modify a builtin game state, in this case the main menu",
minimumGameVersion: ">=1.5.0",
// You can specify this parameter if savegames will still work
// after your mod has been uninstalled
doesNotAffectSavegame: true,
};
class Mod extends shapez.Mod {
init() {
// Add fancy sign to main menu
this.signals.stateEntered.add(state => {
if (state.key === "MainMenuState") {
const element = document.createElement("div");
element.id = "demo_mod_hello_world_element";
document.body.appendChild(element);
const button = document.createElement("button");
button.classList.add("styledButton");
button.innerText = "Hello!";
button.addEventListener("click", () => {
this.dialogs.showInfo("Mod Message", "Button clicked!");
});
element.appendChild(button);
}
});
this.modInterface.registerCss(`
#demo_mod_hello_world_element {
position: absolute;
top: calc(10px * var(--ui-scale));
left: calc(10px * var(--ui-scale));
color: red;
z-index: 0;
}
`);
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,23 +0,0 @@
// @ts-nocheck
const METADATA = {
website: "https://tobspr.io",
author: "tobspr",
name: "Mod Example: Pasting",
version: "1",
id: "pasting",
description: "Shows how to properly receive paste events ingame",
minimumGameVersion: ">=1.5.0",
};
class Mod extends shapez.Mod {
init() {
this.signals.gameInitialized.add(root => {
root.gameState.inputReciever.paste.add(event => {
event.preventDefault();
const data = event.clipboardData.getData("text");
this.dialogs.showInfo("Pasted", `You pasted: '${data}'`);
});
});
}
}

File diff suppressed because one or more lines are too long

View File

@ -1,21 +0,0 @@
// @ts-nocheck
const METADATA = {
website: "https://tobspr.io",
author: "tobspr",
name: "Sandbox",
version: "1",
id: "sandbox",
description: "Blueprints are always unlocked and cost no money, also all buildings are unlocked",
minimumGameVersion: ">=1.5.0",
};
class Mod extends shapez.Mod {
init() {
this.modInterface.replaceMethod(shapez.Blueprint, "getCost", function () {
return 0;
});
this.modInterface.replaceMethod(shapez.HubGoals, "isRewardUnlocked", function () {
return true;
});
}
}

View File

@ -1,58 +0,0 @@
// @ts-nocheck
const METADATA = {
website: "https://tobspr.io",
author: "tobspr",
name: "Smooth Zoom",
version: "1",
id: "smooth_zoom",
description: "Allows to zoom in and out smoothly, also disables map overview",
minimumGameVersion: ">=1.5.0",
};
class Mod extends shapez.Mod {
init() {
this.modInterface.registerIngameKeybinding({
id: "smooth_zoom_zoom_in",
keyCode: shapez.keyToKeyCode("1"),
translation: "Zoom In (use with SHIFT)",
modifiers: {
shift: true,
},
handler: root => {
root.camera.setDesiredZoom(5);
return shapez.STOP_PROPAGATION;
},
});
this.modInterface.registerIngameKeybinding({
id: "smooth_zoom_zoom_out",
keyCode: shapez.keyToKeyCode("0"),
translation: "Zoom Out (use with SHIFT)",
modifiers: {
shift: true,
},
handler: root => {
root.camera.setDesiredZoom(0.1);
return shapez.STOP_PROPAGATION;
},
});
this.modInterface.extendClass(shapez.Camera, ({ $old }) => ({
internalUpdateZooming(now, dt) {
if (!this.currentlyPinching && this.desiredZoom !== null) {
const diff = this.zoomLevel - this.desiredZoom;
if (Math.abs(diff) > 0.0001) {
const speed = 0.0005;
let step = Math.sign(diff) * Math.min(speed, Math.abs(diff));
const pow = 1 / 2;
this.zoomLevel = Math.pow(Math.pow(this.zoomLevel, pow) - step, 1 / pow);
} else {
this.zoomLevel = this.desiredZoom;
this.desiredZoom = null;
}
}
},
}));
shapez.globalConfig.mapChunkOverviewMinZoom = -1;
}
}

View File

@ -1,78 +0,0 @@
// @ts-nocheck
const METADATA = {
website: "https://tobspr.io",
author: "tobspr",
name: "Mod Example: Storing Data in Savegame",
version: "1",
id: "storing-savegame-data",
description: "Shows how to add custom data to a savegame",
minimumGameVersion: ">=1.5.0",
};
class Mod extends shapez.Mod {
init() {
////////////////////////////////////////////////////////////////////
// Option 1: For simple data
this.signals.gameSerialized.add((root, data) => {
data.modExtraData["storing-savegame-data"] = Math.random();
});
this.signals.gameDeserialized.add((root, data) => {
alert("The value stored in the savegame was: " + data.modExtraData["storing-savegame-data"]);
});
////////////////////////////////////////////////////////////////////
// Option 2: If you need a structured way of storing data
class SomeSerializableObject extends shapez.BasicSerializableObject {
static getId() {
return "SomeSerializableObject";
}
static getSchema() {
return {
someInt: shapez.types.int,
someString: shapez.types.string,
someVector: shapez.types.vector,
// this value is allowed to be null
nullableInt: shapez.types.nullable(shapez.types.int),
// There is a lot more .. be sure to checkout src/js/savegame/serialization.js
// You can have maps, classes, arrays etc..
// And if you need something specific you can always ask in the modding discord.
};
}
constructor() {
super();
this.someInt = 42;
this.someString = "Hello World";
this.someVector = new shapez.Vector(1, 2);
this.nullableInt = null;
}
}
// Store our object in the global game root
this.signals.gameInitialized.add(root => {
root.myObject = new SomeSerializableObject();
});
// Save it within the savegame
this.signals.gameSerialized.add((root, data) => {
data.modExtraData["storing-savegame-data-2"] = root.myObject.serialize();
});
// Restore it when the savegame is loaded
this.signals.gameDeserialized.add((root, data) => {
const errorText = root.myObject.deserialize(data.modExtraData["storing-savegame-data-2"]);
if (errorText) {
alert("Mod failed to deserialize from savegame: " + errorText);
}
alert("The other value stored in the savegame (option 2) was " + root.myObject.someInt);
});
////////////////////////////////////////////////////////////////////
}
}

View File

@ -1,66 +0,0 @@
// @ts-nocheck
const METADATA = {
website: "https://tobspr.io",
author: "tobspr",
name: "Mod Example: Translations",
version: "1",
id: "translations",
description: "Shows how to add and modify translations",
minimumGameVersion: ">=1.5.0",
// You can specify this parameter if savegames will still work
// after your mod has been uninstalled
doesNotAffectSavegame: true,
};
class Mod extends shapez.Mod {
init() {
// Replace an existing translation in the english language
this.modInterface.registerTranslations("en", {
ingame: {
interactiveTutorial: {
title: "Hello",
hints: {
"1_1_extractor": "World!",
},
},
},
});
// Replace an existing translation in german
this.modInterface.registerTranslations("de", {
ingame: {
interactiveTutorial: {
title: "Hallo",
hints: {
"1_1_extractor": "Welt!",
},
},
},
});
// Add an entirely new translation which is localized in german and english
this.modInterface.registerTranslations("en", {
mods: {
mymod: {
test: "Test Translation",
},
},
});
this.modInterface.registerTranslations("de", {
mods: {
mymod: {
test: "Test Übersetzung",
},
},
});
// Show a dialog in the main menu
this.signals.stateEntered.add(state => {
if (state instanceof shapez.MainMenuState) {
// Will show differently based on the selected language
this.dialogs.showInfo("My translation", shapez.T.mods.mymod.test);
}
});
}
}

View File

@ -1,148 +0,0 @@
// @ts-nocheck
const METADATA = {
website: "https://tobspr.io",
author: "tobspr",
name: "Mod Example: Usage Statistics",
version: "1",
id: "usage-statistics",
description:
"Shows how to add a new component to the game, how to save additional data and how to add custom logic and drawings",
minimumGameVersion: ">=1.5.0",
};
/**
* Quick info on how this mod works:
*
* It tracks how many ticks a building was idle within X seconds to compute
* the usage percentage.
*
* Every tick the logic checks if the building is idle, if so, it increases aggregatedIdleTime.
* Once X seconds are over, the aggregatedIdleTime is copied to computedUsage which
* is displayed on screen via the UsageStatisticsSystem
*/
const MEASURE_INTERVAL_SECONDS = 5;
class UsageStatisticsComponent extends shapez.Component {
static getId() {
return "UsageStatistics";
}
static getSchema() {
// Here you define which properties should be saved to the savegame
// and get automatically restored
return {
lastTimestamp: shapez.types.float,
computedUsage: shapez.types.float,
aggregatedIdleTime: shapez.types.float,
};
}
constructor() {
super();
this.lastTimestamp = 0;
this.computedUsage = 0;
this.aggregatedIdleTime = 0;
}
}
class UsageStatisticsSystem extends shapez.GameSystemWithFilter {
constructor(root) {
// By specifying the list of components, `this.allEntities` will only
// contain entities which have *all* of the specified components
super(root, [UsageStatisticsComponent, shapez.ItemProcessorComponent]);
}
update() {
const now = this.root.time.now();
for (let i = 0; i < this.allEntities.length; ++i) {
const entity = this.allEntities[i];
const processorComp = entity.components.ItemProcessor;
const usageComp = entity.components.UsageStatistics;
if (now - usageComp.lastTimestamp > MEASURE_INTERVAL_SECONDS) {
usageComp.computedUsage = shapez.clamp(
1 - usageComp.aggregatedIdleTime / MEASURE_INTERVAL_SECONDS
);
usageComp.aggregatedIdleTime = 0;
usageComp.lastTimestamp = now;
}
if (processorComp.ongoingCharges.length === 0) {
usageComp.aggregatedIdleTime += this.root.dynamicTickrate.deltaSeconds;
}
}
}
drawChunk(parameters, chunk) {
const contents = chunk.containedEntitiesByLayer.regular;
for (let i = 0; i < contents.length; ++i) {
const entity = contents[i];
const usageComp = entity.components.UsageStatistics;
if (!usageComp) {
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)) {
// Background badge
context.fillStyle = "rgba(250, 250, 250, 0.8)";
context.beginRoundedRect(center.x - 10, center.y + 3, 20, 8, 2);
context.fill();
// Text
const usage = usageComp.computedUsage * 100.0;
if (usage > 99.99) {
context.fillStyle = "green";
} else if (usage > 70) {
context.fillStyle = "orange";
} else {
context.fillStyle = "red";
}
context.textAlign = "center";
context.font = "7px GameFont";
context.fillText(Math.round(usage) + "%", center.x, center.y + 10);
}
}
}
}
class Mod extends shapez.Mod {
init() {
// Register the component
this.modInterface.registerComponent(UsageStatisticsComponent);
// Add our new component to all item processor buildings so we can see how many items it processed.
// You can also inspect the entity with F8
const buildings = [
shapez.MetaBalancerBuilding,
shapez.MetaCutterBuilding,
shapez.MetaRotaterBuilding,
shapez.MetaStackerBuilding,
shapez.MetaMixerBuilding,
shapez.MetaPainterBuilding,
];
buildings.forEach(metaClass => {
this.modInterface.runAfterMethod(metaClass, "setupEntityComponents", function (entity) {
entity.addComponent(new UsageStatisticsComponent());
});
});
// Register our game system so we can update and draw stuff
this.modInterface.registerGameSystem({
id: "demo_mod",
systemClass: UsageStatisticsSystem,
before: "belt",
drawHooks: ["staticAfter"],
});
}
}