mirror of
https://github.com/tobspr/shapez.io.git
synced 2025-12-13 18:21:51 +00:00
Make mod loading simpler
This commit is contained in:
parent
41a8d4334c
commit
44c4807d74
@ -3,69 +3,62 @@
|
||||
* If you are interested in adding more logic to the game, you should also check out
|
||||
* the advanced example
|
||||
*/
|
||||
registerMod(() => {
|
||||
class MetaDemoModBuilding extends shapez.ModMetaBuilding {
|
||||
constructor() {
|
||||
super("demoModBuilding");
|
||||
}
|
||||
|
||||
static getAllVariantCombinations() {
|
||||
return [
|
||||
{
|
||||
variant: shapez.defaultBuildingVariant,
|
||||
name: "A test name",
|
||||
description: "A test building",
|
||||
const METADATA = {
|
||||
website: "https://tobspr.io",
|
||||
author: "tobspr",
|
||||
name: "Mod Example: Add new basic building",
|
||||
version: "1",
|
||||
id: "add-building-basic",
|
||||
description: "Shows how to add a new basic building",
|
||||
};
|
||||
|
||||
regularImageBase64: RESOURCES["demoBuilding.png"],
|
||||
blueprintImageBase64: RESOURCES["demoBuildingBlueprint.png"],
|
||||
tutorialImageBase64: RESOURCES["demoBuildingBlueprint.png"],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
getSilhouetteColor() {
|
||||
return "red";
|
||||
}
|
||||
|
||||
setupEntityComponents(entity) {
|
||||
// Here you can add components, for example an ItemProcessorComponent.
|
||||
// To get an idea what you can do with the builtin components, have a look
|
||||
// at the builtin buildings in <src/js/game/buildings/>
|
||||
}
|
||||
class MetaDemoModBuilding extends shapez.ModMetaBuilding {
|
||||
constructor() {
|
||||
super("demoModBuilding");
|
||||
}
|
||||
|
||||
return class ModImpl extends shapez.Mod {
|
||||
constructor(app, modLoader) {
|
||||
super(
|
||||
app,
|
||||
{
|
||||
website: "https://tobspr.io",
|
||||
author: "tobspr",
|
||||
name: "Mod Example: Add new basic building",
|
||||
version: "1",
|
||||
id: "add-building-basic",
|
||||
description: "Shows how to add a new basic building",
|
||||
},
|
||||
modLoader
|
||||
);
|
||||
}
|
||||
static getAllVariantCombinations() {
|
||||
return [
|
||||
{
|
||||
variant: shapez.defaultBuildingVariant,
|
||||
name: "A test name",
|
||||
description: "A test building",
|
||||
|
||||
init() {
|
||||
// Register the new building
|
||||
this.modInterface.registerNewBuilding({
|
||||
metaClass: MetaDemoModBuilding,
|
||||
buildingIconBase64: RESOURCES["demoBuilding.png"],
|
||||
});
|
||||
regularImageBase64: RESOURCES["demoBuilding.png"],
|
||||
blueprintImageBase64: RESOURCES["demoBuildingBlueprint.png"],
|
||||
tutorialImageBase64: RESOURCES["demoBuildingBlueprint.png"],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
// Add it to the regular toolbar
|
||||
this.modInterface.addNewBuildingToToolbar({
|
||||
toolbar: "regular",
|
||||
location: "primary",
|
||||
metaClass: MetaDemoModBuilding,
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
getSilhouetteColor() {
|
||||
return "red";
|
||||
}
|
||||
|
||||
setupEntityComponents(entity) {
|
||||
// Here you can add components, for example an ItemProcessorComponent.
|
||||
// To get an idea what you can do with the builtin components, have a look
|
||||
// at the builtin buildings in <src/js/game/buildings/>
|
||||
}
|
||||
}
|
||||
|
||||
class Mod extends shapez.Mod {
|
||||
init() {
|
||||
// Register the new building
|
||||
this.modInterface.registerNewBuilding({
|
||||
metaClass: MetaDemoModBuilding,
|
||||
buildingIconBase64: RESOURCES["demoBuilding.png"],
|
||||
});
|
||||
|
||||
// Add it to the regular toolbar
|
||||
this.modInterface.addNewBuildingToToolbar({
|
||||
toolbar: "regular",
|
||||
location: "primary",
|
||||
metaClass: MetaDemoModBuilding,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@ -2,135 +2,127 @@
|
||||
* This shows how to add a new extended building (A building which flips shapes).
|
||||
* Be sure to check out the "add_building_basic" example first
|
||||
*/
|
||||
registerMod(() => {
|
||||
// Declare a new type of item processor
|
||||
shapez.enumItemProcessorTypes.flipper = "flipper";
|
||||
const METADATA = {
|
||||
website: "https://tobspr.io",
|
||||
author: "tobspr",
|
||||
name: "Mod Example: Add a flipper building",
|
||||
version: "1",
|
||||
id: "add-building-extended",
|
||||
description:
|
||||
"Shows how to add a new building with logic, in this case it flips/mirrors shapez from top to down",
|
||||
};
|
||||
|
||||
// For now, flipper always has the same speed
|
||||
shapez.MOD_ITEM_PROCESSOR_SPEEDS.flipper = () => 10;
|
||||
// Declare a new type of item processor
|
||||
shapez.enumItemProcessorTypes.flipper = "flipper";
|
||||
|
||||
// Declare a handler for the processor so we define the "flip" operation
|
||||
shapez.MOD_ITEM_PROCESSOR_HANDLERS.flipper = function (payload) {
|
||||
const shapeDefinition = payload.items.get(0).definition;
|
||||
// For now, flipper always has the same speed
|
||||
shapez.MOD_ITEM_PROCESSOR_SPEEDS.flipper = () => 10;
|
||||
|
||||
// Flip bottom with top on a new, cloned item (NEVER modify the incoming item!)
|
||||
const newLayers = shapeDefinition.getClonedLayers();
|
||||
newLayers.forEach(layer => {
|
||||
const tr = layer[shapez.TOP_RIGHT];
|
||||
const br = layer[shapez.BOTTOM_RIGHT];
|
||||
const bl = layer[shapez.BOTTOM_LEFT];
|
||||
const tl = layer[shapez.TOP_LEFT];
|
||||
// Declare a handler for the processor so we define the "flip" operation
|
||||
shapez.MOD_ITEM_PROCESSOR_HANDLERS.flipper = function (payload) {
|
||||
const shapeDefinition = payload.items.get(0).definition;
|
||||
|
||||
layer[shapez.BOTTOM_LEFT] = tl;
|
||||
layer[shapez.BOTTOM_RIGHT] = tr;
|
||||
// Flip bottom with top on a new, cloned item (NEVER modify the incoming item!)
|
||||
const newLayers = shapeDefinition.getClonedLayers();
|
||||
newLayers.forEach(layer => {
|
||||
const tr = layer[shapez.TOP_RIGHT];
|
||||
const br = layer[shapez.BOTTOM_RIGHT];
|
||||
const bl = layer[shapez.BOTTOM_LEFT];
|
||||
const tl = layer[shapez.TOP_LEFT];
|
||||
|
||||
layer[shapez.TOP_LEFT] = bl;
|
||||
layer[shapez.TOP_RIGHT] = br;
|
||||
});
|
||||
layer[shapez.BOTTOM_LEFT] = tl;
|
||||
layer[shapez.BOTTOM_RIGHT] = tr;
|
||||
|
||||
const newDefinition = new shapez.ShapeDefinition({ layers: newLayers });
|
||||
payload.outItems.push({
|
||||
item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(newDefinition),
|
||||
});
|
||||
};
|
||||
layer[shapez.TOP_LEFT] = bl;
|
||||
layer[shapez.TOP_RIGHT] = br;
|
||||
});
|
||||
|
||||
// Create the building
|
||||
class MetaModFlipperBuilding extends shapez.ModMetaBuilding {
|
||||
constructor() {
|
||||
super("modFlipperBuilding");
|
||||
}
|
||||
const newDefinition = new shapez.ShapeDefinition({ layers: newLayers });
|
||||
payload.outItems.push({
|
||||
item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(newDefinition),
|
||||
});
|
||||
};
|
||||
|
||||
static getAllVariantCombinations() {
|
||||
return [
|
||||
{
|
||||
name: "Flipper",
|
||||
description: "Flipps/Mirrors shapez from top to bottom",
|
||||
variant: shapez.defaultBuildingVariant,
|
||||
|
||||
regularImageBase64: RESOURCES["flipper.png"],
|
||||
blueprintImageBase64: RESOURCES["flipper.png"],
|
||||
tutorialImageBase64: RESOURCES["flipper.png"],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
getSilhouetteColor() {
|
||||
return "red";
|
||||
}
|
||||
|
||||
getAdditionalStatistics(root) {
|
||||
const speed = root.hubGoals.getProcessorBaseSpeed(shapez.enumItemProcessorTypes.flipper);
|
||||
return [[shapez.T.ingame.buildingPlacement.infoTexts.speed, shapez.formatItemsPerSecond(speed)]];
|
||||
}
|
||||
|
||||
getIsUnlocked(root) {
|
||||
return true;
|
||||
}
|
||||
|
||||
setupEntityComponents(entity) {
|
||||
// Accept shapes from the bottom
|
||||
entity.addComponent(
|
||||
new shapez.ItemAcceptorComponent({
|
||||
slots: [
|
||||
{
|
||||
pos: new shapez.Vector(0, 0),
|
||||
directions: [shapez.enumDirection.bottom],
|
||||
filter: "shape",
|
||||
},
|
||||
],
|
||||
})
|
||||
);
|
||||
|
||||
// Process those shapes with tye processor type "flipper" (which we added above)
|
||||
entity.addComponent(
|
||||
new shapez.ItemProcessorComponent({
|
||||
inputsPerCharge: 1,
|
||||
processorType: shapez.enumItemProcessorTypes.flipper,
|
||||
})
|
||||
);
|
||||
|
||||
// Eject the result to the top
|
||||
entity.addComponent(
|
||||
new shapez.ItemEjectorComponent({
|
||||
slots: [{ pos: new shapez.Vector(0, 0), direction: shapez.enumDirection.top }],
|
||||
})
|
||||
);
|
||||
}
|
||||
// Create the building
|
||||
class MetaModFlipperBuilding extends shapez.ModMetaBuilding {
|
||||
constructor() {
|
||||
super("modFlipperBuilding");
|
||||
}
|
||||
|
||||
return class ModImpl extends shapez.Mod {
|
||||
constructor(app, modLoader) {
|
||||
super(
|
||||
app,
|
||||
{
|
||||
website: "https://tobspr.io",
|
||||
author: "tobspr",
|
||||
name: "Mod Example: Add a flipper building",
|
||||
version: "1",
|
||||
id: "add-building-extended",
|
||||
description:
|
||||
"Shows how to add a new building with logic, in this case it flips/mirrors shapez from top to down",
|
||||
},
|
||||
modLoader
|
||||
);
|
||||
}
|
||||
static getAllVariantCombinations() {
|
||||
return [
|
||||
{
|
||||
name: "Flipper",
|
||||
description: "Flipps/Mirrors shapez from top to bottom",
|
||||
variant: shapez.defaultBuildingVariant,
|
||||
|
||||
init() {
|
||||
// Register the new building
|
||||
this.modInterface.registerNewBuilding({
|
||||
metaClass: MetaModFlipperBuilding,
|
||||
buildingIconBase64: RESOURCES["flipper.png"],
|
||||
});
|
||||
regularImageBase64: RESOURCES["flipper.png"],
|
||||
blueprintImageBase64: RESOURCES["flipper.png"],
|
||||
tutorialImageBase64: RESOURCES["flipper.png"],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
// Add it to the regular toolbar
|
||||
this.modInterface.addNewBuildingToToolbar({
|
||||
toolbar: "regular",
|
||||
location: "primary",
|
||||
metaClass: MetaModFlipperBuilding,
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
getSilhouetteColor() {
|
||||
return "red";
|
||||
}
|
||||
|
||||
getAdditionalStatistics(root) {
|
||||
const speed = root.hubGoals.getProcessorBaseSpeed(shapez.enumItemProcessorTypes.flipper);
|
||||
return [[shapez.T.ingame.buildingPlacement.infoTexts.speed, shapez.formatItemsPerSecond(speed)]];
|
||||
}
|
||||
|
||||
getIsUnlocked(root) {
|
||||
return true;
|
||||
}
|
||||
|
||||
setupEntityComponents(entity) {
|
||||
// Accept shapes from the bottom
|
||||
entity.addComponent(
|
||||
new shapez.ItemAcceptorComponent({
|
||||
slots: [
|
||||
{
|
||||
pos: new shapez.Vector(0, 0),
|
||||
directions: [shapez.enumDirection.bottom],
|
||||
filter: "shape",
|
||||
},
|
||||
],
|
||||
})
|
||||
);
|
||||
|
||||
// Process those shapes with tye processor type "flipper" (which we added above)
|
||||
entity.addComponent(
|
||||
new shapez.ItemProcessorComponent({
|
||||
inputsPerCharge: 1,
|
||||
processorType: shapez.enumItemProcessorTypes.flipper,
|
||||
})
|
||||
);
|
||||
|
||||
// Eject the result to the top
|
||||
entity.addComponent(
|
||||
new shapez.ItemEjectorComponent({
|
||||
slots: [{ pos: new shapez.Vector(0, 0), direction: shapez.enumDirection.top }],
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class Mod extends shapez.Mod {
|
||||
init() {
|
||||
// Register the new building
|
||||
this.modInterface.registerNewBuilding({
|
||||
metaClass: MetaModFlipperBuilding,
|
||||
buildingIconBase64: RESOURCES["flipper.png"],
|
||||
});
|
||||
|
||||
// Add it to the regular toolbar
|
||||
this.modInterface.addNewBuildingToToolbar({
|
||||
toolbar: "regular",
|
||||
location: "primary",
|
||||
metaClass: MetaModFlipperBuilding,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@ -1,25 +1,18 @@
|
||||
/**
|
||||
* This is the minimal structure of a mod
|
||||
*/
|
||||
registerMod(() => {
|
||||
return class ModImpl extends shapez.Mod {
|
||||
constructor(app, modLoader) {
|
||||
super(
|
||||
app,
|
||||
{
|
||||
website: "https://tobspr.io",
|
||||
author: "tobspr",
|
||||
name: "Mod Example: Base",
|
||||
version: "1",
|
||||
id: "base",
|
||||
description: "The most basic mod",
|
||||
},
|
||||
modLoader
|
||||
);
|
||||
}
|
||||
|
||||
init() {
|
||||
// Start the modding here
|
||||
}
|
||||
};
|
||||
});
|
||||
const METADATA = {
|
||||
website: "https://tobspr.io",
|
||||
author: "tobspr",
|
||||
name: "Mod Example: Base",
|
||||
version: "1",
|
||||
id: "base",
|
||||
description: "The most basic mod",
|
||||
};
|
||||
|
||||
class Mod extends shapez.Mod {
|
||||
init() {
|
||||
// Start the modding here
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,39 +1,32 @@
|
||||
/**
|
||||
* This shows how to patch existing methods by making belts have a cost
|
||||
*/
|
||||
registerMod(() => {
|
||||
return class ModImpl extends shapez.Mod {
|
||||
constructor(app, modLoader) {
|
||||
super(
|
||||
app,
|
||||
{
|
||||
website: "https://tobspr.io",
|
||||
author: "tobspr",
|
||||
name: "Mod Example: Patch Methods",
|
||||
version: "1",
|
||||
id: "patch-methods",
|
||||
description:
|
||||
"Shows how to patch existing methods to change the game by making the belts cost shapes",
|
||||
},
|
||||
modLoader
|
||||
);
|
||||
}
|
||||
|
||||
init() {
|
||||
// Define our currency
|
||||
const CURRENCY = "CyCyCyCy:--------:CuCuCuCu";
|
||||
const METADATA = {
|
||||
website: "https://tobspr.io",
|
||||
author: "tobspr",
|
||||
name: "Mod Example: Patch Methods",
|
||||
version: "1",
|
||||
id: "patch-methods",
|
||||
description: "Shows how to patch existing methods to change the game by making the belts cost shapes",
|
||||
};
|
||||
|
||||
// Make sure the currency is always pinned
|
||||
this.modInterface.runAfterMethod(shapez.HUDPinnedShapes, "rerenderFull", function () {
|
||||
this.internalPinShape({
|
||||
key: CURRENCY,
|
||||
canUnpin: false,
|
||||
className: "currency",
|
||||
});
|
||||
class Mod extends shapez.Mod {
|
||||
init() {
|
||||
// Define our currency
|
||||
const CURRENCY = "CyCyCyCy:--------:CuCuCuCu";
|
||||
|
||||
// Make sure the currency is always pinned
|
||||
this.modInterface.runAfterMethod(shapez.HUDPinnedShapes, "rerenderFull", function () {
|
||||
this.internalPinShape({
|
||||
key: CURRENCY,
|
||||
canUnpin: false,
|
||||
className: "currency",
|
||||
});
|
||||
});
|
||||
|
||||
// Style it
|
||||
this.modInterface.registerCss(`
|
||||
// Style it
|
||||
this.modInterface.registerCss(`
|
||||
#ingame_HUD_PinnedShapes .shape.currency::after {
|
||||
content: " ";
|
||||
position: absolute;
|
||||
@ -57,41 +50,40 @@ registerMod(() => {
|
||||
}
|
||||
`);
|
||||
|
||||
// Make the player start with some currency
|
||||
this.modInterface.runAfterMethod(shapez.GameCore, "initNewGame", function () {
|
||||
this.root.hubGoals.storedShapes[CURRENCY] = 100;
|
||||
});
|
||||
// Make the player start with some currency
|
||||
this.modInterface.runAfterMethod(shapez.GameCore, "initNewGame", function () {
|
||||
this.root.hubGoals.storedShapes[CURRENCY] = 100;
|
||||
});
|
||||
|
||||
// Make belts have a cost
|
||||
this.modInterface.replaceMethod(shapez.MetaBeltBuilding, "getAdditionalStatistics", function (
|
||||
$original,
|
||||
[root, variant]
|
||||
) {
|
||||
const oldStats = $original(root, variant);
|
||||
oldStats.push(["Cost", "1 x <span class='currencyIcon small'></span>"]);
|
||||
return oldStats;
|
||||
});
|
||||
// Make belts have a cost
|
||||
this.modInterface.replaceMethod(shapez.MetaBeltBuilding, "getAdditionalStatistics", function (
|
||||
$original,
|
||||
[root, variant]
|
||||
) {
|
||||
const oldStats = $original(root, variant);
|
||||
oldStats.push(["Cost", "1 x <span class='currencyIcon small'></span>"]);
|
||||
return oldStats;
|
||||
});
|
||||
|
||||
// Only allow placing an entity when there is enough currency
|
||||
this.modInterface.replaceMethod(shapez.GameLogic, "checkCanPlaceEntity", function (
|
||||
$original,
|
||||
[entity, offset]
|
||||
) {
|
||||
const storedCurrency = this.root.hubGoals.storedShapes[CURRENCY] || 0;
|
||||
return storedCurrency > 0 && $original(entity, offset);
|
||||
});
|
||||
// Only allow placing an entity when there is enough currency
|
||||
this.modInterface.replaceMethod(shapez.GameLogic, "checkCanPlaceEntity", function (
|
||||
$original,
|
||||
[entity, offset]
|
||||
) {
|
||||
const storedCurrency = this.root.hubGoals.storedShapes[CURRENCY] || 0;
|
||||
return storedCurrency > 0 && $original(entity, offset);
|
||||
});
|
||||
|
||||
// Take shapes when placing a building
|
||||
this.modInterface.replaceMethod(shapez.GameLogic, "tryPlaceBuilding", function ($original, args) {
|
||||
const result = $original(...args);
|
||||
if (result && result.components.Belt) {
|
||||
this.root.hubGoals.storedShapes[CURRENCY]--;
|
||||
}
|
||||
return result;
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
// Take shapes when placing a building
|
||||
this.modInterface.replaceMethod(shapez.GameLogic, "tryPlaceBuilding", function ($original, args) {
|
||||
const result = $original(...args);
|
||||
if (result && result.components.Belt) {
|
||||
this.root.hubGoals.storedShapes[CURRENCY]--;
|
||||
}
|
||||
return result;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const RESOURCES = {
|
||||
"currency.png":
|
||||
|
||||
@ -1,231 +1,189 @@
|
||||
registerMod(() => {
|
||||
class DemoModComponent extends shapez.Component {
|
||||
static getId() {
|
||||
return "DemoMod";
|
||||
}
|
||||
|
||||
static getSchema() {
|
||||
return {
|
||||
magicNumber: shapez.types.uint,
|
||||
};
|
||||
}
|
||||
|
||||
constructor(magicNumber) {
|
||||
super();
|
||||
|
||||
this.magicNumber = magicNumber;
|
||||
}
|
||||
class DemoModComponent extends shapez.Component {
|
||||
static getId() {
|
||||
return "DemoMod";
|
||||
}
|
||||
|
||||
class MetaDemoModBuilding extends shapez.MetaBuilding {
|
||||
constructor() {
|
||||
super("demoModBuilding");
|
||||
}
|
||||
|
||||
getSilhouetteColor() {
|
||||
return "red";
|
||||
}
|
||||
|
||||
setupEntityComponents(entity) {
|
||||
entity.addComponent(new DemoModComponent(Math.floor(Math.random() * 100.0)));
|
||||
}
|
||||
static getSchema() {
|
||||
return {
|
||||
magicNumber: shapez.types.uint,
|
||||
};
|
||||
}
|
||||
|
||||
class DemoModSystem extends shapez.GameSystemWithFilter {
|
||||
constructor(root) {
|
||||
super(root, [DemoModComponent]);
|
||||
}
|
||||
constructor(magicNumber) {
|
||||
super();
|
||||
|
||||
update() {
|
||||
// nothing to do here
|
||||
}
|
||||
this.magicNumber = magicNumber;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
class MetaDemoModBuilding extends shapez.MetaBuilding {
|
||||
constructor() {
|
||||
super("demoModBuilding");
|
||||
}
|
||||
|
||||
const staticComp = entity.components.StaticMapEntity;
|
||||
getSilhouetteColor() {
|
||||
return "red";
|
||||
}
|
||||
|
||||
const context = parameters.context;
|
||||
const center = staticComp.getTileSpaceBounds().getCenter().toWorldSpace();
|
||||
setupEntityComponents(entity) {
|
||||
entity.addComponent(new DemoModComponent(Math.floor(Math.random() * 100.0)));
|
||||
}
|
||||
}
|
||||
|
||||
// Culling for better performance
|
||||
if (parameters.visibleRect.containsCircle(center.x, center.y, 40)) {
|
||||
// Circle
|
||||
context.fillStyle = "#53cf47";
|
||||
context.strokeStyle = "#000";
|
||||
context.lineWidth = 2;
|
||||
class DemoModSystem extends shapez.GameSystemWithFilter {
|
||||
constructor(root) {
|
||||
super(root, [DemoModComponent]);
|
||||
}
|
||||
|
||||
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();
|
||||
update() {
|
||||
// nothing to do here
|
||||
}
|
||||
|
||||
// Text
|
||||
context.fillStyle = "#fff";
|
||||
context.textAlign = "center";
|
||||
context.font = "12px GameFont";
|
||||
context.fillText(demoComp.magicNumber, center.x, center.y + 4);
|
||||
}
|
||||
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 {
|
||||
constructor(app, modLoader) {
|
||||
super(
|
||||
app,
|
||||
class Mod extends shapez.Mod {
|
||||
constructor(app, modLoader) {
|
||||
super(
|
||||
app,
|
||||
{
|
||||
website: "https://tobspr.io",
|
||||
author: "tobspr",
|
||||
name: "Demo Mod",
|
||||
version: "1",
|
||||
id: "demo-mod",
|
||||
description: "A simple mod to demonstrate the capatibilities of the mod loader.",
|
||||
},
|
||||
modLoader
|
||||
);
|
||||
}
|
||||
|
||||
init() {
|
||||
// Add some custom css
|
||||
// this.modInterface.registerCss(`
|
||||
// * {
|
||||
// font-family: "Comic Sans", "Comic Sans MS", Tahoma !important;
|
||||
// }
|
||||
// `);
|
||||
|
||||
// Add an atlas
|
||||
this.modInterface.registerAtlas(RESOURCES["demoAtlas.png"], RESOURCES["demoAtlas.json"]);
|
||||
|
||||
this.modInterface.registerTranslations("en", {
|
||||
ingame: {
|
||||
interactiveTutorial: {
|
||||
title: "Hello",
|
||||
hints: {
|
||||
"1_1_extractor": "World!",
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// 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,
|
||||
buildingIconBase64: RESOURCES["demoBuilding.png"],
|
||||
|
||||
variantsAndRotations: [
|
||||
{
|
||||
website: "https://tobspr.io",
|
||||
author: "tobspr",
|
||||
name: "Demo Mod",
|
||||
version: "1",
|
||||
id: "demo-mod",
|
||||
description: "A simple mod to demonstrate the capatibilities of the mod loader.",
|
||||
description: "A test building",
|
||||
name: "A test name",
|
||||
|
||||
regularImageBase64: RESOURCES["demoBuilding.png"],
|
||||
blueprintImageBase64: RESOURCES["demoBuildingBlueprint.png"],
|
||||
tutorialImageBase64: RESOURCES["demoBuildingBlueprint.png"],
|
||||
},
|
||||
modLoader
|
||||
);
|
||||
}
|
||||
],
|
||||
});
|
||||
|
||||
init() {
|
||||
// Add some custom css
|
||||
// this.modInterface.registerCss(`
|
||||
// * {
|
||||
// font-family: "Comic Sans", "Comic Sans MS", Tahoma !important;
|
||||
// }
|
||||
// `);
|
||||
// Add it to the regular toolbar
|
||||
this.modInterface.addNewBuildingToToolbar({
|
||||
toolbar: "regular",
|
||||
location: "primary",
|
||||
metaClass: MetaDemoModBuilding,
|
||||
});
|
||||
|
||||
// Replace a builtin sprite
|
||||
["red", "green", "blue", "yellow", "purple", "cyan", "white"].forEach(color => {
|
||||
this.modInterface.registerSprite(
|
||||
"sprites/colors/" + color + ".png",
|
||||
RESOURCES[color + ".png"]
|
||||
);
|
||||
});
|
||||
// Register keybinding
|
||||
this.modInterface.registerIngameKeybinding({
|
||||
id: "demo_mod_binding",
|
||||
keyCode: shapez.keyToKeyCode("F"),
|
||||
translation: "mymod: Do something (always with SHIFT)",
|
||||
modifiers: {
|
||||
shift: true,
|
||||
},
|
||||
handler: root => {
|
||||
this.dialogs.showInfo("Mod Message", "It worked!");
|
||||
return shapez.STOP_PROPAGATION;
|
||||
},
|
||||
});
|
||||
|
||||
// Add an atlas
|
||||
this.modInterface.registerAtlas(RESOURCES["demoAtlas.png"], RESOURCES["demoAtlas.json"]);
|
||||
// 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);
|
||||
|
||||
// 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)),
|
||||
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);
|
||||
}
|
||||
});
|
||||
|
||||
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 theme colors
|
||||
this.signals.preprocessTheme.add(({ theme }) => {
|
||||
theme.map.background = "#eee";
|
||||
theme.items.outline = "#000";
|
||||
});
|
||||
|
||||
// Modify the goal of the first level
|
||||
this.signals.modifyLevelDefinitions.add(definitions => {
|
||||
definitions[0].shape = "LuCuLuCu";
|
||||
});
|
||||
|
||||
this.modInterface.registerTranslations("en", {
|
||||
ingame: {
|
||||
interactiveTutorial: {
|
||||
title: "Hello",
|
||||
hints: {
|
||||
"1_1_extractor": "World!",
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// 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,
|
||||
buildingIconBase64: RESOURCES["demoBuilding.png"],
|
||||
|
||||
variantsAndRotations: [
|
||||
{
|
||||
description: "A test building",
|
||||
name: "A test name",
|
||||
|
||||
regularImageBase64: RESOURCES["demoBuilding.png"],
|
||||
blueprintImageBase64: RESOURCES["demoBuildingBlueprint.png"],
|
||||
tutorialImageBase64: RESOURCES["demoBuildingBlueprint.png"],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// Add it to the regular toolbar
|
||||
this.modInterface.addNewBuildingToToolbar({
|
||||
toolbar: "regular",
|
||||
location: "primary",
|
||||
metaClass: MetaDemoModBuilding,
|
||||
});
|
||||
|
||||
// Register keybinding
|
||||
this.modInterface.registerIngameKeybinding({
|
||||
id: "demo_mod_binding",
|
||||
keyCode: shapez.keyToKeyCode("F"),
|
||||
translation: "mymod: Do something (always with SHIFT)",
|
||||
modifiers: {
|
||||
shift: true,
|
||||
},
|
||||
handler: root => {
|
||||
this.dialogs.showInfo("Mod Message", "It worked!");
|
||||
return shapez.STOP_PROPAGATION;
|
||||
},
|
||||
});
|
||||
|
||||
// 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(`
|
||||
this.modInterface.registerCss(`
|
||||
#demo_mod_hello_world_element {
|
||||
position: absolute;
|
||||
top: calc(10px * var(--ui-scale));
|
||||
@ -235,9 +193,8 @@ registerMod(() => {
|
||||
}
|
||||
|
||||
`);
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// @notice: Later this part will be autogenerated
|
||||
|
||||
@ -1,28 +1,21 @@
|
||||
/**
|
||||
* This example shows how to add custom css
|
||||
*/
|
||||
registerMod(() => {
|
||||
return class ModImpl extends shapez.Mod {
|
||||
constructor(app, modLoader) {
|
||||
super(
|
||||
app,
|
||||
{
|
||||
website: "https://tobspr.io",
|
||||
author: "tobspr",
|
||||
name: "Mod Example: Add custom CSS",
|
||||
version: "1",
|
||||
id: "custom-css",
|
||||
description: "Shows how to add custom css",
|
||||
},
|
||||
modLoader
|
||||
);
|
||||
}
|
||||
const METADATA = {
|
||||
website: "https://tobspr.io",
|
||||
author: "tobspr",
|
||||
name: "Mod Example: Add custom CSS",
|
||||
version: "1",
|
||||
id: "custom-css",
|
||||
description: "Shows how to add custom css",
|
||||
};
|
||||
|
||||
init() {
|
||||
// Notice that, since the UI is scaled dynamically, every pixel value
|
||||
// should be wrapped in '$scaled()' (see below)
|
||||
class Mod extends shapez.Mod {
|
||||
init() {
|
||||
// Notice that, since the UI is scaled dynamically, every pixel value
|
||||
// should be wrapped in '$scaled()' (see below)
|
||||
|
||||
this.modInterface.registerCss(`
|
||||
this.modInterface.registerCss(`
|
||||
* {
|
||||
font-family: "Comic Sans", "Comic Sans MS", "ComicSans", Tahoma !important;
|
||||
}
|
||||
@ -39,9 +32,8 @@ registerMod(() => {
|
||||
border: $scaled(5px) solid #000 !important;
|
||||
}
|
||||
`);
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const RESOURCES = {
|
||||
"cat.png":
|
||||
|
||||
@ -1,53 +1,46 @@
|
||||
/**
|
||||
* This shows how to add custom sub shapes
|
||||
*/
|
||||
registerMod(() => {
|
||||
return class ModImpl extends shapez.Mod {
|
||||
constructor(app, modLoader) {
|
||||
super(
|
||||
app,
|
||||
{
|
||||
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",
|
||||
},
|
||||
modLoader
|
||||
);
|
||||
}
|
||||
|
||||
init() {
|
||||
// Add a new type of sub shape ("Line", short code "L")
|
||||
this.modInterface.registerSubShapeType({
|
||||
id: "line",
|
||||
shortCode: "L",
|
||||
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",
|
||||
};
|
||||
|
||||
// Make it spawn on the map
|
||||
weightComputation: distanceToOriginInChunks =>
|
||||
Math.round(20 + Math.max(Math.min(distanceToOriginInChunks, 30), 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",
|
||||
|
||||
// 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();
|
||||
},
|
||||
});
|
||||
// Make it spawn on the map
|
||||
weightComputation: distanceToOriginInChunks =>
|
||||
Math.round(20 + Math.max(Math.min(distanceToOriginInChunks, 30), 0)),
|
||||
|
||||
// Modify the goal of the first level to add our goal
|
||||
this.signals.modifyLevelDefinitions.add(definitions => {
|
||||
definitions[0].shape = "LuLuLuLu";
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
// 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();
|
||||
},
|
||||
});
|
||||
|
||||
// Modify the goal of the first level to add our goal
|
||||
this.signals.modifyLevelDefinitions.add(definitions => {
|
||||
definitions[0].shape = "LuLuLuLu";
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,32 +1,24 @@
|
||||
/**
|
||||
* This example shows how to add a new theme to the game
|
||||
*/
|
||||
registerMod(() => {
|
||||
return class ModImpl extends shapez.Mod {
|
||||
constructor(app, modLoader) {
|
||||
super(
|
||||
app,
|
||||
{
|
||||
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",
|
||||
},
|
||||
modLoader
|
||||
);
|
||||
}
|
||||
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",
|
||||
};
|
||||
|
||||
init() {
|
||||
this.modInterface.registerGameTheme({
|
||||
id: "my-theme",
|
||||
name: "My fancy theme",
|
||||
theme: RESOURCES["my-theme.json"],
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
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": {
|
||||
|
||||
@ -1,36 +1,29 @@
|
||||
/**
|
||||
* This shows how to modify an existing building
|
||||
*/
|
||||
registerMod(() => {
|
||||
return class ModImpl extends shapez.Mod {
|
||||
constructor(app, modLoader) {
|
||||
super(
|
||||
app,
|
||||
{
|
||||
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",
|
||||
},
|
||||
modLoader
|
||||
);
|
||||
}
|
||||
|
||||
init() {
|
||||
// Make Rotator always unlocked
|
||||
this.modInterface.replaceMethod(shapez.MetaRotaterBuilding, "getIsUnlocked", function () {
|
||||
return true;
|
||||
});
|
||||
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",
|
||||
};
|
||||
|
||||
// Add some custom stats to the info panel when selecting the building
|
||||
this.modInterface.replaceMethod(shapez.MetaRotaterBuilding, "getAdditionalStatistics", function (
|
||||
root,
|
||||
variant
|
||||
) {
|
||||
return [["Awesomeness", 5]];
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
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]];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,29 +2,21 @@
|
||||
* This example shows how to modify the builtin themes. If you want to create your own theme,
|
||||
* be sure to check out the "custom_theme" example
|
||||
*/
|
||||
registerMod(() => {
|
||||
return class ModImpl extends shapez.Mod {
|
||||
constructor(app, modLoader) {
|
||||
super(
|
||||
app,
|
||||
{
|
||||
website: "https://tobspr.io",
|
||||
author: "tobspr",
|
||||
name: "Mod Example: Modify Builtin Themes",
|
||||
version: "1",
|
||||
id: "modify-theme",
|
||||
description: "Shows how to modify builtin themes",
|
||||
},
|
||||
modLoader
|
||||
);
|
||||
}
|
||||
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",
|
||||
};
|
||||
|
||||
init() {
|
||||
shapez.THEMES.light.map.background = "#eee";
|
||||
shapez.THEMES.light.items.outline = "#000";
|
||||
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";
|
||||
}
|
||||
};
|
||||
});
|
||||
shapez.THEMES.dark.map.background = "#245";
|
||||
shapez.THEMES.dark.items.outline = "#fff";
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,34 +2,24 @@
|
||||
* This example shows how to replace builtin sprites, in this case
|
||||
* the color sprites
|
||||
*/
|
||||
registerMod(() => {
|
||||
return class ModImpl extends shapez.Mod {
|
||||
constructor(app, modLoader) {
|
||||
super(
|
||||
app,
|
||||
{
|
||||
website: "https://tobspr.io",
|
||||
author: "tobspr",
|
||||
name: "Mod Example: Replace builtin sprites",
|
||||
version: "1",
|
||||
id: "replace-builtin-sprites",
|
||||
description: "Shows how to replace builtin sprites",
|
||||
},
|
||||
modLoader
|
||||
);
|
||||
}
|
||||
|
||||
init() {
|
||||
// Replace a builtin sprite
|
||||
["red", "green", "blue", "yellow", "purple", "cyan", "white"].forEach(color => {
|
||||
this.modInterface.registerSprite(
|
||||
"sprites/colors/" + color + ".png",
|
||||
RESOURCES[color + ".png"]
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
const METADATA = {
|
||||
website: "https://tobspr.io",
|
||||
author: "tobspr",
|
||||
name: "Mod Example: Replace builtin sprites",
|
||||
version: "1",
|
||||
id: "replace-builtin-sprites",
|
||||
description: "Shows how to replace builtin sprites",
|
||||
};
|
||||
|
||||
class Mod extends shapez.Mod {
|
||||
init() {
|
||||
// Replace a builtin sprite
|
||||
["red", "green", "blue", "yellow", "purple", "cyan", "white"].forEach(color => {
|
||||
this.modInterface.registerSprite("sprites/colors/" + color + ".png", RESOURCES[color + ".png"]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@ -1,71 +1,64 @@
|
||||
/**
|
||||
* Shows to add new translations
|
||||
*/
|
||||
registerMod(() => {
|
||||
return class ModImpl extends shapez.Mod {
|
||||
constructor(app, modLoader) {
|
||||
super(
|
||||
app,
|
||||
{
|
||||
website: "https://tobspr.io",
|
||||
author: "tobspr",
|
||||
name: "Mod Example: Translations",
|
||||
version: "1",
|
||||
id: "translations",
|
||||
description: "Shows how to add and modify translations",
|
||||
},
|
||||
modLoader
|
||||
);
|
||||
}
|
||||
|
||||
init() {
|
||||
// Replace an existing translation in the english language
|
||||
this.modInterface.registerTranslations("en", {
|
||||
ingame: {
|
||||
interactiveTutorial: {
|
||||
title: "Hello",
|
||||
hints: {
|
||||
"1_1_extractor": "World!",
|
||||
},
|
||||
const METADATA = {
|
||||
website: "https://tobspr.io",
|
||||
author: "tobspr",
|
||||
name: "Mod Example: Translations",
|
||||
version: "1",
|
||||
id: "translations",
|
||||
description: "Shows how to add and modify translations",
|
||||
};
|
||||
|
||||
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!",
|
||||
},
|
||||
// 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",
|
||||
},
|
||||
// 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",
|
||||
},
|
||||
},
|
||||
});
|
||||
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);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
// 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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
4
src/js/globals.d.ts
vendored
4
src/js/globals.d.ts
vendored
@ -26,8 +26,6 @@ declare const shapez: any;
|
||||
|
||||
declare const ipcRenderer: any;
|
||||
|
||||
declare const registerMod: any;
|
||||
|
||||
// Polyfills
|
||||
declare interface String {
|
||||
replaceAll(search: string, replacement: string): string;
|
||||
@ -98,7 +96,7 @@ declare interface Window {
|
||||
cpmstarAPI: any;
|
||||
|
||||
// Mods
|
||||
registerMod: any;
|
||||
$shapez_registerMod: any;
|
||||
anyModLoaded: any;
|
||||
|
||||
shapez: any;
|
||||
|
||||
@ -7,28 +7,22 @@ import { MOD_SIGNALS } from "./mod_signals";
|
||||
|
||||
export class Mod {
|
||||
/**
|
||||
*
|
||||
* @param {Application} app
|
||||
* @param {object} metadata
|
||||
* @param {string} metadata.name
|
||||
* @param {string} metadata.version
|
||||
* @param {string} metadata.author
|
||||
* @param {string} metadata.website
|
||||
* @param {string} metadata.description
|
||||
* @param {string} metadata.id
|
||||
*
|
||||
* @param {ModLoader} modLoader
|
||||
* @param {import("./modloader").ModMetadata} meta
|
||||
*/
|
||||
constructor(app, metadata, modLoader) {
|
||||
constructor(app, modLoader, meta) {
|
||||
this.app = app;
|
||||
this.metadata = metadata;
|
||||
this.modLoader = modLoader;
|
||||
this.metadata = meta;
|
||||
|
||||
this.signals = MOD_SIGNALS;
|
||||
this.modInterface = modLoader.modInterface;
|
||||
}
|
||||
|
||||
init() {}
|
||||
init() {
|
||||
// to be overridden
|
||||
}
|
||||
|
||||
get dialogs() {
|
||||
return this.modInterface.dialogs;
|
||||
|
||||
@ -9,6 +9,17 @@ import { MOD_SIGNALS } from "./mod_signals";
|
||||
|
||||
const LOG = createLogger("mods");
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* name: string;
|
||||
* version: string;
|
||||
* author: string;
|
||||
* website: string;
|
||||
* description: string;
|
||||
* id: string;
|
||||
* }} ModMetadata
|
||||
*/
|
||||
|
||||
export class ModLoader {
|
||||
constructor() {
|
||||
LOG.log("modloader created");
|
||||
@ -23,7 +34,7 @@ export class ModLoader {
|
||||
|
||||
this.modInterface = new ModInterface(this);
|
||||
|
||||
/** @type {((Object) => (new (Application, ModLoader) => Mod))[]} */
|
||||
/** @type {({ meta: ModMetadata, modClass: typeof Mod})[]} */
|
||||
this.modLoadQueue = [];
|
||||
|
||||
this.initialized = false;
|
||||
@ -73,8 +84,11 @@ export class ModLoader {
|
||||
|
||||
this.exposeExports();
|
||||
|
||||
window.registerMod = mod => {
|
||||
this.modLoadQueue.push(mod);
|
||||
window.$shapez_registerMod = (modClass, meta) => {
|
||||
this.modLoadQueue.push({
|
||||
modClass,
|
||||
meta,
|
||||
});
|
||||
};
|
||||
|
||||
if (G_IS_STANDALONE || G_IS_DEV) {
|
||||
@ -103,6 +117,7 @@ export class ModLoader {
|
||||
|
||||
mods.forEach(modCode => {
|
||||
try {
|
||||
modCode += "\n;window.$shapez_registerMod(Mod, METADATA);";
|
||||
const func = new Function(modCode);
|
||||
func();
|
||||
} catch (ex) {
|
||||
@ -116,9 +131,9 @@ export class ModLoader {
|
||||
}
|
||||
|
||||
this.initialized = true;
|
||||
this.modLoadQueue.forEach(modClass => {
|
||||
this.modLoadQueue.forEach(({ modClass, meta }) => {
|
||||
try {
|
||||
const mod = new (modClass())(this.app, this);
|
||||
const mod = new modClass(this.app, this, meta);
|
||||
mod.init();
|
||||
this.mods.push(mod);
|
||||
} catch (ex) {
|
||||
@ -128,7 +143,7 @@ export class ModLoader {
|
||||
});
|
||||
this.modLoadQueue = [];
|
||||
|
||||
delete window.registerMod;
|
||||
delete window.$shapez_registerMod;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user