1
0
mirror of https://github.com/tobspr/shapez.io.git synced 2026-03-02 03:39:21 +00:00

Add 1x1 compact splitters

This commit is contained in:
tobspr
2020-08-28 21:28:29 +02:00
parent b6328cd9a0
commit 5459e6470b
16 changed files with 844 additions and 695 deletions

View File

@@ -18,6 +18,7 @@ export const CHANGELOG = [
"Updated and added new translations (Thanks to all contributors!)",
"Added setting to be able to delete buildings while placing (inspired by hexy)",
"Mark pinned shapes in statistics dialog and show them first (inspired by davidburhans)",
"There are now compact 1x1 splitters available to be unlocked!",
"Allow editing waypoints (by isaisstillalive)",
"Show confirmation when cutting area which is too expensive to get pasted again (by isaisstillalive)",
"Show mouse and camera tile on debug overlay (F4) (by dengr)",

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 enumCutterVariants = { quad: "quad" };
export class MetaCutterBuilding extends MetaBuilding {
constructor() {
super("cutter");
}
getSilhouetteColor() {
return "#7dcda2";
}
getDimensions(variant) {
switch (variant) {
case defaultBuildingVariant:
return new Vector(2, 1);
case enumCutterVariants.quad:
return new Vector(4, 1);
default:
assertAlways(false, "Unknown splitter variant: " + variant);
}
}
/**
* @param {GameRoot} root
* @param {string} variant
* @returns {Array<[string, string]>}
*/
getAdditionalStatistics(root, variant) {
const speed = root.hubGoals.getProcessorBaseSpeed(
variant === enumCutterVariants.quad
? enumItemProcessorTypes.cutterQuad
: enumItemProcessorTypes.cutter
);
return [[T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(speed)]];
}
/**
* @param {GameRoot} root
*/
getAvailableVariants(root) {
if (root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_cutter_quad)) {
return [defaultBuildingVariant, enumCutterVariants.quad];
}
return super.getAvailableVariants(root);
}
/**
* @param {GameRoot} root
*/
getIsUnlocked(root) {
return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_cutter_and_trash);
}
/**
* Creates the entity at the given location
* @param {Entity} entity
*/
setupEntityComponents(entity) {
entity.addComponent(
new ItemProcessorComponent({
inputsPerCharge: 1,
processorType: enumItemProcessorTypes.cutter,
})
);
entity.addComponent(new ItemEjectorComponent({}));
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.ItemEjector.setSlots([
{ pos: new Vector(0, 0), direction: enumDirection.top },
{ pos: new Vector(1, 0), direction: enumDirection.top },
]);
entity.components.ItemProcessor.type = enumItemProcessorTypes.cutter;
break;
}
case enumCutterVariants.quad: {
entity.components.ItemEjector.setSlots([
{ pos: new Vector(0, 0), direction: enumDirection.top },
{ pos: new Vector(1, 0), direction: enumDirection.top },
{ pos: new Vector(2, 0), direction: enumDirection.top },
{ pos: new Vector(3, 0), direction: enumDirection.top },
]);
entity.components.ItemProcessor.type = enumItemProcessorTypes.cutterQuad;
break;
}
default:
assertAlways(false, "Unknown painter 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 enumCutterVariants = { quad: "quad" };
export class MetaCutterBuilding extends MetaBuilding {
constructor() {
super("cutter");
}
getSilhouetteColor() {
return "#7dcda2";
}
getDimensions(variant) {
switch (variant) {
case defaultBuildingVariant:
return new Vector(2, 1);
case enumCutterVariants.quad:
return new Vector(4, 1);
default:
assertAlways(false, "Unknown cutter variant: " + variant);
}
}
/**
* @param {GameRoot} root
* @param {string} variant
* @returns {Array<[string, string]>}
*/
getAdditionalStatistics(root, variant) {
const speed = root.hubGoals.getProcessorBaseSpeed(
variant === enumCutterVariants.quad
? enumItemProcessorTypes.cutterQuad
: enumItemProcessorTypes.cutter
);
return [[T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(speed)]];
}
/**
* @param {GameRoot} root
*/
getAvailableVariants(root) {
if (root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_cutter_quad)) {
return [defaultBuildingVariant, enumCutterVariants.quad];
}
return super.getAvailableVariants(root);
}
/**
* @param {GameRoot} root
*/
getIsUnlocked(root) {
return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_cutter_and_trash);
}
/**
* Creates the entity at the given location
* @param {Entity} entity
*/
setupEntityComponents(entity) {
entity.addComponent(
new ItemProcessorComponent({
inputsPerCharge: 1,
processorType: enumItemProcessorTypes.cutter,
})
);
entity.addComponent(new ItemEjectorComponent({}));
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.ItemEjector.setSlots([
{ pos: new Vector(0, 0), direction: enumDirection.top },
{ pos: new Vector(1, 0), direction: enumDirection.top },
]);
entity.components.ItemProcessor.type = enumItemProcessorTypes.cutter;
break;
}
case enumCutterVariants.quad: {
entity.components.ItemEjector.setSlots([
{ pos: new Vector(0, 0), direction: enumDirection.top },
{ pos: new Vector(1, 0), direction: enumDirection.top },
{ pos: new Vector(2, 0), direction: enumDirection.top },
{ pos: new Vector(3, 0), direction: enumDirection.top },
]);
entity.components.ItemProcessor.type = enumItemProcessorTypes.cutterQuad;
break;
}
default:
assertAlways(false, "Unknown painter variant: " + variant);
}
}
}

