Move item processing into separate methods

This commit is contained in:
tobspr 2020-09-19 17:55:36 +02:00
parent 3c56c4772f
commit 5ab3afdd30
13 changed files with 795 additions and 742 deletions

View File

@ -197,7 +197,7 @@
<key>scaleMode</key>
<enum type="ScaleMode">Smooth</enum>
<key>extrude</key>
<uint>2</uint>
<uint>4</uint>
<key>trimThreshold</key>
<uint>2</uint>
<key>trimMargin</key>
@ -281,6 +281,7 @@
<key type="filename">sprites/blueprints/underground_belt_exit-tier2.png</key>
<key type="filename">sprites/blueprints/underground_belt_exit.png</key>
<key type="filename">sprites/blueprints/virtual_processor-analyzer.png</key>
<key type="filename">sprites/blueprints/virtual_processor-painter.png</key>
<key type="filename">sprites/blueprints/virtual_processor-rotater.png</key>
<key type="filename">sprites/blueprints/virtual_processor-shapecompare.png</key>
<key type="filename">sprites/blueprints/virtual_processor-stacker.png</key>
@ -309,6 +310,7 @@
<key type="filename">sprites/buildings/underground_belt_exit-tier2.png</key>
<key type="filename">sprites/buildings/underground_belt_exit.png</key>
<key type="filename">sprites/buildings/virtual_processor-analyzer.png</key>
<key type="filename">sprites/buildings/virtual_processor-painter.png</key>
<key type="filename">sprites/buildings/virtual_processor-rotater.png</key>
<key type="filename">sprites/buildings/virtual_processor-shapecompare.png</key>
<key type="filename">sprites/buildings/virtual_processor-stacker.png</key>

View File

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -1,122 +1,122 @@
import { formatItemsPerSecond } from "../../core/utils";
import { enumDirection, Vector } from "../../core/vector";
import { T } from "../../translations";
import { ItemAcceptorComponent } from "../components/item_acceptor";
import { ItemEjectorComponent } from "../components/item_ejector";
import { enumItemProcessorTypes, ItemProcessorComponent } from "../components/item_processor";
import { Entity } from "../entity";
import { defaultBuildingVariant, MetaBuilding } from "../meta_building";
import { GameRoot } from "../root";
import { enumHubGoalRewards } from "../tutorial_goals";
/** @enum {string} */
export const enumRotaterVariants = { ccw: "ccw", fl: "fl" };
export class MetaRotaterBuilding extends MetaBuilding {
constructor() {
super("rotater");
}
getSilhouetteColor() {
return "#7dc6cd";
}
/**
* @param {GameRoot} root
* @param {string} variant
* @returns {Array<[string, string]>}
*/
getAdditionalStatistics(root, variant) {
switch (variant) {
case defaultBuildingVariant: {
const speed = root.hubGoals.getProcessorBaseSpeed(enumItemProcessorTypes.rotater);
return [[T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(speed)]];
}
case enumRotaterVariants.ccw: {
const speed = root.hubGoals.getProcessorBaseSpeed(enumItemProcessorTypes.rotaterCCW);
return [[T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(speed)]];
}
case enumRotaterVariants.fl: {
const speed = root.hubGoals.getProcessorBaseSpeed(enumItemProcessorTypes.rotaterFL);
return [[T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(speed)]];
}
}
}
/**
*
* @param {GameRoot} root
*/
getAvailableVariants(root) {
let variants = [defaultBuildingVariant];
if (root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_rotater_ccw)) {
variants.push(enumRotaterVariants.ccw);
}
if (root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_rotater_fl)) {
variants.push(enumRotaterVariants.fl);
}
return variants;
}
/**
* @param {GameRoot} root
*/
getIsUnlocked(root) {
return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_rotater);
}
/**
* Creates the entity at the given location
* @param {Entity} entity
*/
setupEntityComponents(entity) {
entity.addComponent(
new ItemProcessorComponent({
inputsPerCharge: 1,
processorType: enumItemProcessorTypes.rotater,
})
);
entity.addComponent(
new ItemEjectorComponent({
slots: [{ pos: new Vector(0, 0), direction: enumDirection.top }],
})
);
entity.addComponent(
new ItemAcceptorComponent({
slots: [
{
pos: new Vector(0, 0),
directions: [enumDirection.bottom],
filter: "shape",
},
],
})
);
}
/**
*
* @param {Entity} entity
* @param {number} rotationVariant
* @param {string} variant
*/
updateVariants(entity, rotationVariant, variant) {
switch (variant) {
case defaultBuildingVariant: {
entity.components.ItemProcessor.type = enumItemProcessorTypes.rotater;
break;
}
case enumRotaterVariants.ccw: {
entity.components.ItemProcessor.type = enumItemProcessorTypes.rotaterCCW;
break;
}
case enumRotaterVariants.fl: {
entity.components.ItemProcessor.type = enumItemProcessorTypes.rotaterFL;
break;
}
default:
assertAlways(false, "Unknown rotater variant: " + variant);
}
}
}
import { formatItemsPerSecond } from "../../core/utils";
import { enumDirection, Vector } from "../../core/vector";
import { T } from "../../translations";
import { ItemAcceptorComponent } from "../components/item_acceptor";
import { ItemEjectorComponent } from "../components/item_ejector";
import { enumItemProcessorTypes, ItemProcessorComponent } from "../components/item_processor";
import { Entity } from "../entity";
import { defaultBuildingVariant, MetaBuilding } from "../meta_building";
import { GameRoot } from "../root";
import { enumHubGoalRewards } from "../tutorial_goals";
/** @enum {string} */
export const enumRotaterVariants = { ccw: "ccw", rotate180: "rotate180" };
export class MetaRotaterBuilding extends MetaBuilding {
constructor() {
super("rotater");
}
getSilhouetteColor() {
return "#7dc6cd";
}
/**
* @param {GameRoot} root
* @param {string} variant
* @returns {Array<[string, string]>}
*/
getAdditionalStatistics(root, variant) {
switch (variant) {
case defaultBuildingVariant: {
const speed = root.hubGoals.getProcessorBaseSpeed(enumItemProcessorTypes.rotater);
return [[T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(speed)]];
}
case enumRotaterVariants.ccw: {
const speed = root.hubGoals.getProcessorBaseSpeed(enumItemProcessorTypes.rotaterCCW);
return [[T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(speed)]];
}
case enumRotaterVariants.rotate180: {
const speed = root.hubGoals.getProcessorBaseSpeed(enumItemProcessorTypes.rotater180);
return [[T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(speed)]];
}
}
}
/**
*
* @param {GameRoot} root
*/
getAvailableVariants(root) {
let variants = [defaultBuildingVariant];
if (root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_rotater_ccw)) {
variants.push(enumRotaterVariants.ccw);
}
if (root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_rotater_180)) {
variants.push(enumRotaterVariants.rotate180);
}
return variants;
}
/**
* @param {GameRoot} root
*/
getIsUnlocked(root) {
return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_rotater);
}
/**
* Creates the entity at the given location
* @param {Entity} entity
*/
setupEntityComponents(entity) {
entity.addComponent(
new ItemProcessorComponent({
inputsPerCharge: 1,
processorType: enumItemProcessorTypes.rotater,
})
);
entity.addComponent(
new ItemEjectorComponent({
slots: [{ pos: new Vector(0, 0), direction: enumDirection.top }],
})
);
entity.addComponent(
new ItemAcceptorComponent({
slots: [
{
pos: new Vector(0, 0),
directions: [enumDirection.bottom],
filter: "shape",
},
],
})
);
}
/**
*
* @param {Entity} entity
* @param {number} rotationVariant
* @param {string} variant
*/
updateVariants(entity, rotationVariant, variant) {
switch (variant) {
case defaultBuildingVariant: {
entity.components.ItemProcessor.type = enumItemProcessorTypes.rotater;
break;
}
case enumRotaterVariants.ccw: {
entity.components.ItemProcessor.type = enumItemProcessorTypes.rotaterCCW;
break;
}
case enumRotaterVariants.rotate180: {
entity.components.ItemProcessor.type = enumItemProcessorTypes.rotater180;
break;
}
default:
assertAlways(false, "Unknown rotater variant: " + variant);
}
}
}

