Rebalance the whole game, rename splitter -> balancer

This commit is contained in:
tobspr 2020-09-22 14:47:59 +02:00
parent b0058cb59b
commit c6eb1dad04
42 changed files with 1005 additions and 981 deletions

View File

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

View File

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

View File

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.3 MiB

View File

@ -336,6 +336,46 @@
"spriteSourceSize": {"x":4,"y":4,"w":44,"h":44},
"sourceSize": {"w":48,"h":48}
},
"sprites/blueprints/balancer-merger-inverse.png":
{
"frame": {"x":310,"y":112,"w":48,"h":48},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":48,"h":48},
"sourceSize": {"w":48,"h":48}
},
"sprites/blueprints/balancer-merger.png":
{
"frame": {"x":208,"y":376,"w":47,"h":47},
"rotated": false,
"trimmed": true,
"spriteSourceSize": {"x":1,"y":0,"w":47,"h":47},
"sourceSize": {"w":48,"h":48}
},
"sprites/blueprints/balancer-splitter-inverse.png":
{
"frame": {"x":310,"y":166,"w":48,"h":48},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":48,"h":48},
"sourceSize": {"w":48,"h":48}
},
"sprites/blueprints/balancer-splitter.png":
{
"frame": {"x":261,"y":376,"w":47,"h":47},
"rotated": false,
"trimmed": true,
"spriteSourceSize": {"x":1,"y":0,"w":47,"h":47},
"sourceSize": {"w":48,"h":48}
},
"sprites/blueprints/balancer.png":
{
"frame": {"x":770,"y":58,"w":87,"h":48},
"rotated": false,
"trimmed": true,
"spriteSourceSize": {"x":5,"y":0,"w":87,"h":48},
"sourceSize": {"w":96,"h":48}
},
"sprites/blueprints/belt_left.png":
{
"frame": {"x":305,"y":486,"w":44,"h":44},
@ -378,7 +418,7 @@
},
"sprites/blueprints/cutter.png":
{
"frame": {"x":770,"y":58,"w":87,"h":48},
"frame": {"x":863,"y":58,"w":87,"h":48},
"rotated": false,
"trimmed": true,
"spriteSourceSize": {"x":5,"y":0,"w":87,"h":48},
@ -434,7 +474,7 @@
},
"sprites/blueprints/logic_gate-xor.png":
{
"frame": {"x":310,"y":112,"w":48,"h":48},
"frame": {"x":965,"y":4,"w":48,"h":48},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":48,"h":48},
@ -506,7 +546,7 @@
},
"sprites/blueprints/reader.png":
{
"frame": {"x":310,"y":166,"w":48,"h":48},
"frame": {"x":956,"y":58,"w":48,"h":48},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":48,"h":48},
@ -514,7 +554,7 @@
},
"sprites/blueprints/rotater-ccw.png":
{
"frame": {"x":965,"y":4,"w":48,"h":48},
"frame": {"x":950,"y":112,"w":48,"h":48},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":48,"h":48},
@ -522,29 +562,13 @@
},
"sprites/blueprints/rotater-rotate180.png":
{
"frame": {"x":956,"y":58,"w":48,"h":48},
"frame": {"x":950,"y":166,"w":48,"h":48},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":48,"h":48},
"sourceSize": {"w":48,"h":48}
},
"sprites/blueprints/rotater.png":
{
"frame": {"x":950,"y":112,"w":48,"h":48},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":48,"h":48},
"sourceSize": {"w":48,"h":48}
},
"sprites/blueprints/splitter-compact-inverse.png":
{
"frame": {"x":950,"y":166,"w":48,"h":48},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":48,"h":48},
"sourceSize": {"w":48,"h":48}
},
"sprites/blueprints/splitter-compact-merge-inverse.png":
{
"frame": {"x":582,"y":166,"w":48,"h":48},
"rotated": false,
@ -552,30 +576,6 @@
"spriteSourceSize": {"x":0,"y":0,"w":48,"h":48},
"sourceSize": {"w":48,"h":48}
},
"sprites/blueprints/splitter-compact-merge.png":
{
"frame": {"x":208,"y":376,"w":47,"h":47},
"rotated": false,
"trimmed": true,
"spriteSourceSize": {"x":1,"y":0,"w":47,"h":47},
"sourceSize": {"w":48,"h":48}
},
"sprites/blueprints/splitter-compact.png":
{
"frame": {"x":261,"y":376,"w":47,"h":47},
"rotated": false,
"trimmed": true,
"spriteSourceSize": {"x":1,"y":0,"w":47,"h":47},
"sourceSize": {"w":48,"h":48}
},
"sprites/blueprints/splitter.png":
{
"frame": {"x":863,"y":58,"w":87,"h":48},
"rotated": false,
"trimmed": true,
"spriteSourceSize": {"x":5,"y":0,"w":87,"h":48},
"sourceSize": {"w":96,"h":48}
},
"sprites/blueprints/stacker.png":
{
"frame": {"x":870,"y":4,"w":89,"h":48},
@ -736,6 +736,46 @@
"spriteSourceSize": {"x":0,"y":0,"w":48,"h":47},
"sourceSize": {"w":48,"h":48}
},
"sprites/buildings/balancer-merger-inverse.png":
{
"frame": {"x":442,"y":249,"w":48,"h":47},
"rotated": false,
"trimmed": true,
"spriteSourceSize": {"x":0,"y":0,"w":48,"h":47},
"sourceSize": {"w":48,"h":48}
},
"sprites/buildings/balancer-merger.png":
{
"frame": {"x":207,"y":429,"w":47,"h":47},
"rotated": false,
"trimmed": true,
"spriteSourceSize": {"x":1,"y":0,"w":47,"h":47},
"sourceSize": {"w":48,"h":48}
},
"sprites/buildings/balancer-splitter-inverse.png":
{
"frame": {"x":496,"y":249,"w":48,"h":47},
"rotated": false,
"trimmed": true,
"spriteSourceSize": {"x":0,"y":0,"w":48,"h":47},
"sourceSize": {"w":48,"h":48}
},
"sprites/buildings/balancer-splitter.png":
{
"frame": {"x":260,"y":429,"w":47,"h":47},
"rotated": false,
"trimmed": true,
"spriteSourceSize": {"x":1,"y":0,"w":47,"h":47},
"sourceSize": {"w":48,"h":48}
},
"sprites/buildings/balancer.png":
{
"frame": {"x":582,"y":112,"w":87,"h":48},
"rotated": false,
"trimmed": true,
"spriteSourceSize": {"x":5,"y":0,"w":87,"h":48},
"sourceSize": {"w":96,"h":48}
},
"sprites/buildings/belt_left.png":
{
"frame": {"x":487,"y":302,"w":44,"h":44},
@ -778,7 +818,7 @@
},
"sprites/buildings/cutter.png":
{
"frame": {"x":582,"y":112,"w":87,"h":48},
"frame": {"x":675,"y":112,"w":87,"h":48},
"rotated": false,
"trimmed": true,
"spriteSourceSize": {"x":5,"y":0,"w":87,"h":48},
@ -944,46 +984,6 @@
"spriteSourceSize": {"x":0,"y":0,"w":48,"h":48},
"sourceSize": {"w":48,"h":48}
},
"sprites/buildings/splitter-compact-inverse.png":
{
"frame": {"x":442,"y":249,"w":48,"h":47},
"rotated": false,
"trimmed": true,
"spriteSourceSize": {"x":0,"y":0,"w":48,"h":47},
"sourceSize": {"w":48,"h":48}
},
"sprites/buildings/splitter-compact-merge-inverse.png":
{
"frame": {"x":496,"y":249,"w":48,"h":47},
"rotated": false,
"trimmed": true,
"spriteSourceSize": {"x":0,"y":0,"w":48,"h":47},
"sourceSize": {"w":48,"h":48}
},
"sprites/buildings/splitter-compact-merge.png":
{
"frame": {"x":207,"y":429,"w":47,"h":47},
"rotated": false,
"trimmed": true,
"spriteSourceSize": {"x":1,"y":0,"w":47,"h":47},
"sourceSize": {"w":48,"h":48}
},
"sprites/buildings/splitter-compact.png":
{
"frame": {"x":260,"y":429,"w":47,"h":47},
"rotated": false,
"trimmed": true,
"spriteSourceSize": {"x":1,"y":0,"w":47,"h":47},
"sourceSize": {"w":48,"h":48}
},
"sprites/buildings/splitter.png":
{
"frame": {"x":675,"y":112,"w":87,"h":48},
"rotated": false,
"trimmed": true,
"spriteSourceSize": {"x":5,"y":0,"w":87,"h":48},
"sourceSize": {"w":96,"h":48}
},
"sprites/buildings/stacker.png":
{
"frame": {"x":676,"y":58,"w":88,"h":48},
@ -1551,6 +1551,6 @@
"format": "RGBA8888",
"size": {"w":1024,"h":1024},
"scale": "0.25",
"smartupdate": "$TexturePacker:SmartUpdate:6428a34334a71261053e6374332fbd0f:de76d600c9c903298cb6652e0ef02898:908b89f5ca8ff73e331a35a3b14d0604$"
"smartupdate": "$TexturePacker:SmartUpdate:c57f50d18c59efc0edbd4a3a732323a4:3fcf23da2ddc6370c437cf41f6d44ed0:908b89f5ca8ff73e331a35a3b14d0604$"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 279 KiB

After

Width:  |  Height:  |  Size: 281 KiB

View File

@ -336,6 +336,46 @@
"spriteSourceSize": {"x":9,"y":9,"w":87,"h":87},
"sourceSize": {"w":96,"h":96}
},
"sprites/blueprints/balancer-merger-inverse.png":
{
"frame": {"x":453,"y":1170,"w":95,"h":93},
"rotated": false,
"trimmed": true,
"spriteSourceSize": {"x":0,"y":1,"w":95,"h":93},
"sourceSize": {"w":96,"h":96}
},
"sprites/blueprints/balancer-merger.png":
{
"frame": {"x":4,"y":1320,"w":93,"h":93},
"rotated": false,
"trimmed": true,
"spriteSourceSize": {"x":3,"y":1,"w":93,"h":93},
"sourceSize": {"w":96,"h":96}
},
"sprites/blueprints/balancer-splitter-inverse.png":
{
"frame": {"x":348,"y":1180,"w":95,"h":93},
"rotated": false,
"trimmed": true,
"spriteSourceSize": {"x":0,"y":1,"w":95,"h":93},
"sourceSize": {"w":96,"h":96}
},
"sprites/blueprints/balancer-splitter.png":
{
"frame": {"x":694,"y":1113,"w":93,"h":93},
"rotated": false,
"trimmed": true,
"spriteSourceSize": {"x":3,"y":1,"w":93,"h":93},
"sourceSize": {"w":96,"h":96}
},
"sprites/blueprints/balancer.png":
{
"frame": {"x":184,"y":581,"w":172,"h":96},
"rotated": false,
"trimmed": true,
"spriteSourceSize": {"x":11,"y":0,"w":172,"h":96},
"sourceSize": {"w":192,"h":96}
},
"sprites/blueprints/belt_left.png":
{
"frame": {"x":479,"y":1639,"w":87,"h":87},
@ -378,7 +418,7 @@
},
"sprites/blueprints/cutter.png":
{
"frame": {"x":184,"y":581,"w":172,"h":96},
"frame": {"x":362,"y":616,"w":172,"h":96},
"rotated": false,
"trimmed": true,
"spriteSourceSize": {"x":11,"y":0,"w":172,"h":96},
@ -536,46 +576,6 @@
"spriteSourceSize": {"x":0,"y":0,"w":96,"h":96},
"sourceSize": {"w":96,"h":96}
},
"sprites/blueprints/splitter-compact-inverse.png":
{
"frame": {"x":453,"y":1170,"w":95,"h":93},
"rotated": false,
"trimmed": true,
"spriteSourceSize": {"x":0,"y":1,"w":95,"h":93},
"sourceSize": {"w":96,"h":96}
},
"sprites/blueprints/splitter-compact-merge-inverse.png":
{
"frame": {"x":348,"y":1180,"w":95,"h":93},
"rotated": false,
"trimmed": true,
"spriteSourceSize": {"x":0,"y":1,"w":95,"h":93},
"sourceSize": {"w":96,"h":96}
},
"sprites/blueprints/splitter-compact-merge.png":
{
"frame": {"x":4,"y":1320,"w":93,"h":93},
"rotated": false,
"trimmed": true,
"spriteSourceSize": {"x":3,"y":1,"w":93,"h":93},
"sourceSize": {"w":96,"h":96}
},
"sprites/blueprints/splitter-compact.png":
{
"frame": {"x":694,"y":1113,"w":93,"h":93},
"rotated": false,
"trimmed": true,
"spriteSourceSize": {"x":3,"y":1,"w":93,"h":93},
"sourceSize": {"w":96,"h":96}
},
"sprites/blueprints/splitter.png":
{
"frame": {"x":362,"y":616,"w":172,"h":96},
"rotated": false,
"trimmed": true,
"spriteSourceSize": {"x":11,"y":0,"w":172,"h":96},
"sourceSize": {"w":192,"h":96}
},
"sprites/blueprints/stacker.png":
{
"frame": {"x":185,"y":479,"w":175,"h":96},
@ -736,6 +736,46 @@
"spriteSourceSize": {"x":2,"y":2,"w":93,"h":91},
"sourceSize": {"w":96,"h":96}
},
"sprites/buildings/balancer-merger-inverse.png":
{
"frame": {"x":105,"y":1237,"w":94,"h":91},
"rotated": false,
"trimmed": true,
"spriteSourceSize": {"x":0,"y":2,"w":94,"h":91},
"sourceSize": {"w":96,"h":96}
},
"sprites/buildings/balancer-merger.png":
{
"frame": {"x":554,"y":1174,"w":93,"h":91},
"rotated": false,
"trimmed": true,
"spriteSourceSize": {"x":3,"y":2,"w":93,"h":91},
"sourceSize": {"w":96,"h":96}
},
"sprites/buildings/balancer-splitter-inverse.png":
{
"frame": {"x":246,"y":1183,"w":95,"h":91},
"rotated": false,
"trimmed": true,
"spriteSourceSize": {"x":0,"y":2,"w":95,"h":91},
"sourceSize": {"w":96,"h":96}
},
"sprites/buildings/balancer-splitter.png":
{
"frame": {"x":449,"y":1269,"w":93,"h":91},
"rotated": false,
"trimmed": true,
"spriteSourceSize": {"x":3,"y":2,"w":93,"h":91},
"sourceSize": {"w":96,"h":96}
},
"sprites/buildings/balancer.png":
{
"frame": {"x":740,"y":807,"w":171,"h":96},
"rotated": false,
"trimmed": true,
"spriteSourceSize": {"x":11,"y":0,"w":171,"h":96},
"sourceSize": {"w":192,"h":96}
},
"sprites/buildings/belt_left.png":
{
"frame": {"x":200,"y":1441,"w":87,"h":87},
@ -778,7 +818,7 @@
},
"sprites/buildings/cutter.png":
{
"frame": {"x":740,"y":807,"w":171,"h":96},
"frame": {"x":4,"y":683,"w":171,"h":96},
"rotated": false,
"trimmed": true,
"spriteSourceSize": {"x":11,"y":0,"w":171,"h":96},
@ -944,46 +984,6 @@
"spriteSourceSize": {"x":1,"y":0,"w":95,"h":96},
"sourceSize": {"w":96,"h":96}
},
"sprites/buildings/splitter-compact-inverse.png":
{
"frame": {"x":105,"y":1237,"w":94,"h":91},
"rotated": false,
"trimmed": true,
"spriteSourceSize": {"x":0,"y":2,"w":94,"h":91},
"sourceSize": {"w":96,"h":96}
},
"sprites/buildings/splitter-compact-merge-inverse.png":
{
"frame": {"x":246,"y":1183,"w":95,"h":91},
"rotated": false,
"trimmed": true,
"spriteSourceSize": {"x":0,"y":2,"w":95,"h":91},
"sourceSize": {"w":96,"h":96}
},
"sprites/buildings/splitter-compact-merge.png":
{
"frame": {"x":554,"y":1174,"w":93,"h":91},
"rotated": false,
"trimmed": true,
"spriteSourceSize": {"x":3,"y":2,"w":93,"h":91},
"sourceSize": {"w":96,"h":96}
},
"sprites/buildings/splitter-compact.png":
{
"frame": {"x":449,"y":1269,"w":93,"h":91},
"rotated": false,
"trimmed": true,
"spriteSourceSize": {"x":3,"y":2,"w":93,"h":91},
"sourceSize": {"w":96,"h":96}
},
"sprites/buildings/splitter.png":
{
"frame": {"x":4,"y":683,"w":171,"h":96},
"rotated": false,
"trimmed": true,
"spriteSourceSize": {"x":11,"y":0,"w":171,"h":96},
"sourceSize": {"w":192,"h":96}
},
"sprites/buildings/stacker.png":
{
"frame": {"x":4,"y":581,"w":174,"h":96},
@ -1551,6 +1551,6 @@
"format": "RGBA8888",
"size": {"w":1024,"h":2048},
"scale": "0.5",
"smartupdate": "$TexturePacker:SmartUpdate:6428a34334a71261053e6374332fbd0f:de76d600c9c903298cb6652e0ef02898:908b89f5ca8ff73e331a35a3b14d0604$"
"smartupdate": "$TexturePacker:SmartUpdate:c57f50d18c59efc0edbd4a3a732323a4:3fcf23da2ddc6370c437cf41f6d44ed0:908b89f5ca8ff73e331a35a3b14d0604$"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 697 KiB

After

Width:  |  Height:  |  Size: 700 KiB

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 47 KiB

View File

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 9.0 KiB

View File

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 9.0 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 30 KiB

View File

@ -1,4 +1,4 @@
$buildings: belt, cutter, miner, mixer, painter, rotater, splitter, stacker, trash, underground_belt, wire,
$buildings: belt, cutter, miner, mixer, painter, rotater, balancer, stacker, trash, underground_belt, wire,
constant_signal, logic_gate, lever, filter, wire_tunnel, display, virtual_processor, reader;
@each $building in $buildings {
@ -7,7 +7,7 @@ $buildings: belt, cutter, miner, mixer, painter, rotater, splitter, stacker, tra
}
}
$buildingsAndVariants: belt, splitter, splitter-compact, splitter-compact-inverse, underground_belt,
$buildingsAndVariants: belt, balancer, balancer-merger, balancer-splitter, underground_belt,
underground_belt-tier2, miner, miner-chainable, cutter, cutter-quad, rotater, rotater-ccw, rotater-fl,
stacker, mixer, painter, painter-double, painter-quad, trash, trash-storage;
@each $building in $buildingsAndVariants {
@ -16,10 +16,18 @@ $buildingsAndVariants: belt, splitter, splitter-compact, splitter-compact-invers
}
}
// Special case
// @TODO: New buildings (balancer, wires, etc)
// Special cases for mirrored vairants
[data-icon="building_tutorials/painter-mirrored.png"] {
background-image: uiResource("res/ui/building_tutorials/painter.png") !important;
}
[data-icon="building_tutorials/balancer-merger-inverse.png"] {
background-image: uiResource("res/ui/building_tutorials/balancer-merger.png") !important;
}
[data-icon="building_tutorials/balancer-splitter-inverse.png"] {
background-image: uiResource("res/ui/building_tutorials/balancer-splitter.png") !important;
}
$icons: notification_saved, notification_success, notification_upgrade;
@each $icon in $icons {

View File

@ -24,7 +24,7 @@ export const CHANGELOG = [
"Mark pinned shapes in statistics dialog and show them first (inspired by davidburhans)",
"Added setting to show chunk borders",
"Quad painters have been reworked! They now are integrated with the wires, and only paint the shape when the value is 1 (inspired by dengr1605)",
"There are now compact 1x1 splitters available to be unlocked!",
"There are now compact 1x1 balancers available to be unlocked!",
"Replaced level completion sound to be less distracting",
"Allow editing waypoints (by isaisstillalive)",
"Show confirmation when cutting area which is too expensive to get pasted again (by isaisstillalive)",

View File

@ -11,29 +11,29 @@ 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 const enumBalancerVariants = {
merger: "merger",
mergerInverse: "merger-inverse",
splitter: "splitter",
splitterInverse: "splitter-inverse",
};
export class MetaSplitterBuilding extends MetaBuilding {
export class MetaBalancerBuilding extends MetaBuilding {
constructor() {
super("splitter");
super("balancer");
}
getDimensions(variant) {
switch (variant) {
case defaultBuildingVariant:
return new Vector(2, 1);
case enumSplitterVariants.compact:
case enumSplitterVariants.compactInverse:
case enumSplitterVariants.compactMerge:
case enumSplitterVariants.compactMergeInverse:
case enumBalancerVariants.merger:
case enumBalancerVariants.mergerInverse:
case enumBalancerVariants.splitter:
case enumBalancerVariants.splitterInverse:
return new Vector(1, 1);
default:
assertAlways(false, "Unknown splitter variant: " + variant);
assertAlways(false, "Unknown balancer variant: " + variant);
}
}
@ -43,7 +43,7 @@ export class MetaSplitterBuilding extends MetaBuilding {
* @returns {Array<[string, string]>}
*/
getAdditionalStatistics(root, variant) {
const speed = root.hubGoals.getProcessorBaseSpeed(enumItemProcessorTypes.splitter);
const speed = root.hubGoals.getProcessorBaseSpeed(enumItemProcessorTypes.balancer);
return [[T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(speed)]];
}
@ -57,12 +57,12 @@ export class MetaSplitterBuilding extends MetaBuilding {
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)) {
available.push(enumBalancerVariants.merger, enumBalancerVariants.mergerInverse);
}
if (root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_merger_compact)) {
available.push(enumSplitterVariants.compactMerge, enumSplitterVariants.compactMergeInverse);
if (root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_splitter)) {
available.push(enumBalancerVariants.splitter, enumBalancerVariants.splitterInverse);
}
return available;
@ -72,7 +72,7 @@ export class MetaSplitterBuilding extends MetaBuilding {
* @param {GameRoot} root
*/
getIsUnlocked(root) {
return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_splitter);
return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_balancer);
}
/**
@ -89,7 +89,7 @@ export class MetaSplitterBuilding extends MetaBuilding {
entity.addComponent(
new ItemProcessorComponent({
inputsPerCharge: 1,
processorType: enumItemProcessorTypes.splitter,
processorType: enumItemProcessorTypes.balancer,
})
);
@ -135,8 +135,8 @@ export class MetaSplitterBuilding extends MetaBuilding {
break;
}
case enumSplitterVariants.compact:
case enumSplitterVariants.compactInverse: {
case enumBalancerVariants.merger:
case enumBalancerVariants.mergerInverse: {
entity.components.ItemAcceptor.setSlots([
{
pos: new Vector(0, 0),
@ -145,7 +145,7 @@ export class MetaSplitterBuilding extends MetaBuilding {
{
pos: new Vector(0, 0),
directions: [
variant === enumSplitterVariants.compactInverse
variant === enumBalancerVariants.mergerInverse
? enumDirection.left
: enumDirection.right,
],
@ -162,8 +162,8 @@ export class MetaSplitterBuilding extends MetaBuilding {
break;
}
case enumSplitterVariants.compactMerge:
case enumSplitterVariants.compactMergeInverse: {
case enumBalancerVariants.splitter:
case enumBalancerVariants.splitterInverse: {
entity.components.ItemAcceptor.setSlots([
{
pos: new Vector(0, 0),
@ -179,7 +179,7 @@ export class MetaSplitterBuilding extends MetaBuilding {
{
pos: new Vector(0, 0),
direction:
variant === enumSplitterVariants.compactMergeInverse
variant === enumBalancerVariants.splitterInverse
? enumDirection.left
: enumDirection.right,
},
@ -192,7 +192,7 @@ export class MetaSplitterBuilding extends MetaBuilding {
break;
}
default:
assertAlways(false, "Unknown splitter variant: " + variant);
assertAlways(false, "Unknown balancer variant: " + variant);
}
}
}

View File

@ -895,7 +895,7 @@ export class Camera extends BasicSerializableObject {
return;
}
const panAreaPixels = Math.min(this.root.gameWidth, this.root.gameHeight) * 0.015;
const panAreaPixels = 2;
const panVelocity = new Vector();
if (mousePos.x < panAreaPixels) {

View File

@ -4,7 +4,7 @@ import { Component } from "../component";
/** @enum {string} */
export const enumItemProcessorTypes = {
splitter: "splitter",
balancer: "balancer",
cutter: "cutter",
cutterQuad: "cutterQuad",
rotater: "rotater",
@ -57,14 +57,14 @@ export class ItemProcessorComponent extends Component {
*
*/
constructor({
processorType = enumItemProcessorTypes.splitter,
processorType = enumItemProcessorTypes.balancer,
processingRequirement = null,
inputsPerCharge = 1,
}) {
super();
// Which slot to emit next, this is only a preference and if it can't emit
// it will take the other one. Some machines ignore this (e.g. the splitter) to make
// it will take the other one. Some machines ignore this (e.g. the balancer) to make
// sure the outputs always match
this.nextOutputSlot = 0;

View File

@ -43,11 +43,11 @@ export class HubGoals extends BasicSerializableObject {
// Compute upgrade improvements
for (const upgradeId in UPGRADES) {
const upgradeHandle = UPGRADES[upgradeId];
const tiers = UPGRADES[upgradeId];
const level = this.upgradeLevels[upgradeId] || 0;
let totalImprovement = upgradeHandle.baseValue || 1;
let totalImprovement = 1;
for (let i = 0; i < level; ++i) {
totalImprovement += upgradeHandle.tiers[i].improvement;
totalImprovement += tiers[i].improvement;
}
this.upgradeImprovements[upgradeId] = totalImprovement;
}
@ -98,7 +98,7 @@ export class HubGoals extends BasicSerializableObject {
*/
this.upgradeImprovements = {};
for (const key in UPGRADES) {
this.upgradeImprovements[key] = UPGRADES[key].baseValue || 1;
this.upgradeImprovements[key] = 1;
}
this.createNextGoal();
@ -212,7 +212,7 @@ export class HubGoals extends BasicSerializableObject {
this.currentGoal = {
/** @type {ShapeDefinition} */
definition: this.createRandomShape(),
required: 10000 + findNiceIntegerValue(this.level * 2000),
required: findNiceIntegerValue(5000 + Math.pow(this.level * 2000, 0.75)),
reward: enumHubGoalRewards.no_reward_freeplay,
};
}
@ -243,10 +243,10 @@ export class HubGoals extends BasicSerializableObject {
* @param {string} upgradeId
*/
canUnlockUpgrade(upgradeId) {
const handle = UPGRADES[upgradeId];
const tiers = UPGRADES[upgradeId];
const currentLevel = this.getUpgradeLevel(upgradeId);
if (currentLevel >= handle.tiers.length) {
if (currentLevel >= tiers.length) {
// Max level
return false;
}
@ -255,7 +255,7 @@ export class HubGoals extends BasicSerializableObject {
return true;
}
const tierData = handle.tiers[currentLevel];
const tierData = tiers[currentLevel];
for (let i = 0; i < tierData.required.length; ++i) {
const requirement = tierData.required[i];
@ -290,10 +290,10 @@ export class HubGoals extends BasicSerializableObject {
return false;
}
const handle = UPGRADES[upgradeId];
const upgradeTiers = UPGRADES[upgradeId];
const currentLevel = this.getUpgradeLevel(upgradeId);
const tierData = handle.tiers[currentLevel];
const tierData = upgradeTiers[currentLevel];
if (!tierData) {
return false;
}
@ -399,7 +399,7 @@ export class HubGoals extends BasicSerializableObject {
case enumItemProcessorTypes.trash:
case enumItemProcessorTypes.hub:
return 1e30;
case enumItemProcessorTypes.splitter:
case enumItemProcessorTypes.balancer:
return globalConfig.beltSpeedItemsPerSecond * this.upgradeImprovements.belt * 2;
case enumItemProcessorTypes.reader:
return globalConfig.beltSpeedItemsPerSecond * this.upgradeImprovements.belt;

View File

@ -8,7 +8,7 @@ import { MetaMixerBuilding } from "../../buildings/mixer";
import { MetaPainterBuilding } from "../../buildings/painter";
import { MetaReaderBuilding } from "../../buildings/reader";
import { MetaRotaterBuilding } from "../../buildings/rotater";
import { MetaSplitterBuilding } from "../../buildings/splitter";
import { MetaBalancerBuilding } from "../../buildings/balancer";
import { MetaStackerBuilding } from "../../buildings/stacker";
import { MetaTrashBuilding } from "../../buildings/trash";
import { MetaUndergroundBeltBuilding } from "../../buildings/underground_belt";
@ -16,7 +16,7 @@ import { HUDBaseToolbar } from "./base_toolbar";
const supportedBuildings = [
MetaBeltBuilding,
MetaSplitterBuilding,
MetaBalancerBuilding,
MetaUndergroundBeltBuilding,
MetaMinerBuilding,
MetaCutterBuilding,

View File

@ -1,291 +1,291 @@
import { ClickDetector } from "../../../core/click_detector";
import { formatBigNumber, makeDiv, arrayDeleteValue } from "../../../core/utils";
import { ShapeDefinition } from "../../shape_definition";
import { BaseHUDPart } from "../base_hud_part";
import { blueprintShape, UPGRADES } from "../../upgrades";
import { enumHubGoalRewards } from "../../tutorial_goals";
/**
* Manages the pinned shapes on the left side of the screen
*/
export class HUDPinnedShapes extends BaseHUDPart {
constructor(root) {
super(root);
/**
* Store a list of pinned shapes
* @type {Array<string>}
*/
this.pinnedShapes = [];
/**
* Store handles to the currently rendered elements, so we can update them more
* convenient. Also allows for cleaning up handles.
* @type {Array<{
* key: string,
* amountLabel: HTMLElement,
* lastRenderedValue: string,
* element: HTMLElement,
* detector?: ClickDetector,
* infoDetector?: ClickDetector
* }>}
*/
this.handles = [];
}
createElements(parent) {
this.element = makeDiv(parent, "ingame_HUD_PinnedShapes", []);
}
/**
* Serializes the pinned shapes
*/
serialize() {
return {
shapes: this.pinnedShapes,
};
}
/**
* Deserializes the pinned shapes
* @param {{ shapes: Array<string>}} data
*/
deserialize(data) {
if (!data || !data.shapes || !Array.isArray(data.shapes)) {
return "Invalid pinned shapes data";
}
this.pinnedShapes = data.shapes;
}
/**
* Initializes the hud component
*/
initialize() {
// Connect to any relevant signals
this.root.signals.storyGoalCompleted.add(this.rerenderFull, this);
this.root.signals.upgradePurchased.add(this.updateShapesAfterUpgrade, this);
this.root.signals.postLoadHook.add(this.rerenderFull, this);
this.root.hud.signals.shapePinRequested.add(this.pinNewShape, this);
this.root.hud.signals.shapeUnpinRequested.add(this.unpinShape, this);
// Perform initial render
this.updateShapesAfterUpgrade();
}
/**
* Updates all shapes after an upgrade has been purchased and removes the unused ones
*/
updateShapesAfterUpgrade() {
for (let i = 0; i < this.pinnedShapes.length; ++i) {
const key = this.pinnedShapes[i];
if (key === blueprintShape) {
// Ignore blueprint shapes
continue;
}
let goal = this.findGoalValueForShape(key);
if (!goal) {
// Seems no longer relevant
this.pinnedShapes.splice(i, 1);
i -= 1;
}
}
this.rerenderFull();
}
/**
* Finds the current goal for the given key. If the key is the story goal, returns
* the story goal. If its the blueprint shape, no goal is returned. Otherwise
* it's searched for upgrades.
* @param {string} key
*/
findGoalValueForShape(key) {
if (key === this.root.hubGoals.currentGoal.definition.getHash()) {
return this.root.hubGoals.currentGoal.required;
}
if (key === blueprintShape) {
return null;
}
// Check if this shape is required for any upgrade
for (const upgradeId in UPGRADES) {
const { tiers } = UPGRADES[upgradeId];
const currentTier = this.root.hubGoals.getUpgradeLevel(upgradeId);
const tierHandle = tiers[currentTier];
if (!tierHandle) {
// Max level
continue;
}
for (let i = 0; i < tierHandle.required.length; ++i) {
const { shape, amount } = tierHandle.required[i];
if (shape === key) {
return amount;
}
}
}
return null;
}
/**
* Returns whether a given shape is currently pinned
* @param {string} key
*/
isShapePinned(key) {
if (key === this.root.hubGoals.currentGoal.definition.getHash() || key === blueprintShape) {
// This is a "special" shape which is always pinned
return true;
}
return this.pinnedShapes.indexOf(key) >= 0;
}
/**
* Rerenders the whole component
*/
rerenderFull() {
const currentGoal = this.root.hubGoals.currentGoal;
const currentKey = currentGoal.definition.getHash();
// First, remove all old shapes
for (let i = 0; i < this.handles.length; ++i) {
this.handles[i].element.remove();
const detector = this.handles[i].detector;
if (detector) {
detector.cleanup();
}
const infoDetector = this.handles[i].infoDetector;
if (infoDetector) {
infoDetector.cleanup();
}
}
this.handles = [];
// Pin story goal
this.internalPinShape(currentKey, false, "goal");
// Pin blueprint shape as well
if (this.root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_blueprints)) {
this.internalPinShape(blueprintShape, false, "blueprint");
}
// Pin manually pinned shapes
for (let i = 0; i < this.pinnedShapes.length; ++i) {
const key = this.pinnedShapes[i];
if (key !== currentKey) {
this.internalPinShape(key);
}
}
}
/**
* Pins a new shape
* @param {string} key
* @param {boolean} canUnpin
* @param {string=} className
*/
internalPinShape(key, canUnpin = true, className = null) {
const definition = this.root.shapeDefinitionMgr.getShapeFromShortKey(key);
const element = makeDiv(this.element, null, ["shape"]);
const canvas = definition.generateAsCanvas(120);
element.appendChild(canvas);
if (className) {
element.classList.add(className);
}
let detector = null;
if (canUnpin) {
element.classList.add("unpinable");
detector = new ClickDetector(element, {
consumeEvents: true,
preventDefault: true,
targetOnly: true,
});
detector.click.add(() => this.unpinShape(key));
} else {
element.classList.add("marked");
}
// Show small info icon
const infoButton = document.createElement("button");
infoButton.classList.add("infoButton");
element.appendChild(infoButton);
const infoDetector = new ClickDetector(infoButton, {
consumeEvents: true,
preventDefault: true,
targetOnly: true,
});
infoDetector.click.add(() => this.root.hud.signals.viewShapeDetailsRequested.dispatch(definition));
const amountLabel = makeDiv(element, null, ["amountLabel"], "");
const goal = this.findGoalValueForShape(key);
if (goal) {
makeDiv(element, null, ["goalLabel"], "/" + formatBigNumber(goal));
}
this.handles.push({
key,
element,
amountLabel,
lastRenderedValue: "",
detector,
infoDetector,
});
}
/**
* Updates all amount labels
*/
update() {
for (let i = 0; i < this.handles.length; ++i) {
const handle = this.handles[i];
const currentValue = this.root.hubGoals.getShapesStoredByKey(handle.key);
const currentValueFormatted = formatBigNumber(currentValue);
if (currentValueFormatted !== handle.lastRenderedValue) {
handle.lastRenderedValue = currentValueFormatted;
handle.amountLabel.innerText = currentValueFormatted;
const goal = this.findGoalValueForShape(handle.key);
handle.element.classList.toggle("completed", goal && currentValue > goal);
}
}
}
/**
* Unpins a shape
* @param {string} key
*/
unpinShape(key) {
arrayDeleteValue(this.pinnedShapes, key);
this.rerenderFull();
}
/**
* Requests to pin a new shape
* @param {ShapeDefinition} definition
*/
pinNewShape(definition) {
const key = definition.getHash();
if (key === this.root.hubGoals.currentGoal.definition.getHash()) {
// Can not pin current goal
return;
}
if (key === blueprintShape) {
// Can not pin the blueprint shape
return;
}
// Check if its already pinned
if (this.pinnedShapes.indexOf(key) >= 0) {
return;
}
this.pinnedShapes.push(key);
this.rerenderFull();
}
}
import { ClickDetector } from "../../../core/click_detector";
import { formatBigNumber, makeDiv, arrayDeleteValue } from "../../../core/utils";
import { ShapeDefinition } from "../../shape_definition";
import { BaseHUDPart } from "../base_hud_part";
import { blueprintShape, UPGRADES } from "../../upgrades";
import { enumHubGoalRewards } from "../../tutorial_goals";
/**
* Manages the pinned shapes on the left side of the screen
*/
export class HUDPinnedShapes extends BaseHUDPart {
constructor(root) {
super(root);
/**
* Store a list of pinned shapes
* @type {Array<string>}
*/
this.pinnedShapes = [];
/**
* Store handles to the currently rendered elements, so we can update them more
* convenient. Also allows for cleaning up handles.
* @type {Array<{
* key: string,
* amountLabel: HTMLElement,
* lastRenderedValue: string,
* element: HTMLElement,
* detector?: ClickDetector,
* infoDetector?: ClickDetector
* }>}
*/
this.handles = [];
}
createElements(parent) {
this.element = makeDiv(parent, "ingame_HUD_PinnedShapes", []);
}
/**
* Serializes the pinned shapes
*/
serialize() {
return {
shapes: this.pinnedShapes,
};
}
/**
* Deserializes the pinned shapes
* @param {{ shapes: Array<string>}} data
*/
deserialize(data) {
if (!data || !data.shapes || !Array.isArray(data.shapes)) {
return "Invalid pinned shapes data";
}
this.pinnedShapes = data.shapes;
}
/**
* Initializes the hud component
*/
initialize() {
// Connect to any relevant signals
this.root.signals.storyGoalCompleted.add(this.rerenderFull, this);
this.root.signals.upgradePurchased.add(this.updateShapesAfterUpgrade, this);
this.root.signals.postLoadHook.add(this.rerenderFull, this);
this.root.hud.signals.shapePinRequested.add(this.pinNewShape, this);
this.root.hud.signals.shapeUnpinRequested.add(this.unpinShape, this);
// Perform initial render
this.updateShapesAfterUpgrade();
}
/**
* Updates all shapes after an upgrade has been purchased and removes the unused ones
*/
updateShapesAfterUpgrade() {
for (let i = 0; i < this.pinnedShapes.length; ++i) {
const key = this.pinnedShapes[i];
if (key === blueprintShape) {
// Ignore blueprint shapes
continue;
}
let goal = this.findGoalValueForShape(key);
if (!goal) {
// Seems no longer relevant
this.pinnedShapes.splice(i, 1);
i -= 1;
}
}
this.rerenderFull();
}
/**
* Finds the current goal for the given key. If the key is the story goal, returns
* the story goal. If its the blueprint shape, no goal is returned. Otherwise
* it's searched for upgrades.
* @param {string} key
*/
findGoalValueForShape(key) {
if (key === this.root.hubGoals.currentGoal.definition.getHash()) {
return this.root.hubGoals.currentGoal.required;
}
if (key === blueprintShape) {
return null;
}
// Check if this shape is required for any upgrade
for (const upgradeId in UPGRADES) {
const upgradeTiers = UPGRADES[upgradeId];
const currentTier = this.root.hubGoals.getUpgradeLevel(upgradeId);
const tierHandle = upgradeTiers[currentTier];
if (!tierHandle) {
// Max level
continue;
}
for (let i = 0; i < tierHandle.required.length; ++i) {
const { shape, amount } = tierHandle.required[i];
if (shape === key) {
return amount;
}
}
}
return null;
}
/**
* Returns whether a given shape is currently pinned
* @param {string} key
*/
isShapePinned(key) {
if (key === this.root.hubGoals.currentGoal.definition.getHash() || key === blueprintShape) {
// This is a "special" shape which is always pinned
return true;
}
return this.pinnedShapes.indexOf(key) >= 0;
}
/**
* Rerenders the whole component
*/
rerenderFull() {
const currentGoal = this.root.hubGoals.currentGoal;
const currentKey = currentGoal.definition.getHash();
// First, remove all old shapes
for (let i = 0; i < this.handles.length; ++i) {
this.handles[i].element.remove();
const detector = this.handles[i].detector;
if (detector) {
detector.cleanup();
}
const infoDetector = this.handles[i].infoDetector;
if (infoDetector) {
infoDetector.cleanup();
}
}
this.handles = [];
// Pin story goal
this.internalPinShape(currentKey, false, "goal");
// Pin blueprint shape as well
if (this.root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_blueprints)) {
this.internalPinShape(blueprintShape, false, "blueprint");
}
// Pin manually pinned shapes
for (let i = 0; i < this.pinnedShapes.length; ++i) {
const key = this.pinnedShapes[i];
if (key !== currentKey) {
this.internalPinShape(key);
}
}
}
/**
* Pins a new shape
* @param {string} key
* @param {boolean} canUnpin
* @param {string=} className
*/
internalPinShape(key, canUnpin = true, className = null) {
const definition = this.root.shapeDefinitionMgr.getShapeFromShortKey(key);
const element = makeDiv(this.element, null, ["shape"]);
const canvas = definition.generateAsCanvas(120);
element.appendChild(canvas);
if (className) {
element.classList.add(className);
}
let detector = null;
if (canUnpin) {
element.classList.add("unpinable");
detector = new ClickDetector(element, {
consumeEvents: true,
preventDefault: true,
targetOnly: true,
});
detector.click.add(() => this.unpinShape(key));
} else {
element.classList.add("marked");
}
// Show small info icon
const infoButton = document.createElement("button");
infoButton.classList.add("infoButton");
element.appendChild(infoButton);
const infoDetector = new ClickDetector(infoButton, {
consumeEvents: true,
preventDefault: true,
targetOnly: true,
});
infoDetector.click.add(() => this.root.hud.signals.viewShapeDetailsRequested.dispatch(definition));
const amountLabel = makeDiv(element, null, ["amountLabel"], "");
const goal = this.findGoalValueForShape(key);
if (goal) {
makeDiv(element, null, ["goalLabel"], "/" + formatBigNumber(goal));
}
this.handles.push({
key,
element,
amountLabel,
lastRenderedValue: "",
detector,
infoDetector,
});
}
/**
* Updates all amount labels
*/
update() {
for (let i = 0; i < this.handles.length; ++i) {
const handle = this.handles[i];
const currentValue = this.root.hubGoals.getShapesStoredByKey(handle.key);
const currentValueFormatted = formatBigNumber(currentValue);
if (currentValueFormatted !== handle.lastRenderedValue) {
handle.lastRenderedValue = currentValueFormatted;
handle.amountLabel.innerText = currentValueFormatted;
const goal = this.findGoalValueForShape(handle.key);
handle.element.classList.toggle("completed", goal && currentValue > goal);
}
}
}
/**
* Unpins a shape
* @param {string} key
*/
unpinShape(key) {
arrayDeleteValue(this.pinnedShapes, key);
this.rerenderFull();
}
/**
* Requests to pin a new shape
* @param {ShapeDefinition} definition
*/
pinNewShape(definition) {
const key = definition.getHash();
if (key === this.root.hubGoals.currentGoal.definition.getHash()) {
// Can not pin current goal
return;
}
if (key === blueprintShape) {
// Can not pin the blueprint shape
return;
}
// Check if its already pinned
if (this.pinnedShapes.indexOf(key) >= 0) {
return;
}
this.pinnedShapes.push(key);
this.rerenderFull();
}
}

View File

@ -89,8 +89,8 @@ export class HUDSandboxController extends BaseHUDPart {
}
modifyUpgrade(id, amount) {
const handle = UPGRADES[id];
const maxLevel = handle.tiers.length;
const upgradeTiers = UPGRADES[id];
const maxLevel = upgradeTiers.length;
this.root.hubGoals.upgradeLevels[id] = Math.max(
0,
@ -100,7 +100,7 @@ export class HUDSandboxController extends BaseHUDPart {
// Compute improvement
let improvement = 1;
for (let i = 0; i < this.root.hubGoals.upgradeLevels[id]; ++i) {
improvement += handle.tiers[i].improvement;
improvement += upgradeTiers[i].improvement;
}
this.root.hubGoals.upgradeImprovements[id] = improvement;
this.root.signals.upgradePurchased.dispatch(id);

View File

@ -59,11 +59,11 @@ export class HUDShop extends BaseHUDPart {
rerenderFull() {
for (const upgradeId in this.upgradeToElements) {
const handle = this.upgradeToElements[upgradeId];
const { tiers } = UPGRADES[upgradeId];
const upgradeTiers = UPGRADES[upgradeId];
const currentTier = this.root.hubGoals.getUpgradeLevel(upgradeId);
const currentTierMultiplier = this.root.hubGoals.upgradeImprovements[upgradeId];
const tierHandle = tiers[currentTier];
const tierHandle = upgradeTiers[currentTier];
// Set tier
handle.elemTierLabel.innerText = T.ingame.shop.tier.replace(

View File

@ -45,7 +45,7 @@ export const KEYMAPPINGS = {
buildings: {
belt: { keyCode: key("1") },
splitter: { keyCode: key("2") },
balancer: { keyCode: key("2") },
underground_belt: { keyCode: key("3") },
miner: { keyCode: key("4") },
cutter: { keyCode: key("5") },

View File

@ -7,7 +7,7 @@ import { enumMinerVariants, MetaMinerBuilding } from "./buildings/miner";
import { MetaMixerBuilding } from "./buildings/mixer";
import { enumPainterVariants, MetaPainterBuilding } from "./buildings/painter";
import { enumRotaterVariants, MetaRotaterBuilding } from "./buildings/rotater";
import { enumSplitterVariants, MetaSplitterBuilding } from "./buildings/splitter";
import { enumBalancerVariants, MetaBalancerBuilding } from "./buildings/balancer";
import { MetaStackerBuilding } from "./buildings/stacker";
import { enumTrashVariants, MetaTrashBuilding } from "./buildings/trash";
import { enumUndergroundBeltVariants, MetaUndergroundBeltBuilding } from "./buildings/underground_belt";
@ -26,7 +26,7 @@ import { MetaReaderBuilding } from "./buildings/reader";
const logger = createLogger("building_registry");
export function initMetaBuildingRegistry() {
gMetaBuildingRegistry.register(MetaSplitterBuilding);
gMetaBuildingRegistry.register(MetaBalancerBuilding);
gMetaBuildingRegistry.register(MetaMinerBuilding);
gMetaBuildingRegistry.register(MetaCutterBuilding);
gMetaBuildingRegistry.register(MetaRotaterBuilding);
@ -52,12 +52,12 @@ export function initMetaBuildingRegistry() {
registerBuildingVariant(2, MetaBeltBuilding, defaultBuildingVariant, 1);
registerBuildingVariant(3, MetaBeltBuilding, defaultBuildingVariant, 2);
// Splitter
registerBuildingVariant(4, MetaSplitterBuilding);
registerBuildingVariant(5, MetaSplitterBuilding, enumSplitterVariants.compact);
registerBuildingVariant(6, MetaSplitterBuilding, enumSplitterVariants.compactInverse);
registerBuildingVariant(47, MetaSplitterBuilding, enumSplitterVariants.compactMerge);
registerBuildingVariant(48, MetaSplitterBuilding, enumSplitterVariants.compactMergeInverse);
// Balancer
registerBuildingVariant(4, MetaBalancerBuilding);
registerBuildingVariant(5, MetaBalancerBuilding, enumBalancerVariants.merger);
registerBuildingVariant(6, MetaBalancerBuilding, enumBalancerVariants.mergerInverse);
registerBuildingVariant(47, MetaBalancerBuilding, enumBalancerVariants.splitter);
registerBuildingVariant(48, MetaBalancerBuilding, enumBalancerVariants.splitterInverse);
// Miner
registerBuildingVariant(7, MetaMinerBuilding);

View File

@ -45,7 +45,7 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
* @type {Object<enumItemProcessorTypes, function(ProcessorImplementationPayload) : string>}
*/
this.handlers = {
[enumItemProcessorTypes.splitter]: this.process_SPLITTER,
[enumItemProcessorTypes.balancer]: this.process_BALANCER,
[enumItemProcessorTypes.cutter]: this.process_CUTTER,
[enumItemProcessorTypes.cutterQuad]: this.process_CUTTER_QUAD,
[enumItemProcessorTypes.rotater]: this.process_ROTATER,
@ -305,8 +305,7 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
/**
* @param {ProcessorImplementationPayload} payload
*/
process_SPLITTER(payload) {
// trackProduction = false;
process_BALANCER(payload) {
const availableSlots = payload.entity.components.ItemEjector.slots.length;
const processorComp = payload.entity.components.ItemProcessor;

View File

@ -11,21 +11,19 @@ export const enumHubGoalRewards = {
reward_painter: "reward_painter",
reward_mixer: "reward_mixer",
reward_stacker: "reward_stacker",
reward_splitter: "reward_splitter",
reward_balancer: "reward_balancer",
reward_tunnel: "reward_tunnel",
reward_rotater_ccw: "reward_rotater_ccw",
reward_rotater_180: "reward_rotater_fl",
reward_rotater_180: "reward_rotater_180",
reward_miner_chainable: "reward_miner_chainable",
reward_underground_belt_tier_2: "reward_underground_belt_tier_2",
reward_splitter_compact: "reward_splitter_compact",
reward_splitter: "reward_splitter",
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_merger: "reward_merger",
reward_blueprints: "reward_blueprints",
reward_freeplay: "reward_freeplay",
@ -55,14 +53,14 @@ export const tutorialGoals = [
// Rectangle
{
shape: "RuRuRuRu", // miners t1
required: 100,
reward: enumHubGoalRewards.reward_splitter,
required: 85,
reward: enumHubGoalRewards.reward_balancer,
},
// 4
{
shape: "RuRu----", // processors t2
required: 120,
required: 100,
reward: enumHubGoalRewards.reward_rotater,
},
@ -70,14 +68,14 @@ export const tutorialGoals = [
// Rotater
{
shape: "Cu----Cu", // belts t2
required: 200,
required: 175,
reward: enumHubGoalRewards.reward_tunnel,
},
// 6
{
shape: "Cu------", // miners t2
required: 400,
required: 250,
reward: enumHubGoalRewards.reward_painter,
},
@ -85,14 +83,14 @@ export const tutorialGoals = [
// Painter
{
shape: "CrCrCrCr", // unused
required: 800,
required: 500,
reward: enumHubGoalRewards.reward_rotater_ccw,
},
// 8
{
shape: "RbRb----", // painter t2
required: 1000,
required: 700,
reward: enumHubGoalRewards.reward_mixer,
},
@ -100,15 +98,15 @@ export const tutorialGoals = [
// Mixing (purple)
{
shape: "CpCpCpCp", // belts t3
required: 1400,
reward: enumHubGoalRewards.reward_splitter_compact,
required: 800,
reward: enumHubGoalRewards.reward_splitter,
},
// 10
// Star shape + cyan
{
shape: "ScScScSc", // miners t3
required: 1600,
required: 900,
reward: enumHubGoalRewards.reward_stacker,
},
@ -116,7 +114,7 @@ export const tutorialGoals = [
// Stacker
{
shape: "CgScScCg", // processors t3
required: 1800,
required: 1000,
reward: enumHubGoalRewards.reward_miner_chainable,
},
@ -124,49 +122,56 @@ export const tutorialGoals = [
// Blueprints
{
shape: "CbCbCbRb:CwCwCwCw",
required: 2000,
required: 1250,
reward: enumHubGoalRewards.reward_blueprints,
},
// 13
{
shape: "RpRpRpRp:CwCwCwCw", // painting t3
required: 12000,
required: 5000,
reward: enumHubGoalRewards.reward_underground_belt_tier_2,
},
// 14
{
shape: "SrSrSrSr:CyCyCyCy", // unused
required: 16000,
required: 7500,
reward: enumHubGoalRewards.reward_storage,
},
// 15
{
shape: "SrSrSrSr:CyCyCyCy:SwSwSwSw", // belts t4 (two variants)
required: 25000,
required: 15000,
reward: enumHubGoalRewards.reward_cutter_quad,
},
// 16
{
shape: "CbRbRbCb:CwCwCwCw:WbWbWbWb", // miner t4 (two variants)
required: 50000,
required: 20000,
reward: enumHubGoalRewards.reward_painter_double,
},
// 17
{
shape: "WrRgWrRg:CwCrCwCr:SgSgSgSg", // processors t4 (two variants)
required: 120000,
reward: enumHubGoalRewards.reward_painter_quad,
shape: "CbRbRbCb:CwCwCwCw:WbWbWbWb", // rotater 180
required: 25000,
reward: enumHubGoalRewards.reward_rotater_180,
},
// 18
{
shape: "WrRgWrRg:CwCrCwCr:SgSgSgSg", // processors t4 (two variants)
required: 30000,
reward: enumHubGoalRewards.reward_painter_quad,
},
// 19
{
shape: finalGameShape,
required: 250000,
required: 50000,
reward: enumHubGoalRewards.reward_freeplay,
},
];

View File

@ -4,7 +4,7 @@ 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 { MetaBalancerBuilding, enumBalancerVariants } from "./buildings/balancer";
import { MetaUndergroundBeltBuilding, enumUndergroundBeltVariants } from "./buildings/underground_belt";
import { MetaMinerBuilding, enumMinerVariants } from "./buildings/miner";
import { MetaTrashBuilding, enumTrashVariants } from "./buildings/trash";
@ -29,7 +29,7 @@ export const enumHubGoalRewardsToContentUnlocked = {
[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_balancer]: typed([[MetaBalancerBuilding, defaultBuildingVariant]]),
[enumHubGoalRewards.reward_tunnel]: typed([[MetaUndergroundBeltBuilding, defaultBuildingVariant]]),
[enumHubGoalRewards.reward_rotater_ccw]: typed([[MetaRotaterBuilding, enumRotaterVariants.ccw]]),
@ -38,9 +38,8 @@ export const enumHubGoalRewardsToContentUnlocked = {
[enumHubGoalRewards.reward_underground_belt_tier_2]: typed([
[MetaUndergroundBeltBuilding, enumUndergroundBeltVariants.tier2],
]),
[enumHubGoalRewards.reward_splitter_compact]: typed([
[MetaSplitterBuilding, enumSplitterVariants.compact],
]),
[enumHubGoalRewards.reward_splitter]: typed([[MetaBalancerBuilding, enumBalancerVariants.splitter]]),
[enumHubGoalRewards.reward_merger]: typed([[MetaBalancerBuilding, enumBalancerVariants.merger]]),
[enumHubGoalRewards.reward_cutter_quad]: typed([[MetaCutterBuilding, enumCutterVariants.quad]]),
[enumHubGoalRewards.reward_painter_double]: typed([[MetaPainterBuilding, enumPainterVariants.double]]),
[enumHubGoalRewards.reward_painter_quad]: typed([[MetaPainterBuilding, enumPainterVariants.quad]]),

View File

@ -1,175 +1,160 @@
import { findNiceIntegerValue } from "../core/utils";
import { ShapeDefinition } from "./shape_definition";
export const finalGameShape = "RuCw--Cw:----Ru--";
export const blueprintShape = "CbCbCbRb:CwCwCwCw";
export const UPGRADES = {
belt: {
tiers: [
{
required: [{ shape: "CuCuCuCu", amount: 150 }],
improvement: 1,
},
{
required: [{ shape: "--CuCu--", amount: 1200 }],
improvement: 2,
},
{
required: [{ shape: "CpCpCpCp", amount: 15000 }],
improvement: 2,
},
{
required: [{ shape: "SrSrSrSr:CyCyCyCy", amount: 40000 }],
improvement: 2,
},
{
required: [{ shape: "SrSrSrSr:CyCyCyCy:SwSwSwSw", amount: 40000 }],
improvement: 2,
},
{
required: [{ shape: finalGameShape, amount: 150000 }],
improvement: 5,
excludePrevious: true,
},
],
},
miner: {
tiers: [
{
required: [{ shape: "RuRuRuRu", amount: 400 }],
improvement: 1,
},
{
required: [{ shape: "Cu------", amount: 4000 }],
improvement: 2,
},
{
required: [{ shape: "ScScScSc", amount: 20000 }],
improvement: 2,
},
{
required: [{ shape: "CwCwCwCw:WbWbWbWb", amount: 40000 }],
improvement: 2,
},
{
required: [{ shape: "CbRbRbCb:CwCwCwCw:WbWbWbWb", amount: 40000 }],
improvement: 2,
},
{
required: [{ shape: finalGameShape, amount: 150000 }],
improvement: 5,
excludePrevious: true,
},
],
},
processors: {
tiers: [
{
required: [{ shape: "SuSuSuSu", amount: 1000 }],
improvement: 1,
},
{
required: [{ shape: "RuRu----", amount: 2000 }],
improvement: 2,
},
{
required: [{ shape: "CgScScCg", amount: 25000 }],
improvement: 2,
},
{
required: [{ shape: "CwCrCwCr:SgSgSgSg", amount: 40000 }],
improvement: 2,
},
{
required: [{ shape: "WrRgWrRg:CwCrCwCr:SgSgSgSg", amount: 40000 }],
improvement: 2,
},
{
required: [{ shape: finalGameShape, amount: 150000 }],
improvement: 5,
excludePrevious: true,
},
],
},
painting: {
tiers: [
{
required: [{ shape: "RbRb----", amount: 1500 }],
improvement: 2,
},
{
required: [{ shape: "WrWrWrWr", amount: 4000 }],
improvement: 1,
},
{
required: [{ shape: "RpRpRpRp:CwCwCwCw", amount: 30000 }],
improvement: 2,
},
{
required: [{ shape: "WpWpWpWp:CwCwCwCw:WpWpWpWp", amount: 40000 }],
improvement: 2,
},
{
required: [{ shape: "WpWpWpWp:CwCwCwCw:WpWpWpWp:CwCwCwCw", amount: 40000 }],
improvement: 2,
},
{
required: [{ shape: finalGameShape, amount: 150000 }],
improvement: 5,
excludePrevious: true,
},
],
},
};
// Tiers need % of the previous tier as requirement too
const tierGrowth = 2.5;
// Automatically generate tier levels
for (const upgradeId in UPGRADES) {
const upgrade = UPGRADES[upgradeId];
let currentTierRequirements = [];
for (let i = 0; i < upgrade.tiers.length; ++i) {
const tierHandle = upgrade.tiers[i];
const originalRequired = tierHandle.required.slice();
for (let k = currentTierRequirements.length - 1; k >= 0; --k) {
const oldTierRequirement = currentTierRequirements[k];
if (!tierHandle.excludePrevious) {
tierHandle.required.unshift({
shape: oldTierRequirement.shape,
amount: oldTierRequirement.amount,
});
}
}
currentTierRequirements.push(
...originalRequired.map(req => ({
amount: req.amount,
shape: req.shape,
}))
);
currentTierRequirements.forEach(tier => {
tier.amount = findNiceIntegerValue(tier.amount * tierGrowth);
});
}
}
if (G_IS_DEV) {
for (const upgradeId in UPGRADES) {
const upgrade = UPGRADES[upgradeId];
upgrade.tiers.forEach(tier => {
tier.required.forEach(({ shape }) => {
try {
ShapeDefinition.fromShortKey(shape);
} catch (ex) {
throw new Error("Invalid upgrade goal: '" + ex + "' for shape" + shape);
}
});
});
}
}
import { findNiceIntegerValue } from "../core/utils";
import { ShapeDefinition } from "./shape_definition";
export const finalGameShape = "RuCw--Cw:----Ru--";
export const blueprintShape = "CbCbCbRb:CwCwCwCw";
const fixedImprovements = [0.5, 0.5, 1, 1, 2, 2];
/** @typedef {{
* shape: string,
* amount: number
* }} UpgradeRequirement */
/** @typedef {{
* required: Array<UpgradeRequirement>
* improvement?: number,
* excludePrevious?: boolean
* }} TierRequirement */
/** @typedef {Array<TierRequirement>} UpgradeTiers */
/** @type {Object<string, UpgradeTiers>} */
export const UPGRADES = {
belt: [
{
required: [{ shape: "CuCuCuCu", amount: 150 }],
},
{
required: [{ shape: "--CuCu--", amount: 1000 }],
},
{
required: [{ shape: "CpCpCpCp", amount: 5000 }],
},
{
required: [{ shape: "SrSrSrSr:CyCyCyCy", amount: 12000 }],
},
{
required: [{ shape: "SrSrSrSr:CyCyCyCy:SwSwSwSw", amount: 20000 }],
},
{
required: [{ shape: finalGameShape, amount: 75000 }],
excludePrevious: true,
},
],
miner: [
{
required: [{ shape: "RuRuRuRu", amount: 400 }],
},
{
required: [{ shape: "Cu------", amount: 3000 }],
},
{
required: [{ shape: "ScScScSc", amount: 7000 }],
},
{
required: [{ shape: "CwCwCwCw:WbWbWbWb", amount: 15000 }],
},
{
required: [{ shape: "CbRbRbCb:CwCwCwCw:WbWbWbWb", amount: 30000 }],
},
{
required: [{ shape: finalGameShape, amount: 85000 }],
excludePrevious: true,
},
],
processors: [
{
required: [{ shape: "SuSuSuSu", amount: 600 }],
},
{
required: [{ shape: "RuRu----", amount: 2000 }],
},
{
required: [{ shape: "CgScScCg", amount: 15000 }],
},
{
required: [{ shape: "CwCrCwCr:SgSgSgSg", amount: 20000 }],
},
{
required: [{ shape: "WrRgWrRg:CwCrCwCr:SgSgSgSg", amount: 30000 }],
},
{
required: [{ shape: finalGameShape, amount: 100000 }],
excludePrevious: true,
},
],
painting: [
{
required: [{ shape: "RbRb----", amount: 1000 }],
},
{
required: [{ shape: "WrWrWrWr", amount: 3000 }],
},
{
required: [{ shape: "RpRpRpRp:CwCwCwCw", amount: 15000 }],
},
{
required: [{ shape: "WpWpWpWp:CwCwCwCw:WpWpWpWp", amount: 20000 }],
},
{
required: [{ shape: "WpWpWpWp:CwCwCwCw:WpWpWpWp:CwCwCwCw", amount: 30000 }],
},
{
required: [{ shape: finalGameShape, amount: 125000 }],
excludePrevious: true,
},
],
};
// Tiers need % of the previous tier as requirement too
const tierGrowth = 1.8;
// Automatically generate tier levels
for (const upgradeId in UPGRADES) {
const upgradeTiers = UPGRADES[upgradeId];
let currentTierRequirements = [];
for (let i = 0; i < upgradeTiers.length; ++i) {
const tierHandle = upgradeTiers[i];
tierHandle.improvement = fixedImprovements[i];
const originalRequired = tierHandle.required.slice();
for (let k = currentTierRequirements.length - 1; k >= 0; --k) {
const oldTierRequirement = currentTierRequirements[k];
if (!tierHandle.excludePrevious) {
tierHandle.required.unshift({
shape: oldTierRequirement.shape,
amount: oldTierRequirement.amount,
});
}
}
currentTierRequirements.push(
...originalRequired.map(req => ({
amount: req.amount,
shape: req.shape,
}))
);
currentTierRequirements.forEach(tier => {
tier.amount = findNiceIntegerValue(tier.amount * tierGrowth);
});
}
}
// VALIDATE
if (G_IS_DEV) {
for (const upgradeId in UPGRADES) {
UPGRADES[upgradeId].forEach(tier => {
tier.required.forEach(({ shape }) => {
try {
ShapeDefinition.fromShortKey(shape);
} catch (ex) {
throw new Error("Invalid upgrade goal: '" + ex + "' for shape" + shape);
}
});
});
}
}

View File

@ -192,10 +192,9 @@ export class ShapezGameAnalytics extends GameAnalyticsInterface {
// Check if its required to unlock an upgrade
for (const upgradeKey in UPGRADES) {
const handle = UPGRADES[upgradeKey];
const tiers = handle.tiers;
for (let i = 0; i < tiers.length; ++i) {
const tier = tiers[i];
const upgradeTiers = UPGRADES[upgradeKey];
for (let i = 0; i < upgradeTiers.length; ++i) {
const tier = upgradeTiers[i];
const required = tier.required;
for (let k = 0; k < required.length; ++k) {
if (required[k].shape === key) {

View File

@ -7,7 +7,7 @@ import { enumMinerVariants, MetaMinerBuilding } from "../../game/buildings/miner
import { MetaMixerBuilding } from "../../game/buildings/mixer.js";
import { enumPainterVariants, MetaPainterBuilding } from "../../game/buildings/painter.js";
import { enumRotaterVariants, MetaRotaterBuilding } from "../../game/buildings/rotater.js";
import { enumSplitterVariants, MetaSplitterBuilding } from "../../game/buildings/splitter.js";
import { enumBalancerVariants, MetaBalancerBuilding } from "../../game/buildings/balancer.js";
import { MetaStackerBuilding } from "../../game/buildings/stacker.js";
import { enumTrashVariants, MetaTrashBuilding } from "../../game/buildings/trash.js";
import {
@ -33,6 +33,15 @@ function findCode(metaBuilding, variant = defaultBuildingVariant, rotationVarian
return getCodeFromBuildingData(gMetaBuildingRegistry.findByClass(metaBuilding), variant, rotationVariant);
}
/**
* Rebalances a value from the old balancing to the new one
* @param {number} value
* @returns {number}
*/
function rebalance(value) {
return Math.round(Math.pow(value, 0.75));
}
export class SavegameInterface_V1006 extends SavegameInterface_V1005 {
getVersion() {
return 1006;
@ -49,15 +58,15 @@ export class SavegameInterface_V1006 extends SavegameInterface_V1005 {
"sprites/blueprints/belt_left.png": findCode(MetaBeltBuilding, defaultBuildingVariant, 1),
"sprites/blueprints/belt_right.png": findCode(MetaBeltBuilding, defaultBuildingVariant, 2),
// Splitter
"sprites/blueprints/splitter.png": findCode(MetaSplitterBuilding),
// Splitter (=Balancer)
"sprites/blueprints/splitter.png": findCode(MetaBalancerBuilding),
"sprites/blueprints/splitter-compact.png": findCode(
MetaSplitterBuilding,
enumSplitterVariants.compact
MetaBalancerBuilding,
enumBalancerVariants.merger
),
"sprites/blueprints/splitter-compact-inverse.png": findCode(
MetaSplitterBuilding,
enumSplitterVariants.compactInverse
MetaBalancerBuilding,
enumBalancerVariants.mergerInverse
),
// Underground belt
@ -133,6 +142,17 @@ export class SavegameInterface_V1006 extends SavegameInterface_V1005 {
return true;
}
// Reduce stored shapes
const stored = dump.hubGoals.storedShapes;
for (const shapeKey in stored) {
stored[shapeKey] = rebalance(stored[shapeKey]);
}
// Reduce goals
if (dump.hubGoals.currentGoal) {
dump.hubGoals.currentGoal.required = rebalance(dump.hubGoals.currentGoal.required);
}
// Update entities
const entities = dump.entities;
for (let i = 0; i < entities.length; ++i) {

View File

@ -484,25 +484,25 @@ buildings:
name: Tunnel Tier II
description: Allows you to tunnel resources under buildings and belts.
# Internal name for the Balancer
splitter:
# Balancer
balancer:
default:
name: &splitter Balancer
name: &balancer Balancer
description: Multifunctional - Evenly distributes all inputs onto all outputs.
compact:
merger:
name: Merger (compact)
description: Merges two conveyor belts into one.
compact-inverse:
merger-inverse:
name: Merger (compact)
description: Merges two conveyor belts into one.
compact-merge:
splitter:
name: Splitter (compact)
description: Splits one conveyor belt into two.
compact-merge-inverse:
splitter-inverse:
name: Splitter (compact)
description: Splits one conveyor belt into two.
@ -673,8 +673,8 @@ storyRewards:
title: Combiner
desc: You can now combine shapes with the <strong>combiner</strong>! Both inputs are combined, and if they can be put next to each other, they will be <strong>fused</strong>. If not, the right input is <strong>stacked on top</strong> of the left input!
reward_splitter:
title: Splitter/Merger
reward_balancer:
title: Balancer
desc: The multifunctional <strong>balancer</strong> has been unlocked - It can be used to build bigger factories by <strong>splitting and merging items</strong> onto multiple belts!<br><br>
reward_tunnel:
@ -693,10 +693,15 @@ storyRewards:
title: Tunnel Tier II
desc: You have unlocked a new variant of the <strong>tunnel</strong> - It has a <strong>bigger range</strong>, and you can also mix-n-match those tunnels now!
reward_splitter_compact:
title: Compact Balancer
reward_merger:
title: Compact Merger
desc: >-
You have unlocked a compact variant of the <strong>balancer</strong> - It accepts two inputs and merges them into one belt!
You have unlocked a merger variant of the <strong>balancer</strong> - It accepts two inputs and merges them into one belt!
reward_splitter:
title: Compact Splitter
desc: >-
You have unlocked a merger variant of the <strong>balancer</strong> - It accepts one input and splits them into two!
reward_cutter_quad:
title: Quad Cutting
@ -722,6 +727,10 @@ storyRewards:
title: Blueprints
desc: You can now <strong>copy and paste</strong> parts of your factory! Select an area (Hold CTRL, then drag with your mouse), and press 'C' to copy it.<br><br>Pasting it is <strong>not free</strong>, you need to produce <strong>blueprint shapes</strong> to afford it! (Those you just delivered).
reward_rotater_180:
title: Rotater (180 degrees)
desc: You just unlocked the 180 degress <strong>rotater</strong>! - It allows you to rotate a shape by 180 degress (Surprise! :D)
# Special reward, which is shown when there is no reward actually
no_reward:
title: Next level
@ -962,7 +971,7 @@ keybindings:
# --- Do not translate the values in this section
belt: *belt
splitter: *splitter
balancer: *balancer
underground_belt: *underground_belt
miner: *miner
cutter: *cutter