View File

@@ -1,158 +1,197 @@
import { enumDirection, Vector } from "../../core/vector";
import { ItemAcceptorComponent } from "../components/item_acceptor";
import { ItemEjectorComponent } from "../components/item_ejector";
import { enumItemProcessorTypes, ItemProcessorComponent } from "../components/item_processor";
import { Entity } from "../entity";
import { MetaBuilding, defaultBuildingVariant } from "../meta_building";
import { GameRoot } from "../root";
import { enumHubGoalRewards } from "../tutorial_goals";
import { T } from "../../translations";
import { formatItemsPerSecond } from "../../core/utils";
import { BeltUnderlaysComponent } from "../components/belt_underlays";
/** @enum {string} */
export const enumSplitterVariants = { compact: "compact", compactInverse: "compact-inverse" };
export class MetaSplitterBuilding extends MetaBuilding {
constructor() {
super("splitter");
}
getDimensions(variant) {
switch (variant) {
case defaultBuildingVariant:
return new Vector(2, 1);
case enumSplitterVariants.compact:
case enumSplitterVariants.compactInverse:
return new Vector(1, 1);
default:
assertAlways(false, "Unknown splitter variant: " + variant);
}
}
/**
* @param {GameRoot} root
* @param {string} variant
* @returns {Array<[string, string]>}
*/
getAdditionalStatistics(root, variant) {
const speed = root.hubGoals.getProcessorBaseSpeed(enumItemProcessorTypes.splitter);
return [[T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(speed)]];
}
getSilhouetteColor() {
return "#444";
}
/**
* @param {GameRoot} root
*/
getAvailableVariants(root) {
if (root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_splitter_compact)) {
return [
defaultBuildingVariant,
enumSplitterVariants.compact,
enumSplitterVariants.compactInverse,
];
}
return super.getAvailableVariants(root);
}
/**
* @param {GameRoot} root
*/
getIsUnlocked(root) {
return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_splitter);
}
/**
* Creates the entity at the given location
* @param {Entity} entity
*/
setupEntityComponents(entity) {
entity.addComponent(
new ItemAcceptorComponent({
slots: [], // set later
})
);
entity.addComponent(
new ItemProcessorComponent({
inputsPerCharge: 1,
processorType: enumItemProcessorTypes.splitter,
})
);
entity.addComponent(
new ItemEjectorComponent({
slots: [], // set later
})
);
entity.addComponent(new BeltUnderlaysComponent({ underlays: [] }));
}
/**
*
* @param {Entity} entity
* @param {number} rotationVariant
* @param {string} variant
*/
updateVariants(entity, rotationVariant, variant) {
switch (variant) {
case defaultBuildingVariant: {
entity.components.ItemAcceptor.setSlots([
{
pos: new Vector(0, 0),
directions: [enumDirection.bottom],
},
{
pos: new Vector(1, 0),
directions: [enumDirection.bottom],
},
]);
entity.components.ItemEjector.setSlots([
{ pos: new Vector(0, 0), direction: enumDirection.top },
{ pos: new Vector(1, 0), direction: enumDirection.top },
]);
entity.components.BeltUnderlays.underlays = [
{ pos: new Vector(0, 0), direction: enumDirection.top },
{ pos: new Vector(1, 0), direction: enumDirection.top },
];
break;
}
case enumSplitterVariants.compact:
case enumSplitterVariants.compactInverse: {
entity.components.ItemAcceptor.setSlots([
{
pos: new Vector(0, 0),
directions: [enumDirection.bottom],
},
{
pos: new Vector(0, 0),
directions: [
variant === enumSplitterVariants.compactInverse
? enumDirection.left
: enumDirection.right,
],
},
]);
entity.components.ItemEjector.setSlots([
{ pos: new Vector(0, 0), direction: enumDirection.top },
]);
entity.components.BeltUnderlays.underlays = [
{ pos: new Vector(0, 0), direction: enumDirection.top },
];
break;
}
default:
assertAlways(false, "Unknown painter variant: " + variant);
}
}
}
import { enumDirection, Vector } from "../../core/vector";
import { ItemAcceptorComponent } from "../components/item_acceptor";
import { ItemEjectorComponent } from "../components/item_ejector";
import { enumItemProcessorTypes, ItemProcessorComponent } from "../components/item_processor";
import { Entity } from "../entity";
import { MetaBuilding, defaultBuildingVariant } from "../meta_building";
import { GameRoot } from "../root";
import { enumHubGoalRewards } from "../tutorial_goals";
import { T } from "../../translations";
import { formatItemsPerSecond } from "../../core/utils";
import { BeltUnderlaysComponent } from "../components/belt_underlays";
/** @enum {string} */
export const enumSplitterVariants = {
compact: "compact",
compactInverse: "compact-inverse",
compactMerge: "compact-merge",
compactMergeInverse: "compact-merge-inverse",
};
export class MetaSplitterBuilding extends MetaBuilding {
constructor() {
super("splitter");
}
getDimensions(variant) {
switch (variant) {
case defaultBuildingVariant:
return new Vector(2, 1);
case enumSplitterVariants.compact:
case enumSplitterVariants.compactInverse:
case enumSplitterVariants.compactMerge:
case enumSplitterVariants.compactMergeInverse:
return new Vector(1, 1);
default:
assertAlways(false, "Unknown splitter variant: " + variant);
}
}
/**
* @param {GameRoot} root
* @param {string} variant
* @returns {Array<[string, string]>}
*/
getAdditionalStatistics(root, variant) {
const speed = root.hubGoals.getProcessorBaseSpeed(enumItemProcessorTypes.splitter);
return [[T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(speed)]];
}
getSilhouetteColor() {
return "#444";
}
/**
* @param {GameRoot} root
*/
getAvailableVariants(root) {
let available = [defaultBuildingVariant];
if (root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_splitter_compact)) {
available.push(enumSplitterVariants.compact, enumSplitterVariants.compactInverse);
}
if (root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_merger_compact)) {
available.push(enumSplitterVariants.compactMerge, enumSplitterVariants.compactMergeInverse);
}
return available;
}
/**
* @param {GameRoot} root
*/
getIsUnlocked(root) {
return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_splitter);
}
/**
* Creates the entity at the given location
* @param {Entity} entity
*/
setupEntityComponents(entity) {
entity.addComponent(
new ItemAcceptorComponent({
slots: [], // set later
})
);
entity.addComponent(
new ItemProcessorComponent({
inputsPerCharge: 1,
processorType: enumItemProcessorTypes.splitter,
})
);
entity.addComponent(
new ItemEjectorComponent({
slots: [], // set later
})
);
entity.addComponent(new BeltUnderlaysComponent({ underlays: [] }));
}
/**
*
* @param {Entity} entity
* @param {number} rotationVariant
* @param {string} variant
*/
updateVariants(entity, rotationVariant, variant) {
switch (variant) {
case defaultBuildingVariant: {
entity.components.ItemAcceptor.setSlots([
{
pos: new Vector(0, 0),
directions: [enumDirection.bottom],
},
{
pos: new Vector(1, 0),
directions: [enumDirection.bottom],
},
]);
entity.components.ItemEjector.setSlots([
{ pos: new Vector(0, 0), direction: enumDirection.top },
{ pos: new Vector(1, 0), direction: enumDirection.top },
]);
entity.components.BeltUnderlays.underlays = [
{ pos: new Vector(0, 0), direction: enumDirection.top },
{ pos: new Vector(1, 0), direction: enumDirection.top },
];
break;
}
case enumSplitterVariants.compact:
case enumSplitterVariants.compactInverse: {
entity.components.ItemAcceptor.setSlots([
{
pos: new Vector(0, 0),
directions: [enumDirection.bottom],
},
{
pos: new Vector(0, 0),
directions: [
variant === enumSplitterVariants.compactInverse
? enumDirection.left
: enumDirection.right,
],
},
]);
entity.components.ItemEjector.setSlots([
{ pos: new Vector(0, 0), direction: enumDirection.top },
]);
entity.components.BeltUnderlays.underlays = [
{ pos: new Vector(0, 0), direction: enumDirection.top },
];
break;
}
case enumSplitterVariants.compactMerge:
case enumSplitterVariants.compactMergeInverse: {
entity.components.ItemAcceptor.setSlots([
{
pos: new Vector(0, 0),
directions: [enumDirection.bottom],
},
]);
entity.components.ItemEjector.setSlots([
{
pos: new Vector(0, 0),
direction: enumDirection.top,
},
{
pos: new Vector(0, 0),
direction:
variant === enumSplitterVariants.compactMergeInverse
? enumDirection.left
: enumDirection.right,
},
]);
entity.components.BeltUnderlays.underlays = [
{ pos: new Vector(0, 0), direction: enumDirection.top },
];
break;
}
default:
assertAlways(false, "Unknown splitter variant: " + variant);
}
}
}