View File

@ -1,17 +1,15 @@
import { types } from "../../savegame/serialization";
import { BaseItem } from "../base_item";
import { Component } from "../component";
import { typeItemSingleton } from "../item_resolver";
/** @enum {string} */
export const enumItemProcessorTypes = {
splitter: "splitter",
splitterWires: "splitterWires",
cutter: "cutter",
cutterQuad: "cutterQuad",
rotater: "rotater",
rotaterCCW: "rotaterCCW",
rotaterFL: "rotaterFL",
rotater180: "rotater180",
stacker: "stacker",
trash: "trash",
mixer: "mixer",

View File

@ -396,9 +396,6 @@ export class HubGoals extends BasicSerializableObject {
*/
getProcessorBaseSpeed(processorType) {
switch (processorType) {
case enumItemProcessorTypes.splitterWires:
return globalConfig.wiresSpeedItemsPerSecond * 2;
case enumItemProcessorTypes.trash:
case enumItemProcessorTypes.hub:
return 1e30;
@ -427,7 +424,7 @@ export class HubGoals extends BasicSerializableObject {
case enumItemProcessorTypes.cutterQuad:
case enumItemProcessorTypes.rotater:
case enumItemProcessorTypes.rotaterCCW:
case enumItemProcessorTypes.rotaterFL:
case enumItemProcessorTypes.rotater180:
case enumItemProcessorTypes.stacker: {
assert(
globalConfig.buildingSpeeds[processorType],

View File

@ -70,7 +70,7 @@ export function initMetaBuildingRegistry() {
// Rotater
registerBuildingVariant(11, MetaRotaterBuilding);
registerBuildingVariant(12, MetaRotaterBuilding, enumRotaterVariants.ccw);
registerBuildingVariant(13, MetaRotaterBuilding, enumRotaterVariants.fl);
registerBuildingVariant(13, MetaRotaterBuilding, enumRotaterVariants.rotate180);
// Stacker
registerBuildingVariant(14, MetaStackerBuilding);

View File

@ -487,10 +487,10 @@ export class ShapeDefinition extends BasicSerializableObject {
}
/**
* Returns a definition which was rotated 180 degrees (flipped)
* Returns a definition which was rotated 180 degrees
* @returns {ShapeDefinition}
*/
cloneRotateFL() {
cloneRotate180() {
const newLayers = this.internalCloneLayers();
for (let layerIndex = 0; layerIndex < newLayers.length; ++layerIndex) {
const quadrants = newLayers[layerIndex];

View File

@ -1,259 +1,259 @@
import { createLogger } from "../core/logging";
import { BasicSerializableObject } from "../savegame/serialization";
import { enumColors } from "./colors";
import { ShapeItem } from "./items/shape_item";
import { GameRoot } from "./root";
import { enumSubShape, ShapeDefinition } from "./shape_definition";
const logger = createLogger("shape_definition_manager");
export class ShapeDefinitionManager extends BasicSerializableObject {
static getId() {
return "ShapeDefinitionManager";
}
/**
*
* @param {GameRoot} root
*/
constructor(root) {
super();
this.root = root;
/**
* Store a cache from key -> definition
* @type {Object<string, ShapeDefinition>}
*/
this.shapeKeyToDefinition = {};
/**
* Store a cache from key -> item
*/
this.shapeKeyToItem = {};
// Caches operations in the form of 'operation:def1[:def2]'
/** @type {Object.<string, Array<ShapeDefinition>|ShapeDefinition>} */
this.operationCache = {};
}
/**
* Returns a shape instance from a given short key
* @param {string} hash
* @returns {ShapeDefinition}
*/
getShapeFromShortKey(hash) {
const cached = this.shapeKeyToDefinition[hash];
if (cached) {
return cached;
}
return (this.shapeKeyToDefinition[hash] = ShapeDefinition.fromShortKey(hash));
}
/**
* Returns a item instance from a given short key
* @param {string} hash
* @returns {ShapeItem}
*/
getShapeItemFromShortKey(hash) {
const cached = this.shapeKeyToItem[hash];
if (cached) {
return cached;
}
const definition = this.getShapeFromShortKey(hash);
return (this.shapeKeyToItem[hash] = new ShapeItem(definition));
}
/**
* Returns a shape item for a given definition
* @param {ShapeDefinition} definition
* @returns {ShapeItem}
*/
getShapeItemFromDefinition(definition) {
return this.getShapeItemFromShortKey(definition.getHash());
}
/**
* Registers a new shape definition
* @param {ShapeDefinition} definition
*/
registerShapeDefinition(definition) {
const id = definition.getHash();
assert(!this.shapeKeyToDefinition[id], "Shape Definition " + id + " already exists");
this.shapeKeyToDefinition[id] = definition;
// logger.log("Registered shape with key", id);
}
/**
* Generates a definition for splitting a shape definition in two halfs
* @param {ShapeDefinition} definition
* @returns {[ShapeDefinition, ShapeDefinition]}
*/
shapeActionCutHalf(definition) {
const key = "cut:" + definition.getHash();
if (this.operationCache[key]) {
return /** @type {[ShapeDefinition, ShapeDefinition]} */ (this.operationCache[key]);
}
const rightSide = definition.cloneFilteredByQuadrants([2, 3]);
const leftSide = definition.cloneFilteredByQuadrants([0, 1]);
return /** @type {[ShapeDefinition, ShapeDefinition]} */ (this.operationCache[key] = [
this.registerOrReturnHandle(rightSide),
this.registerOrReturnHandle(leftSide),
]);
}
/**
* Generates a definition for splitting a shape definition in four quads
* @param {ShapeDefinition} definition
* @returns {[ShapeDefinition, ShapeDefinition, ShapeDefinition, ShapeDefinition]}
*/
shapeActionCutQuad(definition) {
const key = "cut-quad:" + definition.getHash();
if (this.operationCache[key]) {
return /** @type {[ShapeDefinition, ShapeDefinition, ShapeDefinition, ShapeDefinition]} */ (this
.operationCache[key]);
}
return /** @type {[ShapeDefinition, ShapeDefinition, ShapeDefinition, ShapeDefinition]} */ (this.operationCache[
key
] = [
this.registerOrReturnHandle(definition.cloneFilteredByQuadrants([0])),
this.registerOrReturnHandle(definition.cloneFilteredByQuadrants([1])),
this.registerOrReturnHandle(definition.cloneFilteredByQuadrants([2])),
this.registerOrReturnHandle(definition.cloneFilteredByQuadrants([3])),
]);
}
/**
* Generates a definition for rotating a shape clockwise
* @param {ShapeDefinition} definition
* @returns {ShapeDefinition}
*/
shapeActionRotateCW(definition) {
const key = "rotate-cw:" + definition.getHash();
if (this.operationCache[key]) {
return /** @type {ShapeDefinition} */ (this.operationCache[key]);
}
const rotated = definition.cloneRotateCW();
return /** @type {ShapeDefinition} */ (this.operationCache[key] = this.registerOrReturnHandle(
rotated
));
}
/**
* Generates a definition for rotating a shape counter clockwise
* @param {ShapeDefinition} definition
* @returns {ShapeDefinition}
*/
shapeActionRotateCCW(definition) {
const key = "rotate-ccw:" + definition.getHash();
if (this.operationCache[key]) {
return /** @type {ShapeDefinition} */ (this.operationCache[key]);
}
const rotated = definition.cloneRotateCCW();
return /** @type {ShapeDefinition} */ (this.operationCache[key] = this.registerOrReturnHandle(
rotated
));
}
/**
* Generates a definition for rotating a shape counter clockwise
* @param {ShapeDefinition} definition
* @returns {ShapeDefinition}
*/
shapeActionRotateFL(definition) {
const key = "rotate-fl:" + definition.getHash();
if (this.operationCache[key]) {
return /** @type {ShapeDefinition} */ (this.operationCache[key]);
}
const rotated = definition.cloneRotateFL();
return /** @type {ShapeDefinition} */ (this.operationCache[key] = this.registerOrReturnHandle(
rotated
));
}
/**
* Generates a definition for stacking the upper definition onto the lower one
* @param {ShapeDefinition} lowerDefinition
* @param {ShapeDefinition} upperDefinition
* @returns {ShapeDefinition}
*/
shapeActionStack(lowerDefinition, upperDefinition) {
const key = "stack:" + lowerDefinition.getHash() + ":" + upperDefinition.getHash();
if (this.operationCache[key]) {
return /** @type {ShapeDefinition} */ (this.operationCache[key]);
}
const stacked = lowerDefinition.cloneAndStackWith(upperDefinition);
return /** @type {ShapeDefinition} */ (this.operationCache[key] = this.registerOrReturnHandle(
stacked
));
}
/**
* Generates a definition for painting it with the given color
* @param {ShapeDefinition} definition
* @param {enumColors} color
* @returns {ShapeDefinition}
*/
shapeActionPaintWith(definition, color) {
const key = "paint:" + definition.getHash() + ":" + color;
if (this.operationCache[key]) {
return /** @type {ShapeDefinition} */ (this.operationCache[key]);
}
const colorized = definition.cloneAndPaintWith(color);
return /** @type {ShapeDefinition} */ (this.operationCache[key] = this.registerOrReturnHandle(
colorized
));
}
/**
* Generates a definition for painting it with the 4 colors
* @param {ShapeDefinition} definition
* @param {[enumColors, enumColors, enumColors, enumColors]} colors
* @returns {ShapeDefinition}
*/
shapeActionPaintWith4Colors(definition, colors) {
const key = "paint4:" + definition.getHash() + ":" + colors.join(",");
if (this.operationCache[key]) {
return /** @type {ShapeDefinition} */ (this.operationCache[key]);
}
const colorized = definition.cloneAndPaintWith4Colors(colors);
return /** @type {ShapeDefinition} */ (this.operationCache[key] = this.registerOrReturnHandle(
colorized
));
}
/**
* Checks if we already have cached this definition, and if so throws it away and returns the already
* cached variant
* @param {ShapeDefinition} definition
*/
registerOrReturnHandle(definition) {
const id = definition.getHash();
if (this.shapeKeyToDefinition[id]) {
return this.shapeKeyToDefinition[id];
}
this.shapeKeyToDefinition[id] = definition;
// logger.log("Registered shape with key (2)", id);
return definition;
}
/**
*
* @param {[enumSubShape, enumSubShape, enumSubShape, enumSubShape]} subShapes
* @returns {ShapeDefinition}
*/
getDefinitionFromSimpleShapes(subShapes, color = enumColors.uncolored) {
const shapeLayer = /** @type {import("./shape_definition").ShapeLayer} */ (subShapes.map(
subShape => ({ subShape, color })
));
return this.registerOrReturnHandle(new ShapeDefinition({ layers: [shapeLayer] }));
}
}
import { createLogger } from "../core/logging";
import { BasicSerializableObject } from "../savegame/serialization";
import { enumColors } from "./colors";
import { ShapeItem } from "./items/shape_item";
import { GameRoot } from "./root";
import { enumSubShape, ShapeDefinition } from "./shape_definition";
const logger = createLogger("shape_definition_manager");
export class ShapeDefinitionManager extends BasicSerializableObject {
static getId() {
return "ShapeDefinitionManager";
}
/**
*
* @param {GameRoot} root
*/
constructor(root) {
super();
this.root = root;
/**
* Store a cache from key -> definition
* @type {Object<string, ShapeDefinition>}
*/
this.shapeKeyToDefinition = {};
/**
* Store a cache from key -> item
*/
this.shapeKeyToItem = {};
// Caches operations in the form of 'operation:def1[:def2]'
/** @type {Object.<string, Array<ShapeDefinition>|ShapeDefinition>} */
this.operationCache = {};
}
/**
* Returns a shape instance from a given short key
* @param {string} hash
* @returns {ShapeDefinition}
*/
getShapeFromShortKey(hash) {
const cached = this.shapeKeyToDefinition[hash];
if (cached) {
return cached;
}
return (this.shapeKeyToDefinition[hash] = ShapeDefinition.fromShortKey(hash));
}
/**
* Returns a item instance from a given short key
* @param {string} hash
* @returns {ShapeItem}
*/
getShapeItemFromShortKey(hash) {
const cached = this.shapeKeyToItem[hash];
if (cached) {
return cached;
}
const definition = this.getShapeFromShortKey(hash);
return (this.shapeKeyToItem[hash] = new ShapeItem(definition));
}
/**
* Returns a shape item for a given definition
* @param {ShapeDefinition} definition
* @returns {ShapeItem}
*/
getShapeItemFromDefinition(definition) {
return this.getShapeItemFromShortKey(definition.getHash());
}
/**
* Registers a new shape definition
* @param {ShapeDefinition} definition
*/
registerShapeDefinition(definition) {
const id = definition.getHash();
assert(!this.shapeKeyToDefinition[id], "Shape Definition " + id + " already exists");
this.shapeKeyToDefinition[id] = definition;
// logger.log("Registered shape with key", id);
}
/**
* Generates a definition for splitting a shape definition in two halfs
* @param {ShapeDefinition} definition
* @returns {[ShapeDefinition, ShapeDefinition]}
*/
shapeActionCutHalf(definition) {
const key = "cut:" + definition.getHash();
if (this.operationCache[key]) {
return /** @type {[ShapeDefinition, ShapeDefinition]} */ (this.operationCache[key]);
}
const rightSide = definition.cloneFilteredByQuadrants([2, 3]);
const leftSide = definition.cloneFilteredByQuadrants([0, 1]);
return /** @type {[ShapeDefinition, ShapeDefinition]} */ (this.operationCache[key] = [
this.registerOrReturnHandle(rightSide),
this.registerOrReturnHandle(leftSide),
]);
}
/**
* Generates a definition for splitting a shape definition in four quads
* @param {ShapeDefinition} definition
* @returns {[ShapeDefinition, ShapeDefinition, ShapeDefinition, ShapeDefinition]}
*/
shapeActionCutQuad(definition) {
const key = "cut-quad:" + definition.getHash();
if (this.operationCache[key]) {
return /** @type {[ShapeDefinition, ShapeDefinition, ShapeDefinition, ShapeDefinition]} */ (this
.operationCache[key]);
}
return /** @type {[ShapeDefinition, ShapeDefinition, ShapeDefinition, ShapeDefinition]} */ (this.operationCache[
key
] = [
this.registerOrReturnHandle(definition.cloneFilteredByQuadrants([0])),
this.registerOrReturnHandle(definition.cloneFilteredByQuadrants([1])),
this.registerOrReturnHandle(definition.cloneFilteredByQuadrants([2])),
this.registerOrReturnHandle(definition.cloneFilteredByQuadrants([3])),
]);
}
/**
* Generates a definition for rotating a shape clockwise
* @param {ShapeDefinition} definition
* @returns {ShapeDefinition}
*/
shapeActionRotateCW(definition) {
const key = "rotate-cw:" + definition.getHash();
if (this.operationCache[key]) {
return /** @type {ShapeDefinition} */ (this.operationCache[key]);
}
const rotated = definition.cloneRotateCW();
return /** @type {ShapeDefinition} */ (this.operationCache[key] = this.registerOrReturnHandle(
rotated
));
}
/**
* Generates a definition for rotating a shape counter clockwise
* @param {ShapeDefinition} definition
* @returns {ShapeDefinition}
*/
shapeActionRotateCCW(definition) {
const key = "rotate-ccw:" + definition.getHash();
if (this.operationCache[key]) {
return /** @type {ShapeDefinition} */ (this.operationCache[key]);
}
const rotated = definition.cloneRotateCCW();
return /** @type {ShapeDefinition} */ (this.operationCache[key] = this.registerOrReturnHandle(
rotated
));
}
/**
* Generates a definition for rotating a shape FL
* @param {ShapeDefinition} definition
* @returns {ShapeDefinition}
*/
shapeActionRotate180(definition) {
const key = "rotate-fl:" + definition.getHash();
if (this.operationCache[key]) {
return /** @type {ShapeDefinition} */ (this.operationCache[key]);
}
const rotated = definition.cloneRotate180();
return /** @type {ShapeDefinition} */ (this.operationCache[key] = this.registerOrReturnHandle(
rotated
));
}
/**
* Generates a definition for stacking the upper definition onto the lower one
* @param {ShapeDefinition} lowerDefinition
* @param {ShapeDefinition} upperDefinition
* @returns {ShapeDefinition}
*/
shapeActionStack(lowerDefinition, upperDefinition) {
const key = "stack:" + lowerDefinition.getHash() + ":" + upperDefinition.getHash();
if (this.operationCache[key]) {
return /** @type {ShapeDefinition} */ (this.operationCache[key]);
}
const stacked = lowerDefinition.cloneAndStackWith(upperDefinition);
return /** @type {ShapeDefinition} */ (this.operationCache[key] = this.registerOrReturnHandle(
stacked
));
}
/**
* Generates a definition for painting it with the given color
* @param {ShapeDefinition} definition
* @param {enumColors} color
* @returns {ShapeDefinition}
*/
shapeActionPaintWith(definition, color) {
const key = "paint:" + definition.getHash() + ":" + color;
if (this.operationCache[key]) {
return /** @type {ShapeDefinition} */ (this.operationCache[key]);
}
const colorized = definition.cloneAndPaintWith(color);
return /** @type {ShapeDefinition} */ (this.operationCache[key] = this.registerOrReturnHandle(
colorized
));
}
/**
* Generates a definition for painting it with the 4 colors
* @param {ShapeDefinition} definition
* @param {[enumColors, enumColors, enumColors, enumColors]} colors
* @returns {ShapeDefinition}
*/
shapeActionPaintWith4Colors(definition, colors) {
const key = "paint4:" + definition.getHash() + ":" + colors.join(",");
if (this.operationCache[key]) {
return /** @type {ShapeDefinition} */ (this.operationCache[key]);
}
const colorized = definition.cloneAndPaintWith4Colors(colors);
return /** @type {ShapeDefinition} */ (this.operationCache[key] = this.registerOrReturnHandle(
colorized
));
}
/**
* Checks if we already have cached this definition, and if so throws it away and returns the already
* cached variant
* @param {ShapeDefinition} definition
*/
registerOrReturnHandle(definition) {
const id = definition.getHash();
if (this.shapeKeyToDefinition[id]) {
return this.shapeKeyToDefinition[id];
}
this.shapeKeyToDefinition[id] = definition;
// logger.log("Registered shape with key (2)", id);
return definition;
}
/**
*
* @param {[enumSubShape, enumSubShape, enumSubShape, enumSubShape]} subShapes
* @returns {ShapeDefinition}
*/
getDefinitionFromSimpleShapes(subShapes, color = enumColors.uncolored) {
const shapeLayer = /** @type {import("./shape_definition").ShapeLayer} */ (subShapes.map(
subShape => ({ subShape, color })
));
return this.registerOrReturnHandle(new ShapeDefinition({ layers: [shapeLayer] }));
}
}

View File

@ -16,9 +16,56 @@ import { ShapeItem } from "../items/shape_item";
*/
const MAX_QUEUED_CHARGES = 2;
/**
* Whole data for a produced item
*
* @typedef {{
* item: BaseItem,
* preferredSlot?: number,
* requiredSlot?: number,
* doNotTrack?: boolean
* }} ProducedItem
*/
/**
* Type of a processor implementation
* @typedef {{
* entity: Entity,
* items: Array<{ item: BaseItem, sourceSlot: number }>,
* itemsBySlot: Object<number, BaseItem>,
* outItems: Array<ProducedItem>
* }} ProcessorImplementationPayload
*/
export class ItemProcessorSystem extends GameSystemWithFilter {
constructor(root) {
super(root, [ItemProcessorComponent]);
/**
* @type {Object<enumItemProcessorTypes, function(ProcessorImplementationPayload) : string>}
*/
this.handlers = {
[enumItemProcessorTypes.splitter]: this.process_SPLITTER,
[enumItemProcessorTypes.cutter]: this.process_CUTTER,
[enumItemProcessorTypes.cutterQuad]: this.process_CUTTER_QUAD,
[enumItemProcessorTypes.rotater]: this.process_ROTATER,
[enumItemProcessorTypes.rotaterCCW]: this.process_ROTATER_CCW,
[enumItemProcessorTypes.rotater180]: this.process_ROTATER_180,
[enumItemProcessorTypes.stacker]: this.process_STACKER,
[enumItemProcessorTypes.trash]: this.process_TRASH,
[enumItemProcessorTypes.mixer]: this.process_MIXER,
[enumItemProcessorTypes.painter]: this.process_PAINTER,
[enumItemProcessorTypes.painterDouble]: this.process_PAINTER_DOUBLE,
[enumItemProcessorTypes.painterQuad]: this.process_PAINTER_QUAD,
[enumItemProcessorTypes.hub]: this.process_HUB,
[enumItemProcessorTypes.filter]: this.process_FILTER,
[enumItemProcessorTypes.reader]: this.process_READER,
};
// Bind all handlers
for (const key in this.handlers) {
this.handlers[key] = this.handlers[key].bind(this);
}
}
update() {
@ -238,310 +285,30 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
const items = processorComp.inputSlots;
processorComp.inputSlots = [];
/** @type {Object.<string, { item: BaseItem, sourceSlot: number }>} */
/** @type {Object<string, BaseItem>} */
const itemsBySlot = {};
for (let i = 0; i < items.length; ++i) {
itemsBySlot[items[i].sourceSlot] = items[i];
itemsBySlot[items[i].sourceSlot] = items[i].item;
}
/** @type {Array<{item: BaseItem, requiredSlot?: number, preferredSlot?: number}>} */
/** @type {Array<ProducedItem>} */
const outItems = [];
// Whether to track the production towards the analytics
let trackProduction = true;
/** @type {function(ProcessorImplementationPayload) : void} */
const handler = this.handlers[processorComp.type];
assert(handler, "No handler for processor type defined: " + processorComp.type);
// DO SOME MAGIC
switch (processorComp.type) {
// SPLITTER
case enumItemProcessorTypes.splitterWires:
case enumItemProcessorTypes.splitter: {
trackProduction = false;
const availableSlots = entity.components.ItemEjector.slots.length;
let nextSlot = processorComp.nextOutputSlot++ % availableSlots;
for (let i = 0; i < items.length; ++i) {
outItems.push({
item: items[i].item,
preferredSlot: (nextSlot + i) % availableSlots,
});
}
break;
}
// CUTTER
case enumItemProcessorTypes.cutter: {
const inputItem = /** @type {ShapeItem} */ (items[0].item);
assert(inputItem instanceof ShapeItem, "Input for cut is not a shape");
const inputDefinition = inputItem.definition;
const cutDefinitions = this.root.shapeDefinitionMgr.shapeActionCutHalf(inputDefinition);
for (let i = 0; i < cutDefinitions.length; ++i) {
const definition = cutDefinitions[i];
if (!definition.isEntirelyEmpty()) {
outItems.push({
item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(definition),
requiredSlot: i,
});
}
}
break;
}
// CUTTER (Quad)
case enumItemProcessorTypes.cutterQuad: {
const inputItem = /** @type {ShapeItem} */ (items[0].item);
assert(inputItem instanceof ShapeItem, "Input for cut is not a shape");
const inputDefinition = inputItem.definition;
const cutDefinitions = this.root.shapeDefinitionMgr.shapeActionCutQuad(inputDefinition);
for (let i = 0; i < cutDefinitions.length; ++i) {
const definition = cutDefinitions[i];
if (!definition.isEntirelyEmpty()) {
outItems.push({
item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(definition),
requiredSlot: i,
});
}
}
break;
}
// ROTATER
case enumItemProcessorTypes.rotater: {
const inputItem = /** @type {ShapeItem} */ (items[0].item);
assert(inputItem instanceof ShapeItem, "Input for rotation is not a shape");
const inputDefinition = inputItem.definition;
const rotatedDefinition = this.root.shapeDefinitionMgr.shapeActionRotateCW(inputDefinition);
outItems.push({
item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(rotatedDefinition),
});
break;
}
// ROTATER (CCW)
case enumItemProcessorTypes.rotaterCCW: {
const inputItem = /** @type {ShapeItem} */ (items[0].item);
assert(inputItem instanceof ShapeItem, "Input for rotation is not a shape");
const inputDefinition = inputItem.definition;
const rotatedDefinition = this.root.shapeDefinitionMgr.shapeActionRotateCCW(inputDefinition);
outItems.push({
item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(rotatedDefinition),
});
break;
}
// ROTATER (FL)
case enumItemProcessorTypes.rotaterFL: {
const inputItem = /** @type {ShapeItem} */ (items[0].item);
assert(inputItem instanceof ShapeItem, "Input for rotation is not a shape");
const inputDefinition = inputItem.definition;
const rotatedDefinition = this.root.shapeDefinitionMgr.shapeActionRotateFL(inputDefinition);
outItems.push({
item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(rotatedDefinition),
});
break;
}
// STACKER
case enumItemProcessorTypes.stacker: {
const lowerItem = /** @type {ShapeItem} */ (itemsBySlot[0].item);
const upperItem = /** @type {ShapeItem} */ (itemsBySlot[1].item);
assert(lowerItem instanceof ShapeItem, "Input for lower stack is not a shape");
assert(upperItem instanceof ShapeItem, "Input for upper stack is not a shape");
const stackedDefinition = this.root.shapeDefinitionMgr.shapeActionStack(
lowerItem.definition,
upperItem.definition
);
outItems.push({
item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(stackedDefinition),
});
break;
}
// TRASH
case enumItemProcessorTypes.trash: {
// Well this one is easy .. simply do nothing with the item
break;
}
// MIXER
case enumItemProcessorTypes.mixer: {
// Find both colors and combine them
const item1 = /** @type {ColorItem} */ (items[0].item);
const item2 = /** @type {ColorItem} */ (items[1].item);
assert(item1 instanceof ColorItem, "Input for color mixer is not a color");
assert(item2 instanceof ColorItem, "Input for color mixer is not a color");
const color1 = item1.color;
const color2 = item2.color;
// Try finding mixer color, and if we can't mix it we simply return the same color
const mixedColor = enumColorMixingResults[color1][color2];
let resultColor = color1;
if (mixedColor) {
resultColor = mixedColor;
}
outItems.push({
item: COLOR_ITEM_SINGLETONS[resultColor],
});
break;
}
// PAINTER
case enumItemProcessorTypes.painter: {
const shapeItem = /** @type {ShapeItem} */ (itemsBySlot[0].item);
const colorItem = /** @type {ColorItem} */ (itemsBySlot[1].item);
const colorizedDefinition = this.root.shapeDefinitionMgr.shapeActionPaintWith(
shapeItem.definition,
colorItem.color
);
outItems.push({
item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(colorizedDefinition),
});
break;
}
// PAINTER (DOUBLE)
case enumItemProcessorTypes.painterDouble: {
const shapeItem1 = /** @type {ShapeItem} */ (itemsBySlot[0].item);
const shapeItem2 = /** @type {ShapeItem} */ (itemsBySlot[1].item);
const colorItem = /** @type {ColorItem} */ (itemsBySlot[2].item);
assert(shapeItem1 instanceof ShapeItem, "Input for painter is not a shape");
assert(shapeItem2 instanceof ShapeItem, "Input for painter is not a shape");
assert(colorItem instanceof ColorItem, "Input for painter is not a color");
const colorizedDefinition1 = this.root.shapeDefinitionMgr.shapeActionPaintWith(
shapeItem1.definition,
colorItem.color
);
const colorizedDefinition2 = this.root.shapeDefinitionMgr.shapeActionPaintWith(
shapeItem2.definition,
colorItem.color
);
outItems.push({
item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(colorizedDefinition1),
});
outItems.push({
item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(colorizedDefinition2),
});
break;
}
// PAINTER (QUAD)
case enumItemProcessorTypes.painterQuad: {
const shapeItem = /** @type {ShapeItem} */ (itemsBySlot[0].item);
assert(shapeItem instanceof ShapeItem, "Input for painter is not a shape");
/** @type {Array<enumColors>} */
const colors = [null, null, null, null];
for (let i = 0; i < 4; ++i) {
if (itemsBySlot[i + 1]) {
colors[i] = /** @type {ColorItem} */ (itemsBySlot[i + 1].item).color;
}
}
const colorizedDefinition = this.root.shapeDefinitionMgr.shapeActionPaintWith4Colors(
shapeItem.definition,
/** @type {[string, string, string, string]} */ (colors)
);
outItems.push({
item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(colorizedDefinition),
});
break;
}
// FILTER
case enumItemProcessorTypes.filter: {
// TODO
trackProduction = false;
const item = itemsBySlot[0].item;
const network = entity.components.WiredPins.slots[0].linkedNetwork;
if (!network || !network.currentValue) {
outItems.push({
item,
requiredSlot: 1,
});
break;
}
const value = network.currentValue;
if (value.equals(BOOL_TRUE_SINGLETON) || value.equals(item)) {
outItems.push({
item,
requiredSlot: 0,
});
} else {
outItems.push({
item,
requiredSlot: 1,
});
}
break;
}
// READER
case enumItemProcessorTypes.reader: {
// Pass through the item
const item = itemsBySlot[0].item;
outItems.push({ item });
// Track the item
const readerComp = entity.components.BeltReader;
readerComp.lastItemTimes.push(this.root.time.now());
readerComp.lastItem = item;
break;
}
// HUB
case enumItemProcessorTypes.hub: {
trackProduction = false;
const hubComponent = entity.components.Hub;
assert(hubComponent, "Hub item processor has no hub component");
for (let i = 0; i < items.length; ++i) {
const item = /** @type {ShapeItem} */ (items[i].item);
this.root.hubGoals.handleDefinitionDelivered(item.definition);
}
break;
}
default:
assertAlways(false, "Unkown item processor type: " + processorComp.type);
}
// Call implementation
handler({
entity,
items,
itemsBySlot,
outItems,
});
// Track produced items
if (trackProduction) {
for (let i = 0; i < outItems.length; ++i) {
for (let i = 0; i < outItems.length; ++i) {
if (!outItems[i].doNotTrack) {
this.root.signals.itemProduced.dispatch(outItems[i].item);
}
}
@ -559,4 +326,292 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
remainingTime: timeToProcess,
});
}
/**
* @param {ProcessorImplementationPayload} payload
*/
process_SPLITTER(payload) {
// trackProduction = false;
const availableSlots = payload.entity.components.ItemEjector.slots.length;
const processorComp = payload.entity.components.ItemProcessor;
const nextSlot = processorComp.nextOutputSlot++ % availableSlots;
for (let i = 0; i < payload.items.length; ++i) {
payload.outItems.push({
item: payload.items[i].item,
preferredSlot: (nextSlot + i) % availableSlots,
doNotTrack: true,
});
}
return true;
}
/**
* @param {ProcessorImplementationPayload} payload
*/
process_CUTTER(payload) {
const inputItem = /** @type {ShapeItem} */ (payload.items[0].item);
assert(inputItem instanceof ShapeItem, "Input for cut is not a shape");
const inputDefinition = inputItem.definition;
const cutDefinitions = this.root.shapeDefinitionMgr.shapeActionCutHalf(inputDefinition);
for (let i = 0; i < cutDefinitions.length; ++i) {
const definition = cutDefinitions[i];
if (!definition.isEntirelyEmpty()) {
payload.outItems.push({
item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(definition),
requiredSlot: i,
});
}
}
}
/**
* @param {ProcessorImplementationPayload} payload
*/
process_CUTTER_QUAD(payload) {
const inputItem = /** @type {ShapeItem} */ (payload.items[0].item);
assert(inputItem instanceof ShapeItem, "Input for cut is not a shape");
const inputDefinition = inputItem.definition;
const cutDefinitions = this.root.shapeDefinitionMgr.shapeActionCutQuad(inputDefinition);
for (let i = 0; i < cutDefinitions.length; ++i) {
const definition = cutDefinitions[i];
if (!definition.isEntirelyEmpty()) {
payload.outItems.push({
item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(definition),
requiredSlot: i,
});
}
}
}
/**
* @param {ProcessorImplementationPayload} payload
*/
process_ROTATER(payload) {
const inputItem = /** @type {ShapeItem} */ (payload.items[0].item);
assert(inputItem instanceof ShapeItem, "Input for rotation is not a shape");
const inputDefinition = inputItem.definition;
const rotatedDefinition = this.root.shapeDefinitionMgr.shapeActionRotateCW(inputDefinition);
payload.outItems.push({
item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(rotatedDefinition),
});
}
/**
* @param {ProcessorImplementationPayload} payload
*/
process_ROTATER_CCW(payload) {
const inputItem = /** @type {ShapeItem} */ (payload.items[0].item);
assert(inputItem instanceof ShapeItem, "Input for rotation is not a shape");
const inputDefinition = inputItem.definition;
const rotatedDefinition = this.root.shapeDefinitionMgr.shapeActionRotateCCW(inputDefinition);
payload.outItems.push({
item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(rotatedDefinition),
});
}
/**
* @param {ProcessorImplementationPayload} payload
*/
process_ROTATER_180(payload) {
const inputItem = /** @type {ShapeItem} */ (payload.items[0].item);
assert(inputItem instanceof ShapeItem, "Input for rotation is not a shape");
const inputDefinition = inputItem.definition;
const rotatedDefinition = this.root.shapeDefinitionMgr.shapeActionRotate180(inputDefinition);
payload.outItems.push({
item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(rotatedDefinition),
});
}
/**
* @param {ProcessorImplementationPayload} payload
*/
process_STACKER(payload) {
const lowerItem = /** @type {ShapeItem} */ (payload.itemsBySlot[0]);
const upperItem = /** @type {ShapeItem} */ (payload.itemsBySlot[1]);
assert(lowerItem instanceof ShapeItem, "Input for lower stack is not a shape");
assert(upperItem instanceof ShapeItem, "Input for upper stack is not a shape");
const stackedDefinition = this.root.shapeDefinitionMgr.shapeActionStack(
lowerItem.definition,
upperItem.definition
);
payload.outItems.push({
item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(stackedDefinition),
});
}
/**
* @param {ProcessorImplementationPayload} payload
*/
process_TRASH(payload) {
// Do nothing ..
}
/**
* @param {ProcessorImplementationPayload} payload
*/
process_MIXER(payload) {
// Find both colors and combine them
const item1 = /** @type {ColorItem} */ (payload.items[0].item);
const item2 = /** @type {ColorItem} */ (payload.items[1].item);
assert(item1 instanceof ColorItem, "Input for color mixer is not a color");
assert(item2 instanceof ColorItem, "Input for color mixer is not a color");
const color1 = item1.color;
const color2 = item2.color;
// Try finding mixer color, and if we can't mix it we simply return the same color
const mixedColor = enumColorMixingResults[color1][color2];
let resultColor = color1;
if (mixedColor) {
resultColor = mixedColor;
}
payload.outItems.push({
item: COLOR_ITEM_SINGLETONS[resultColor],
});
}
/**
* @param {ProcessorImplementationPayload} payload
*/
process_PAINTER(payload) {
const shapeItem = /** @type {ShapeItem} */ (payload.itemsBySlot[0]);
const colorItem = /** @type {ColorItem} */ (payload.itemsBySlot[1]);
const colorizedDefinition = this.root.shapeDefinitionMgr.shapeActionPaintWith(
shapeItem.definition,
colorItem.color
);
payload.outItems.push({
item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(colorizedDefinition),
});
}
/**
* @param {ProcessorImplementationPayload} payload
*/
process_PAINTER_DOUBLE(payload) {
const shapeItem1 = /** @type {ShapeItem} */ (payload.itemsBySlot[0]);
const shapeItem2 = /** @type {ShapeItem} */ (payload.itemsBySlot[1]);
const colorItem = /** @type {ColorItem} */ (payload.itemsBySlot[2]);
assert(shapeItem1 instanceof ShapeItem, "Input for painter is not a shape");
assert(shapeItem2 instanceof ShapeItem, "Input for painter is not a shape");
assert(colorItem instanceof ColorItem, "Input for painter is not a color");
const colorizedDefinition1 = this.root.shapeDefinitionMgr.shapeActionPaintWith(
shapeItem1.definition,
colorItem.color
);
const colorizedDefinition2 = this.root.shapeDefinitionMgr.shapeActionPaintWith(
shapeItem2.definition,
colorItem.color
);
payload.outItems.push({
item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(colorizedDefinition1),
});
payload.outItems.push({
item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(colorizedDefinition2),
});
}
/**
* @param {ProcessorImplementationPayload} payload
*/
process_PAINTER_QUAD(payload) {
const shapeItem = /** @type {ShapeItem} */ (payload.itemsBySlot[0]);
assert(shapeItem instanceof ShapeItem, "Input for painter is not a shape");
/** @type {Array<enumColors>} */
const colors = [null, null, null, null];
for (let i = 0; i < 4; ++i) {
if (payload.itemsBySlot[i + 1]) {
colors[i] = /** @type {ColorItem} */ (payload.itemsBySlot[i + 1]).color;
}
}
const colorizedDefinition = this.root.shapeDefinitionMgr.shapeActionPaintWith4Colors(
shapeItem.definition,
/** @type {[string, string, string, string]} */ (colors)
);
payload.outItems.push({
item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(colorizedDefinition),
});
}
/**
* @param {ProcessorImplementationPayload} payload
*/
process_FILTER(payload) {
const item = payload.itemsBySlot[0];
const network = payload.entity.components.WiredPins.slots[0].linkedNetwork;
if (!network || !network.currentValue) {
payload.outItems.push({
item,
requiredSlot: 1,
doNotTrack: true,
});
return;
}
const value = network.currentValue;
if (value.equals(BOOL_TRUE_SINGLETON) || value.equals(item)) {
payload.outItems.push({
item,
requiredSlot: 0,
doNotTrack: true,
});
} else {
payload.outItems.push({
item,
requiredSlot: 1,
doNotTrack: true,
});
}
}
/**
* @param {ProcessorImplementationPayload} payload
*/
process_READER(payload) {
// Pass through the item
const item = payload.itemsBySlot[0];
payload.outItems.push({
item,
doNotTrack: true,
});
// Track the item
const readerComp = payload.entity.components.BeltReader;
readerComp.lastItemTimes.push(this.root.time.now());
readerComp.lastItem = item;
}
/**
* @param {ProcessorImplementationPayload} payload
*/
process_HUB(payload) {
const hubComponent = payload.entity.components.Hub;
assert(hubComponent, "Hub item processor has no hub component");
for (let i = 0; i < payload.items.length; ++i) {
const item = /** @type {ShapeItem} */ (payload.items[i].item);
this.root.hubGoals.handleDefinitionDelivered(item.definition);
}
}
}

View File

@ -15,7 +15,7 @@ export const enumHubGoalRewards = {
reward_tunnel: "reward_tunnel",
reward_rotater_ccw: "reward_rotater_ccw",
reward_rotater_fl: "reward_rotater_fl",
reward_rotater_180: "reward_rotater_fl",
reward_miner_chainable: "reward_miner_chainable",
reward_underground_belt_tier_2: "reward_underground_belt_tier_2",
reward_splitter_compact: "reward_splitter_compact",

View File

@ -1,52 +1,52 @@
import { MetaBuilding, defaultBuildingVariant } from "./meta_building";
import { MetaCutterBuilding, enumCutterVariants } from "./buildings/cutter";
import { MetaRotaterBuilding, enumRotaterVariants } from "./buildings/rotater";
import { MetaPainterBuilding, enumPainterVariants } from "./buildings/painter";
import { MetaMixerBuilding } from "./buildings/mixer";
import { MetaStackerBuilding } from "./buildings/stacker";
import { MetaSplitterBuilding, enumSplitterVariants } from "./buildings/splitter";
import { MetaUndergroundBeltBuilding, enumUndergroundBeltVariants } from "./buildings/underground_belt";
import { MetaMinerBuilding, enumMinerVariants } from "./buildings/miner";
import { MetaTrashBuilding, enumTrashVariants } from "./buildings/trash";
/** @typedef {Array<[typeof MetaBuilding, string]>} TutorialGoalReward */
import { enumHubGoalRewards } from "./tutorial_goals";
/**
* Helper method for proper types
* @returns {TutorialGoalReward}
*/
const typed = x => x;
/**
* Stores which reward unlocks what
* @enum {TutorialGoalReward?}
*/
export const enumHubGoalRewardsToContentUnlocked = {
[enumHubGoalRewards.reward_cutter_and_trash]: typed([[MetaCutterBuilding, defaultBuildingVariant]]),
[enumHubGoalRewards.reward_rotater]: typed([[MetaRotaterBuilding, defaultBuildingVariant]]),
[enumHubGoalRewards.reward_painter]: typed([[MetaPainterBuilding, defaultBuildingVariant]]),
[enumHubGoalRewards.reward_mixer]: typed([[MetaMixerBuilding, defaultBuildingVariant]]),
[enumHubGoalRewards.reward_stacker]: typed([[MetaStackerBuilding, defaultBuildingVariant]]),
[enumHubGoalRewards.reward_splitter]: typed([[MetaSplitterBuilding, defaultBuildingVariant]]),
[enumHubGoalRewards.reward_tunnel]: typed([[MetaUndergroundBeltBuilding, defaultBuildingVariant]]),
[enumHubGoalRewards.reward_rotater_ccw]: typed([[MetaRotaterBuilding, enumRotaterVariants.ccw]]),
[enumHubGoalRewards.reward_rotater_fl]: typed([[MetaRotaterBuilding, enumRotaterVariants.fl]]),
[enumHubGoalRewards.reward_miner_chainable]: typed([[MetaMinerBuilding, enumMinerVariants.chainable]]),
[enumHubGoalRewards.reward_underground_belt_tier_2]: typed([
[MetaUndergroundBeltBuilding, enumUndergroundBeltVariants.tier2],
]),
[enumHubGoalRewards.reward_splitter_compact]: typed([
[MetaSplitterBuilding, enumSplitterVariants.compact],
]),
[enumHubGoalRewards.reward_cutter_quad]: typed([[MetaCutterBuilding, enumCutterVariants.quad]]),
[enumHubGoalRewards.reward_painter_double]: typed([[MetaPainterBuilding, enumPainterVariants.double]]),
[enumHubGoalRewards.reward_painter_quad]: typed([[MetaPainterBuilding, enumPainterVariants.quad]]),
[enumHubGoalRewards.reward_storage]: typed([[MetaTrashBuilding, enumTrashVariants.storage]]),
[enumHubGoalRewards.reward_freeplay]: null,
[enumHubGoalRewards.no_reward]: null,
[enumHubGoalRewards.no_reward_freeplay]: null,
};
import { MetaBuilding, defaultBuildingVariant } from "./meta_building";
import { MetaCutterBuilding, enumCutterVariants } from "./buildings/cutter";
import { MetaRotaterBuilding, enumRotaterVariants } from "./buildings/rotater";
import { MetaPainterBuilding, enumPainterVariants } from "./buildings/painter";
import { MetaMixerBuilding } from "./buildings/mixer";
import { MetaStackerBuilding } from "./buildings/stacker";
import { MetaSplitterBuilding, enumSplitterVariants } from "./buildings/splitter";
import { MetaUndergroundBeltBuilding, enumUndergroundBeltVariants } from "./buildings/underground_belt";
import { MetaMinerBuilding, enumMinerVariants } from "./buildings/miner";
import { MetaTrashBuilding, enumTrashVariants } from "./buildings/trash";
/** @typedef {Array<[typeof MetaBuilding, string]>} TutorialGoalReward */
import { enumHubGoalRewards } from "./tutorial_goals";
/**
* Helper method for proper types
* @returns {TutorialGoalReward}
*/
const typed = x => x;
/**
* Stores which reward unlocks what
* @enum {TutorialGoalReward?}
*/
export const enumHubGoalRewardsToContentUnlocked = {
[enumHubGoalRewards.reward_cutter_and_trash]: typed([[MetaCutterBuilding, defaultBuildingVariant]]),
[enumHubGoalRewards.reward_rotater]: typed([[MetaRotaterBuilding, defaultBuildingVariant]]),
[enumHubGoalRewards.reward_painter]: typed([[MetaPainterBuilding, defaultBuildingVariant]]),
[enumHubGoalRewards.reward_mixer]: typed([[MetaMixerBuilding, defaultBuildingVariant]]),
[enumHubGoalRewards.reward_stacker]: typed([[MetaStackerBuilding, defaultBuildingVariant]]),
[enumHubGoalRewards.reward_splitter]: typed([[MetaSplitterBuilding, defaultBuildingVariant]]),
[enumHubGoalRewards.reward_tunnel]: typed([[MetaUndergroundBeltBuilding, defaultBuildingVariant]]),
[enumHubGoalRewards.reward_rotater_ccw]: typed([[MetaRotaterBuilding, enumRotaterVariants.ccw]]),
[enumHubGoalRewards.reward_rotater_180]: typed([[MetaRotaterBuilding, enumRotaterVariants.rotate180]]),
[enumHubGoalRewards.reward_miner_chainable]: typed([[MetaMinerBuilding, enumMinerVariants.chainable]]),
[enumHubGoalRewards.reward_underground_belt_tier_2]: typed([
[MetaUndergroundBeltBuilding, enumUndergroundBeltVariants.tier2],
]),
[enumHubGoalRewards.reward_splitter_compact]: typed([
[MetaSplitterBuilding, enumSplitterVariants.compact],
]),
[enumHubGoalRewards.reward_cutter_quad]: typed([[MetaCutterBuilding, enumCutterVariants.quad]]),
[enumHubGoalRewards.reward_painter_double]: typed([[MetaPainterBuilding, enumPainterVariants.double]]),
[enumHubGoalRewards.reward_painter_quad]: typed([[MetaPainterBuilding, enumPainterVariants.quad]]),
[enumHubGoalRewards.reward_storage]: typed([[MetaTrashBuilding, enumTrashVariants.storage]]),
[enumHubGoalRewards.reward_freeplay]: null,
[enumHubGoalRewards.no_reward]: null,
[enumHubGoalRewards.no_reward_freeplay]: null,
};

View File

@ -2,5 +2,6 @@
"compilerOptions": {
"target": "es6",
"checkJs": true
}
},
"include": ["./**/*.js"]
}

View File

@ -518,7 +518,7 @@ buildings:
ccw:
name: Rotate (CCW)
description: Rotates shapes counter-clockwise by 90 degrees.
fl:
rotate180:
name: Rotate (180)
description: Rotates shapes by 180 degrees.