From b94f60c5d97b5566a2c78d8fb77e50fc95603267 Mon Sep 17 00:00:00 2001 From: TcePrepK <56453014+TcePrepK@users.noreply.github.com> Date: Sat, 7 Nov 2020 23:08:23 +0300 Subject: [PATCH] Fixed Branch --- .../wireless_display-remote_control.png | Bin 0 -> 9880 bytes .../sprites/blueprints/wireless_display.png | Bin 0 -> 3404 bytes .../wireless_display-remote_control.png | Bin 0 -> 8675 bytes .../sprites/buildings/wireless_display.png | Bin 0 -> 4479 bytes src/css/resources.scss | 4 +- src/js/game/buildings/wire.js | 2 +- src/js/game/buildings/wire_tunnel.js | 2 +- src/js/game/buildings/wireless_display.js | 87 ++++++ src/js/game/component_registry.js | 4 + src/js/game/components/wire.js | 2 +- src/js/game/components/wire_tunnel.js | 3 +- src/js/game/components/wireless_code.js | 23 ++ src/js/game/components/wireless_display.js | 36 +++ src/js/game/core.js | 1 - src/js/game/entity.js | 1 + src/js/game/entity_components.js | 8 + src/js/game/game_system_manager.js | 5 + src/js/game/hub_goals.js | 3 +- src/js/game/hud/parts/buildings_toolbar.js | 2 + src/js/game/hud/parts/mass_selector.js | 23 +- src/js/game/hud/parts/wires_toolbar.js | 2 + src/js/game/key_action_mapper.js | 1 + src/js/game/logic.js | 7 +- src/js/game/map_chunk_view.js | 1 + src/js/game/meta_building.js | 7 +- src/js/game/meta_building_registry.js | 8 +- src/js/game/systems/constant_signal.js | 1 - .../game/systems/item_processor_overlays.js | 4 +- src/js/game/systems/wire.js | 46 ++- src/js/game/systems/wired_pins.js | 5 +- src/js/game/systems/wireless_display.js | 273 ++++++++++++++++++ translations/base-en.yaml | 105 +++++-- 32 files changed, 587 insertions(+), 79 deletions(-) create mode 100644 res_raw/sprites/blueprints/wireless_display-remote_control.png create mode 100644 res_raw/sprites/blueprints/wireless_display.png create mode 100644 res_raw/sprites/buildings/wireless_display-remote_control.png create mode 100644 res_raw/sprites/buildings/wireless_display.png create mode 100644 src/js/game/buildings/wireless_display.js create mode 100644 src/js/game/components/wireless_code.js create mode 100644 src/js/game/components/wireless_display.js create mode 100644 src/js/game/systems/wireless_display.js diff --git a/res_raw/sprites/blueprints/wireless_display-remote_control.png b/res_raw/sprites/blueprints/wireless_display-remote_control.png new file mode 100644 index 0000000000000000000000000000000000000000..304ad1af40627724fd74f04f77a100203bf2f384 GIT binary patch literal 9880 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 ZTI>=(BI~QnRp$RYkY5nTuyoy~{{Ze+O+o+w literal 0 HcmV?d00001 diff --git a/res_raw/sprites/buildings/wireless_display-remote_control.png b/res_raw/sprites/buildings/wireless_display-remote_control.png new file mode 100644 index 0000000000000000000000000000000000000000..26e8dbd426274f711aa24ac718282a8c323c4543 GIT binary patch literal 8675 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 literal 0 HcmV?d00001 diff --git a/res_raw/sprites/buildings/wireless_display.png b/res_raw/sprites/buildings/wireless_display.png new file mode 100644 index 0000000000000000000000000000000000000000..0533d5136b8f1198192368aaee7ddd2622d9ac08 GIT binary patch literal 4479 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 literal 0 HcmV?d00001 diff --git a/src/css/resources.scss b/src/css/resources.scss index 08bfa43f..9412c83e 100644 --- a/src/css/resources.scss +++ b/src/css/resources.scss @@ -1,5 +1,5 @@ $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, storage, + constant_signal, logic_gate, lever, filter, wire_tunnel, display, wireless_display, virtual_processor, reader, storage, transistor, analyzer, comparator, item_producer; @each $building in $buildings { @@ -11,7 +11,7 @@ $buildings: belt, cutter, miner, mixer, painter, rotater, balancer, stacker, tra $buildingsAndVariants: belt, balancer, underground_belt, underground_belt-tier2, miner, miner-chainable, cutter, cutter-quad, rotater, rotater-ccw, stacker, mixer, painter-double, painter-quad, trash, storage, - reader, rotater-rotate180, display, constant_signal, wire, wire_tunnel, logic_gate-or, logic_gate-not, + reader, rotater-rotate180, display, wireless_display, wireless_display-remote_control, constant_signal, wire, wire_tunnel, logic_gate-or, logic_gate-not, logic_gate-xor, analyzer, virtual_processor-rotater, virtual_processor-unstacker, item_producer, virtual_processor-stacker, virtual_processor-painter, wire-second, painter, painter-mirrored, comparator; @each $building in $buildingsAndVariants { diff --git a/src/js/game/buildings/wire.js b/src/js/game/buildings/wire.js index 61b75073..c574ae4d 100644 --- a/src/js/game/buildings/wire.js +++ b/src/js/game/buildings/wire.js @@ -267,4 +267,4 @@ export class MetaWireBuilding extends MetaBuilding { rotationVariant: arrayWireRotationVariantToType.indexOf(targetType), }; } -} +} \ No newline at end of file diff --git a/src/js/game/buildings/wire_tunnel.js b/src/js/game/buildings/wire_tunnel.js index 2626dd12..bdc4b9c9 100644 --- a/src/js/game/buildings/wire_tunnel.js +++ b/src/js/game/buildings/wire_tunnel.js @@ -55,4 +55,4 @@ export class MetaWireTunnelBuilding extends MetaBuilding { setupEntityComponents(entity) { entity.addComponent(new WireTunnelComponent()); } -} +} \ No newline at end of file diff --git a/src/js/game/buildings/wireless_display.js b/src/js/game/buildings/wireless_display.js new file mode 100644 index 00000000..93a1bb54 --- /dev/null +++ b/src/js/game/buildings/wireless_display.js @@ -0,0 +1,87 @@ +import { enumDirection, Vector } from "../../core/vector"; +import { enumPinSlotType, WiredPinsComponent } from "../components/wired_pins"; +import { Entity } from "../entity"; +import { defaultBuildingVariant, MetaBuilding } from "../meta_building"; +import { GameRoot } from "../root"; +import { WirelessDisplayComponent } from "../components/wireless_display"; +import { enumHubGoalRewards } from "../tutorial_goals"; +import { formatItemsPerSecond, generateMatrixRotations } from "../../core/utils"; + + +/** @enum {string} */ +export const enumWirelessDisplayVariants = { + remote_control: "remote_control", +}; + +const overlayMatrices = { + [defaultBuildingVariant]: null, + [enumWirelessDisplayVariants.remote_control]: generateMatrixRotations([0, 1, 0, 0, 1, 1, 0, 1, 0]), +}; + +export class MetaWirelessDisplayBuilding extends MetaBuilding { + constructor() { + super("wireless_display"); + } + + getSilhouetteColor() { + return "#aaaaaa"; + } + + /** + * @param {GameRoot} root + */ + getIsUnlocked(root) { + return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_display); + } + + /** + * @param {GameRoot} root + */ + getAvailableVariants(root) { + let available = [defaultBuildingVariant]; + + if (true || root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_display)) { + available.push(enumWirelessDisplayVariants.remote_control); + } + + return available; + } + + getDimensions() { + return new Vector(1, 1); + } + + getShowWiresLayerPreview() { + return true; + } + + /** + * Creates the entity at the given location + * @param {Entity} entity + */ + setupEntityComponents(entity) { + entity.addComponent(new WirelessDisplayComponent({})); + } + + /** + * + * @param {Entity} entity + * @param {number} rotationVariant + * @param {string} variant + */ + updateVariants(entity, rotationVariant, variant) { + if (variant == enumWirelessDisplayVariants.remote_control && !entity.components.WiredPins) { + entity.addComponent( + new WiredPinsComponent({ + slots: [ + { + pos: new Vector(0, 0), + direction: enumDirection.bottom, + type: enumPinSlotType.logicalAcceptor, + }, + ], + }), + ); + } + } +} diff --git a/src/js/game/component_registry.js b/src/js/game/component_registry.js index f094e60d..de83a929 100644 --- a/src/js/game/component_registry.js +++ b/src/js/game/component_registry.js @@ -19,6 +19,8 @@ import { DisplayComponent } from "./components/display"; import { BeltReaderComponent } from "./components/belt_reader"; import { FilterComponent } from "./components/filter"; import { ItemProducerComponent } from "./components/item_producer"; +import { WirelessDisplayComponent } from "./components/wireless_display"; +import { WirelessCodeComponent } from "./components/wireless_code"; export function initComponentRegistry() { gComponentRegistry.register(StaticMapEntityComponent); @@ -38,9 +40,11 @@ export function initComponentRegistry() { gComponentRegistry.register(LeverComponent); gComponentRegistry.register(WireTunnelComponent); gComponentRegistry.register(DisplayComponent); + gComponentRegistry.register(WirelessDisplayComponent); gComponentRegistry.register(BeltReaderComponent); gComponentRegistry.register(FilterComponent); gComponentRegistry.register(ItemProducerComponent); + gComponentRegistry.register(WirelessCodeComponent); // IMPORTANT ^^^^^ UPDATE ENTITY COMPONENT STORAGE AFTERWARDS diff --git a/src/js/game/components/wire.js b/src/js/game/components/wire.js index d0e354e2..8c210abd 100644 --- a/src/js/game/components/wire.js +++ b/src/js/game/components/wire.js @@ -39,4 +39,4 @@ export class WireComponent extends Component { */ this.linkedNetwork = null; } -} +} \ No newline at end of file diff --git a/src/js/game/components/wire_tunnel.js b/src/js/game/components/wire_tunnel.js index 1c170484..17ac15ea 100644 --- a/src/js/game/components/wire_tunnel.js +++ b/src/js/game/components/wire_tunnel.js @@ -1,3 +1,4 @@ + import { Component } from "../component"; export class WireTunnelComponent extends Component { @@ -14,4 +15,4 @@ export class WireTunnelComponent extends Component { */ this.linkedNetworks = []; } -} +} \ No newline at end of file diff --git a/src/js/game/components/wireless_code.js b/src/js/game/components/wireless_code.js new file mode 100644 index 00000000..2c732e22 --- /dev/null +++ b/src/js/game/components/wireless_code.js @@ -0,0 +1,23 @@ +import { Component } from "../component"; +import { types } from "../../savegame/serialization"; + +export class WirelessCodeComponent extends Component { + static getId() { + return "WirelessCode"; + } + + static getSchema() { + return { + wireless_code: types.string + }; + } + + /** + * + * @param {object} id + */ + constructor(id) { + super(); + this.wireless_code = id; + } +} diff --git a/src/js/game/components/wireless_display.js b/src/js/game/components/wireless_display.js new file mode 100644 index 00000000..063e8990 --- /dev/null +++ b/src/js/game/components/wireless_display.js @@ -0,0 +1,36 @@ +import { Component } from "../component"; +import { types } from "../../savegame/serialization"; +import { BaseItem } from "../base_item"; +import { typeItemSingleton } from "../item_resolver"; + +export class WirelessDisplayComponent extends Component { + static getId() { + return "WirelessDisplay"; + } + + static getSchema() { + return { + signal: types.nullable(typeItemSingleton), + }; + } + + /** + * Copy the current state to another component + * @param {WirelessDisplayComponent} otherComponent + */ + copyAdditionalStateTo(otherComponent) { + otherComponent.signal = this.signal; + } + + /** + * + * @param {object} param0 + * @param {BaseItem=} param0.signal The signal to store + */ + constructor({ signal = null }) { + super(); + this.signal = signal; + } +} + + diff --git a/src/js/game/core.js b/src/js/game/core.js index 2df8989f..93871f62 100644 --- a/src/js/game/core.js +++ b/src/js/game/core.js @@ -89,7 +89,6 @@ export class GameCore { this.root.savegame = savegame; this.root.gameWidth = this.app.screenWidth; this.root.gameHeight = this.app.screenHeight; - // Initialize canvas element & context this.internalInitCanvas(); diff --git a/src/js/game/entity.js b/src/js/game/entity.js index d7dd715e..c15c88a8 100644 --- a/src/js/game/entity.js +++ b/src/js/game/entity.js @@ -96,6 +96,7 @@ export class Entity extends BasicSerializableObject { rotation: staticComp.rotation, rotationVariant: buildingData.rotationVariant, variant: buildingData.variant, + wireless_code: this.components.WirelessCode, }); for (const key in this.components) { diff --git a/src/js/game/entity_components.js b/src/js/game/entity_components.js index 7dee590a..801f0097 100644 --- a/src/js/game/entity_components.js +++ b/src/js/game/entity_components.js @@ -19,6 +19,8 @@ import { DisplayComponent } from "./components/display"; import { BeltReaderComponent } from "./components/belt_reader"; import { FilterComponent } from "./components/filter"; import { ItemProducerComponent } from "./components/item_producer"; +import { WirelessDisplayComponent } from "./components/wireless_display"; +import { WirelessCodeComponent } from "./components/wireless_code"; /* typehints:end */ /** @@ -80,6 +82,9 @@ export class EntityComponentStorage { /** @type {DisplayComponent} */ this.Display; + /** @type {WirelessDisplayComponent} */ + this.WirelessDisplay; + /** @type {BeltReaderComponent} */ this.BeltReader; @@ -89,6 +94,9 @@ export class EntityComponentStorage { /** @type {ItemProducerComponent} */ this.ItemProducer; + /** @type {WirelessCodeComponent} */ + this.WirelessCode; + /* typehints:end */ } } diff --git a/src/js/game/game_system_manager.js b/src/js/game/game_system_manager.js index 74ba798f..6bcc44b1 100644 --- a/src/js/game/game_system_manager.js +++ b/src/js/game/game_system_manager.js @@ -20,6 +20,7 @@ import { ConstantSignalSystem } from "./systems/constant_signal"; import { LogicGateSystem } from "./systems/logic_gate"; import { LeverSystem } from "./systems/lever"; import { DisplaySystem } from "./systems/display"; +import { WirelessDisplaySystem } from "./systems/wireless_display"; import { ItemProcessorOverlaysSystem } from "./systems/item_processor_overlays"; import { BeltReaderSystem } from "./systems/belt_reader"; import { FilterSystem } from "./systems/filter"; @@ -88,6 +89,9 @@ export class GameSystemManager { /** @type {DisplaySystem} */ display: null, + /** @type {WirelessDisplaySystem} */ + wirelessDisplay: null, + /** @type {ItemProcessorOverlaysSystem} */ itemProcessorOverlays: null, @@ -162,6 +166,7 @@ export class GameSystemManager { add("beltReader", BeltReaderSystem); add("display", DisplaySystem); + add("wirelessDisplay", WirelessDisplaySystem); add("itemProcessorOverlays", ItemProcessorOverlaysSystem); diff --git a/src/js/game/hub_goals.js b/src/js/game/hub_goals.js index c9d9494f..9a945128 100644 --- a/src/js/game/hub_goals.js +++ b/src/js/game/hub_goals.js @@ -238,7 +238,8 @@ export class HubGoals extends BasicSerializableObject { return; } - const required = Math.min(200, 4 + (this.level - 27) * 0.25); + //Floor Required amount to remove confusion + const required = Math.min(200, Math.floor(4 + (this.level - 27) * 0.25)); this.currentGoal = { definition: this.computeFreeplayShape(this.level), required, diff --git a/src/js/game/hud/parts/buildings_toolbar.js b/src/js/game/hud/parts/buildings_toolbar.js index 05ffc795..bed5c80f 100644 --- a/src/js/game/hud/parts/buildings_toolbar.js +++ b/src/js/game/hud/parts/buildings_toolbar.js @@ -1,6 +1,7 @@ import { MetaBeltBuilding } from "../../buildings/belt"; import { MetaCutterBuilding } from "../../buildings/cutter"; import { MetaDisplayBuilding } from "../../buildings/display"; +import { MetaWirelessDisplayBuilding } from "../../buildings/wireless_display"; import { MetaFilterBuilding } from "../../buildings/filter"; import { MetaLeverBuilding } from "../../buildings/lever"; import { MetaMinerBuilding } from "../../buildings/miner"; @@ -39,6 +40,7 @@ export class HUDBuildingsToolbar extends HUDBaseToolbar { MetaLeverBuilding, MetaFilterBuilding, MetaDisplayBuilding, + MetaWirelessDisplayBuilding, ], visibilityCondition: () => !this.root.camera.getIsMapOverlayActive() && this.root.currentLayer === "regular", diff --git a/src/js/game/hud/parts/mass_selector.js b/src/js/game/hud/parts/mass_selector.js index 08a11769..c6e03e2f 100644 --- a/src/js/game/hud/parts/mass_selector.js +++ b/src/js/game/hud/parts/mass_selector.js @@ -14,6 +14,7 @@ import { KEYMAPPINGS } from "../../key_action_mapper"; import { THEME } from "../../theme"; import { enumHubGoalRewards } from "../../tutorial_goals"; import { Blueprint } from "../../blueprint"; +import { drawRotatedSprite } from "../../../core/draw_utils"; const logger = createLogger("hud/mass_selector"); @@ -304,16 +305,9 @@ export class HUDMassSelector extends BaseHUDPart { renderedUids.add(uid); const staticComp = contents.components.StaticMapEntity; - const bounds = staticComp.getTileSpaceBounds(); - parameters.context.beginRoundedRect( - bounds.x * globalConfig.tileSize + boundsBorder, - bounds.y * globalConfig.tileSize + boundsBorder, - bounds.w * globalConfig.tileSize - 2 * boundsBorder, - bounds.h * globalConfig.tileSize - 2 * boundsBorder, - 2 - ); - parameters.context.fill(); + staticComp.drawSpriteOnBoundsClipped(parameters, staticComp.getBlueprintSprite(), 0); } + parameters.context.globalAlpha = 1; } } } @@ -322,15 +316,8 @@ export class HUDMassSelector extends BaseHUDPart { this.selectedUids.forEach(uid => { const entity = this.root.entityMgr.findByUid(uid); const staticComp = entity.components.StaticMapEntity; - const bounds = staticComp.getTileSpaceBounds(); - parameters.context.beginRoundedRect( - bounds.x * globalConfig.tileSize + boundsBorder, - bounds.y * globalConfig.tileSize + boundsBorder, - bounds.w * globalConfig.tileSize - 2 * boundsBorder, - bounds.h * globalConfig.tileSize - 2 * boundsBorder, - 2 - ); - parameters.context.fill(); + + staticComp.drawSpriteOnBoundsClipped(parameters, staticComp.getBlueprintSprite(), 0); }); } } diff --git a/src/js/game/hud/parts/wires_toolbar.js b/src/js/game/hud/parts/wires_toolbar.js index 5141bbeb..f5d0c5a3 100644 --- a/src/js/game/hud/parts/wires_toolbar.js +++ b/src/js/game/hud/parts/wires_toolbar.js @@ -11,6 +11,7 @@ import { MetaComparatorBuilding } from "../../buildings/comparator"; import { MetaReaderBuilding } from "../../buildings/reader"; import { MetaFilterBuilding } from "../../buildings/filter"; import { MetaDisplayBuilding } from "../../buildings/display"; +import { MetaWirelessDisplayBuilding } from "../../buildings/wireless_display"; import { MetaStorageBuilding } from "../../buildings/storage"; export class HUDWiresToolbar extends HUDBaseToolbar { @@ -32,6 +33,7 @@ export class HUDWiresToolbar extends HUDBaseToolbar { MetaLeverBuilding, MetaFilterBuilding, MetaDisplayBuilding, + MetaWirelessDisplayBuilding, ], visibilityCondition: () => !this.root.camera.getIsMapOverlayActive() && this.root.currentLayer === "wires", diff --git a/src/js/game/key_action_mapper.js b/src/js/game/key_action_mapper.js index 9fa4ffe1..0c8bbac8 100644 --- a/src/js/game/key_action_mapper.js +++ b/src/js/game/key_action_mapper.js @@ -65,6 +65,7 @@ export const KEYMAPPINGS = { lever: { keyCode: key("I") }, filter: { keyCode: key("O") }, display: { keyCode: key("P") }, + wireless_display: { keyCode: key("K") }, // Wires toolbar wire: { keyCode: key("1") }, diff --git a/src/js/game/logic.js b/src/js/game/logic.js index 7ec7b8ab..9951ca36 100644 --- a/src/js/game/logic.js +++ b/src/js/game/logic.js @@ -193,6 +193,9 @@ export class GameLogic { * @param {enumDirection} param0.edge The edge to check for */ computeWireEdgeStatus({ wireVariant, tile, edge }) { + /** + * @type {Vector} + */ const offset = enumDirectionToVector[edge]; const targetTile = tile.add(offset); @@ -240,10 +243,10 @@ export class GameLogic { const targetStaticComp = targetEntity.components.StaticMapEntity; - // Check if its a crossing + // Check if its a tunnel const wireTunnelComp = targetEntity.components.WireTunnel; if (wireTunnelComp) { - return true; + return wireTunnelComp.CanConnectWorld(targetStaticComp, offset); } // Check if its a wire diff --git a/src/js/game/map_chunk_view.js b/src/js/game/map_chunk_view.js index 848afbab..9548ac0b 100644 --- a/src/js/game/map_chunk_view.js +++ b/src/js/game/map_chunk_view.js @@ -68,6 +68,7 @@ export class MapChunkView extends MapChunk { systems.staticMapEntities.drawChunk(parameters, this); systems.lever.drawChunk(parameters, this); systems.display.drawChunk(parameters, this); + systems.wirelessDisplay.drawChunk(parameters, this); systems.storage.drawChunk(parameters, this); systems.itemProcessorOverlays.drawChunk(parameters, this); } diff --git a/src/js/game/meta_building.js b/src/js/game/meta_building.js index 9deee272..a2ba3307 100644 --- a/src/js/game/meta_building.js +++ b/src/js/game/meta_building.js @@ -199,7 +199,7 @@ export class MetaBuilding { * @param {number} param0.rotationVariant Rotation variant * @param {string} param0.variant */ - createEntity({ root, origin, rotation, originalRotation, rotationVariant, variant }) { + createEntity({ root, origin, rotation, originalRotation, rotationVariant, variant, wireless_code }) { const entity = new Entity(root); entity.layer = this.getLayer(); entity.addComponent( @@ -213,6 +213,11 @@ export class MetaBuilding { ); this.setupEntityComponents(entity, root); this.updateVariants(entity, rotationVariant, variant); + if (entity.components.WirelessDisplay && wireless_code) { + if (!entity.components.WirelessCode) { + entity.components.WirelessCode = wireless_code; + } + } return entity; } diff --git a/src/js/game/meta_building_registry.js b/src/js/game/meta_building_registry.js index 0613103e..105727ff 100644 --- a/src/js/game/meta_building_registry.js +++ b/src/js/game/meta_building_registry.js @@ -26,6 +26,7 @@ import { enumUndergroundBeltVariants, MetaUndergroundBeltBuilding } from "./buil import { enumVirtualProcessorVariants, MetaVirtualProcessorBuilding } from "./buildings/virtual_processor"; import { MetaWireBuilding } from "./buildings/wire"; import { MetaWireTunnelBuilding } from "./buildings/wire_tunnel"; +import { MetaWirelessDisplayBuilding, enumWirelessDisplayVariants } from "./buildings/wireless_display"; import { buildBuildingCodeCache, gBuildingVariants, registerBuildingVariant } from "./building_codes"; import { enumWireVariant } from "./components/wire"; import { KEYMAPPINGS } from "./key_action_mapper"; @@ -59,6 +60,7 @@ export function initMetaBuildingRegistry() { gMetaBuildingRegistry.register(MetaAnalyzerBuilding); gMetaBuildingRegistry.register(MetaComparatorBuilding); gMetaBuildingRegistry.register(MetaItemProducerBuilding); + gMetaBuildingRegistry.register(MetaWirelessDisplayBuilding); // Belt registerBuildingVariant(1, MetaBeltBuilding, defaultBuildingVariant, 0); @@ -121,7 +123,7 @@ export function initMetaBuildingRegistry() { registerBuildingVariant(52, MetaWireBuilding, enumWireVariant.second, 0); registerBuildingVariant(53, MetaWireBuilding, enumWireVariant.second, 1); registerBuildingVariant(54, MetaWireBuilding, enumWireVariant.second, 2); - registerBuildingVariant(55, MetaWireBuilding, enumWireVariant.second, 3); + registerBuildingVariant(55, MetaWireBuilding, enumWireVariant.second, 3); // Constant signal registerBuildingVariant(31, MetaConstantSignalBuilding); @@ -165,6 +167,10 @@ export function initMetaBuildingRegistry() { // Item producer registerBuildingVariant(61, MetaItemProducerBuilding); + // Wireless Display + registerBuildingVariant(62, MetaWirelessDisplayBuilding); + registerBuildingVariant(63, MetaWirelessDisplayBuilding, enumWirelessDisplayVariants.remote_control); + // Propagate instances for (const key in gBuildingVariants) { gBuildingVariants[key].metaInstance = gMetaBuildingRegistry.findByClass( diff --git a/src/js/game/systems/constant_signal.js b/src/js/game/systems/constant_signal.js index aaf31a19..e5c8c4f9 100644 --- a/src/js/game/systems/constant_signal.js +++ b/src/js/game/systems/constant_signal.js @@ -99,7 +99,6 @@ export class ConstantSignalSystem extends GameSystemWithFilter { } if (itemInput.chosenItem) { - console.log(itemInput.chosenItem); constantComp.signal = itemInput.chosenItem; } else { constantComp.signal = this.parseSignalCode(signalValueInput.getValue()); diff --git a/src/js/game/systems/item_processor_overlays.js b/src/js/game/systems/item_processor_overlays.js index 3ba44c7b..0377f779 100644 --- a/src/js/game/systems/item_processor_overlays.js +++ b/src/js/game/systems/item_processor_overlays.js @@ -1,6 +1,6 @@ import { globalConfig } from "../../core/config"; import { Loader } from "../../core/loader"; -import { smoothPulse } from "../../core/utils"; +import { round1DigitLocalized, smoothPulse } from "../../core/utils"; import { enumItemProcessorRequirements, enumItemProcessorTypes } from "../components/item_processor"; import { Entity } from "../entity"; import { GameSystem } from "../game_system"; @@ -92,7 +92,7 @@ export class ItemProcessorOverlaysSystem extends GameSystem { parameters.context.textAlign = "center"; parameters.context.font = "bold 10px GameFont"; parameters.context.fillText( - "" + Math.round(readerComp.lastThroughput * 10) / 10, + round1DigitLocalized(readerComp.lastThroughput), (staticComp.origin.x + 0.5) * globalConfig.tileSize, (staticComp.origin.y + 0.62) * globalConfig.tileSize ); diff --git a/src/js/game/systems/wire.js b/src/js/game/systems/wire.js index 4d0e6de4..73fcf3ac 100644 --- a/src/js/game/systems/wire.js +++ b/src/js/game/systems/wire.js @@ -316,6 +316,11 @@ export class WireSystem extends GameSystemWithFilter { } } + const tunnelComp = nextEntity.components.WireTunnel; + if (tunnelComp) { + //const outputDir = tunnelComp.GetOutputDirection(staticComp, offset); + } + if (newSearchTile) { // Find new surrounding wire targets const newTargets = this.findSurroundingWireTargets( @@ -364,7 +369,7 @@ export class WireSystem extends GameSystemWithFilter { * @param {Vector} initialTile * @param {Array} directions * @param {WireNetwork} network - * @param {enumWireVariant=} variantMask Only accept connections to this mask + * @param {enumWireVariant} variantMask Only accept connections to this mask * @returns {Array} */ findSurroundingWireTargets(initialTile, directions, network, variantMask = null) { @@ -386,9 +391,6 @@ export class WireSystem extends GameSystemWithFilter { const offset = enumDirectionToVector[direction]; const initialSearchTile = initialTile.add(offset); - // Store which tunnels we already visited to avoid infinite loops - const visitedTunnels = new Set(); - // First, find the initial connected entities const initialContents = this.root.map.getLayersContentsMultipleXY( initialSearchTile.x, @@ -396,17 +398,18 @@ export class WireSystem extends GameSystemWithFilter { ); // Link the initial tile to the initial entities, since it may change - /** @type {Array<{entity: Entity, tile: Vector}>} */ + /** @type {Array<{entity: Entity, tile: Vector, dir: Vector}>} */ const contents = []; for (let j = 0; j < initialContents.length; ++j) { contents.push({ entity: initialContents[j], tile: initialSearchTile, + dir: offset, }); } for (let k = 0; k < contents.length; ++k) { - const { entity, tile } = contents[k]; + const { entity, tile, dir } = contents[k]; const wireComp = entity.components.Wire; // Check for wire @@ -438,8 +441,17 @@ export class WireSystem extends GameSystemWithFilter { } // Check if the direction (inverted) matches - const pinDirection = staticComp.localDirectionToWorld(slot.direction); - if (pinDirection !== enumInvertedDirections[direction]) { + // const pinDirection = staticComp.localDirectionToWorld(slot.direction); + // if (pinDirection !== enumInvertedDirections[direction]) { + // continue; + // } + // /** + // * @type {Vector} + // */ + const worldDir = staticComp.localDirectionToWorld(slot.direction); + const invDir = enumInvertedDirections[worldDir]; + const pinDirection = enumDirectionToVector[invDir]; + if (!pinDirection.equals(dir)) { continue; } @@ -458,14 +470,20 @@ export class WireSystem extends GameSystemWithFilter { // Check if it's a tunnel, if so, go to the forwarded item const tunnelComp = entity.components.WireTunnel; if (tunnelComp) { - if (visitedTunnels.has(entity.uid)) { - continue; - } const staticComp = entity.components.StaticMapEntity; + //const localDir = staticComp.worldToLocalTile(tile.sub(offset)); + //staticComp.localDirectionToWorld(); + const outputDir = tunnelComp.GetOutputDirection(staticComp, dir); + if (!outputDir) { + continue; + } + const forwardedTile = staticComp.origin.add(outputDir); + + //TODO: Alter to Allow for different tunnel Types // Compute where this tunnel connects to - const forwardedTile = staticComp.origin.add(offset); + //const forwardedTile = staticComp.origin.add(offset); VERBOSE_WIRES && logger.log( " Found tunnel", @@ -487,6 +505,7 @@ export class WireSystem extends GameSystemWithFilter { contents.push({ entity: connectedContents[h], tile: forwardedTile, + dir: outputDir, }); } @@ -497,9 +516,6 @@ export class WireSystem extends GameSystemWithFilter { if (network.tunnels.indexOf(entity) < 0) { network.tunnels.push(entity); } - - // Remember this tunnel - visitedTunnels.add(entity.uid); } } } diff --git a/src/js/game/systems/wired_pins.js b/src/js/game/systems/wired_pins.js index e8bc1882..6c893caa 100644 --- a/src/js/game/systems/wired_pins.js +++ b/src/js/game/systems/wired_pins.js @@ -189,16 +189,17 @@ export class WiredPinsSystem extends GameSystemWithFilter { ); if (staticComp.getMetaBuilding().getRenderPins()) { + this.sprite = this.pinSprites[slot.type]; drawRotatedSprite({ parameters, - sprite: this.pinSprites[slot.type], + sprite: this.sprite, x: worldPos.x, y: worldPos.y, angle: effectiveRotation, size: globalConfig.tileSize + 2, offsetX: 0, offsetY: 0, - }); + }); } // Draw contained item to visualize whats emitted diff --git a/src/js/game/systems/wireless_display.js b/src/js/game/systems/wireless_display.js new file mode 100644 index 00000000..25e83d6c --- /dev/null +++ b/src/js/game/systems/wireless_display.js @@ -0,0 +1,273 @@ +import { globalConfig } from "../../core/config"; +import { Loader } from "../../core/loader"; +import { BaseItem } from "../base_item"; +import { enumColors } from "../colors"; +import { WirelessDisplayComponent } from "../components/wireless_display"; +import { GameSystemWithFilter } from "../game_system_with_filter"; +import { isTrueItem } from "../items/boolean_item"; +import { ColorItem, COLOR_ITEM_SINGLETONS } from "../items/color_item"; +import { MapChunkView } from "../map_chunk_view"; +import { THIRDPARTY_URLS } from "../../core/config"; +import { DialogWithForm } from "../../core/modal_dialog_elements"; +import { FormElementInput, FormElementItemChooser } from "../../core/modal_dialog_forms"; +import { fillInLinkIntoTranslation } from "../../core/utils"; +import { T } from "../../translations"; +import { Entity } from "../entity"; +import { WirelessCodeComponent } from "../components/wireless_code"; +import { THEME} from "../theme"; + +export class WirelessDisplaySystem extends GameSystemWithFilter { + constructor(root) { + super(root, [WirelessDisplayComponent]); + + this.root.signals.entityManuallyPlaced.add(this.channelSignalValue, this); + + /** @type {Object} */ + this.displaySprites = {}; + + for (const colorId in enumColors) { + if (colorId === enumColors.uncolored) { + continue; + } + this.displaySprites[colorId] = Loader.getSprite("sprites/wires/display/" + colorId + ".png"); + } + + this.wirelessMachineList = {}; + + this.displayNumber = 0; + this.entityCount = 0; + } + + update() { + if (this.entityCount != this.allEntities.length) { + for (let i = 0; i < this.allEntities.length; i++) { + const entity = this.allEntities[i]; + if (entity.components.WirelessDisplay && entity.components.WiredPins && entity.components.WirelessCode && !this.wirelessMachineList[entity.components.WirelessCode]) { + this.wirelessMachineList[entity.components.WirelessCode["wireless_code"]] = entity; + } + } + this.entityCount = this.allEntities.length; + } + const mousePos = this.root.app.mousePosition; + } + + /** + * Asks the entity to enter a valid signal code + * @param {Entity} entity + */ + channelSignalValue(entity) { + if (entity.components.WirelessDisplay) { + // Ok, query, but also save the uid because it could get stale + const uid = entity.uid; + + const signalValueInput = new FormElementInput({ + id: "channelValue", + label: fillInLinkIntoTranslation(T.dialogs.editChannel.descShortKey, THIRDPARTY_URLS.shapeViewer), + placeholder: "", + defaultValue: "", + validator: val => val, + }); + + const channeldialog = new DialogWithForm({ + app: this.root.app, + title: T.dialogs.editChannel.title, + desc: T.dialogs.editChannel.descItems, + formElements: [signalValueInput], + buttons: ["cancel:bad:escape", "ok:good:enter"], + closeButton: false, + }); + this.root.hud.parts.dialogs.internalShowDialog(channeldialog); + + // When confirmed, set the signal + const closeHandler = () => { + if (!this.root || !this.root.entityMgr) { + // Game got stopped + return; + } + + const entityRef = this.root.entityMgr.findByUid(uid, false); + if (!entityRef) { + // outdated + return; + } + + const constantComp = entityRef.components.WirelessDisplay; + if (!constantComp) { + // no longer interesting + return; + } + + if (signalValueInput.getValue() && !entity.components.WiredPins) { + entity.addComponent(new WirelessCodeComponent(signalValueInput.getValue())); + } else if (signalValueInput.getValue() && entity.components.WiredPins) { + entity.addComponent(new WirelessCodeComponent(signalValueInput.getValue())); + this.wirelessMachineList[entity.components.WirelessCode["wireless_code"]] = entity; + } + }; + + channeldialog.buttonSignals.ok.add(closeHandler); + channeldialog.valueChosen.add(closeHandler); + + // When cancelled, destroy the entity again + channeldialog.buttonSignals.cancel.add(() => { + if (!this.root || !this.root.entityMgr) { + // Game got stopped + return; + } + + const entityRef = this.root.entityMgr.findByUid(uid, false); + if (!entityRef) { + // outdated + return; + } + + const constantComp = entityRef.components.WirelessDisplay; + if (!constantComp) { + // no longer interesting + return; + } + + this.root.logic.tryDeleteBuilding(entityRef); + }); + } + } + + /** + * Returns the color / value a display should show + * @param {BaseItem} value + * @returns {BaseItem} + */ + getDisplayItem(value) { + if (!value) { + return null; + } + + switch (value.getItemType()) { + case "boolean": { + return isTrueItem(value) ? COLOR_ITEM_SINGLETONS[enumColors.white] : null; + } + + case "color": { + const item = /**@type {ColorItem} */ (value); + return item.color === enumColors.uncolored ? null : item; + } + + case "shape": { + return value; + } + + default: + assertAlways(false, "Unknown item type: " + value.getItemType()); + } + } + + /** + * Computes the color below the current tile + * @returns {number} + */ + computeColorBelowTile() { + const mousePosition = this.root.app.mousePosition; + if (!mousePosition) { + // Not on screen + return null; + } + + const worldPos = this.root.camera.screenToWorld(mousePosition); + const tile = worldPos.toTileSpace(); + const contents = this.root.map.getTileContent(tile, "regular"); + + if (contents && contents.components.WirelessDisplay) { + // We hovered a lower layer, show the color there + if (contents && contents.components.WirelessCode && contents.components.WirelessCode.wireless_code) { + return contents.components.WirelessCode.wireless_code; + } + } + + return null; + } + + /** + * Draws Text Storked + * @param {string} text + * @param {number} y + * @param {number} x + * @param {number=} width + */ + drawStroked(ctx, text, x, y, width = undefined) { + ctx.font = '15px Sans-serif'; + ctx.strokeStyle = 'black'; + ctx.lineWidth = 1; + ctx.miterLimit=2 + ctx.strokeText(text, x, y, width); + ctx.fillStyle = 'white'; + ctx.fillText(text, x, y, width); + } + + /** + * Draws a given chunk + * @param {import("../../core/draw_utils").DrawParameters} parameters + * @param {MapChunkView} chunk + */ + drawChunk(parameters, chunk) { + const contents = chunk.containedEntitiesByLayer.regular; + for (let i = 0; i < contents.length; ++i) { + const entity_a = contents[i]; + if (entity_a && entity_a.components.WirelessDisplay) { + const below = this.computeColorBelowTile(); + if (below) { + // We have something below our tile + const mousePosition = this.root.app.mousePosition; + const worldPos = this.root.camera.screenToWorld(mousePosition); + const tile = worldPos.toTileSpace().toWorldSpace(); + + this.drawStroked(parameters.context, below.toString(), worldPos.x + 5, worldPos.y + 5) + parameters.context.strokeStyle = THEME.map.colorBlindPickerTile; + parameters.context.beginPath(); + parameters.context.rect(tile.x, tile.y, globalConfig.tileSize, globalConfig.tileSize); + parameters.context.stroke(); + } + if (!entity_a.components.WiredPins) { + const entity_b = this.wirelessMachineList[entity_a.components.WirelessCode["wireless_code"]]; + if (entity_b) { + if (!this.allEntities.includes(entity_b)) { + this.wirelessMachineList[entity_b] = undefined; + return; + } + const origin = entity_a.components.StaticMapEntity.origin; + const pinsComp = entity_b.components.WiredPins; + const network = pinsComp.slots[0].linkedNetwork; + + if (!network) { + continue; + } + + const value = this.getDisplayItem(network.currentValue); + + if (!value) { + continue; + } + + if (value.getItemType()) { + if (value.getItemType() === "color") { + this.displaySprites[/** @type {ColorItem} */ (value).color].drawCachedCentered( + parameters, + (origin.x + 0.5) * globalConfig.tileSize, + (origin.y + 0.5) * globalConfig.tileSize, + globalConfig.tileSize + ); + } else if (value.getItemType() === "shape") { + value.drawItemCenteredClipped( + (origin.x + 0.5) * globalConfig.tileSize, + (origin.y + 0.5) * globalConfig.tileSize, + parameters, + 30 + ); + } + } + } + } + } + } + } +} + diff --git a/translations/base-en.yaml b/translations/base-en.yaml index 9d145999..198e84b4 100644 --- a/translations/base-en.yaml +++ b/translations/base-en.yaml @@ -3,7 +3,7 @@ # # Contributing: # -# If you want to contribute, please make a pull request on this respository +# If you want to contribute, please make a pull request on this repository # and I will have a look. # # Placeholders: @@ -30,9 +30,11 @@ steamPage: intro: >- Do you like automation games? Then you are in the right place! - shapez.io is a relaxed game in which you have to build factories for the automated production of geometric shapes. As the level increases, the shapes become more and more complex, and you have to spread out on the infinite map. + shapez.io is a relaxed game in which you have to build factories for the automated production of geometric shapes. As the level increases, the shapes become more and more complex, and you + have to spread out on the infinite map. - And as if that wasn't enough, you also have to produce exponentially more to satisfy the demands - the only thing that helps is scaling! While you only have to process shapes at the beginning, you will later have to color them - by extracting and mixing colors! + And as if that wasn't enough, you also have to produce exponentially more to satisfy the demands - the only thing that helps is scaling! While you only have to process shapes at the + beginning, you will later have to color them - by extracting and mixing colors! Buying the game on Steam gives you access to the full version, but you can also play a demo at shapez.io first and decide later! @@ -40,7 +42,7 @@ steamPage: advantages: - 12 New Levels for a total of 26 levels - 18 New Buildings for a fully automated factory! - - 20 Upgrade Tiers for many hours of fun! + - Unlimited Upgrade Tiers for many hours of fun! - Wires Update for an entirely new dimension! - Dark Mode! - Unlimited Savegames @@ -79,7 +81,7 @@ global: # How big numbers are rendered, e.g. "10,000" thousandsDivider: "," - # What symbol to use to seperate the integer part from the fractional part of a number, e.g. "0.4" + # What symbol to use to separate the integer part from the fractional part of a number, e.g. "0.4" decimalSeparator: "." # The suffix for large numbers, e.g. 1.3k, 400.2M, etc. @@ -255,6 +257,12 @@ dialogs: titleEdit: Edit Marker desc: Give it a meaningful name, you can also include a short key of a shape (Which you can generate here) + editChannel: + title: Set Channel + descItems: >- + Enter used channel name + descShortKey: or enter new channel name + editSignal: title: Set Signal descItems: >- @@ -402,7 +410,9 @@ ingame: waypoints: waypoints: Markers hub: HUB - description: Left-click a marker to jump to it, right-click to delete it.

Press to create a marker from the current view, or right-click to create a marker at the selected location. + description: >- + Left-click a marker to jump to it, right-click to delete it.

Press to create a marker from the current view, or right-click to create a marker at the + selected location. creationSuccessNotification: Marker has been created. # Shape viewer @@ -420,7 +430,8 @@ ingame: Connect the extractor with a conveyor belt to your hub!

Tip: Click and drag the belt with your mouse! 1_3_expand: >- - This is NOT an idle game! Build more extractors and belts to finish the goal quicker.

Tip: Hold SHIFT to place multiple extractors, and use R to rotate them. + This is NOT an idle game! Build more extractors and belts to finish the goal quicker.

Tip: Hold SHIFT to place multiple extractors, and use + R to rotate them. 2_1_place_cutter: >- Now place a Cutter to cut the circles in two halves!

@@ -482,7 +493,7 @@ ingame: desc: As many as your heart desires! upgrades: - title: 20 Upgrade Tiers + title: ∞ Upgrade Tiers desc: This demo version has only 5! markers: @@ -499,7 +510,7 @@ ingame: support: title: Support me - desc: I develop it in my spare time! + desc: I develop the game in my spare time! # All shop upgrades shopUpgrades: @@ -581,13 +592,13 @@ buildings: rotater: default: - name: &rotater Rotate + name: &rotater Rotator description: Rotates shapes clockwise by 90 degrees. ccw: - name: Rotate (CCW) + name: Rotator (CCW) description: Rotates shapes counter-clockwise by 90 degrees. rotate180: - name: Rotate (180°) + name: Rotator (180°) description: Rotates shapes by 180 degrees. stacker: @@ -636,10 +647,23 @@ buildings: name: *wire description: *wire_desc + third: + name: *wire + description: *wire_desc + wire_tunnel: default: - name: &wire_tunnel Wire Crossing + name: &wire_tunnel Wire Tunnel description: Allows two wires to cross without connecting to each other. + elbow: + name: Elbow Tunnel + description: Allows a wire to turn a corner without connecting to anything else + straight: + name: Straight tunnel + description: Allows a wire to go straight without connecting to anything else + double_elbow: + name: Double Elbow Tunnel + description: Allows two wires to turn corners without connecting to each other. constant_signal: default: @@ -684,6 +708,14 @@ buildings: name: &display Display description: Connect a signal to show it on the display - It can be a shape, color or boolean. + wireless_display: + default: + name: &wireless_display Wireless Display + description: Connect to a channel and if channel connects, it receives the signal coming from Remote Control - Signal can be a shape, color or boolean. + remote_control: + name: Remote Control + description: Connect to a channel and if channel connects, it emits signal to Wireless Control - Signal can be a shape, color or boolean. + reader: default: name: &reader Belt Reader @@ -705,7 +737,7 @@ buildings: description: Virtually cuts the shape into two halves. rotater: - name: Virtual Rotater + name: Virtual Rotator description: Virtually rotates the shape clockwise. unstacker: @@ -729,16 +761,19 @@ storyRewards: # Those are the rewards gained from completing the store reward_cutter_and_trash: title: Cutting Shapes - desc: You just unlocked the cutter, which cuts shapes in half from top to bottom regardless of its orientation!

Be sure to get rid of the waste, or otherwise it will clog and stall - For this purpose I have given you the trash, which destroys everything you put into it! + desc: >- + You just unlocked the cutter, which cuts shapes in half from top to bottom regardless of its orientation!

Be sure to get rid of the waste, or + otherwise it will clog and stall - For this purpose I have given you the trash, which destroys everything you put into it! reward_rotater: title: Rotating - desc: The rotater has been unlocked! It rotates shapes clockwise by 90 degrees. + desc: The rotator has been unlocked! It rotates shapes clockwise by 90 degrees. reward_painter: title: Painting desc: >- - The painter has been unlocked - Extract some color veins (just as you do with shapes) and combine it with a shape in the painter to color them!

PS: If you are colorblind, there is a colorblind mode in the settings! + The painter has been unlocked - Extract some color veins (just as you do with shapes) and combine it with a shape in the painter to color them!

PS: If you are + colorblind, there is a colorblind mode in the settings! reward_mixer: title: Color Mixing @@ -746,7 +781,9 @@ storyRewards: reward_stacker: title: Stacker - desc: You can now combine shapes with the stacker! Both inputs are combined, and if they can be put next to each other, they will be fused. If not, the right input is stacked on top of the left input! + desc: >- + You can now combine shapes with the stacker! Both inputs are combined, and if they can be put next to each other, they will be fused. If not, the right + input is stacked on top of the left input! reward_balancer: title: Balancer @@ -758,7 +795,9 @@ storyRewards: reward_rotater_ccw: title: CCW Rotating - desc: You have unlocked a variant of the rotater - It allows you to rotate shapes counter-clockwise! To build it, select the rotater and press 'T' to cycle through its variants! + desc: >- + You have unlocked a variant of the rotator - It allows you to rotate shapes counter-clockwise! To build it, select the rotator and + press 'T' to cycle through its variants! reward_miner_chainable: title: Chaining Extractor @@ -791,7 +830,9 @@ storyRewards: reward_painter_double: title: Double Painting - desc: You have unlocked a variant of the painter - It works similar to the regular painter but processes two shapes at once, consuming just one color instead of two! + desc: >- + You have unlocked a variant of the painter - It works similar to the regular painter but processes two shapes at once, consuming just one color + instead of two! reward_storage: title: Storage @@ -801,11 +842,13 @@ storyRewards: reward_blueprints: title: Blueprints - desc: You can now copy and paste parts of your factory! Select an area (Hold CTRL, then drag with your mouse), and press 'C' to copy it.

Pasting it is not free, you need to produce blueprint shapes to afford it! (Those you just delivered). + desc: >- + You can now copy and paste parts of your factory! Select an area (Hold CTRL, then drag with your mouse), and press 'C' to copy it.

Pasting it is + not free, you need to produce blueprint shapes to afford it! (Those you just delivered). reward_rotater_180: - title: Rotater (180°) - desc: You just unlocked the 180 degrees rotater! - It allows you to rotate a shape by 180 degress (Surprise! :D) + title: Rotator (180°) + desc: You just unlocked the 180 degrees rotator! - It allows you to rotate a shape by 180 degrees (Surprise! :D) reward_wires_painter_and_levers: title: >- @@ -846,18 +889,19 @@ storyRewards: title: Virtual Processing desc: >- I just gave a whole bunch of new buildings which allow you to simulate the processing of shapes!

- You can now simulate a cutter, rotater, stacker and more on the wires layer! + You can now simulate a cutter, rotator, stacker and more on the wires layer! With this you now have three options to continue the game:

- Build an automated machine to create any possible shape requested by the HUB (I recommend to try it!).

- Build something cool with wires.

- - Continue to play regulary.

+ - Continue to play normally.

Whatever you choose, remember to have fun! # Special reward, which is shown when there is no reward actually no_reward: title: Next level desc: >- - This level gave you no reward, but the next one will!

PS: Better not destroy your existing factory - You'll need all those shapes later to unlock upgrades! + This level gave you no reward, but the next one will!

PS: Better not destroy your existing factory - You'll need all those shapes later to + unlock upgrades! no_reward_freeplay: title: Next level @@ -986,7 +1030,8 @@ settings: refreshRate: title: Tick Rate description: >- - This determines how many game ticks happen per second. In general, a higher tick rate means better precision but also worse performance. On lower tickrates, the throughput may not be exact. + This determines how many game ticks happen per second. In general, a higher tick rate means better precision but also worse performance. On lower tickrates, the throughput may not be + exact. alwaysMultiplace: title: Multiplace @@ -1037,7 +1082,8 @@ settings: clearCursorOnDeleteWhilePlacing: title: Clear Cursor on Right Click description: >- - Enabled by default, clears the cursor whenever you right click while you have a building selected for placement. If disabled, you can delete buildings by right-clicking while placing a building. + Enabled by default, clears the cursor whenever you right click while you have a building selected for placement. If disabled, you can delete buildings by right-clicking while placing + a building. lowQualityTextures: title: Low quality textures (Ugly) @@ -1132,6 +1178,7 @@ keybindings: filter: *filter wire_tunnel: *wire_tunnel display: *display + wireless_display: *wireless_display reader: *reader virtual_processor: *virtual_processor transistor: *transistor @@ -1211,7 +1258,7 @@ tips: - Cutters always cut vertically, regardless of their orientation. - The storage buffer prioritises the left output. - Invest time to build repeatable designs - it's worth it! - - Holding SHIFT lets you place multiple buildings in one click. + - Holding SHIFT lets you place multiple buildings at a time. - You can hold ALT to invert the direction of placed belts. - Efficiency is key! - Shape patches that are further away from the hub are more complex.