View File

@@ -55,6 +55,8 @@ export function initMetaBuildingRegistry() {
registerBuildingVariant(4, MetaSplitterBuilding);
registerBuildingVariant(5, MetaSplitterBuilding, enumSplitterVariants.compact);
registerBuildingVariant(6, MetaSplitterBuilding, enumSplitterVariants.compactInverse);
registerBuildingVariant(47, MetaSplitterBuilding, enumSplitterVariants.compactMerge);
registerBuildingVariant(48, MetaSplitterBuilding, enumSplitterVariants.compactMergeInverse);
// Miner
registerBuildingVariant(7, MetaMinerBuilding);

View File

@@ -1,179 +1,182 @@
import { ShapeDefinition } from "./shape_definition";
import { finalGameShape } from "./upgrades";
/**
* Don't forget to also update tutorial_goals_mappings.js as well as the translations!
* @enum {string}
*/
export const enumHubGoalRewards = {
reward_cutter_and_trash: "reward_cutter_and_trash",
reward_rotater: "reward_rotater",
reward_painter: "reward_painter",
reward_mixer: "reward_mixer",
reward_stacker: "reward_stacker",
reward_splitter: "reward_splitter",
reward_tunnel: "reward_tunnel",
reward_rotater_ccw: "reward_rotater_ccw",
reward_rotater_fl: "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",
reward_cutter_quad: "reward_cutter_quad",
reward_painter_double: "reward_painter_double",
reward_painter_quad: "reward_painter_quad",
reward_storage: "reward_storage",
reward_blueprints: "reward_blueprints",
reward_freeplay: "reward_freeplay",
no_reward: "no_reward",
no_reward_freeplay: "no_reward_freeplay",
};
export const tutorialGoals = [
// 1
// Circle
{
shape: "CuCuCuCu", // belts t1
required: 40,
reward: enumHubGoalRewards.reward_cutter_and_trash,
},
// 2
// Cutter
{
shape: "----CuCu", //
required: 40,
reward: enumHubGoalRewards.no_reward,
},
// 3
// Rectangle
{
shape: "RuRuRuRu", // miners t1
required: 100,
reward: enumHubGoalRewards.reward_splitter,
},
// 4
{
shape: "RuRu----", // processors t2
required: 120,
reward: enumHubGoalRewards.reward_rotater,
},
// 5
// Rotater
{
shape: "Cu----Cu", // belts t2
required: 200,
reward: enumHubGoalRewards.reward_tunnel,
},
// 6
{
shape: "Cu------", // miners t2
required: 400,
reward: enumHubGoalRewards.reward_painter,
},
// 7
// Painter
{
shape: "CrCrCrCr", // unused
required: 800,
reward: enumHubGoalRewards.reward_rotater_ccw,
},
// 8
{
shape: "RbRb----", // painter t2
required: 1000,
reward: enumHubGoalRewards.reward_mixer,
},
// 9
// Mixing (purple)
{
shape: "CpCpCpCp", // belts t3
required: 1400,
reward: enumHubGoalRewards.reward_splitter_compact,
},
// 10
// Star shape + cyan
{
shape: "ScScScSc", // miners t3
required: 1600,
reward: enumHubGoalRewards.reward_stacker,
},
// 11
// Stacker
{
shape: "CgScScCg", // processors t3
required: 1800,
reward: enumHubGoalRewards.reward_miner_chainable,
},
// 12
// Blueprints
{
shape: "CbCbCbRb:CwCwCwCw",
required: 2000,
reward: enumHubGoalRewards.reward_blueprints,
},
// 13
{
shape: "RpRpRpRp:CwCwCwCw", // painting t3
required: 12000,
reward: enumHubGoalRewards.reward_underground_belt_tier_2,
},
// 14
{
shape: "SrSrSrSr:CyCyCyCy", // unused
required: 16000,
reward: enumHubGoalRewards.reward_storage,
},
// 15
{
shape: "SrSrSrSr:CyCyCyCy:SwSwSwSw", // belts t4 (two variants)
required: 25000,
reward: enumHubGoalRewards.reward_cutter_quad,
},
// 16
{
shape: "CbRbRbCb:CwCwCwCw:WbWbWbWb", // miner t4 (two variants)
required: 50000,
reward: enumHubGoalRewards.reward_painter_double,
},
// 17
{
shape: "WrRgWrRg:CwCrCwCr:SgSgSgSg", // processors t4 (two variants)
required: 120000,
reward: enumHubGoalRewards.reward_painter_quad,
},
// 18
{
shape: finalGameShape,
required: 250000,
reward: enumHubGoalRewards.reward_freeplay,
},
];
if (G_IS_DEV) {
tutorialGoals.forEach(({ shape }) => {
try {
ShapeDefinition.fromShortKey(shape);
} catch (ex) {
throw new Error("Invalid tutorial goal: '" + ex + "' for shape" + shape);
}
});
}
import { ShapeDefinition } from "./shape_definition";
import { finalGameShape } from "./upgrades";
/**
* Don't forget to also update tutorial_goals_mappings.js as well as the translations!
* @enum {string}
*/
export const enumHubGoalRewards = {
reward_cutter_and_trash: "reward_cutter_and_trash",
reward_rotater: "reward_rotater",
reward_painter: "reward_painter",
reward_mixer: "reward_mixer",
reward_stacker: "reward_stacker",
reward_splitter: "reward_splitter",
reward_tunnel: "reward_tunnel",
reward_rotater_ccw: "reward_rotater_ccw",
reward_rotater_fl: "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",
reward_cutter_quad: "reward_cutter_quad",
reward_painter_double: "reward_painter_double",
reward_painter_quad: "reward_painter_quad",
reward_storage: "reward_storage",
// @todo: unlock
reward_merger_compact: "reward_compact_merger",
reward_blueprints: "reward_blueprints",
reward_freeplay: "reward_freeplay",
no_reward: "no_reward",
no_reward_freeplay: "no_reward_freeplay",
};
export const tutorialGoals = [
// 1
// Circle
{
shape: "CuCuCuCu", // belts t1
required: 40,
reward: enumHubGoalRewards.reward_cutter_and_trash,
},
// 2
// Cutter
{
shape: "----CuCu", //
required: 40,
reward: enumHubGoalRewards.no_reward,
},
// 3
// Rectangle
{
shape: "RuRuRuRu", // miners t1
required: 100,
reward: enumHubGoalRewards.reward_splitter,
},
// 4
{
shape: "RuRu----", // processors t2
required: 120,
reward: enumHubGoalRewards.reward_rotater,
},
// 5
// Rotater
{
shape: "Cu----Cu", // belts t2
required: 200,
reward: enumHubGoalRewards.reward_tunnel,
},
// 6
{
shape: "Cu------", // miners t2
required: 400,
reward: enumHubGoalRewards.reward_painter,
},
// 7
// Painter
{
shape: "CrCrCrCr", // unused
required: 800,
reward: enumHubGoalRewards.reward_rotater_ccw,
},
// 8
{
shape: "RbRb----", // painter t2
required: 1000,
reward: enumHubGoalRewards.reward_mixer,
},
// 9
// Mixing (purple)
{
shape: "CpCpCpCp", // belts t3
required: 1400,
reward: enumHubGoalRewards.reward_splitter_compact,
},
// 10
// Star shape + cyan
{
shape: "ScScScSc", // miners t3
required: 1600,
reward: enumHubGoalRewards.reward_stacker,
},
// 11
// Stacker
{
shape: "CgScScCg", // processors t3
required: 1800,
reward: enumHubGoalRewards.reward_miner_chainable,
},
// 12
// Blueprints
{
shape: "CbCbCbRb:CwCwCwCw",
required: 2000,
reward: enumHubGoalRewards.reward_blueprints,
},
// 13
{
shape: "RpRpRpRp:CwCwCwCw", // painting t3
required: 12000,
reward: enumHubGoalRewards.reward_underground_belt_tier_2,
},
// 14
{
shape: "SrSrSrSr:CyCyCyCy", // unused
required: 16000,
reward: enumHubGoalRewards.reward_storage,
},
// 15
{
shape: "SrSrSrSr:CyCyCyCy:SwSwSwSw", // belts t4 (two variants)
required: 25000,
reward: enumHubGoalRewards.reward_cutter_quad,
},
// 16
{
shape: "CbRbRbCb:CwCwCwCw:WbWbWbWb", // miner t4 (two variants)
required: 50000,
reward: enumHubGoalRewards.reward_painter_double,
},
// 17
{
shape: "WrRgWrRg:CwCrCwCr:SgSgSgSg", // processors t4 (two variants)
required: 120000,
reward: enumHubGoalRewards.reward_painter_quad,
},
// 18
{
shape: finalGameShape,
required: 250000,
reward: enumHubGoalRewards.reward_freeplay,
},
];
if (G_IS_DEV) {
tutorialGoals.forEach(({ shape }) => {
try {
ShapeDefinition.fromShortKey(shape);
} catch (ex) {
throw new Error("Invalid tutorial goal: '" + ex + "' for shape" + shape);
}
});
}