From 36cf28029ecdd68a085a848184eb989f03b40e83 Mon Sep 17 00:00:00 2001 From: Phlosioneer Date: Tue, 16 Jun 2020 21:11:26 -0400 Subject: [PATCH 01/24] Remove ejector cache; use slot caches instead This is a small refactoring that removes the main ejector cache. The cache is now tracked by slots and ejector components. It avoids a large array allocation and many small object allocations, but adds many small array allocations. It's net neutral for performance. --- src/js/game/components/item_ejector.js | 12 +++- src/js/game/systems/item_ejector.js | 91 ++++++++++++++------------ 2 files changed, 61 insertions(+), 42 deletions(-) diff --git a/src/js/game/components/item_ejector.js b/src/js/game/components/item_ejector.js index 5a40870b..e78faa36 100644 --- a/src/js/game/components/item_ejector.js +++ b/src/js/game/components/item_ejector.js @@ -3,13 +3,16 @@ import { BaseItem } from "../base_item"; import { Component } from "../component"; import { types } from "../../savegame/serialization"; import { gItemRegistry } from "../../core/global_registries"; +import { Entity } from "../entity"; /** * @typedef {{ * pos: Vector, * direction: enumDirection, * item: BaseItem, - * progress: number? + * progress: number?, + * cachedDestSlot?: import("./item_acceptor").ItemAcceptorLocatedSlot, + * cachedTargetEntity?: Entity * }} ItemEjectorSlot */ @@ -19,6 +22,8 @@ export class ItemEjectorComponent extends Component { } static getSchema() { + // The cachedDestSlot, cachedTargetEntity, and cachedConnectedSlots fields + // are not serialized. return { instantEject: types.bool, slots: types.array( @@ -61,6 +66,9 @@ export class ItemEjectorComponent extends Component { this.instantEject = instantEject; this.setSlots(slots); + + /** @type {ItemEjectorSlot[]} */ + this.cachedConnectedSlots = null; } /** @@ -76,6 +84,8 @@ export class ItemEjectorComponent extends Component { direction: slot.direction, item: null, progress: 0, + cachedDestSlot: null, + cachedTargetEntity: null, }); } } diff --git a/src/js/game/systems/item_ejector.js b/src/js/game/systems/item_ejector.js index 2f327725..06e5d449 100644 --- a/src/js/game/systems/item_ejector.js +++ b/src/js/game/systems/item_ejector.js @@ -14,15 +14,6 @@ export class ItemEjectorSystem extends GameSystemWithFilter { constructor(root) { super(root, [ItemEjectorComponent]); - /** - * @type {Array<{ - * targetEntity: Entity, - * sourceSlot: import("../components/item_ejector").ItemEjectorSlot, - * destSlot: import("../components/item_acceptor").ItemAcceptorLocatedSlot - * }>} - */ - this.cache = []; - this.cacheNeedsUpdate = true; this.root.signals.entityAdded.add(this.invalidateCache, this); @@ -39,18 +30,24 @@ export class ItemEjectorSystem extends GameSystemWithFilter { recomputeCache() { logger.log("Recomputing cache"); - const cache = []; - // Try to find acceptors for every ejector + let entryCount = 0; for (let i = 0; i < this.allEntities.length; ++i) { const entity = this.allEntities[i]; const ejectorComp = entity.components.ItemEjector; const staticComp = entity.components.StaticMapEntity; + // Clear the old cache. + ejectorComp.cachedConnectedSlots = null; + // For every ejector slot, try to find an acceptor for (let ejectorSlotIndex = 0; ejectorSlotIndex < ejectorComp.slots.length; ++ejectorSlotIndex) { const ejectorSlot = ejectorComp.slots[ejectorSlotIndex]; + // Clear the old cache. + ejectorSlot.cachedDestSlot = null; + ejectorSlot.cachedTargetEntity = null; + // Figure out where and into which direction we eject items const ejectSlotWsTile = staticComp.localTileToWorld(ejectorSlot.pos); const ejectSlotWsDirection = staticComp.localDirectionToWorld(ejectorSlot.direction); @@ -82,16 +79,18 @@ export class ItemEjectorSystem extends GameSystemWithFilter { } // Ok we found a connection - cache.push({ - targetEntity, - sourceSlot: ejectorSlot, - destSlot: matchingSlot, - }); + if (ejectorComp.cachedConnectedSlots) { + ejectorComp.cachedConnectedSlots.push(ejectorSlot); + } else { + ejectorComp.cachedConnectedSlots = [ejectorSlot]; + } + ejectorSlot.cachedTargetEntity = targetEntity; + ejectorSlot.cachedDestSlot = matchingSlot; + entryCount += 1; } } - this.cache = cache; - logger.log("Found", cache.length, "entries to update"); + logger.log("Found", entryCount, "entries to update"); } update() { @@ -109,35 +108,45 @@ export class ItemEjectorSystem extends GameSystemWithFilter { } // Go over all cache entries - for (let i = 0; i < this.cache.length; ++i) { - const { sourceSlot, destSlot, targetEntity } = this.cache[i]; - const item = sourceSlot.item; - - if (!item) { - // No item available to be ejected + for (let i = 0; i < this.allEntities.length; ++i) { + const sourceEntity = this.allEntities[i]; + const sourceEjectorComp = sourceEntity.components.ItemEjector; + if (!sourceEjectorComp.cachedConnectedSlots) { continue; } + for (let j = 0; j < sourceEjectorComp.cachedConnectedSlots.length; j++) { + const sourceSlot = sourceEjectorComp.cachedConnectedSlots[j]; + const destSlot = sourceSlot.cachedDestSlot; + const targetEntity = sourceSlot.cachedTargetEntity; - // Advance items on the slot - sourceSlot.progress = Math_min(1, sourceSlot.progress + progressGrowth); + const item = sourceSlot.item; - // Check if we are still in the process of ejecting, can't proceed then - if (sourceSlot.progress < 1.0) { - continue; - } + if (!item) { + // No item available to be ejected + continue; + } - // Check if the target acceptor can actually accept this item - const targetAcceptorComp = targetEntity.components.ItemAcceptor; - if (!targetAcceptorComp.canAcceptItem(destSlot.index, item)) { - continue; - } + // Advance items on the slot + sourceSlot.progress = Math_min(1, sourceSlot.progress + progressGrowth); - // Try to hand over the item - if (this.tryPassOverItem(item, targetEntity, destSlot.index)) { - // Handover successful, clear slot - targetAcceptorComp.onItemAccepted(destSlot.index, destSlot.acceptedDirection, item); - sourceSlot.item = null; - continue; + // Check if we are still in the process of ejecting, can't proceed then + if (sourceSlot.progress < 1.0) { + continue; + } + + // Check if the target acceptor can actually accept this item + const targetAcceptorComp = targetEntity.components.ItemAcceptor; + if (!targetAcceptorComp.canAcceptItem(destSlot.index, item)) { + continue; + } + + // Try to hand over the item + if (this.tryPassOverItem(item, targetEntity, destSlot.index)) { + // Handover successful, clear slot + targetAcceptorComp.onItemAccepted(destSlot.index, destSlot.acceptedDirection, item); + sourceSlot.item = null; + continue; + } } } } From aef96cff6ebd21a19c1019c0ae8800fec2893dbb Mon Sep 17 00:00:00 2001 From: Phlosioneer Date: Tue, 16 Jun 2020 21:50:16 -0400 Subject: [PATCH 02/24] Optimize ejector cache for common case This commit optimizes the ejector cache for clicking and dragging belts, or adding/removing a building. It's a big performance improvement for large maps; on average, it only has to visit 60 slots per update, compared to 20,000+ slots. --- src/js/game/systems/item_ejector.js | 165 +++++++++++++++++++--------- 1 file changed, 111 insertions(+), 54 deletions(-) diff --git a/src/js/game/systems/item_ejector.js b/src/js/game/systems/item_ejector.js index 06e5d449..310767f7 100644 --- a/src/js/game/systems/item_ejector.js +++ b/src/js/game/systems/item_ejector.js @@ -7,6 +7,7 @@ import { Entity } from "../entity"; import { GameSystemWithFilter } from "../game_system_with_filter"; import { Math_min } from "../../core/builtins"; import { createLogger } from "../../core/logging"; +import { Rectangle } from "../../core/rectangle"; const logger = createLogger("systems/ejector"); @@ -18,10 +19,30 @@ export class ItemEjectorSystem extends GameSystemWithFilter { this.root.signals.entityAdded.add(this.invalidateCache, this); this.root.signals.entityDestroyed.add(this.invalidateCache, this); + + /** + * @type {Rectangle[]} + */ + this.smallCacheAreas = []; } - invalidateCache() { + /** + * + * @param {Entity} entity + */ + invalidateCache(entity) { + if (!entity.components.StaticMapEntity) { + return; + } + this.cacheNeedsUpdate = true; + + // Optimize for the common case: adding or removing one building at a time. Clicking + // and dragging can cause up to 4 add/remove signals. + const staticComp = entity.components.StaticMapEntity; + const bounds = staticComp.getTileSpaceBounds(); + const expandedBounds = bounds.expandedInAllDirections(2); + this.smallCacheAreas.push(expandedBounds); } /** @@ -30,67 +51,103 @@ export class ItemEjectorSystem extends GameSystemWithFilter { recomputeCache() { logger.log("Recomputing cache"); - // Try to find acceptors for every ejector let entryCount = 0; - for (let i = 0; i < this.allEntities.length; ++i) { - const entity = this.allEntities[i]; - const ejectorComp = entity.components.ItemEjector; - const staticComp = entity.components.StaticMapEntity; + if (this.smallCacheAreas.length <= 4) { + // Only recompute caches of entities inside the rectangles. + for (let i = 0; i < this.smallCacheAreas.length; i++) { + entryCount += this.recomputeAreaCaches(this.smallCacheAreas[i]); + } + } else { + // Try to find acceptors for every ejector + for (let i = 0; i < this.allEntities.length; ++i) { + const entity = this.allEntities[i]; + entryCount += this.recomputeSingleEntityCache(entity); + } - // Clear the old cache. - ejectorComp.cachedConnectedSlots = null; + } + logger.log("Found", entryCount, "entries to update"); - // For every ejector slot, try to find an acceptor - for (let ejectorSlotIndex = 0; ejectorSlotIndex < ejectorComp.slots.length; ++ejectorSlotIndex) { - const ejectorSlot = ejectorComp.slots[ejectorSlotIndex]; + this.smallCacheAreas = []; + } - // Clear the old cache. - ejectorSlot.cachedDestSlot = null; - ejectorSlot.cachedTargetEntity = null; - - // Figure out where and into which direction we eject items - const ejectSlotWsTile = staticComp.localTileToWorld(ejectorSlot.pos); - const ejectSlotWsDirection = staticComp.localDirectionToWorld(ejectorSlot.direction); - const ejectSlotWsDirectionVector = enumDirectionToVector[ejectSlotWsDirection]; - const ejectSlotTargetWsTile = ejectSlotWsTile.add(ejectSlotWsDirectionVector); - - // Try to find the given acceptor component to take the item - const targetEntity = this.root.map.getTileContent(ejectSlotTargetWsTile); - if (!targetEntity) { - // No consumer for item - continue; + /** + * + * @param {Rectangle} area + */ + recomputeAreaCaches(area) { + let entryCount = 0; + for (let x = area.x; x < area.right(); ++x) { + for (let y = area.y; y < area.bottom(); ++y) { + const entity = this.root.map.getTileContentXY(x, y); + if (entity && entity.components.ItemEjector) { + entryCount += this.recomputeSingleEntityCache(entity); } - - const targetAcceptorComp = targetEntity.components.ItemAcceptor; - const targetStaticComp = targetEntity.components.StaticMapEntity; - if (!targetAcceptorComp) { - // Entity doesn't accept items - continue; - } - - const matchingSlot = targetAcceptorComp.findMatchingSlot( - targetStaticComp.worldToLocalTile(ejectSlotTargetWsTile), - targetStaticComp.worldDirectionToLocal(ejectSlotWsDirection) - ); - - if (!matchingSlot) { - // No matching slot found - continue; - } - - // Ok we found a connection - if (ejectorComp.cachedConnectedSlots) { - ejectorComp.cachedConnectedSlots.push(ejectorSlot); - } else { - ejectorComp.cachedConnectedSlots = [ejectorSlot]; - } - ejectorSlot.cachedTargetEntity = targetEntity; - ejectorSlot.cachedDestSlot = matchingSlot; - entryCount += 1; } } + return entryCount; + } - logger.log("Found", entryCount, "entries to update"); + /** + * + * @param {Entity} entity + */ + recomputeSingleEntityCache(entity) { + const ejectorComp = entity.components.ItemEjector; + const staticComp = entity.components.StaticMapEntity; + + // Clear the old cache. + ejectorComp.cachedConnectedSlots = null; + + // For every ejector slot, try to find an acceptor + let entryCount = 0; + for (let ejectorSlotIndex = 0; ejectorSlotIndex < ejectorComp.slots.length; ++ejectorSlotIndex) { + const ejectorSlot = ejectorComp.slots[ejectorSlotIndex]; + + // Clear the old cache. + ejectorSlot.cachedDestSlot = null; + ejectorSlot.cachedTargetEntity = null; + + // Figure out where and into which direction we eject items + const ejectSlotWsTile = staticComp.localTileToWorld(ejectorSlot.pos); + const ejectSlotWsDirection = staticComp.localDirectionToWorld(ejectorSlot.direction); + const ejectSlotWsDirectionVector = enumDirectionToVector[ejectSlotWsDirection]; + const ejectSlotTargetWsTile = ejectSlotWsTile.add(ejectSlotWsDirectionVector); + + // Try to find the given acceptor component to take the item + const targetEntity = this.root.map.getTileContent(ejectSlotTargetWsTile); + if (!targetEntity) { + // No consumer for item + continue; + } + + const targetAcceptorComp = targetEntity.components.ItemAcceptor; + const targetStaticComp = targetEntity.components.StaticMapEntity; + if (!targetAcceptorComp) { + // Entity doesn't accept items + continue; + } + + const matchingSlot = targetAcceptorComp.findMatchingSlot( + targetStaticComp.worldToLocalTile(ejectSlotTargetWsTile), + targetStaticComp.worldDirectionToLocal(ejectSlotWsDirection) + ); + + if (!matchingSlot) { + // No matching slot found + continue; + } + + // Ok we found a connection + if (ejectorComp.cachedConnectedSlots) { + ejectorComp.cachedConnectedSlots.push(ejectorSlot); + } else { + ejectorComp.cachedConnectedSlots = [ejectorSlot]; + } + ejectorSlot.cachedTargetEntity = targetEntity; + ejectorSlot.cachedDestSlot = matchingSlot; + entryCount += 1; + } + return entryCount; } update() { From eb182d6e94468e742a78671ea49789de4b531d8d Mon Sep 17 00:00:00 2001 From: Phlosioneer Date: Tue, 16 Jun 2020 22:48:29 -0400 Subject: [PATCH 03/24] Fix lint issues --- src/js/game/systems/item_ejector.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/js/game/systems/item_ejector.js b/src/js/game/systems/item_ejector.js index 310767f7..f5837ac1 100644 --- a/src/js/game/systems/item_ejector.js +++ b/src/js/game/systems/item_ejector.js @@ -27,8 +27,8 @@ export class ItemEjectorSystem extends GameSystemWithFilter { } /** - * - * @param {Entity} entity + * + * @param {Entity} entity */ invalidateCache(entity) { if (!entity.components.StaticMapEntity) { @@ -63,7 +63,6 @@ export class ItemEjectorSystem extends GameSystemWithFilter { const entity = this.allEntities[i]; entryCount += this.recomputeSingleEntityCache(entity); } - } logger.log("Found", entryCount, "entries to update"); @@ -71,8 +70,8 @@ export class ItemEjectorSystem extends GameSystemWithFilter { } /** - * - * @param {Rectangle} area + * + * @param {Rectangle} area */ recomputeAreaCaches(area) { let entryCount = 0; @@ -88,8 +87,8 @@ export class ItemEjectorSystem extends GameSystemWithFilter { } /** - * - * @param {Entity} entity + * + * @param {Entity} entity */ recomputeSingleEntityCache(entity) { const ejectorComp = entity.components.ItemEjector; From c7f8b50d1358127b777840721177bb64f9ade9cb Mon Sep 17 00:00:00 2001 From: Magnus Grimstvedt Saltnes Date: Thu, 18 Jun 2020 22:18:32 +0200 Subject: [PATCH 04/24] Adds tracking for rotation per building type. Adds a setting to go back to one global base rotation. --- src/js/game/hud/parts/building_placer.js | 16 +++---- .../game/hud/parts/building_placer_logic.js | 48 +++++++++++++++---- src/js/profile/application_settings.js | 11 ++++- 3 files changed, 56 insertions(+), 19 deletions(-) diff --git a/src/js/game/hud/parts/building_placer.js b/src/js/game/hud/parts/building_placer.js index 5faec6ab..e3a75400 100644 --- a/src/js/game/hud/parts/building_placer.js +++ b/src/js/game/hud/parts/building_placer.js @@ -82,9 +82,9 @@ export class HUDBuildingPlacer extends HUDBuildingPlacerLogic { this.buildingInfoElements.tutorialImage.setAttribute( "data-icon", "building_tutorials/" + - metaBuilding.getId() + - (variant === defaultBuildingVariant ? "" : "-" + variant) + - ".png" + metaBuilding.getId() + + (variant === defaultBuildingVariant ? "" : "-" + variant) + + ".png" ); removeAllChildren(this.buildingInfoElements.additionalInfo); @@ -122,10 +122,10 @@ export class HUDBuildingPlacer extends HUDBuildingPlacerLogic { T.ingame.buildingPlacement.cycleBuildingVariants.replace( "", "" + - this.root.keyMapper - .getBinding(KEYMAPPINGS.placement.cycleBuildingVariants) - .getKeyCodeString() + - "" + this.root.keyMapper + .getBinding(KEYMAPPINGS.placement.cycleBuildingVariants) + .getKeyCodeString() + + "" ) ); @@ -201,7 +201,7 @@ export class HUDBuildingPlacer extends HUDBuildingPlacerLogic { } = metaBuilding.computeOptimalDirectionAndRotationVariantAtTile( this.root, mouseTile, - this.currentBaseRotation, + this.getBaseRotation(), this.currentVariant.get() ); diff --git a/src/js/game/hud/parts/building_placer_logic.js b/src/js/game/hud/parts/building_placer_logic.js index 6aee65b6..a58d2b64 100644 --- a/src/js/game/hud/parts/building_placer_logic.js +++ b/src/js/game/hud/parts/building_placer_logic.js @@ -1,3 +1,4 @@ + import { Math_abs, Math_degrees, Math_round } from "../../../core/builtins"; import { globalConfig } from "../../../core/config"; import { gMetaBuildingRegistry } from "../../../core/global_registries"; @@ -45,7 +46,34 @@ export class HUDBuildingPlacerLogic extends BaseHUDPart { * The current rotation * @type {number} */ - this.currentBaseRotation = 0; + this.currentBaseRotationGeneral = 0; + + /** + * The current rotation preference for each building. + * @type {Object.} + */ + this.preferredRotations = {}; + + this.getBaseRotation = function () { + const rotationByBuilding = this.root.app.settings.getAllSettings().rotationByBuilding; + if (!rotationByBuilding) { + return this.currentBaseRotationGeneral; + } + const id = this.currentMetaBuilding.get().getId(); + return this.preferredRotations[id] || this.currentBaseRotationGeneral; + + } + + this.setBaseRotation = function (rotation) { + const rotationByBuilding = this.root.app.settings.getAllSettings().rotationByBuilding; + if (!rotationByBuilding) { + this.currentBaseRotationGeneral = rotation; + } else { + const id = this.currentMetaBuilding.get().getId(); + this.preferredRotations[id] = rotation; + } + } + /** * Whether we are currently dragging @@ -200,12 +228,12 @@ export class HUDBuildingPlacerLogic extends BaseHUDPart { const selectedBuilding = this.currentMetaBuilding.get(); if (selectedBuilding) { if (this.root.keyMapper.getBinding(KEYMAPPINGS.placement.rotateInverseModifier).pressed) { - this.currentBaseRotation = (this.currentBaseRotation + 270) % 360; + this.setBaseRotation((this.getBaseRotation() + 270) % 360); } else { - this.currentBaseRotation = (this.currentBaseRotation + 90) % 360; + this.setBaseRotation((this.getBaseRotation() + 90) % 360); } const staticComp = this.fakeEntity.components.StaticMapEntity; - staticComp.rotation = this.currentBaseRotation; + staticComp.rotation = this.getBaseRotation(); } } /** @@ -377,7 +405,7 @@ export class HUDBuildingPlacerLogic extends BaseHUDPart { const { rotation, rotationVariant } = metaBuilding.computeOptimalDirectionAndRotationVariantAtTile( this.root, tile, - this.currentBaseRotation, + this.getBaseRotation(), this.currentVariant.get() ); @@ -385,7 +413,7 @@ export class HUDBuildingPlacerLogic extends BaseHUDPart { origin: tile, rotation, rotationVariant, - originalRotation: this.currentBaseRotation, + originalRotation: this.getBaseRotation(), building: this.currentMetaBuilding.get(), variant: this.currentVariant.get(), }); @@ -401,7 +429,7 @@ export class HUDBuildingPlacerLogic extends BaseHUDPart { KEYMAPPINGS.placementModifiers.placementDisableAutoOrientation ).pressed ) { - this.currentBaseRotation = (180 + this.currentBaseRotation) % 360; + this.setBaseRotation((180 + this.getBaseRotation()) % 360); } // Check if we should stop placement @@ -451,7 +479,7 @@ export class HUDBuildingPlacerLogic extends BaseHUDPart { for (let i = 0; i < path.length; ++i) { const { rotation, tile } = path[i]; - this.currentBaseRotation = rotation; + this.setBaseRotation(rotation); this.tryPlaceCurrentBuildingAt(tile); } }); @@ -634,11 +662,11 @@ export class HUDBuildingPlacerLogic extends BaseHUDPart { ) { const delta = newPos.sub(oldPos); const angleDeg = Math_degrees(delta.angle()); - this.currentBaseRotation = (Math.round(angleDeg / 90) * 90 + 360) % 360; + this.setBaseRotation((Math.round(angleDeg / 90) * 90 + 360) % 360); // Holding alt inverts the placement if (this.root.keyMapper.getBinding(KEYMAPPINGS.placementModifiers.placeInverse).pressed) { - this.currentBaseRotation = (180 + this.currentBaseRotation) % 360; + this.setBaseRotation((180 + this.getBaseRotation()) % 360); } } diff --git a/src/js/profile/application_settings.js b/src/js/profile/application_settings.js index 16ec4cbd..0e03e902 100644 --- a/src/js/profile/application_settings.js +++ b/src/js/profile/application_settings.js @@ -248,9 +248,10 @@ export const allApplicationSettings = [ new BoolSetting("alwaysMultiplace", categoryGame, (app, value) => {}), new BoolSetting("enableTunnelSmartplace", categoryGame, (app, value) => {}), - new BoolSetting("vignette", categoryGame, (app, value) => {}), + new BoolSetting("vignette", categoryGame, (app, value) => {}),<<<<<<< HEAD new BoolSetting("compactBuildingInfo", categoryGame, (app, value) => {}), new BoolSetting("disableCutDeleteWarnings", categoryGame, (app, value) => {}), + new BoolSetting("rotationByBuilding", categoryGame, (app, value) => {}), ]; export function getApplicationSettingById(id) { @@ -277,6 +278,8 @@ class SettingsStorage { this.vignette = true; this.compactBuildingInfo = false; this.disableCutDeleteWarnings = false; + this.rotationByBuilding = true; + this.enableColorBlindHelper = false; @@ -527,6 +530,7 @@ export class ApplicationSettings extends ReadWriteProxy { } if (data.version < 13) { +<<<<<<< HEAD data.settings.compactBuildingInfo = false; data.version = 13; } @@ -550,6 +554,11 @@ export class ApplicationSettings extends ReadWriteProxy { if (data.version < 17) { data.settings.enableColorBlindHelper = false; data.version = 17; +======= + data.settings.rotationByBuilding = true; + data.version = 13; + +>>>>>>> 655c356... Adds tracking for rotation per building type. } return ExplainedResult.good(); From efdaf74659e4c74bae5d778165abb23b28b0a0fa Mon Sep 17 00:00:00 2001 From: Magnus Grimstvedt Saltnes Date: Thu, 18 Jun 2020 22:30:17 +0200 Subject: [PATCH 05/24] Adds English strings for rotationByBuilding. --- translations/base-en.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/translations/base-en.yaml b/translations/base-en.yaml index 2ad5ef07..458086ac 100644 --- a/translations/base-en.yaml +++ b/translations/base-en.yaml @@ -721,6 +721,11 @@ settings: title: Vignette description: >- Enables the vignette which darkens the screen corners and makes text easier to read. + + rotationByBuilding: + title: Rotation by building type + description: >- + Each building type remembers the rotation you last set it to individually. This may be more comfortable if you frequently switch between placing different building types. compactBuildingInfo: title: Compact Building Infos From 9a00931c3fb3d602e4c01e7ade63039b5628591e Mon Sep 17 00:00:00 2001 From: Magnus Grimstvedt Saltnes Date: Thu, 18 Jun 2020 22:48:36 +0200 Subject: [PATCH 06/24] Implements some linter recommendations for CI check. --- src/js/game/hud/parts/building_placer_logic.js | 7 ++----- src/js/profile/application_settings.js | 8 +++----- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/js/game/hud/parts/building_placer_logic.js b/src/js/game/hud/parts/building_placer_logic.js index a58d2b64..5a462f47 100644 --- a/src/js/game/hud/parts/building_placer_logic.js +++ b/src/js/game/hud/parts/building_placer_logic.js @@ -1,4 +1,3 @@ - import { Math_abs, Math_degrees, Math_round } from "../../../core/builtins"; import { globalConfig } from "../../../core/config"; import { gMetaBuildingRegistry } from "../../../core/global_registries"; @@ -61,8 +60,7 @@ export class HUDBuildingPlacerLogic extends BaseHUDPart { } const id = this.currentMetaBuilding.get().getId(); return this.preferredRotations[id] || this.currentBaseRotationGeneral; - - } + }; this.setBaseRotation = function (rotation) { const rotationByBuilding = this.root.app.settings.getAllSettings().rotationByBuilding; @@ -72,8 +70,7 @@ export class HUDBuildingPlacerLogic extends BaseHUDPart { const id = this.currentMetaBuilding.get().getId(); this.preferredRotations[id] = rotation; } - } - + }; /** * Whether we are currently dragging diff --git a/src/js/profile/application_settings.js b/src/js/profile/application_settings.js index 0e03e902..92edf124 100644 --- a/src/js/profile/application_settings.js +++ b/src/js/profile/application_settings.js @@ -530,7 +530,6 @@ export class ApplicationSettings extends ReadWriteProxy { } if (data.version < 13) { -<<<<<<< HEAD data.settings.compactBuildingInfo = false; data.version = 13; } @@ -554,11 +553,10 @@ export class ApplicationSettings extends ReadWriteProxy { if (data.version < 17) { data.settings.enableColorBlindHelper = false; data.version = 17; -======= + } + if(data.version < 18) { data.settings.rotationByBuilding = true; - data.version = 13; - ->>>>>>> 655c356... Adds tracking for rotation per building type. + data.version = 18; } return ExplainedResult.good(); From e18a888210ecc72132d7dfee91ddde142b98b1d7 Mon Sep 17 00:00:00 2001 From: Magnus Grimstvedt Saltnes Date: Thu, 18 Jun 2020 22:55:03 +0200 Subject: [PATCH 07/24] Attempts to fix some whitespace differences. --- src/js/game/hud/parts/building_placer.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/js/game/hud/parts/building_placer.js b/src/js/game/hud/parts/building_placer.js index e3a75400..6ccf00ad 100644 --- a/src/js/game/hud/parts/building_placer.js +++ b/src/js/game/hud/parts/building_placer.js @@ -82,9 +82,9 @@ export class HUDBuildingPlacer extends HUDBuildingPlacerLogic { this.buildingInfoElements.tutorialImage.setAttribute( "data-icon", "building_tutorials/" + - metaBuilding.getId() + - (variant === defaultBuildingVariant ? "" : "-" + variant) + - ".png" + metaBuilding.getId() + + (variant === defaultBuildingVariant ? "" : "-" + variant) + + ".png" ); removeAllChildren(this.buildingInfoElements.additionalInfo); @@ -122,10 +122,10 @@ export class HUDBuildingPlacer extends HUDBuildingPlacerLogic { T.ingame.buildingPlacement.cycleBuildingVariants.replace( "", "" + - this.root.keyMapper - .getBinding(KEYMAPPINGS.placement.cycleBuildingVariants) - .getKeyCodeString() + - "" + this.root.keyMapper + .getBinding(KEYMAPPINGS.placement.cycleBuildingVariants) + .getKeyCodeString() + + "" ) ); From 553ebb5ef6ae7ab721dac2af6c3170d981b1f80e Mon Sep 17 00:00:00 2001 From: Magnus Grimstvedt Saltnes Date: Tue, 23 Jun 2020 12:03:32 +0200 Subject: [PATCH 08/24] Switches to using ES6 get/set for currentBaseRotation. --- src/js/game/hud/parts/building_placer.js | 2 +- .../game/hud/parts/building_placer_logic.js | 69 +++++++++++-------- src/js/profile/application_settings.js | 5 +- 3 files changed, 42 insertions(+), 34 deletions(-) diff --git a/src/js/game/hud/parts/building_placer.js b/src/js/game/hud/parts/building_placer.js index 6ccf00ad..5faec6ab 100644 --- a/src/js/game/hud/parts/building_placer.js +++ b/src/js/game/hud/parts/building_placer.js @@ -201,7 +201,7 @@ export class HUDBuildingPlacer extends HUDBuildingPlacerLogic { } = metaBuilding.computeOptimalDirectionAndRotationVariantAtTile( this.root, mouseTile, - this.getBaseRotation(), + this.currentBaseRotation, this.currentVariant.get() ); diff --git a/src/js/game/hud/parts/building_placer_logic.js b/src/js/game/hud/parts/building_placer_logic.js index 5a462f47..43abaf6a 100644 --- a/src/js/game/hud/parts/building_placer_logic.js +++ b/src/js/game/hud/parts/building_placer_logic.js @@ -49,28 +49,9 @@ export class HUDBuildingPlacerLogic extends BaseHUDPart { /** * The current rotation preference for each building. - * @type {Object.} + * @type{Object.} */ - this.preferredRotations = {}; - - this.getBaseRotation = function () { - const rotationByBuilding = this.root.app.settings.getAllSettings().rotationByBuilding; - if (!rotationByBuilding) { - return this.currentBaseRotationGeneral; - } - const id = this.currentMetaBuilding.get().getId(); - return this.preferredRotations[id] || this.currentBaseRotationGeneral; - }; - - this.setBaseRotation = function (rotation) { - const rotationByBuilding = this.root.app.settings.getAllSettings().rotationByBuilding; - if (!rotationByBuilding) { - this.currentBaseRotationGeneral = rotation; - } else { - const id = this.currentMetaBuilding.get().getId(); - this.preferredRotations[id] = rotation; - } - }; + this.preferredBaseRotations = {}; /** * Whether we are currently dragging @@ -139,6 +120,34 @@ export class HUDBuildingPlacerLogic extends BaseHUDPart { this.root.camera.upPostHandler.add(this.onMouseUp, this); } + /** + * Returns the current base rotation for the current meta-building. + * @returns {number} + */ + get currentBaseRotation() { + const rotationByBuilding = this.root.app.settings.getAllSettings().rotationByBuilding; + if (!rotationByBuilding) { + return this.currentBaseRotationGeneral; + } + const id = this.currentMetaBuilding.get().getId(); + return this.preferredBaseRotations[id] == null + ? this.currentBaseRotationGeneral + : this.preferredBaseRotations[id]; + } + + /** + * Sets the base rotation for the current meta-building. + */ + set currentBaseRotation(rotation) { + const rotationByBuilding = this.root.app.settings.getAllSettings().rotationByBuilding; + if (!rotationByBuilding) { + this.currentBaseRotationGeneral = rotation; + } else { + const id = this.currentMetaBuilding.get().getId(); + this.preferredBaseRotations[id] = rotation; + } + } + /** * Returns if the direction lock is currently active * @returns {boolean} @@ -225,12 +234,12 @@ export class HUDBuildingPlacerLogic extends BaseHUDPart { const selectedBuilding = this.currentMetaBuilding.get(); if (selectedBuilding) { if (this.root.keyMapper.getBinding(KEYMAPPINGS.placement.rotateInverseModifier).pressed) { - this.setBaseRotation((this.getBaseRotation() + 270) % 360); + this.currentBaseRotation = (this.currentBaseRotation + 270) % 360; } else { - this.setBaseRotation((this.getBaseRotation() + 90) % 360); + this.currentBaseRotation = (this.currentBaseRotation + 90) % 360; } const staticComp = this.fakeEntity.components.StaticMapEntity; - staticComp.rotation = this.getBaseRotation(); + staticComp.rotation = this.currentBaseRotation; } } /** @@ -402,7 +411,7 @@ export class HUDBuildingPlacerLogic extends BaseHUDPart { const { rotation, rotationVariant } = metaBuilding.computeOptimalDirectionAndRotationVariantAtTile( this.root, tile, - this.getBaseRotation(), + this.currentBaseRotation, this.currentVariant.get() ); @@ -410,7 +419,7 @@ export class HUDBuildingPlacerLogic extends BaseHUDPart { origin: tile, rotation, rotationVariant, - originalRotation: this.getBaseRotation(), + originalRotation: this.currentBaseRotation, building: this.currentMetaBuilding.get(), variant: this.currentVariant.get(), }); @@ -426,7 +435,7 @@ export class HUDBuildingPlacerLogic extends BaseHUDPart { KEYMAPPINGS.placementModifiers.placementDisableAutoOrientation ).pressed ) { - this.setBaseRotation((180 + this.getBaseRotation()) % 360); + this.currentBaseRotation = (180 + this.currentBaseRotation) % 360; } // Check if we should stop placement @@ -476,7 +485,7 @@ export class HUDBuildingPlacerLogic extends BaseHUDPart { for (let i = 0; i < path.length; ++i) { const { rotation, tile } = path[i]; - this.setBaseRotation(rotation); + this.currentBaseRotation = rotation; this.tryPlaceCurrentBuildingAt(tile); } }); @@ -659,11 +668,11 @@ export class HUDBuildingPlacerLogic extends BaseHUDPart { ) { const delta = newPos.sub(oldPos); const angleDeg = Math_degrees(delta.angle()); - this.setBaseRotation((Math.round(angleDeg / 90) * 90 + 360) % 360); + this.currentBaseRotation = (Math.round(angleDeg / 90) * 90 + 360) % 360; // Holding alt inverts the placement if (this.root.keyMapper.getBinding(KEYMAPPINGS.placementModifiers.placeInverse).pressed) { - this.setBaseRotation((180 + this.getBaseRotation()) % 360); + this.currentBaseRotation = (180 + this.currentBaseRotation) % 360; } } diff --git a/src/js/profile/application_settings.js b/src/js/profile/application_settings.js index 92edf124..8fd80df0 100644 --- a/src/js/profile/application_settings.js +++ b/src/js/profile/application_settings.js @@ -248,7 +248,7 @@ export const allApplicationSettings = [ new BoolSetting("alwaysMultiplace", categoryGame, (app, value) => {}), new BoolSetting("enableTunnelSmartplace", categoryGame, (app, value) => {}), - new BoolSetting("vignette", categoryGame, (app, value) => {}),<<<<<<< HEAD + new BoolSetting("vignette", categoryGame, (app, value) => {}), new BoolSetting("compactBuildingInfo", categoryGame, (app, value) => {}), new BoolSetting("disableCutDeleteWarnings", categoryGame, (app, value) => {}), new BoolSetting("rotationByBuilding", categoryGame, (app, value) => {}), @@ -280,7 +280,6 @@ class SettingsStorage { this.disableCutDeleteWarnings = false; this.rotationByBuilding = true; - this.enableColorBlindHelper = false; /** @@ -554,7 +553,7 @@ export class ApplicationSettings extends ReadWriteProxy { data.settings.enableColorBlindHelper = false; data.version = 17; } - if(data.version < 18) { + if (data.version < 18) { data.settings.rotationByBuilding = true; data.version = 18; } From bbe6edce9fdbb622bdd8ff080445eafc7620bab4 Mon Sep 17 00:00:00 2001 From: YellowSugarHK <67306569+YellowSugarHK@users.noreply.github.com> Date: Tue, 23 Jun 2020 22:57:42 +0800 Subject: [PATCH 09/24] Update base-zh-TW.yaml --- translations/base-zh-TW.yaml | 892 ++++++++++++++++++----------------- 1 file changed, 461 insertions(+), 431 deletions(-) diff --git a/translations/base-zh-TW.yaml b/translations/base-zh-TW.yaml index aac39324..f2fa0fe0 100644 --- a/translations/base-zh-TW.yaml +++ b/translations/base-zh-TW.yaml @@ -19,9 +19,36 @@ # the basic structure so the game also detects it. # +# Chinese (traditional) translation dictionary. TODO: better names for the buildings. +# Standalone:獨立版 +# Demo:演示版 +# Level:關 +# Shape:圖形 +# tile:格子 +# Keybind:按鍵設置 +# Menu:主界面 +# Center/Hub:基地 +# Upgrade:升級 +# Efficiency:效率 +# Building:建築 +# Variant:建築變體 +# Belt: 傳送帶 +# Balancer:平衡機 +# Compact Balancer:小型合流機 +# Merger:合併機 +# Tunnel:隧道 +# Extractor:開採機 +# Cutter:切割機 +# Rotate:旋轉機 +# Stacker:混合機 +# Color Mixer:混色機 +# Painter:上色機 +# Trash:垃圾桶 + steamPage: # This is the short text appearing on the steam page - shortText: shapez.io is a game about building factories to automate the creation and combination of increasingly complex shapes within an infinite map. + shortText: shapez.io 是一款在一個無邊際的地圖上建造工廠、自動化生產與組合愈加複雜圖形的遊戲。 + # shortText: shapez.io is a game about building factories to automate the creation and combination of increasingly complex shapes within an infinite map. # This is the long description for the steam page - It is contained here so you can help to translate it, and I will regulary update the store page. # NOTICE: @@ -30,77 +57,78 @@ steamPage: longText: >- [img]{STEAM_APP_IMAGE}/extras/store_page_gif.gif[/img] - shapez.io is a game about building factories to automate the creation and combination of shapes. Deliver the requested, increasingly complex shapes to progress within the game and unlock upgrades to speed up your factory. + shapez.io 是一款在無邊際的地圖上建造工廠、自動化生產與組合愈加複雜的圖形的遊戲。提交任務,製造更複雜的流水線,解鎖升級來提升您工廠的運作速度。 - Since the demand raises you will have to scale up your factory to fit the needs - Don't forget about resources though, you will have to expand in the [b]infinite map[/b]! + 你將會需要隨著不斷上升得需求擴大你的工廠。當然,不要忘記你可以在[b]無盡[/b]的地圖上開採資源! - Since shapes can get boring soon you need to mix colors and paint your shapes with it - Combine red, green and blue color resources to produce different colors and paint shapes with it to satisfy the demand. + 只對圖形進行加工可能會使你感到無聊。我們為你準備了顏色資源——將紅、綠、藍三種顏色混合,生產更多不同的顏色並粉刷在圖形上以滿足需求。 - This game features 18 levels (Which should keep you busy for hours already!) but I'm constantly adding new content - There is a lot planned! + 這個遊戲目前有18個關卡(這應該已經能讓你忙碌幾個小時了!),並且遊戲正在不斷地更新中。很多新關卡已經在開發計劃當中! - [b]Standalone Advantages[/b] + [b]獨立版優勢[/b] [list] - [*] Waypoints - [*] Unlimited Savegames - [*] Dark Mode - [*] More settings - [*] Allow me to further develop shapez.io ❤️ - [*] More features in the future! + [*] 地圖標記 + [*] 無限存檔 + [*] 深色模式 + [*] 更多設置 + [*] 支持作者繼續開發shapez.io❤️ + [*] 在以後還有更多特性! [/list] - [b]Planned features & Community suggestions[/b] - This game is open source - Anybody can contribute! Besides of that, I listen [b]a lot[/b] to the community! I try to read all suggestions and take as much feedback into account as possible. + [b]開發計劃與社區意見[/b] + + 本遊戲已開源,所有人都能參與遊戲內容的開發!除此以外,我[b]非常重視[/b]玩家社區的反饋!我會閱讀每一條建議並儘量顧及所有建議。 [list] - [*] Story mode where buildings cost shapes - [*] More levels & buildings (standalone exclusive) - [*] Different maps, and maybe map obstacles - [*] Configurable map creation (Edit number and size of patches, seed, and more) - [*] More types of shapes - [*] More performance improvements (Although the game already runs pretty good!) - [*] Color blind mode - [*] And much more! + [*] 要消耗圖形來造建築的的故事模式 + [*] 更多關卡&建築(單機版獨有) + [*] 更多地圖,也許會有障礙物 + [*] 可配置的地圖生成(礦脈密度與大小、 隨機種子以及其他地圖設置) + [*] 更多圖形 + [*] 更多的性能改進(當然,現在遊戲已經非常流暢了!) + [*] 色盲模式 + [*] 以及更多其他的功能! [/list] - Be sure to check out my trello board for the full roadmap! https://trello.com/b/ISQncpJP/shapezio + 記得查看我的Trello計劃板!那裡有所有的開發計劃! https://trello.com/b/ISQncpJP/shapezio global: - loading: Loading - error: Error + loading: 加載中 + error: 錯誤 # How big numbers are rendered, e.g. "10,000" - thousandsDivider: "," + thousandsDivider: " " # The suffix for large numbers, e.g. 1.3k, 400.2M, etc. suffix: - thousands: k - millions: M - billions: B - trillions: T + thousands: 千 + millions: 百萬 + billions: 十億 + trillions: 兆 # Shown for infinitely big numbers - infinite: inf + infinite: 無限 time: # Used for formatting past time dates - oneSecondAgo: one second ago - xSecondsAgo: seconds ago - oneMinuteAgo: one minute ago - xMinutesAgo: minutes ago - oneHourAgo: one hour ago - xHoursAgo: hours ago - oneDayAgo: one day ago - xDaysAgo: days ago + oneSecondAgo: 1秒前 + xSecondsAgo: 秒前 + oneMinuteAgo: 1分鐘前 + xMinutesAgo: 分鐘前 + oneHourAgo: 1小時前 + xHoursAgo: 小時前 + oneDayAgo: 1天前 + xDaysAgo: 天前 # Short formats for times, e.g. '5h 23m' - secondsShort: s - minutesAndSecondsShort: m s - hoursAndMinutesShort: h s + secondsShort: 秒 + minutesAndSecondsShort: 秒 + hoursAndMinutesShort: 小時 秒 - xMinutes: minutes + xMinutes: 分鐘 keys: tab: TAB @@ -108,224 +136,222 @@ global: alt: ALT escape: ESC shift: SHIFT - space: SPACE + space: 空格 demoBanners: # This is the "advertisement" shown in the main menu and other various places - title: Demo Version + title: 演示版 intro: >- - Get the standalone to unlock all features! + 獲取獨立版以解鎖所有功能! mainMenu: - play: Play - changelog: Changelog - importSavegame: Import - openSourceHint: This game is open source! - discordLink: Official Discord Server - helpTranslate: Help translate! + play: 開始遊戲 + changelog: 更新日誌 + importSavegame: 導入 + openSourceHint: 本游戏已开源頭! + discordLink: 官方Discord服務器 + helpTranslate: 幫助我們翻譯! # This is shown when using firefox and other browsers which are not supported. browserWarning: >- - Sorry, but the game is known to run slow on your browser! Get the standalone version or download chrome for the full experience. + 很抱歉, 本遊戲在當前瀏覽器上可能運行緩慢! 使用chrome或者獲取獨立版以得到更好的體驗。 - savegameLevel: Level - savegameLevelUnknown: Unknown Level + savegameLevel: 第關/關 + savegameLevelUnknown: 未知關卡 contests: contest_01_03062020: - title: "Contest #01" - desc: Win $25 for the coolest base! + title: "比賽 #01" + desc: 最屌的工廠將能贏得25美元的獎金! longDesc: >- - To give something back to you, I thought it would be cool to make weekly contests! -

- This weeks topic: Build the coolest base! -

- Here's the deal:
-
    -
  • Submit a screenshot of your base to contest@shapez.io
  • -
  • Bonus points if you share it on social media!
  • -
  • I will choose 5 screenshots and propose it to the discord community to vote.
  • -
  • The winner gets $25 (Paypal, Amazon Gift Card, whatever you prefer)
  • -
  • Deadline: 07.06.2020 12:00 AM CEST
  • -
-
- I'm looking forward to seeing your awesome creations! - - showInfo: View - contestOver: This contest has ended - Join the discord to get noticed about new contests! - continue: Continue - newGame: New Game - madeBy: Made by + 為了回饋您,我認為每週進行比賽也很酷! +

+ 本週主題:建立最酷的基地! +

+ 這是交易:
+
    +
  • 將您的基地的屏幕截圖提交到 contest@shapez.io +
  • 如果您在社交媒體上分享會有額外獎勵積分! +
  • 我將選擇5張屏幕截圖,並將其提交給 discord 社區進行投票。 +
  • 獲勝者將獲得 $ 25 (貝寶paypal,亞馬遜禮品卡,隨您喜歡) +
  • 截止日期:CEST 07.06.2020 12:00 AM + +
    + 我期待看到您的出色創作! + + showInfo: 詳細信息 + contestOver: 本次競賽已結束。加入官方Discord以收到關於新競賽的提醒! + continue: 繼續 + newGame: 新遊戲 + madeBy: 作者: subreddit: Reddit dialogs: buttons: - ok: OK - delete: Delete - cancel: Cancel - later: Later - restart: Restart - reset: Reset - getStandalone: Get Standalone - deleteGame: I know what I do - viewUpdate: View Update - showUpgrades: Show Upgrades - showKeybindings: Show Keybindings + ok: 確認 + delete: 删除 + cancel: 取消 + later: 之後 + restart: 重啟 + reset: 重置 + getStandalone: 獲得獨立版 + deleteGame: 我知道我在做什麼 + viewUpdate: 查看更新 + showUpgrades: 顯示建築升級 + showKeybindings: 顯示按鍵設置 importSavegameError: - title: Import Error + title: 導入錯誤 text: >- - Failed to import your savegame: + 未能導入你這存檔: importSavegameSuccess: - title: Savegame Imported + title: 導入成功 text: >- - Your savegame has been successfully imported. + 存檔被成功導入 gameLoadFailure: - title: Game is broken + title: 存檔被損壞 text: >- - Failed to load your savegame: + 未能導入你這存檔: confirmSavegameDelete: - title: Confirm deletion + title: 確認刪除 text: >- - Are you sure you want to delete the game? + 你確定要刪除這存檔嗎? savegameDeletionError: - title: Failed to delete + title: 刪除錯誤 text: >- - Failed to delete the savegame: + 未能删除你這存档 restartRequired: - title: Restart required + title: 需要重啟 text: >- - You need to restart the game to apply the settings. + 你需要重啟遊戲以應用變更的設置。 editKeybinding: - title: Change Keybinding - desc: Press the key or mouse button you want to assign, or escape to cancel. + title: 更改按鍵設置 + desc: 請按下你想要使用的按鍵,或者按下ESC鍵來取消設置。 resetKeybindingsConfirmation: - title: Reset keybindings - desc: This will reset all keybindings to their default values. Please confirm. + title: 重置所有按鍵 + desc: 這將會重置所有按鍵,請確認。 keybindingsResetOk: - title: Keybindings reset - desc: The keybindings have been reset to their respective defaults! + title: 重置了所有按鍵 + desc: 成功重置了所有按鍵! featureRestriction: - title: Demo Version - desc: You tried to access a feature () which is not available in the demo. Consider to get the standalone for the full experience! + title: 演示版 + desc: 你嘗試使用了 功能。該功能在演示版中不可用。請考慮購買獨立版以獲得更好的體驗。 oneSavegameLimit: - title: Limited savegames - desc: You can only have one savegame at a time in the demo version. Please remove the existing one or get the standalone! + title: 存檔數量限制 + desc: 演示版中只能保存一份存檔。請刪除舊存檔或者獲取獨立版! updateSummary: - title: New update! + title: 更新了! desc: >- - Here are the changes since you last played: + 以下為自上次遊戲以來更新的內容: upgradesIntroduction: - title: Unlock Upgrades + title: 解鎖建築升級 desc: >- - All shapes you produce can be used to unlock upgrades - Don't destroy your old factories! - The upgrades tab can be found on the top right corner of the screen. +
    你生產過的所有圖形都會被用來升級建築。升級菜單在屏幕右上角。 + 不要銷毀你之前建造的工廠! massDeleteConfirm: - title: Confirm delete + title: 確認刪除 desc: >- - You are deleting a lot of buildings ( to be exact)! Are you sure you want to do this? + 你將要刪除很多建築,準確來說有幢!你確定要這麼做嗎? blueprintsNotUnlocked: - title: Not unlocked yet + title: 未解鎖 desc: >- - Blueprints have not been unlocked yet! Complete more levels to unlock them. + 你還沒有解鎖藍圖功能!完成更多的關卡來解鎖藍圖。 keybindingsIntroduction: - title: Useful keybindings + title: 實用按鍵 desc: >- - This game has a lot of keybindings which make it easier to build big factories. - Here are a few, but be sure to check out the keybindings!

    - CTRL + Drag: Select area to copy / delete.
    - SHIFT: Hold to place multiple of one building.
    - ALT: Invert orientation of placed belts.
    + 這個遊戲有很多能幫助搭建工廠的使用按鍵。 + 以下是其中的一些,記得在按鍵設置中查看其他的!

    + CTRL + 拖動:選擇區域以復製或刪除。
    + SHIFT: 按住以放置多個。
    + ALT: 反向放置傳送帶。
    createMarker: - title: New Marker - desc: Give it a meaningful name, you can also include a short key of a shape (Which you can generate here) + title: 創建標記 + desc: 給地圖標記起一個的名字。你可以在名字中加入一個短代碼以加入圖形。 (你可以在這裡生成短代碼。) markerDemoLimit: - desc: You can only create two custom markers in the demo. Get the standalone for unlimited markers! + desc: 在演示版中你只能創建兩個地圖標記。請獲取獨立版以創建更多標記。 massCutConfirm: - title: Confirm cut + title: 確認剪割 desc: >- - You are cutting a lot of buildings ( to be exact)! Are you sure you - want to do this? + 你將要剪割很多建築,準確來說有幢!你確定要這麼做嗎? exportScreenshotWarning: - title: Export screenshot + title: 工廠截圖 desc: >- - You requested to export your base as a screenshot. Please note that this can - be quite slow for a big base and even crash your game! + 你將要導出你的工廠的截圖。如果你的基地很大,截圖過程將會很慢,甚至有可能導致遊戲崩潰! ingame: # This is shown in the top left corner and displays useful keybindings in # every situation keybindingsOverlay: - moveMap: Move - selectBuildings: Select area - stopPlacement: Stop placement - rotateBuilding: Rotate building - placeMultiple: Place multiple - reverseOrientation: Reverse orientation - disableAutoOrientation: Disable auto orientation - toggleHud: Toggle HUD - placeBuilding: Place building - createMarker: Create Marker - delete: Destroy - pasteLastBlueprint: Paste last blueprint - lockBeltDirection: Enable belt planner - plannerSwitchSide: Flip planner side - cutSelection: Cut - copySelection: Copy - clearSelection: Clear Selection - pipette: Pipette + moveMap: 移動 + selectBuildings: 選擇區域 + stopPlacement: 停止放置 + rotateBuilding: 轉動建築 + placeMultiple: 放置多個 + reverseOrientation: 反向放置 + disableAutoOrientation: 關閉自動定向 + toggleHud: 開關HUD + placeBuilding: 放置建築 + createMarker: 創建地圖標記 + delete: 銷毀 + pasteLastBlueprint: 貼上一個藍圖 + lockBeltDirection: 啟用傳送帶規劃 + plannerSwitchSide: 規劃器換邊 + cutSelection: 剪下選項 + copySelection: 複製選項 + clearSelection: 清空選項 + pipette: 吸附 # Everything related to placing buildings (I.e. as soon as you selected a building # from the toolbar) buildingPlacement: # Buildings can have different variants which are unlocked at later levels, # and this is the hint shown when there are multiple variants available. - cycleBuildingVariants: Press to cycle variants. + cycleBuildingVariants: 按鍵以選擇建築變體. # Shows the hotkey in the ui, e.g. "Hotkey: Q" hotkeyLabel: >- - Hotkey: + 快捷鍵: infoTexts: - speed: Speed - range: Range - storage: Storage - oneItemPerSecond: 1 item / second - itemsPerSecond: items / s - itemsPerSecondDouble: (x2) + speed: 效率 + range: 範圍 + storage: 容量 + oneItemPerSecond: 1個/秒 + itemsPerSecond: 個/秒 + itemsPerSecondDouble: (2倍) - tiles: tiles + tiles: 格 # The notification when completing a level levelCompleteNotification: # is replaced by the actual level, so this gets 'Level 03' for example. - levelTitle: Level - completed: Completed - unlockText: Unlocked ! - buttonNextLevel: Next Level + levelTitle: 第關 / 關 + completed: 完成 + unlockText: 解鎖! + buttonNextLevel: 下一關 # Notifications on the lower right notifications: - newUpgrade: A new upgrade is available! - gameSaved: Your game has been saved. + newUpgrade: 有新的更新啦! + gameSaved: 遊戲已保存。 # The "Upgrades" window shop: @@ -336,431 +362,436 @@ ingame: tier: Tier # The roman number for each tier - tierLabels: [I, II, III, IV, V, VI, VII, VIII, IX, X] - - maximumLevel: MAXIMUM LEVEL (Speed x) + # Chinese translation: Chinese characters for each tier + tierLabels: [壹、貳、叁、肆、伍、陸、柒、捌、玖、拾] + maximumLevel: 最高级(倍效率) + # The "Statistics" window statistics: - title: Statistics + title: 統計信息 dataSources: stored: - title: Stored - description: Displaying amount of stored shapes in your central building. + title: 儲存 + description: 顯示基地中每種圖形儲存的數量。 produced: - title: Produced - description: Displaying all shapes your whole factory produces, including intermediate products. + title: 生產 + description: 顯示所有正在被生產的圖形數量,包括中間產物。 delivered: - title: Delivered - description: Displaying shapes which are delivered to your central building. - noShapesProduced: No shapes have been produced so far. + title: 送達 + description: 顯示圖形送達基地的速度。 + noShapesProduced: 你還沒有生產任何圖形。 # Displays the shapes per minute, e.g. '523 / m' - shapesPerMinute: / m + shapesPerMinute: 個/分鐘 # Settings menu, when you press "ESC" settingsMenu: - playtime: Playtime + playtime: 遊戲時間 - buildingsPlaced: Buildings - beltsPlaced: Belts + buildingsPlaced: 建築數量 + beltsPlaced: 傳送帶數量 buttons: - continue: Continue - settings: Settings - menu: Return to menu + continue: 繼續 + settings: 設置 + menu: 回到主界面 # Bottom left tutorial hints tutorialHints: - title: Need help? - showHint: Show hint - hideHint: Close + title: 需要幫助? + showHint: 顯示 + hideHint: 關閉 # When placing a blueprint blueprintPlacer: - cost: Cost + cost: 需要 # Map markers 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. - creationSuccessNotification: Marker has been created. - + waypoints: 地圖標記 + hub: 基地 + description: 滑鼠左鍵按標記跳轉到它,按右鍵將其刪除。

    從當前視圖創建一個標記,或按右鍵創建一個標記。 所選位置的標記。 + creationSuccessNotification: 成功創建地圖標記。 + # Interactive tutorial interactiveTutorial: - title: Tutorial + title: 教程 hints: - 1_1_extractor: Place an extractor on top of a circle shape to extract it! + 1_1_extractor: 在圓形礦脈上放一個開採機來獲取圓形! 1_2_conveyor: >- - 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. + 這不是一個挂機遊戲!建造更多的開採機和傳送帶來更快地完成目標。

    + 提示:按住SHIFT鍵來放置多個開採機,用R鍵旋轉它們。 colors: - red: Red - green: Green - blue: Blue - yellow: Yellow - purple: Purple - cyan: Cyan - white: White - uncolored: No color + red: 紅 + green: 綠 + blue: 藍 + yellow: 黃 + purple: 紫 + cyan: 淺藍 + white: 白 + uncolored: 無顏色 shapeViewer: - title: Layers - empty: Empty + title: 層 + empty: 空 # All shop upgrades shopUpgrades: belt: - name: Belts, Distributor & Tunnels - description: Speed x → x + name: 傳送帶、平衡機、隧道 + description: 效率 倍 → 倍 miner: - name: Extraction - description: Speed x → x + name: 開採 + description: 效率 倍 → 倍 processors: - name: Cutting, Rotating & Stacking - description: Speed x → x + name: 切割、旋轉、混合 + description: 效率 倍 → 倍 painting: - name: Mixing & Painting - description: Speed x → x + name: 混色、上色 + description: 效率 倍 → 倍 # Buildings and their name / description buildings: belt: default: - name: &belt Conveyor Belt - description: Transports items, hold and drag to place multiple. + name: &belt 傳送帶 + description: 運送物品,按住並拖動來放置多個。 miner: # Internal name for the Extractor default: - name: &miner Extractor - description: Place over a shape or color to extract it. + name: &miner 開採機 + description: 在圖形或者顏色上放置來開採他們。 chainable: - name: Extractor (Chain) - description: Place over a shape or color to extract it. Can be chained. + name: 鏈式開採機 + description: 在圖形或者顏色上放置來開採他們。可以被鏈接在一起。 underground_belt: # Internal name for the Tunnel default: - name: &underground_belt Tunnel - description: Allows to tunnel resources under buildings and belts. + name: &underground_belt 隧道 + description: 可以從其他傳送帶或建築底下方運送物品。 tier2: - name: Tunnel Tier II - description: Allows to tunnel resources under buildings and belts. + name: 貳級隧道 + description: 可以從其他傳送帶或建築底下方運送物品。 splitter: # Internal name for the Balancer default: - name: &splitter Balancer - description: Multifunctional - Evenly distributes all inputs onto all outputs. + name: &splitter 平衡機 + description: 多功能——將所有輸入平均分配到所有輸出。 compact: - name: Merger (compact) - description: Merges two conveyor belts into one. + name: 小型合流機 + description: 把兩個輸入合併到一個輸出上。 compact-inverse: - name: Merger (compact) - description: Merges two conveyor belts into one. + name: 小型合流機 (反轉) + description: 把兩個輸入合併到一個輸出上。 cutter: default: - name: &cutter Cutter - description: Cuts shapes from top to bottom and outputs both halfs. If you use only one part, be sure to destroy the other part or it will stall! + name: &cutter 切割機 + description: 將圖形從上到下切開並輸出。 如果你只需要其中一半,記得把另一半銷毀掉,否則切割機會停止工作! quad: - name: Cutter (Quad) - description: Cuts shapes into four parts. If you use only one part, be sure to destroy the other part or it will stall! + name: 切割機(四分) + description: 將輸入的圖形切成四塊。 如果你只需要其中一塊,記得把其他的銷毀掉,否則切割機會停止工作! rotater: default: - name: &rotater Rotate - description: Rotates shapes clockwise by 90 degrees. + name: &rotater 旋轉機 + description: 將圖形順時針旋轉90度。 ccw: - name: Rotate (CCW) - description: Rotates shapes counter clockwise by 90 degrees. + name: 旋轉機(逆時針) + description: 將圖形逆時針旋轉90度。 stacker: default: - name: &stacker Stacker - description: Stacks both items. If they can not be merged, the right item is placed above the left item. + name: &stacker 混合機 + description: 將輸入的圖形拼貼在一起。如果不能被直接拼貼,右邊的圖形會被混合在左邊的圖形上面. mixer: default: - name: &mixer Color Mixer - description: Mixes two colors using additive blending. + name: &mixer 混色機 + description: 將兩個顏色混合在一起。 (加法混合) painter: default: - name: &painter Painter - description: &painter_desc Colors the whole shape on the left input with the color from the right input. + name: &painter 上色機 + description: &painter_desc 將整個圖形塗上輸入的顏色。 double: - name: Painter (Double) - description: Colors the shapes on the left inputs with the color from the top input. + name: 上色機(雙倍) + description: 同時為兩個輸入的圖形上色,每次上色只消耗一份顏色。 quad: - name: Painter (Quad) - description: Allows to color each quadrant of the shape with a different color. + name: 上色機(四向) + description: 為圖形的四個角塗上不同的顏色。 mirrored: - name: *painter + name: &painter 上色機 (反向) description: *painter_desc trash: default: - name: &trash Trash - description: Accepts inputs from all sides and destroys them. Forever. + name: &trash 垃圾桶 + description: 從所有四個方向上輸入物品並永遠銷毀它們。 storage: - name: Storage - description: Stores excess items, up to a given capacity. Can be used as an overflow gate. + name: 倉庫 + description: 儲存多餘的物品,有一定儲存上限。可以被用來作為溢流門。最多五千個。 hub: - deliver: Deliver - toUnlock: to unlock + deliver: 交付 + toUnlock: 來解鎖 levelShortcut: LVL - + storyRewards: # Those are the rewards gained from completing the store reward_cutter_and_trash: - title: Cutting Shapes - desc: You just unlocked the cutter - it cuts shapes half from top to bottom regardless of its orientation!

    Be sure to get rid of the waste, or otherwise it will stall - For this purpose I gave you a trash, which destroys everything you put into it! + title: 切割圖形 + desc: 切割機已解鎖。不論切割機的方向,它都會把圖形從上到下切成兩半。

    記得把不需要的部分處理掉,否則這個這個建築會停止工作。為此我給你準備了垃圾桶,它會把所有放進去的物品銷毀掉。 reward_rotater: - title: Rotating - desc: The rotater has been unlocked! It rotates shapes clockwise by 90 degrees. + title: 順時針旋轉 + desc: 旋轉機已解鎖。它會順時針旋轉輸入的圖形90度。 reward_painter: - title: Painting + title: 上色 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, I'm working on a solution already! + 上色機已解鎖。和圖形一樣,從顏色礦脈中開採顏色,然後將在上色機中將顏色塗在圖形上。

    PS:我們正在開發色盲模式! reward_mixer: - title: Color Mixing - desc: The mixer has been unlocked - Combine two colors using additive blending with this building! + title: 混色 + desc: 混色器已解鎖-在此建築物中使用附加混合結合兩種顏色! reward_stacker: - title: Combiner - desc: You can now combine shapes with the combiner! 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! + title: 混合 + desc: 混合機已解鎖。混合機會嘗試把兩個輸入的圖形拼貼在一起。如果沒有重疊的部分,右邊的輸入會被混合到左邊的輸入上方! reward_splitter: - title: Splitter/Merger - desc: The multifunctional balancer has been unlocked - It can be used to build bigger factories by splitting and merging items onto multiple belts!

    + title: 分離/合併 + desc: 平衡機已解鎖。在大型工廠中,平衡機負責合併或分離多個傳送帶上的物品。

    reward_tunnel: - title: Tunnel - desc: The tunnel has been unlocked - You can now pipe items through belts and buildings with it! + title: 隧道 + desc: 隧道已解鎖。你現在可以從其他傳送帶或建築底下運送物品了(最長距離為四)! reward_rotater_ccw: - title: CCW Rotating - desc: You have unlocked a variant of the rotater - It allows to rotate counter clockwise! To build it, select the rotater and press 'T' to cycle its variants! + title: 逆時針旋轉 + desc: 您已解鎖了旋轉器的變體-它可以逆時針旋轉! 要構建它,請選擇旋轉器,然後按“ T”鍵循環其變種! reward_miner_chainable: - title: Chaining Extractor - desc: You have unlocked the chaining extractor! It can forward its resources to other extractors so you can more efficiently extract resources! + title: 鏈式開採機 + desc: 鏈式開採機變體已解鎖。它是開採機的一個變體。它可以將開采出來的資源傳遞給其他的開採機,使得資源提取更加高效! reward_underground_belt_tier_2: - title: Tunnel Tier II - desc: You have unlocked a new variant of the tunnel - It has a bigger range, and you can also mix-n-match those tunnels now! + title: 貳級隧道 + desc: 貳級隧道變體已解鎖。這個隧道有更長的傳輸距離。你還可以混用不同的隧道變體(點與點最長距離為7)! reward_splitter_compact: - title: Compact Balancer + title: 小型合流機 desc: >- - You have unlocked a compact variant of the balancer - It accepts two inputs and merges them into one! + 小型合流機變體已解鎖。它可以把兩個輸入合併到一個輸出上。 reward_cutter_quad: - title: Quad Cutting - desc: You have unlocked a variant of the cutter - It allows you to cut shapes in four parts instead of just two! - + title: 四分切割機 + desc: 您已解鎖了四分切割機的變體-它允許您將形狀直接切割為四個部分,而不是兩個! + reward_painter_double: - title: Double Painting - desc: You have unlocked a variant of the painter - It works as the regular painter but processes two shapes at once consuming just one color instead of two! + title: 雙倍上色機 + desc: 您已經解鎖了雙倍上色機的變體-它可以作為常規畫家使用,但一次只能處理兩個形狀,而只消耗一種顏色而不是兩種顏色! reward_painter_quad: - title: Quad Painting - desc: You have unlocked a variant of the painter - It allows to paint each part of the shape individually! + title: 四向上色機 + desc: 上色機四向變體已解鎖。它可以在一個圖形的四個角上塗不同的顏色! reward_storage: - title: Storage Buffer - desc: You have unlocked a variant of the trash - It allows to store items up to a given capacity! + title: 倉庫 + desc: 倉庫變體已解鎖。它可以暫時儲存一些材料,有容量上限。 reward_freeplay: - title: Freeplay - desc: You did it! You unlocked the free-play mode! This means that shapes are now randomly generated! (No worries, more content is planned for the standalone!) + title: 自由模式 + desc: 恭喜你!你解鎖了自由模式!現在圖形將會是隨機生成的! (不用擔心,我計劃在獨立版本中加入更多內容!) 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). + title: 藍圖 + desc: 現在,您可以複製並貼上工廠的各個部分! 選擇一個區域(按住CTRL,然後用鼠標拖動),然後按'C'將其複制。

    貼上為非空閒,您需要生成藍圖 形狀來負擔得起! (您剛交付的那些)。 # Special reward, which is shown when there is no reward actually no_reward: - title: Next level + title: 下一關 desc: >- - This level gave you no reward, but the next one will!

    PS: Better don't destroy your existing factory - You need all those shapes later again to unlock upgrades! + 這一關沒有獎勵,但是下一關有!

    PS: 你生產過的所有圖形都會被用來升級建築。 no_reward_freeplay: - title: Next level + title: 下一關 desc: >- - Congratulations! By the way, more content is planned for the standalone! + 恭喜你!另外,我們已經計劃在獨立版中加入更多內容! settings: - title: Settings + title: 設置 categories: - game: Game - app: Application + game: 遊戲內容 + app: 應用 versionBadges: - dev: Development - staging: Staging - prod: Production - buildDate: Built + dev: 開發版本 # Development + staging: 預覽版本 # Staging + prod: 正式版本 # Production + buildDate: 於創建 labels: uiScale: - title: Interface scale + title: 用戶界面大小 description: >- - Changes the size of the user interface. The interface will still scale based on your device resolution, but this setting controls the amount of scale. + 改變用戶界面大小。用戶界面會隨著設備分辨率縮放,這個設置決定縮放比例。 scales: - super_small: Super small - small: Small - regular: Regular - large: Large - huge: Huge + super_small: 最小 + small: 較小 + regular: 正常 + large: 較大 + huge: 最大 scrollWheelSensitivity: - title: Zoom sensitivity + title: 縮放靈敏度 description: >- - Changes how sensitive the zoom is (Either mouse wheel or trackpad). + 改變縮放靈敏度(鼠標滾輪或者觸控板)。 sensitivity: - super_slow: Super slow - slow: Slow - regular: Regular - fast: Fast - super_fast: Super fast + super_slow: 最低 + slow: 較低 + regular: 正常 + fast: 較高 + super_fast: 最高 language: - title: Language + title: 語言 description: >- - Change the language. All translations are user contributed and might be incomplete! + 改變語言。所有的翻譯皆由玩家提供,且有可能正在施工中! fullscreen: - title: Fullscreen + title: 全屏 description: >- - It is recommended to play the game in fullscreen to get the best experience. Only available in the standalone. + 全屏以獲得更好的遊戲體驗。僅在獨立版中可用。 soundsMuted: - title: Mute Sounds + title: 關閉音效 description: >- - If enabled, mutes all sound effects. + 關閉所有音效。 musicMuted: - title: Mute Music + title: 關閉音樂 description: >- - If enabled, mutes all music. + 關閉所有音樂。 theme: - title: Game theme + title: 界面主題 description: >- - Choose the game theme (light / dark). + 選擇界面主題(深色或淺色)。 themes: - dark: Dark - light: Light + dark: 深色 + light: 淺色 refreshRate: - title: Simulation Target + title: 模擬頻率、刷新頻率 description: >- - If you have a 144hz monitor, change the refresh rate here so the game will properly simulate at higher refresh rates. This might actually decrease the FPS if your computer is too slow. + 如果你的顯示器是144hz的,請在這裡更改刷新頻率,這樣遊戲可以正確地根據你的屏幕進行模擬。但是如果你的電腦性能不佳,提高刷新頻率可能降低幀數。 + # description: >- + # If you have a 144hz monitor, change the refresh rate here so the game will properly simulate at higher refresh rates. This might actually decrease the FPS if your computer is too slow. alwaysMultiplace: - title: Multiplace + title: 多重放置 description: >- - If enabled, all buildings will stay selected after placement until you cancel it. This is equivalent to holding SHIFT permanently. + 開啟這個選項之後放下建築將不會取消建築選擇。等同於一直按下SHIFT鍵。 + # description: >- + # If enabled, all buildings will stay selected after placement until you cancel it. This is equivalent to holding SHIFT permanently. offerHints: - title: Hints & Tutorials + title: 提示與教程 description: >- - Whether to offer hints and tutorials while playing. Also hides certain UI elements onto a given level to make it easier to get into the game. + 是否顯示提示、教程以及一些其他的幫助理解遊戲的UI元素。 + # description: >- + # Whether to offer hints and tutorials while playing. Also hides certain UI elements onto a given level to make it easier to get into the game. movementSpeed: - title: Movement speed - description: Changes how fast the view moves when using the keyboard. + title: 移動速度 + description: 改變攝像頭移動速度 speeds: - super_slow: Super slow - slow: Slow - regular: Regular - fast: Fast - super_fast: Super Fast - extremely_fast: Extremely Fast + super_slow: 最慢 + slow: 較慢 + regular: 正常 + fast: 較快 + super_fast: 非常快 + extremely_fast: 最快 + enableTunnelSmartplace: - title: Smart Tunnels + title: 智能隧道放置 description: >- - When enabled, placing tunnels will automatically remove unnecessary belts. - This also enables to drag tunnels and excess tunnels will get removed. + 啟用後,放置隧道時會將多餘的傳送帶移除。 + 此外,拖動隧道可以快速鋪設隧道,以及移除不必要的隧道。 vignette: - title: Vignette + title: 暈映 description: >- - Enables the vignette which darkens the screen corners and makes text easier - to read. + 啟用暈映,將屏幕角落裡的顏色變深,更容易閱讀文本。 autosaveInterval: - title: Autosave Interval + title: 自動刷新時間 description: >- - Controls how often the game saves automatically. You can also disable it - entirely here. + 控制遊戲自動刷新的頻率。 您也可以禁用它。 intervals: - one_minute: 1 Minute - two_minutes: 2 Minutes - five_minutes: 5 Minutes - ten_minutes: 10 Minutes - twenty_minutes: 20 Minutes - disabled: Disabled + one_minute: 1分鐘 + two_minutes: 2分鐘 + five_minutes: 5分鐘 + ten_minutes: 10分鐘 + twenty_minutes: 20分鐘 + disabled: 禁用 compactBuildingInfo: - title: Compact Building Infos + title: 省略建築信息 description: >- - Shortens info boxes for buildings by only showing their ratios. Otherwise a - description and image is shown. + 通過顯示建築物的比率來縮短建築物的信息框。 否則 + 顯示所有說明+圖像。 disableCutDeleteWarnings: - title: Disable Cut/Delete Warnings + title: 禁用剪下/刪除的警告 description: >- - Disable the warning dialogs brought up when cutting/deleting more than 100 - entities. + 剪下/刪除超過100幢建築物時禁用警告。 enableColorBlindHelper: - title: Color Blind Mode - description: Enables various tools which allow to play the game if you are color blind. - + title: 色盲模式 + description: 如果您是色盲者,啟用了這設定,就可以玩遊戲了。 keybindings: - title: Keybindings + title: 按鍵設置 hint: >- - Tip: Be sure to make use of CTRL, SHIFT and ALT! They enable different placement options. - - resetKeybindings: Reset Keyinbindings + 提示:使用CTRL、SHIFT、ALT! 這些建在放置建築時有不同的效果。 + + resetKeybindings: 重置按鍵設置 categoryLabels: - general: Application - ingame: Game - navigation: Navigating - placement: Placement - massSelect: Mass Select - buildings: Building Shortcuts - placementModifiers: Placement Modifiers + general: 通用 + ingame: 遊戲 + navigation: 視角 + placement: 放置 + massSelect: 批量選擇 + buildings: 建築快捷鍵 + placementModifiers: 放置建築修飾鍵 mappings: - confirm: Confirm - back: Back - mapMoveUp: Move Up - mapMoveRight: Move Right - mapMoveDown: Move Down - mapMoveLeft: Move Left - centerMap: Center Map + confirm: 確認 + back: 返回 + mapMoveUp: 上 + mapMoveRight: 右 + mapMoveDown: 下 + mapMoveLeft: 左 + centerMap: 回到基地 - mapZoomIn: Zoom in - mapZoomOut: Zoom out - createMarker: Create Marker + mapZoomIn: 放大 + mapZoomOut: 縮小 + createMarker: 創建地圖標記 - menuOpenShop: Upgrades - menuOpenStats: Statistics + menuOpenShop: 升級菜單 + menuOpenStats: 統計菜單 - toggleHud: Toggle HUD - toggleFPSInfo: Toggle FPS and Debug Info + toggleHud: 開關HUD + toggleFPSInfo: 開關幀數與調試信息 belt: *belt splitter: *splitter underground_belt: *underground_belt @@ -772,57 +803,56 @@ keybindings: painter: *painter trash: *trash - rotateWhilePlacing: Rotate + rotateWhilePlacing: 順時針旋轉 rotateInverseModifier: >- - Modifier: Rotate CCW instead - cycleBuildingVariants: Cycle Variants - confirmMassDelete: Confirm Mass Delete - cycleBuildings: Cycle Buildings + 修飾鍵: 改為逆時針旋轉 + cycleBuildingVariants: 選擇建築變體 + confirmMassDelete: 確認批量刪除 + cycleBuildings: 選擇建築 + massSelectStart: 開始批量選擇 + massSelectSelectMultiple: 選擇多個區域 + massSelectCopy: 複製 - massSelectStart: Hold and drag to start - massSelectSelectMultiple: Select multiple areas - massSelectCopy: Copy area + placementDisableAutoOrientation: 取消自動定向 + placeMultiple: 繼續放置 + placeInverse: 反向放置傳送帶 + pasteLastBlueprint: 粘貼上一張藍圖 + massSelectCut: 剪切 + exportScreenshot: 導出截圖 + mapMoveFaster: 快速移動 - placementDisableAutoOrientation: Disable automatic orientation - placeMultiple: Stay in placement mode - placeInverse: Invert automatic belt orientation - pasteLastBlueprint: Paste last blueprint - massSelectCut: Cut area - exportScreenshot: Export whole Base as Image - mapMoveFaster: Move Faster - lockBeltDirection: Enable belt planner - switchDirectionLockSide: "Planner: Switch side" + lockBeltDirection: 啟用傳送帶規劃 + switchDirectionLockSide: "規劃器:換邊" pipette: Pipette about: - title: About this Game + title: 關於遊戲 + # title: About this Game body: >- - This game is open source and developed by Tobias Springer (this is me).

    + 本遊戲由Tobias Springer(我)開發,並且已經開源。

    - If you want to contribute, check out shapez.io on github.

    + 如果你想參與開發,請查看shapez.io on github

    - This game wouldn't have been possible without the great discord community - around my games - You should really join the discord server!

    + 這個遊戲的開發少不了熱情的Discord社區。請加入我們的Discord 服務器

    - The soundtrack was made by Peppsen - He's awesome.

    + 本遊戲的音樂由Peppsen製作——他是個很棒的伙伴。

    - Finally, huge thanks to my best friend Niklas - Without our - factorio sessions this game would never have existed. + 最後,我想感謝我最好的朋友Niklas——如果沒有與他的異星工廠(factorio)的遊戲體驗,shapez.io將不會存在。 changelog: - title: Changelog + title: 版本日誌 demo: features: - restoringGames: Restoring savegames - importingGames: Importing savegames - oneGameLimit: Limited to one savegame - customizeKeybindings: Customizing Keybindings - exportingBase: Exporting whole Base as Image - - settingNotAvailable: Not available in the demo. + restoringGames: 恢復存檔 #中? + importingGames: 倒入存檔 #中? + oneGameLimit: 最多一個存檔 + customizeKeybindings: 按鍵設置 + # customizeKeybindings: Customizing Keybindings + exportingBase: 導出工廠截圖 + settingNotAvailable: 在演示版中不可用。 From b67248f310180b6e191661e87530aa1fd7aa55c9 Mon Sep 17 00:00:00 2001 From: nukuuu <39561939+nukuuu@users.noreply.github.com> Date: Tue, 23 Jun 2020 16:21:59 +0100 Subject: [PATCH 10/24] Update PT-PT translation to the latest version Also corrected minor typos --- translations/base-pt-PT.yaml | 70 ++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/translations/base-pt-PT.yaml b/translations/base-pt-PT.yaml index 253dc37c..f22e1c39 100644 --- a/translations/base-pt-PT.yaml +++ b/translations/base-pt-PT.yaml @@ -287,10 +287,10 @@ ingame: pasteLastBlueprint: Colar o último blueprint lockBeltDirection: Ativa o planeamento de tapetes plannerSwitchSide: Lado de rotação do planeamento - cutSelection: Cut - copySelection: Copy - clearSelection: Clear Selection - pipette: Pipette + cutSelection: Cortar + copySelection: Copiar + clearSelection: Cancelar + pipette: Pipeta # Everything related to placing buildings (I.e. as soon as you selected a building # from the toolbar) @@ -398,17 +398,17 @@ ingame: Isto NÃO é um jogo idle! Constrói mais extratores e tapetes para atingir o objetivo mais rapidamente.

    Dica: Pressiona SHIFT para colocar vários extratores, e usa R para os rodar. colors: - red: Red - green: Green - blue: Blue - yellow: Yellow - purple: Purple - cyan: Cyan - white: White - uncolored: No color + red: Vermelho + green: Verde + blue: Azul + yellow: Amarelo + purple: Roxo + cyan: Azul-bebé + white: Branco + uncolored: Sem cor shapeViewer: - title: Layers - empty: Empty + title: Camadas + empty: Vazio # All shop upgrades shopUpgrades: @@ -679,14 +679,14 @@ settings: Se ativado, dá dicas e tutoriais de apoio ao jogo. Adicionalmente, esconde certos elementos da interface do utilizador até ao nível em que são desbloqueados de forma a simplificar o início do jogo. movementSpeed: - title: Velociade de movimentação + title: Velocidade de movimentação description: Define quão rápida é a movimentação usando o teclado. speeds: super_slow: Muito lenta slow: Lenta regular: Média - fast: Rápiada - super_fast: Muito rádida + fast: Rápida + super_fast: Muito rápida extremely_fast: Extremamente rápida enableTunnelSmartplace: title: Túneis inteligentes @@ -700,31 +700,31 @@ settings: mais fácil. autosaveInterval: - title: Autosave Interval + title: Intervalo de gravação automática description: >- - Controls how often the game saves automatically. You can also disable it - entirely here. + Define o quão frequentemente o jogo grava automaticamente. Também podes desativar + aqui. intervals: - one_minute: 1 Minute - two_minutes: 2 Minutes - five_minutes: 5 Minutes - ten_minutes: 10 Minutes - twenty_minutes: 20 Minutes - disabled: Disabled + one_minute: 1 Minuto + two_minutes: 2 Minutos + five_minutes: 5 Minutos + ten_minutes: 10 Minutos + twenty_minutes: 20 Minutos + disabled: Desligado compactBuildingInfo: - title: Compact Building Infos + title: Informações de construções compactas description: >- - Shortens info boxes for buildings by only showing their ratios. Otherwise a - description and image is shown. + Encurta caixas de informação mostrando apenas os respetivos rácios. Caso contrário + é mostrada a descrição e a imagem. disableCutDeleteWarnings: - title: Disable Cut/Delete Warnings + title: Desativar Avisos de Corte/Eliminação description: >- - Disable the warning dialogs brought up when cutting/deleting more than 100 - entities. + Desativa os avisos mostrados quando é feito o corte ou a eliminação de mais de 100 + entidades. enableColorBlindHelper: - title: Color Blind Mode - description: Enables various tools which allow to play the game if you are color blind. + title: Modo Daltónico + description: Ativa várias ferramentas para daltónicos. keybindings: title: Atalhos @@ -791,7 +791,7 @@ keybindings: mapMoveFaster: Mover rapidamente lockBeltDirection: Ativa o planeamento de tapetes switchDirectionLockSide: "Planeador: Troca o lado" - pipette: Pipette + pipette: Pipeta about: title: Sobre o jogo body: >- From 62303ce8e3d93c176b028faecf9e62ee4a04a851 Mon Sep 17 00:00:00 2001 From: tobspr Date: Tue, 23 Jun 2020 19:02:08 +0200 Subject: [PATCH 11/24] Enable traditional chinese --- src/js/languages.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/js/languages.js b/src/js/languages.js index 2747c24e..5b23f2dd 100644 --- a/src/js/languages.js +++ b/src/js/languages.js @@ -80,12 +80,23 @@ export const LANGUAGES = { code: "no", region: "", }, + "zh-CN": { + // simplified name: "简体中文", data: require("./built-temp/base-zh-CN.json"), code: "zh", region: "CN", }, + + "zh-TW": { + // traditional + name: "中国传统的", + data: require("./built-temp/base-zh-TW.json"), + code: "zh", + region: "TW", + }, + "sv": { name: "Svenska", data: require("./built-temp/base-sv.json"), From e5646e4612ee064db7779494a9ad6aad2f155296 Mon Sep 17 00:00:00 2001 From: YellowSugarHK <67306569+YellowSugarHK@users.noreply.github.com> Date: Wed, 24 Jun 2020 06:43:24 +0800 Subject: [PATCH 12/24] Update base-zh-TW.yaml --- translations/base-zh-TW.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/translations/base-zh-TW.yaml b/translations/base-zh-TW.yaml index f2fa0fe0..152e7448 100644 --- a/translations/base-zh-TW.yaml +++ b/translations/base-zh-TW.yaml @@ -359,7 +359,7 @@ ingame: buttonUnlock: Upgrade # Gets replaced to e.g. "Tier IX" - tier: Tier + tier: 級 # The roman number for each tier # Chinese translation: Chinese characters for each tier From 43ee1fa6a99292442c8dd12203364a39cda3af52 Mon Sep 17 00:00:00 2001 From: findaldudu Date: Tue, 23 Jun 2020 21:40:04 -0500 Subject: [PATCH 13/24] Chinese translation 1.1.17 --- translations/base-zh-CN.yaml | 103 +++++++++++++++++------------------ 1 file changed, 50 insertions(+), 53 deletions(-) diff --git a/translations/base-zh-CN.yaml b/translations/base-zh-CN.yaml index 08708f35..0169aeda 100644 --- a/translations/base-zh-CN.yaml +++ b/translations/base-zh-CN.yaml @@ -319,10 +319,10 @@ ingame: pasteLastBlueprint: 粘贴上一个蓝图 lockBeltDirection: 启用传送带规划 plannerSwitchSide: 规划器换边 - cutSelection: Cut - copySelection: Copy - clearSelection: Clear Selection - pipette: Pipette + cutSelection: 剪切 + copySelection: 复制 + clearSelection: 取消选择 + pipette: 选取器 # Everything related to placing buildings (I.e. as soon as you selected a building # from the toolbar) @@ -416,7 +416,7 @@ ingame: waypoints: waypoints: 地图标记 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: 左键跳转到地图标记,右键删除地图标记。

    在当前地点创建地图标记,或者在选定位置上右键创建地图标记. creationSuccessNotification: 成功创建地图标记。 # Interactive tutorial @@ -432,17 +432,17 @@ ingame: 提示:按住SHIFT键来放置多个开采机,用R键旋转它们。 colors: - red: Red - green: Green - blue: Blue - yellow: Yellow - purple: Purple - cyan: Cyan - white: White - uncolored: No color + red: 红色 + green: 绿色 + blue: 蓝色 + yellow: 黄色 + purple: 紫色 + cyan: 青色 + white: 白色 + uncolored: 无色 shapeViewer: - title: Layers - empty: Empty + title: 层 # TODO: find better translation + empty: 空 # All shop upgrades shopUpgrades: @@ -502,7 +502,7 @@ buildings: name: &cutter 切割机 description: 将图形从上到下切开并输出。 如果你只需要其中一半,记得把另一半销毁掉,否则切割机会停止工作! quad: - name: 切割机(四分) + name: 切割机(四向) description: 将输入的图形切成四块。 如果你只需要其中一块,记得把其他的销毁掉,否则切割机会停止工作! rotater: @@ -554,73 +554,73 @@ storyRewards: # Those are the rewards gained from completing the store reward_cutter_and_trash: title: 切割图形 - desc: 切割机已解锁。不论切割机的方向,它都会把图形从上到下切成两半。

    记得把不需要的部分处理掉,否则这个这个建筑会停止工作。为此我给你准备了垃圾桶,它会把所有放进去的物品销毁掉。 + desc: 恭喜!你解锁了切割机。切割机会把图形从上到下切成两半。注意切割的方向和切割机的朝向无关。

    记得把不需要的部分处理掉,否则这个这个建筑会停止工作。为此我给你准备了垃圾桶,它会把所有放进去的物品销毁掉。 reward_rotater: title: 顺时针旋转 - desc: 旋转机已解锁。它会顺时针旋转输入的图形90度。 + desc: 恭喜!你解锁了旋转机。它会顺时针旋转输入的图形90度。 reward_painter: title: 上色 desc: >- - 上色机已解锁。和图形一样,从颜色矿脉中开采颜色,然后将在上色机中将颜色涂在图形上。

    PS:我们正在开发色盲模式! + 恭喜!你解锁了上色机。和图形一样,从颜色矿脉中开采颜色,然后将在上色机中将颜色涂在图形上。

    PS:我们正在开发色盲模式! reward_mixer: title: 混合颜色 - desc: The mixer has been unlocked - Combine two colors using additive blending with this building! + desc: 恭喜!你解锁了混合机。在这个建筑中将两个颜色混合在一起(加法混合)。 reward_stacker: title: 堆叠 - desc: 堆叠机已解锁。堆叠机会尝试把两个输入的图形拼贴在一起。如果有重叠的部分,右边的输入会被堆叠在左边的输入上方! + desc: 恭喜!你解锁了堆叠机。堆叠机会尝试把两个输入的图形拼贴在一起。如果有重叠的部分,右边的输入会被堆叠在左边的输入上方! reward_splitter: title: 分离与合并 - desc: 平衡机已解锁。在大型工厂中,平衡机负责合并或分离多个传送带上的物品。

    + desc: 恭喜!你解锁了平衡机。在大型工厂中,平衡机负责合并或分离多个传送带上的物品。

    reward_tunnel: title: 隧道 - desc: 隧道已解锁。你现在可以从其他传送带或建筑底下运送物品了! + desc: 恭喜!你解锁了隧道。你现在可以从其他传送带或建筑底下运送物品了! reward_rotater_ccw: title: 逆时针旋转 - desc: You have unlocked a variant of the rotater - It allows to rotate counter clockwise! To build it, select the rotater and press 'T' to cycle its variants! + desc: 恭喜!你解锁了旋转机逆时针变体。这个变体可以逆时针旋转图形。选择旋转机然后按“T”键来选取这个变体。 reward_miner_chainable: title: 链式开采机 - desc: 链式开采机变体已解锁。它是开采机的一个变体。它可以将开采出来的资源传递给其他的开采机,使得资源提取更加高效! + desc: 恭喜!你解锁了开采机链式变体。它可以将开采出来的资源传递给其他的开采机,使得资源提取更加高效! reward_underground_belt_tier_2: title: 二级隧道 - desc: 二级隧道变体已解锁。这个隧道有更长的传输距离。你还可以混用不同的隧道变体! + desc: 恭喜!你解锁了二级隧道。这是隧道的一个变体。二级隧道有更长的传输距离。你还可以混用不同的隧道变体! reward_splitter_compact: title: 小型合流机 desc: >- - 小型合流机变体已解锁。它可以把两个输入合并到一个输出上。 + 恭喜!你解锁了平衡机小型合流机变体。它可以把两个输入合并到一个输出上。 reward_cutter_quad: - title: 四分切割机 - desc: You have unlocked a variant of the cutter - It allows you to cut shapes in four parts instead of just two! + title: 四向切割机 + desc: 恭喜!你解锁了切割机四向变体。它可以将输入的图形切成四块而不只是左右两块! reward_painter_double: title: 双倍上色机 - desc: You have unlocked a variant of the painter - It works as the regular painter but processes two shapes at once consuming just one color instead of two! + desc: 恭喜!你解锁了上色机双倍变体。它可以同时为两个图形上色,每次只消耗一份颜色! reward_painter_quad: title: 四向上色机 - desc: 上色机四向变体已解锁。它可以在一个图形的四个角上涂不同的颜色! + desc: 恭喜!你解锁了上色机四向变体。它可以在一个图形的四个角上涂不同的颜色! reward_storage: title: 仓库 - desc: 仓库变体已解锁。它可以暂时储存一些材料,有容量上限。 - + desc: 恭喜!你解锁了垃圾桶仓库变体。它可以暂时储存一些材料,有容量上限。 + reward_freeplay: title: 自由模式 - desc: 恭喜你!你解锁了自由模式!现在图形将会是随机生成的!(不用担心,我计划在独立版本中加入更多内容!) + desc: 恭喜你!你解锁了自由模式。现在图形将会是随机生成的!(不用担心,我计划在独立版本中加入更多内容!) reward_blueprints: title: 蓝图 - 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: 你现在可以复制粘贴你的工厂的一部分了!按住CTRL键并拖动鼠标来选择一块区域,然后按C键复制。

    粘贴并不是免费的,你需要使用蓝图图形来粘贴你的蓝图。蓝图图形是你刚刚交付的图形。 # Special reward, which is shown when there is no reward actually no_reward: @@ -740,31 +740,28 @@ settings: 启用晕映,将屏幕角落里的颜色变深,更容易阅读文本。 autosaveInterval: - title: Autosave Interval + title: 自动保存间隔 description: >- - Controls how often the game saves automatically. You can also disable it - entirely here. + 在这里控制你的游戏多长时间保存一次,或者完全关闭这个功能。 intervals: - one_minute: 1 Minute - two_minutes: 2 Minutes - five_minutes: 5 Minutes - ten_minutes: 10 Minutes - twenty_minutes: 20 Minutes - disabled: Disabled + one_minute: 1分钟 + two_minutes: 2分钟 + five_minutes: 5分钟 + ten_minutes: 10分钟 + twenty_minutes: 20分钟 + disabled: 关闭 compactBuildingInfo: - title: Compact Building Infos + title: 精简建筑信息 description: >- - Shortens info boxes for buildings by only showing their ratios. Otherwise a - description and image is shown. + 缩小建筑信息展示框。如果打开,放置建筑时建筑将不再显示建筑说明和图片,只显示建筑速度或其他数据。 disableCutDeleteWarnings: - title: Disable Cut/Delete Warnings + title: 关闭剪切/删除警告 description: >- - Disable the warning dialogs brought up when cutting/deleting more than 100 - entities. + 如果打开,将不再在剪切或者删除100+建筑时显示警告信息。 enableColorBlindHelper: - title: Color Blind Mode - description: Enables various tools which allow to play the game if you are color blind. + title: 色盲模式 + description: 提供一些分辨颜色的工具。目前当鼠标移至颜色资源上方时,屏幕上方会显示颜色名称。 keybindings: title: 按键设置 @@ -840,7 +837,7 @@ keybindings: lockBeltDirection: 启用传送带规划 switchDirectionLockSide: "规划器:换边" - pipette: Pipette + pipette: 选取器 about: title: 关于游戏 From f7e27f95fd23f58149e2a68a5bdfb8dd0d62e32f Mon Sep 17 00:00:00 2001 From: findaldudu Date: Tue, 23 Jun 2020 21:46:50 -0500 Subject: [PATCH 14/24] Chinese translation patch --- translations/base-zh-CN.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/translations/base-zh-CN.yaml b/translations/base-zh-CN.yaml index 0169aeda..dbbe87bd 100644 --- a/translations/base-zh-CN.yaml +++ b/translations/base-zh-CN.yaml @@ -377,14 +377,14 @@ ingame: title: 统计信息 dataSources: stored: - title: 储存 - description: 显示基地中每种图形储存的数量。 + title: 已交付 + description: 显示基地中每种图形已交付且未使用的数量。 produced: title: 生产 description: 显示所有正在被生产的图形数量,包括中间产物。 delivered: title: 送达 - description: 显示图形送达基地的速度。 + description: 显示图形送达基地并交付的速度。 noShapesProduced: 你还没有生产任何图形。 # Displays the shapes per minute, e.g. '523 / m' From a125ceede923d3f1b8f573c025e5567face283dc Mon Sep 17 00:00:00 2001 From: tobspr Date: Wed, 24 Jun 2020 08:35:31 +0200 Subject: [PATCH 15/24] Update language names --- src/js/languages.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/js/languages.js b/src/js/languages.js index 5b23f2dd..0bea19c7 100644 --- a/src/js/languages.js +++ b/src/js/languages.js @@ -83,7 +83,7 @@ export const LANGUAGES = { "zh-CN": { // simplified - name: "简体中文", + name: "中文简体", data: require("./built-temp/base-zh-CN.json"), code: "zh", region: "CN", @@ -91,7 +91,7 @@ export const LANGUAGES = { "zh-TW": { // traditional - name: "中国传统的", + name: "中文繁體", data: require("./built-temp/base-zh-TW.json"), code: "zh", region: "TW", From bae6b702bb3dba8249990145bda27fa0ef8fbbb4 Mon Sep 17 00:00:00 2001 From: tobspr Date: Wed, 24 Jun 2020 12:50:51 +0200 Subject: [PATCH 16/24] Update translations --- artwork/steam/devlog.png | Bin 16129 -> 14345 bytes artwork/steam/devlog.psd | 4 ++-- translations/base-ar.yaml | 2 +- translations/base-cz.yaml | 2 +- translations/base-de.yaml | 2 +- translations/base-el.yaml | 2 +- translations/base-en.yaml | 2 +- translations/base-es.yaml | 3 ++- translations/base-fr.yaml | 2 +- translations/base-hr.yaml | 2 +- translations/base-hu.yaml | 2 +- translations/base-it.yaml | 2 +- translations/base-ja.yaml | 2 +- translations/base-kor.yaml | 2 +- translations/base-lt.yaml | 2 +- translations/base-nl.yaml | 8 ++++---- translations/base-no.yaml | 2 +- translations/base-pl.yaml | 2 +- translations/base-pt-BR.yaml | 2 +- translations/base-pt-PT.yaml | 2 +- translations/base-ro.yaml | 2 +- translations/base-ru.yaml | 2 +- translations/base-sv.yaml | 2 +- translations/base-tr.yaml | 2 +- translations/base-zh-CN.yaml | 2 +- translations/base-zh-TW.yaml | 14 +++++++------- 26 files changed, 36 insertions(+), 35 deletions(-) diff --git a/artwork/steam/devlog.png b/artwork/steam/devlog.png index 8e5c29e631271f2242b56014750672ac65cb031b..9abdefe1baec132cba0baca397137760bca04809 100644 GIT binary patch literal 14345 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 diff --git a/artwork/steam/devlog.psd b/artwork/steam/devlog.psd index 0c06ba5d..7cd9b0a7 100644 --- a/artwork/steam/devlog.psd +++ b/artwork/steam/devlog.psd @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:746c6cf3f0284798a78c08f77d7e9d0c28b02323081fda42b5fa876a7ade29a0 -size 205925 +oid sha256:40d458f800344d819ad4d3f38943f53f399258369ee5c20f6583d1b49847465f +size 188255 diff --git a/translations/base-ar.yaml b/translations/base-ar.yaml index 9edce612..00c6d0b0 100644 --- a/translations/base-ar.yaml +++ b/translations/base-ar.yaml @@ -531,7 +531,7 @@ storyRewards: 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, I'm working on a solution already! + 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 color blind mode in the settings! reward_mixer: title: Color Mixing diff --git a/translations/base-cz.yaml b/translations/base-cz.yaml index a79d5a66..6c7b5052 100644 --- a/translations/base-cz.yaml +++ b/translations/base-cz.yaml @@ -512,7 +512,7 @@ storyRewards: reward_painter: title: Barvení desc: >- - Barvič byl právě odemčen - vytěžte nějakou barvu (stejně jako těžíte tvary) a skombinujte ji v barviči s tvarem pro obarvení!

    PS: Pokud jste barvoslepí, nebojte, již pracuju na řešení.! + 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 color blind mode in the settings! reward_mixer: title: Míchání barev diff --git a/translations/base-de.yaml b/translations/base-de.yaml index 08bc12e4..69d747dd 100644 --- a/translations/base-de.yaml +++ b/translations/base-de.yaml @@ -532,7 +532,7 @@ storyRewards: reward_painter: title: Färben desc: >- - Der Färber wurde freigeschaltet! Extrahiere ein paar Farben (genauso wie die Formen) und lasse sie vom Färber bemalen!

    PS: Falls du farbenblind bist: Ich arbeite bereits an einer Lösung! + 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 color blind mode in the settings! reward_mixer: title: Farben mischen diff --git a/translations/base-el.yaml b/translations/base-el.yaml index 19ab5a83..4c37ed19 100644 --- a/translations/base-el.yaml +++ b/translations/base-el.yaml @@ -531,7 +531,7 @@ storyRewards: 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, I'm working on a solution already! + 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 color blind mode in the settings! reward_mixer: title: Color Mixing diff --git a/translations/base-en.yaml b/translations/base-en.yaml index 2ad5ef07..d349bbe3 100644 --- a/translations/base-en.yaml +++ b/translations/base-en.yaml @@ -534,7 +534,7 @@ storyRewards: 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, I'm working on a solution already! + 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 color blind mode in the settings! reward_mixer: title: Color Mixing diff --git a/translations/base-es.yaml b/translations/base-es.yaml index f7381e13..61903c93 100644 --- a/translations/base-es.yaml +++ b/translations/base-es.yaml @@ -528,7 +528,8 @@ storyRewards: reward_painter: title: Pintor desc: >- - El pintor ha sido desbloqueado - ¡Extrae color de las betas (al igual que haces con las figuras) y combínalo con una figura para pintarla de ese color!

    PS: Si eres daltónico, estoy trabajando en una solución! + 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 color blind mode in the settings! + reward_mixer: title: Mezclador de Color desc: El mezclador ha sido desbloqueado - ¡Combina dos colores usando mezcla aditiva con este edificio! diff --git a/translations/base-fr.yaml b/translations/base-fr.yaml index 716f3c78..c0a9788c 100644 --- a/translations/base-fr.yaml +++ b/translations/base-fr.yaml @@ -533,7 +533,7 @@ storyRewards: reward_painter: title: Peintre desc: >- - Le peintre a été débloqué - Extrayez des pigments de couleur (comme vous le faites avec les formes) et combinez les avec une forme dans un peintre pour les colorier !

    PS: Si vous êtes daltonien, je travaille déjà à une solution ! + 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 color blind mode in the settings! reward_mixer: title: Mélangeur de couleurs diff --git a/translations/base-hr.yaml b/translations/base-hr.yaml index 3ff34010..3b49c01d 100644 --- a/translations/base-hr.yaml +++ b/translations/base-hr.yaml @@ -531,7 +531,7 @@ storyRewards: 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, I'm working on a solution already! + 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 color blind mode in the settings! reward_mixer: title: Color Mixing diff --git a/translations/base-hu.yaml b/translations/base-hu.yaml index 7d1bbc9e..b750fd1f 100644 --- a/translations/base-hu.yaml +++ b/translations/base-hu.yaml @@ -530,7 +530,7 @@ storyRewards: 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, I'm working on a solution already! + 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 color blind mode in the settings! reward_mixer: title: Color Mixing diff --git a/translations/base-it.yaml b/translations/base-it.yaml index c434af7a..c6314099 100644 --- a/translations/base-it.yaml +++ b/translations/base-it.yaml @@ -531,7 +531,7 @@ storyRewards: 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, I'm working on a solution already! + 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 color blind mode in the settings! reward_mixer: title: Color Mixing diff --git a/translations/base-ja.yaml b/translations/base-ja.yaml index c4cb3536..22805f52 100644 --- a/translations/base-ja.yaml +++ b/translations/base-ja.yaml @@ -530,7 +530,7 @@ storyRewards: reward_painter: title: 着色 desc: >- - 着色機が利用可能になりました。 - 色の鉱脈から形の手順と同様に色を抽出し、着色機で形と組み合わせることで着色できます。

    PS:もしあなたが色の認識に問題があっても安心してください。我々はすでにその解決に着手しています! + 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 color blind mode in the settings! reward_mixer: title: 色の混合 diff --git a/translations/base-kor.yaml b/translations/base-kor.yaml index a39225b1..a1c08a32 100644 --- a/translations/base-kor.yaml +++ b/translations/base-kor.yaml @@ -531,7 +531,7 @@ storyRewards: reward_painter: title: 색칠기 desc: >- - 색칠기가 잠금 해제 되었습니다! 색소 광물을 추출해서 이 기계로 도형을 색칠하세요.

    PS: 당신이 색맹이라면, 해결책을 찾고 있으니 잠시만 기다려주세요! + 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 color blind mode in the settings! reward_mixer: title: 혼합기 diff --git a/translations/base-lt.yaml b/translations/base-lt.yaml index 6792283b..8a927559 100644 --- a/translations/base-lt.yaml +++ b/translations/base-lt.yaml @@ -531,7 +531,7 @@ storyRewards: 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, I'm working on a solution already! + 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 color blind mode in the settings! reward_mixer: title: Color Mixing diff --git a/translations/base-nl.yaml b/translations/base-nl.yaml index d8917721..11dadd27 100644 --- a/translations/base-nl.yaml +++ b/translations/base-nl.yaml @@ -529,7 +529,7 @@ storyRewards: reward_painter: title: Verven desc: >- - De verver is ontgrendeld - Onttrek wat kleurstoffen (net als met vormen) en combineer deze met een vorm in de verver om de vorm een kleur te geven!

    PS: Ik werk aan een oplossing voor kleurenblinden! + 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 color blind mode in the settings! reward_mixer: title: Kleuren mengen @@ -701,9 +701,9 @@ settings: autosaveInterval: title: Autosave Interval description: >- - Bepaalt hoe vaak het spel automatisch opslaat. Je kan het hier ook volledig + Bepaalt hoe vaak het spel automatisch opslaat. Je kan het hier ook volledig mee uitschakelen. - + intervals: one_minute: 1 Minuut two_minutes: 2 Minuten @@ -715,7 +715,7 @@ settings: title: Combacte gebouwinformatie description: >- Informatie weergeven bij gebouwen wordt beperkt tot alleen hun 'ratios'. Anders - zie je een beschrijving en een afbeelding. + zie je een beschrijving en een afbeelding. disableCutDeleteWarnings: title: Schakel knip/delete waarschuwingen uit. description: >- diff --git a/translations/base-no.yaml b/translations/base-no.yaml index f730a2bd..9dea10a5 100644 --- a/translations/base-no.yaml +++ b/translations/base-no.yaml @@ -529,7 +529,7 @@ storyRewards: reward_painter: title: Maling desc: >- - Maleren har blitt tilgjengelig - Hent ut fargeressurser (på samme måte som du gjør med objekter) og kombiner det med et objekt i maleren for å male de!

    PS: Hvis du er fargeblind, så jobber jeg med en løsning alt! + 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 color blind mode in the settings! reward_mixer: title: Fargemikser diff --git a/translations/base-pl.yaml b/translations/base-pl.yaml index 815799a4..3627d86b 100644 --- a/translations/base-pl.yaml +++ b/translations/base-pl.yaml @@ -546,7 +546,7 @@ storyRewards: reward_painter: title: Malowanie desc: >- - Odblokowano nową maszynę: Malarz - Wydobądź barwniki (tak jak w przypadku kształtów) i połącz z kształtem, by go pomalować!

    PS: Jeśli cierpisz na ślepotę barw, pracuję nad tym! + 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 color blind mode in the settings! reward_mixer: title: Mieszanie diff --git a/translations/base-pt-BR.yaml b/translations/base-pt-BR.yaml index 30e9c30c..224b16f0 100644 --- a/translations/base-pt-BR.yaml +++ b/translations/base-pt-BR.yaml @@ -532,7 +532,7 @@ storyRewards: reward_painter: title: Pintura desc: >- - O pintor foi desbloqueado - Extraia algumas fontes coloridas (como você faz com formas) e combine-as com uma forma no pintor para colorí-las!

    PS: Se você é daltônico, já estou trabalhando em uma solução! + 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 color blind mode in the settings! reward_mixer: title: Misturando cores diff --git a/translations/base-pt-PT.yaml b/translations/base-pt-PT.yaml index f22e1c39..d956438e 100644 --- a/translations/base-pt-PT.yaml +++ b/translations/base-pt-PT.yaml @@ -529,7 +529,7 @@ storyRewards: reward_painter: title: Pintura desc: >- - O Pintor foi desbloqueado - Extrai alguns pigmentos coloridos (tal como fazes com as formas) e combina-os com uma forma no pintor para a colorir!

    PS: Se fores daltónico, já estou a trabalhar para encontrar uma solução! + 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 color blind mode in the settings! reward_mixer: title: Mistura de Cores diff --git a/translations/base-ro.yaml b/translations/base-ro.yaml index aac39324..334ddeda 100644 --- a/translations/base-ro.yaml +++ b/translations/base-ro.yaml @@ -530,7 +530,7 @@ storyRewards: 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, I'm working on a solution already! + 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 color blind mode in the settings! reward_mixer: title: Color Mixing diff --git a/translations/base-ru.yaml b/translations/base-ru.yaml index 732f0fc2..f2cac852 100644 --- a/translations/base-ru.yaml +++ b/translations/base-ru.yaml @@ -532,7 +532,7 @@ storyRewards: reward_painter: title: Покраска desc: >- - Разблокирован покрасчик! Добудьте краситель из жилы (так же как и фигуры) и объедините его с фигурой в покрасчике, чтобы раскрасить ее!

    PS: Если вы дальтоник, я уже работаю над решением! + 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 color blind mode in the settings! reward_mixer: title: Смешивание Цветов diff --git a/translations/base-sv.yaml b/translations/base-sv.yaml index e7c88eea..b8665e33 100644 --- a/translations/base-sv.yaml +++ b/translations/base-sv.yaml @@ -530,7 +530,7 @@ storyRewards: reward_painter: title: Måleri desc: >- - Färgläggaren har blivit upplåst - Extrahera färg (precis som du gör med former) och kombinera dem med former för att färglägga dem!

    PS: Om du är färgblind, jag jobbar redan på en lösning! + 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 color blind mode in the settings! reward_mixer: title: Färgblandning diff --git a/translations/base-tr.yaml b/translations/base-tr.yaml index 6bd1b97a..532eb3fc 100644 --- a/translations/base-tr.yaml +++ b/translations/base-tr.yaml @@ -531,7 +531,7 @@ storyRewards: 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, I'm working on a solution already! + 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 color blind mode in the settings! reward_mixer: title: Color Mixing diff --git a/translations/base-zh-CN.yaml b/translations/base-zh-CN.yaml index 08708f35..5dff6f47 100644 --- a/translations/base-zh-CN.yaml +++ b/translations/base-zh-CN.yaml @@ -563,7 +563,7 @@ storyRewards: reward_painter: title: 上色 desc: >- - 上色机已解锁。和图形一样,从颜色矿脉中开采颜色,然后将在上色机中将颜色涂在图形上。

    PS:我们正在开发色盲模式! + 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 color blind mode in the settings! reward_mixer: title: 混合颜色 diff --git a/translations/base-zh-TW.yaml b/translations/base-zh-TW.yaml index f2fa0fe0..28f32dfa 100644 --- a/translations/base-zh-TW.yaml +++ b/translations/base-zh-TW.yaml @@ -178,7 +178,7 @@ mainMenu:
    我期待看到您的出色創作! - + showInfo: 詳細信息 contestOver: 本次競賽已結束。加入官方Discord以收到關於新競賽的提醒! continue: 繼續 @@ -363,10 +363,10 @@ ingame: # The roman number for each tier # Chinese translation: Chinese characters for each tier - tierLabels: [壹、貳、叁、肆、伍、陸、柒、捌、玖、拾] + tierLabels: [壹, 貳, 叁, 肆, 伍, 陸, 柒, 捌, 玖, 拾] maximumLevel: 最高级(倍效率) - + # The "Statistics" window statistics: title: 統計信息 @@ -413,7 +413,7 @@ ingame: hub: 基地 description: 滑鼠左鍵按標記跳轉到它,按右鍵將其刪除。

    從當前視圖創建一個標記,或按右鍵創建一個標記。 所選位置的標記。 creationSuccessNotification: 成功創建地圖標記。 - + # Interactive tutorial interactiveTutorial: title: 教程 @@ -544,7 +544,7 @@ buildings: deliver: 交付 toUnlock: 來解鎖 levelShortcut: LVL - + storyRewards: # Those are the rewards gained from completing the store reward_cutter_and_trash: @@ -596,7 +596,7 @@ storyRewards: reward_cutter_quad: title: 四分切割機 desc: 您已解鎖了四分切割機的變體-它允許您將形狀直接切割為四個部分,而不是兩個! - + reward_painter_double: title: 雙倍上色機 desc: 您已經解鎖了雙倍上色機的變體-它可以作為常規畫家使用,但一次只能處理兩個形狀,而只消耗一種顏色而不是兩種顏色! @@ -762,7 +762,7 @@ keybindings: title: 按鍵設置 hint: >- 提示:使用CTRL、SHIFT、ALT! 這些建在放置建築時有不同的效果。 - + resetKeybindings: 重置按鍵設置 categoryLabels: From 8ec646a8f634d1bb4fb84c0a522e69878ef151d2 Mon Sep 17 00:00:00 2001 From: Magnus Grimstvedt Saltnes Date: Wed, 24 Jun 2020 14:20:16 +0200 Subject: [PATCH 17/24] Guards against undefined values/keys in base rotation logic. One-lines setting check per tobspr's recommendation. --- .../game/hud/parts/building_placer_logic.js | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/js/game/hud/parts/building_placer_logic.js b/src/js/game/hud/parts/building_placer_logic.js index 43abaf6a..106e642c 100644 --- a/src/js/game/hud/parts/building_placer_logic.js +++ b/src/js/game/hud/parts/building_placer_logic.js @@ -125,26 +125,30 @@ export class HUDBuildingPlacerLogic extends BaseHUDPart { * @returns {number} */ get currentBaseRotation() { - const rotationByBuilding = this.root.app.settings.getAllSettings().rotationByBuilding; - if (!rotationByBuilding) { + if (!this.root.app.settings.getAllSettings().rotationByBuilding) { + return this.currentBaseRotationGeneral; + } + const building = this.currentMetaBuilding.get(); + if (building != undefined && this.preferredBaseRotations.hasOwnProperty(building.getId())) { + return this.preferredBaseRotations[building.getId()]; + } else { return this.currentBaseRotationGeneral; } - const id = this.currentMetaBuilding.get().getId(); - return this.preferredBaseRotations[id] == null - ? this.currentBaseRotationGeneral - : this.preferredBaseRotations[id]; } /** * Sets the base rotation for the current meta-building. */ set currentBaseRotation(rotation) { - const rotationByBuilding = this.root.app.settings.getAllSettings().rotationByBuilding; - if (!rotationByBuilding) { + if (!this.root.app.settings.getAllSettings().rotationByBuilding) { this.currentBaseRotationGeneral = rotation; } else { - const id = this.currentMetaBuilding.get().getId(); - this.preferredBaseRotations[id] = rotation; + const building = this.currentMetaBuilding.get(); + if (building != undefined) { + this.preferredBaseRotations[building.getId()] = rotation; + } else { + this.currentBaseRotationGeneral = rotation; + } } } From fbc543cf3f44f8d643a58bc1c23a07428771efbd Mon Sep 17 00:00:00 2001 From: Magnus Grimstvedt Saltnes Date: Wed, 24 Jun 2020 16:28:01 +0200 Subject: [PATCH 18/24] Corrects some code style issues. --- src/js/game/hud/parts/building_placer_logic.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/js/game/hud/parts/building_placer_logic.js b/src/js/game/hud/parts/building_placer_logic.js index 106e642c..9bf031c0 100644 --- a/src/js/game/hud/parts/building_placer_logic.js +++ b/src/js/game/hud/parts/building_placer_logic.js @@ -128,9 +128,9 @@ export class HUDBuildingPlacerLogic extends BaseHUDPart { if (!this.root.app.settings.getAllSettings().rotationByBuilding) { return this.currentBaseRotationGeneral; } - const building = this.currentMetaBuilding.get(); - if (building != undefined && this.preferredBaseRotations.hasOwnProperty(building.getId())) { - return this.preferredBaseRotations[building.getId()]; + const metaBuilding = this.currentMetaBuilding.get(); + if (metaBuilding && this.preferredBaseRotations.hasOwnProperty(metaBuilding.getId())) { + return this.preferredBaseRotations[metaBuilding.getId()]; } else { return this.currentBaseRotationGeneral; } @@ -138,14 +138,15 @@ export class HUDBuildingPlacerLogic extends BaseHUDPart { /** * Sets the base rotation for the current meta-building. + * @param {number} rotation The new rotation/angle. */ set currentBaseRotation(rotation) { if (!this.root.app.settings.getAllSettings().rotationByBuilding) { this.currentBaseRotationGeneral = rotation; } else { - const building = this.currentMetaBuilding.get(); - if (building != undefined) { - this.preferredBaseRotations[building.getId()] = rotation; + const metaBuilding = this.currentMetaBuilding.get(); + if (metaBuilding) { + this.preferredBaseRotations[metaBuilding.getId()] = rotation; } else { this.currentBaseRotationGeneral = rotation; } From 25cf1ea93bea95f57e132150e13407f99292e4ee Mon Sep 17 00:00:00 2001 From: tobspr Date: Wed, 24 Jun 2020 16:41:31 +0200 Subject: [PATCH 19/24] Change spanish (latin america) to spanish --- res/ui/languages/es-419.svg | 91 +++++++++++++++++++++++++++++-------- src/js/languages.js | 4 +- 2 files changed, 74 insertions(+), 21 deletions(-) diff --git a/res/ui/languages/es-419.svg b/res/ui/languages/es-419.svg index 7deefd93..5f671625 100644 --- a/res/ui/languages/es-419.svg +++ b/res/ui/languages/es-419.svg @@ -1,25 +1,78 @@ - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/js/languages.js b/src/js/languages.js index 0bea19c7..cac22bcf 100644 --- a/src/js/languages.js +++ b/src/js/languages.js @@ -51,10 +51,10 @@ export const LANGUAGES = { region: "", }, "es-419": { - name: "Español (Latinoamérica)", + name: "Español", data: require("./built-temp/base-es.json"), code: "es", - region: "419", + region: "", }, "pl": { name: "Polski", From b3de1f9207b50dbc4221ce5d62233317b409f8f4 Mon Sep 17 00:00:00 2001 From: tobspr Date: Wed, 24 Jun 2020 17:59:43 +0200 Subject: [PATCH 20/24] Re-enable analytics --- gulp/webpack.config.js | 1 - gulp/webpack.production.config.js | 1 - src/js/application.js | 4 +- src/js/core/config.js | 2 +- src/js/core/utils.js | 2 +- src/js/globals.d.ts | 1 - src/js/platform/browser/game_analytics.js | 125 ++++++++++++++-------- src/js/platform/game_analytics.js | 4 +- src/js/states/ingame.js | 2 +- 9 files changed, 85 insertions(+), 57 deletions(-) diff --git a/gulp/webpack.config.js b/gulp/webpack.config.js index 274cc3ef..26923796 100644 --- a/gulp/webpack.config.js +++ b/gulp/webpack.config.js @@ -36,7 +36,6 @@ module.exports = ({ watch = false, standalone = false }) => { lzString.compressToEncodedURIComponent("http://localhost:10005/v1") ), G_IS_DEV: "true", - G_IS_PROD: "false", G_IS_RELEASE: "false", G_IS_MOBILE_APP: "false", G_IS_BROWSER: "true", diff --git a/gulp/webpack.production.config.js b/gulp/webpack.production.config.js index 837cfe8b..f80a69d2 100644 --- a/gulp/webpack.production.config.js +++ b/gulp/webpack.production.config.js @@ -24,7 +24,6 @@ module.exports = ({ assertAlways: "window.assert", abstract: "window.assert(false, 'abstract method called');", G_IS_DEV: "false", - G_IS_PROD: "true", G_IS_RELEASE: environment === "prod" ? "true" : "false", G_IS_STANDALONE: standalone ? "true" : "false", G_IS_BROWSER: isBrowser ? "true" : "false", diff --git a/src/js/application.js b/src/js/application.js index ee913a3f..f08b467e 100644 --- a/src/js/application.js +++ b/src/js/application.js @@ -33,6 +33,7 @@ import { MainMenuState } from "./states/main_menu"; import { MobileWarningState } from "./states/mobile_warning"; import { PreloadState } from "./states/preload"; import { SettingsState } from "./states/settings"; +import { ShapezGameAnalytics } from "./platform/browser/game_analytics"; const logger = createLogger("application"); @@ -130,8 +131,7 @@ export class Application { this.adProvider = new NoAdProvider(this); this.sound = new SoundImplBrowser(this); this.analytics = new GoogleAnalyticsImpl(this); - // this.gameAnalytics = new ShapezGameAnalytics(this); - this.gameAnalytics = new NoGameAnalytics(this); + this.gameAnalytics = new ShapezGameAnalytics(this); } /** diff --git a/src/js/core/config.js b/src/js/core/config.js index 62e5d87d..6825e762 100644 --- a/src/js/core/config.js +++ b/src/js/core/config.js @@ -9,7 +9,7 @@ export const IS_DEBUG = export const IS_DEMO = queryParamOptions.fullVersion ? false - : (G_IS_PROD && !G_IS_STANDALONE) || + : (!G_IS_DEV && !G_IS_STANDALONE) || (typeof window !== "undefined" && window.location.search.indexOf("demo") >= 0); export const SUPPORT_TOUCH = false; diff --git a/src/js/core/utils.js b/src/js/core/utils.js index fdf97880..23368317 100644 --- a/src/js/core/utils.js +++ b/src/js/core/utils.js @@ -769,7 +769,7 @@ export function quantizeFloat(value) { * @param {number} tickRate Interval of the timer */ export function checkTimerExpired(now, lastTick, tickRate) { - if (!G_IS_PROD) { + if (G_IS_DEV) { if (quantizeFloat(now) !== now) { console.error("Got non-quantizied time:" + now + " vs " + quantizeFloat(now)); now = quantizeFloat(now); diff --git a/src/js/globals.d.ts b/src/js/globals.d.ts index dc2246d0..bc99d55e 100644 --- a/src/js/globals.d.ts +++ b/src/js/globals.d.ts @@ -1,7 +1,6 @@ // Globals defined by webpack declare const G_IS_DEV: boolean; -declare const G_IS_PROD: boolean; declare function assert(condition: boolean | object | string, ...errorMessage: string[]): void; declare function assertAlways(condition: boolean | object | string, ...errorMessage: string[]): void; diff --git a/src/js/platform/browser/game_analytics.js b/src/js/platform/browser/game_analytics.js index be1a8940..73da3696 100644 --- a/src/js/platform/browser/game_analytics.js +++ b/src/js/platform/browser/game_analytics.js @@ -1,11 +1,12 @@ -import { GameAnalyticsInterface } from "../game_analytics"; -import { createLogger } from "../../core/logging"; -import { ShapeDefinition } from "../../game/shape_definition"; -import { Savegame } from "../../savegame/savegame"; -import { FILE_NOT_FOUND } from "../storage"; import { globalConfig } from "../../core/config"; -import { InGameState } from "../../states/ingame"; +import { createLogger } from "../../core/logging"; import { GameRoot } from "../../game/root"; +import { InGameState } from "../../states/ingame"; +import { GameAnalyticsInterface } from "../game_analytics"; +import { FILE_NOT_FOUND } from "../storage"; +import { blueprintShape, UPGRADES } from "../../game/upgrades"; +import { tutorialGoals } from "../../game/tutorial_goals"; +import { BeltComponent } from "../../game/components/belt"; import { StaticMapEntityComponent } from "../../game/components/static_map_entity"; const logger = createLogger("game_analytics"); @@ -14,16 +15,32 @@ const analyticsUrl = G_IS_DEV ? "http://localhost:8001" : "https://analytics.sha // Be sure to increment the ID whenever it changes to make sure all // users are tracked -const analyticsLocalFile = "analytics_token.3.bin"; +const analyticsLocalFile = "shapez_token_123.bin"; export class ShapezGameAnalytics extends GameAnalyticsInterface { + get environment() { + if (G_IS_DEV) { + return "dev"; + } + + if (G_IS_STANDALONE) { + return "steam"; + } + + if (G_IS_RELEASE) { + return "prod"; + } + + return "beta"; + } + /** * @returns {Promise} */ initialize() { this.syncKey = null; - setInterval(() => this.sendTimePoints(), 120 * 1000); + setInterval(() => this.sendTimePoints(), 60 * 1000); // Retrieve sync key from player return this.app.storage.readFileAsync(analyticsLocalFile).then( @@ -38,7 +55,7 @@ export class ShapezGameAnalytics extends GameAnalyticsInterface { // Perform call to get a new key from the API this.sendToApi("/v1/register", { - environment: G_APP_ENVIRONMENT, + environment: this.environment, }) .then(res => { // Try to read and parse the key from the api @@ -135,10 +152,12 @@ export class ShapezGameAnalytics extends GameAnalyticsInterface { playerKey: this.syncKey, gameKey: savegameId, ingameTime: root.time.now(), + environment: this.environment, category, value, version: G_BUILD_VERSION, - gameDump: this.generateGameDump(root, category === "sync"), + level: root.hubGoals.level, + gameDump: this.generateGameDump(root), }); } @@ -151,52 +170,58 @@ export class ShapezGameAnalytics extends GameAnalyticsInterface { } /** - * Generates a game dump - * @param {GameRoot} root - * @param {boolean=} metaOnly + * Returns true if the shape is interesting + * @param {string} key */ - generateGameDump(root, metaOnly = false) { - let staticEntities = []; + isInterestingShape(key) { + if (key === blueprintShape) { + return true; + } - const entities = root.entityMgr.getAllWithComponent(StaticMapEntityComponent); - - // Limit the entities - if (!metaOnly && entities.length < 500) { - for (let i = 0; i < entities.length; ++i) { - const entity = entities[i]; - const staticComp = entity.components.StaticMapEntity; - const payload = {}; - payload.origin = staticComp.origin; - payload.tileSize = staticComp.tileSize; - payload.rotation = staticComp.rotation; - - if (entity.components.Belt) { - payload.type = "belt"; - } else if (entity.components.UndergroundBelt) { - payload.type = "tunnel"; - } else if (entity.components.ItemProcessor) { - payload.type = entity.components.ItemProcessor.type; - } else if (entity.components.Miner) { - payload.type = "extractor"; - } else { - logger.warn("Unkown entity type", entity); - } - staticEntities.push(payload); + // Check if its a story goal + for (let i = 0; i < tutorialGoals.length; ++i) { + if (key === tutorialGoals[i].shape) { + return true; } } - return { - storedShapes: root.hubGoals.storedShapes, - gainedRewards: root.hubGoals.gainedRewards, - upgradeLevels: root.hubGoals.upgradeLevels, - staticEntities, - }; + // 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 required = tier.required; + for (let k = 0; k < required.length; ++k) { + if (required[k].shape === key) { + return true; + } + } + } + } + + return false; } /** - * @param {ShapeDefinition} definition + * Generates a game dump + * @param {GameRoot} root */ - handleShapeDelivered(definition) {} + generateGameDump(root) { + const shapeIds = Object.keys(root.hubGoals.storedShapes).filter(this.isInterestingShape.bind(this)); + let shapes = {}; + for (let i = 0; i < shapeIds.length; ++i) { + shapes[shapeIds[i]] = root.hubGoals.storedShapes[shapeIds[i]]; + } + return { + shapes, + upgrades: root.hubGoals.upgradeLevels, + belts: root.entityMgr.getAllWithComponent(BeltComponent).length, + buildings: + root.entityMgr.getAllWithComponent(StaticMapEntityComponent).length - + root.entityMgr.getAllWithComponent(BeltComponent).length, + }; + } /** */ @@ -204,6 +229,12 @@ export class ShapezGameAnalytics extends GameAnalyticsInterface { this.sendGameEvent("game_start", ""); } + /** + */ + handleGameResumed() { + this.sendTimePoints(); + } + /** * Handles the given level completed * @param {number} level diff --git a/src/js/platform/game_analytics.js b/src/js/platform/game_analytics.js index c3e2fa64..ea6aa293 100644 --- a/src/js/platform/game_analytics.js +++ b/src/js/platform/game_analytics.js @@ -25,9 +25,9 @@ export class GameAnalyticsInterface { handleGameStarted() {} /** - * @param {ShapeDefinition} definition + * Handles a resumed game */ - handleShapeDelivered(definition) {} + handleGameResumed() {} /** * Handles the given level completed diff --git a/src/js/states/ingame.js b/src/js/states/ingame.js index a5901a3d..33d7e15b 100644 --- a/src/js/states/ingame.js +++ b/src/js/states/ingame.js @@ -217,7 +217,6 @@ export class InGameState extends GameState { this.core.initializeRoot(this, this.savegame); if (this.savegame.hasGameDump()) { - this.app.gameAnalytics.handleGameStarted(); this.stage4bResumeGame(); } else { this.app.gameAnalytics.handleGameStarted(); @@ -245,6 +244,7 @@ export class InGameState extends GameState { this.onInitializationFailure("Savegame is corrupt and can not be restored."); return; } + this.app.gameAnalytics.handleGameResumed(); this.stage5FirstUpdate(); } } From 24694f7f23640967c2e486dda9943a48251a89ff Mon Sep 17 00:00:00 2001 From: tobspr Date: Wed, 24 Jun 2020 18:01:14 +0200 Subject: [PATCH 21/24] Bump version --- src/js/changelog.js | 5 +++++ version | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/js/changelog.js b/src/js/changelog.js index 9da13e2c..2d6e364c 100644 --- a/src/js/changelog.js +++ b/src/js/changelog.js @@ -1,4 +1,9 @@ export const CHANGELOG = [ + { + version: "1.1.18", + date: "24.06.2020", + entries: ["Added chinese (traditional) translation", "Updated translations"], + }, { version: "1.1.17", date: "22.06.2020", diff --git a/version b/version index 95ce23d4..b0c8928e 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.1.17 \ No newline at end of file +1.1.18 \ No newline at end of file From 98a117a9658adabd671dbae2e4e89fa4782161a5 Mon Sep 17 00:00:00 2001 From: tobspr Date: Wed, 24 Jun 2020 18:57:43 +0200 Subject: [PATCH 22/24] Update changelog --- src/js/changelog.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/js/changelog.js b/src/js/changelog.js index 2d6e364c..f5d032f9 100644 --- a/src/js/changelog.js +++ b/src/js/changelog.js @@ -2,7 +2,12 @@ export const CHANGELOG = [ { version: "1.1.18", date: "24.06.2020", - entries: ["Added chinese (traditional) translation", "Updated translations"], + entries: [ + "Preparations for the wires update", + "Add setting (on by default) to store the last used rotation per building instead of globally storing it (by Magos)", + "Added chinese (traditional) translation", + "Updated translations", + ], }, { version: "1.1.17", From 15a4e0c58056ca28e0a2f8bd311197f97b8db608 Mon Sep 17 00:00:00 2001 From: tobspr Date: Wed, 24 Jun 2020 19:01:19 +0200 Subject: [PATCH 23/24] Fix settings version --- src/js/profile/application_settings.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/js/profile/application_settings.js b/src/js/profile/application_settings.js index 8fd80df0..74734949 100644 --- a/src/js/profile/application_settings.js +++ b/src/js/profile/application_settings.js @@ -481,7 +481,7 @@ export class ApplicationSettings extends ReadWriteProxy { } getCurrentVersion() { - return 17; + return 18; } /** @param {{settings: SettingsStorage, version: number}} data */ @@ -553,6 +553,7 @@ export class ApplicationSettings extends ReadWriteProxy { data.settings.enableColorBlindHelper = false; data.version = 17; } + if (data.version < 18) { data.settings.rotationByBuilding = true; data.version = 18; From 944463a914de661ff65a982500214b2d1ebe6980 Mon Sep 17 00:00:00 2001 From: tobspr Date: Wed, 24 Jun 2020 19:15:35 +0200 Subject: [PATCH 24/24] Update translations --- sync-translations.js | 4 ++-- translations/base-ar.yaml | 6 ++++++ translations/base-cz.yaml | 6 ++++++ translations/base-de.yaml | 6 ++++++ translations/base-el.yaml | 6 ++++++ translations/base-es.yaml | 6 ++++++ translations/base-fr.yaml | 6 ++++++ translations/base-hr.yaml | 6 ++++++ translations/base-hu.yaml | 6 ++++++ translations/base-it.yaml | 6 ++++++ translations/base-ja.yaml | 6 ++++++ translations/base-kor.yaml | 6 ++++++ translations/base-lt.yaml | 6 ++++++ translations/base-nl.yaml | 6 ++++++ translations/base-no.yaml | 6 ++++++ translations/base-pl.yaml | 6 ++++++ translations/base-pt-BR.yaml | 6 ++++++ translations/base-pt-PT.yaml | 6 ++++++ translations/base-ro.yaml | 6 ++++++ translations/base-ru.yaml | 6 ++++++ translations/base-sv.yaml | 6 ++++++ translations/base-tr.yaml | 6 ++++++ translations/base-zh-CN.yaml | 15 ++++++++++----- translations/base-zh-TW.yaml | 17 ++++++++++++----- 24 files changed, 150 insertions(+), 12 deletions(-) diff --git a/sync-translations.js b/sync-translations.js index afe78cfe..4815704d 100644 --- a/sync-translations.js +++ b/sync-translations.js @@ -21,7 +21,7 @@ const placeholderRegexp = /<([a-zA-Z_0-9]+)>/gi; function match(originalObj, translatedObj, path = "/") { for (const key in originalObj) { - if (!translatedObj[key]) { + if (!translatedObj.hasOwnProperty(key)) { console.warn(" | Missing key", path + key); translatedObj[key] = originalObj[key]; continue; @@ -60,7 +60,7 @@ function match(originalObj, translatedObj, path = "/") { } for (const key in translatedObj) { - if (!originalObj[key]) { + if (!originalObj.hasOwnProperty(key)) { console.warn(" | Obsolete key", path + key); delete translatedObj[key]; } diff --git a/translations/base-ar.yaml b/translations/base-ar.yaml index 00c6d0b0..9e10dd65 100644 --- a/translations/base-ar.yaml +++ b/translations/base-ar.yaml @@ -728,6 +728,12 @@ settings: enableColorBlindHelper: title: Color Blind Mode description: Enables various tools which allow to play the game if you are color blind. + rotationByBuilding: + title: Rotation by building type + description: >- + Each building type remembers the rotation you last set it to individually. + This may be more comfortable if you frequently switch between placing + different building types. keybindings: title: Keybindings diff --git a/translations/base-cz.yaml b/translations/base-cz.yaml index 6c7b5052..dd34958c 100644 --- a/translations/base-cz.yaml +++ b/translations/base-cz.yaml @@ -708,6 +708,12 @@ settings: enableColorBlindHelper: title: Režim pro barvoslepé description: Zapné různé nástroje, které vám umožní hrát hru i pokud jste barvoslepí. + rotationByBuilding: + title: Rotation by building type + description: >- + Each building type remembers the rotation you last set it to individually. + This may be more comfortable if you frequently switch between placing + different building types. keybindings: title: Klávesové zkratky diff --git a/translations/base-de.yaml b/translations/base-de.yaml index 69d747dd..057020ea 100644 --- a/translations/base-de.yaml +++ b/translations/base-de.yaml @@ -728,6 +728,12 @@ settings: enableColorBlindHelper: title: Modus für Farbenblinde description: Aktiviert verschiedene Werkzeuge, die dir das Spielen trotz Farbenblindheit ermöglichen. + rotationByBuilding: + title: Rotation by building type + description: >- + Each building type remembers the rotation you last set it to individually. + This may be more comfortable if you frequently switch between placing + different building types. keybindings: title: Tastenbelegung diff --git a/translations/base-el.yaml b/translations/base-el.yaml index 4c37ed19..3b7bec15 100644 --- a/translations/base-el.yaml +++ b/translations/base-el.yaml @@ -729,6 +729,12 @@ settings: enableColorBlindHelper: title: Color Blind Mode description: Enables various tools which allow to play the game if you are color blind. + rotationByBuilding: + title: Rotation by building type + description: >- + Each building type remembers the rotation you last set it to individually. + This may be more comfortable if you frequently switch between placing + different building types. keybindings: title: Keybindings diff --git a/translations/base-es.yaml b/translations/base-es.yaml index 61903c93..77273462 100644 --- a/translations/base-es.yaml +++ b/translations/base-es.yaml @@ -717,6 +717,12 @@ settings: enableColorBlindHelper: title: Modo para Daltonicos description: Activa varias herramientas que permiten jugar si eres daltonico. + rotationByBuilding: + title: Rotation by building type + description: >- + Each building type remembers the rotation you last set it to individually. + This may be more comfortable if you frequently switch between placing + different building types. keybindings: title: Atajos de Teclado diff --git a/translations/base-fr.yaml b/translations/base-fr.yaml index c0a9788c..7140e456 100644 --- a/translations/base-fr.yaml +++ b/translations/base-fr.yaml @@ -727,6 +727,12 @@ settings: enableColorBlindHelper: title: Mode Daltonien description: Active divers outils qui permettent de jouer à ce jeu si vous êtes daltonien. + rotationByBuilding: + title: Rotation by building type + description: >- + Each building type remembers the rotation you last set it to individually. + This may be more comfortable if you frequently switch between placing + different building types. keybindings: title: Contrôles diff --git a/translations/base-hr.yaml b/translations/base-hr.yaml index 3b49c01d..2cad65a1 100644 --- a/translations/base-hr.yaml +++ b/translations/base-hr.yaml @@ -727,6 +727,12 @@ settings: enableColorBlindHelper: title: Color Blind Mode description: Enables various tools which allow to play the game if you are color blind. + rotationByBuilding: + title: Rotation by building type + description: >- + Each building type remembers the rotation you last set it to individually. + This may be more comfortable if you frequently switch between placing + different building types. keybindings: title: Keybindings diff --git a/translations/base-hu.yaml b/translations/base-hu.yaml index b750fd1f..45a51e37 100644 --- a/translations/base-hu.yaml +++ b/translations/base-hu.yaml @@ -726,6 +726,12 @@ settings: enableColorBlindHelper: title: Color Blind Mode description: Enables various tools which allow to play the game if you are color blind. + rotationByBuilding: + title: Rotation by building type + description: >- + Each building type remembers the rotation you last set it to individually. + This may be more comfortable if you frequently switch between placing + different building types. keybindings: title: Keybindings diff --git a/translations/base-it.yaml b/translations/base-it.yaml index c6314099..412d947a 100644 --- a/translations/base-it.yaml +++ b/translations/base-it.yaml @@ -727,6 +727,12 @@ settings: enableColorBlindHelper: title: Color Blind Mode description: Enables various tools which allow to play the game if you are color blind. + rotationByBuilding: + title: Rotation by building type + description: >- + Each building type remembers the rotation you last set it to individually. + This may be more comfortable if you frequently switch between placing + different building types. keybindings: title: Keybindings diff --git a/translations/base-ja.yaml b/translations/base-ja.yaml index 22805f52..500238db 100644 --- a/translations/base-ja.yaml +++ b/translations/base-ja.yaml @@ -722,6 +722,12 @@ settings: enableColorBlindHelper: title: 色覚モード description: 色覚異常を持っていてもゲームがプレイできるようにするための各種ツールを有効化します。 + rotationByBuilding: + title: Rotation by building type + description: >- + Each building type remembers the rotation you last set it to individually. + This may be more comfortable if you frequently switch between placing + different building types. keybindings: title: キー設定 diff --git a/translations/base-kor.yaml b/translations/base-kor.yaml index a1c08a32..e9f19157 100644 --- a/translations/base-kor.yaml +++ b/translations/base-kor.yaml @@ -728,6 +728,12 @@ settings: enableColorBlindHelper: title: Color Blind Mode description: Enables various tools which allow to play the game if you are color blind. + rotationByBuilding: + title: Rotation by building type + description: >- + Each building type remembers the rotation you last set it to individually. + This may be more comfortable if you frequently switch between placing + different building types. keybindings: title: 키바인딩 diff --git a/translations/base-lt.yaml b/translations/base-lt.yaml index 8a927559..bd33d5b1 100644 --- a/translations/base-lt.yaml +++ b/translations/base-lt.yaml @@ -726,6 +726,12 @@ settings: enableColorBlindHelper: title: Color Blind Mode description: Enables various tools which allow to play the game if you are color blind. + rotationByBuilding: + title: Rotation by building type + description: >- + Each building type remembers the rotation you last set it to individually. + This may be more comfortable if you frequently switch between placing + different building types. keybindings: title: Keybindings diff --git a/translations/base-nl.yaml b/translations/base-nl.yaml index 11dadd27..2deb72b7 100644 --- a/translations/base-nl.yaml +++ b/translations/base-nl.yaml @@ -724,6 +724,12 @@ settings: enableColorBlindHelper: title: Kleurenblindmodus description: Schakelt verschillende hulpmiddelen in zodat je het spel alsnog kunt spelen wanneer je kleurenblind bent. + rotationByBuilding: + title: Rotation by building type + description: >- + Each building type remembers the rotation you last set it to individually. + This may be more comfortable if you frequently switch between placing + different building types. keybindings: title: Sneltoetsen diff --git a/translations/base-no.yaml b/translations/base-no.yaml index 9dea10a5..b3c1bc84 100644 --- a/translations/base-no.yaml +++ b/translations/base-no.yaml @@ -726,6 +726,12 @@ settings: enableColorBlindHelper: title: Fargeblind Modus description: Aktiverer forskjellige verktøy som lar deg spille spillet om du er fargeblind. + rotationByBuilding: + title: Rotation by building type + description: >- + Each building type remembers the rotation you last set it to individually. + This may be more comfortable if you frequently switch between placing + different building types. keybindings: title: Hurtigtaster diff --git a/translations/base-pl.yaml b/translations/base-pl.yaml index 3627d86b..8f66d2a0 100644 --- a/translations/base-pl.yaml +++ b/translations/base-pl.yaml @@ -752,6 +752,12 @@ settings: enableColorBlindHelper: title: Color Blind Mode description: Enables various tools which allow to play the game if you are color blind. + rotationByBuilding: + title: Rotation by building type + description: >- + Each building type remembers the rotation you last set it to individually. + This may be more comfortable if you frequently switch between placing + different building types. keybindings: title: Klawiszologia diff --git a/translations/base-pt-BR.yaml b/translations/base-pt-BR.yaml index 224b16f0..e53c845f 100644 --- a/translations/base-pt-BR.yaml +++ b/translations/base-pt-BR.yaml @@ -727,6 +727,12 @@ settings: enableColorBlindHelper: title: Color Blind Mode description: Enables various tools which allow to play the game if you are color blind. + rotationByBuilding: + title: Rotation by building type + description: >- + Each building type remembers the rotation you last set it to individually. + This may be more comfortable if you frequently switch between placing + different building types. keybindings: title: Controles diff --git a/translations/base-pt-PT.yaml b/translations/base-pt-PT.yaml index d956438e..a9f93efc 100644 --- a/translations/base-pt-PT.yaml +++ b/translations/base-pt-PT.yaml @@ -725,6 +725,12 @@ settings: enableColorBlindHelper: title: Modo Daltónico description: Ativa várias ferramentas para daltónicos. + rotationByBuilding: + title: Rotation by building type + description: >- + Each building type remembers the rotation you last set it to individually. + This may be more comfortable if you frequently switch between placing + different building types. keybindings: title: Atalhos diff --git a/translations/base-ro.yaml b/translations/base-ro.yaml index 334ddeda..48b29af4 100644 --- a/translations/base-ro.yaml +++ b/translations/base-ro.yaml @@ -726,6 +726,12 @@ settings: enableColorBlindHelper: title: Color Blind Mode description: Enables various tools which allow to play the game if you are color blind. + rotationByBuilding: + title: Rotation by building type + description: >- + Each building type remembers the rotation you last set it to individually. + This may be more comfortable if you frequently switch between placing + different building types. keybindings: title: Keybindings diff --git a/translations/base-ru.yaml b/translations/base-ru.yaml index f2cac852..1e0fee9e 100644 --- a/translations/base-ru.yaml +++ b/translations/base-ru.yaml @@ -727,6 +727,12 @@ settings: enableColorBlindHelper: title: Режим Дальтоника description: Включает различные инструменты, которые позволяют играть в игру дальтоникам. + rotationByBuilding: + title: Rotation by building type + description: >- + Each building type remembers the rotation you last set it to individually. + This may be more comfortable if you frequently switch between placing + different building types. keybindings: title: Настройки управления diff --git a/translations/base-sv.yaml b/translations/base-sv.yaml index b8665e33..2aebe2ff 100644 --- a/translations/base-sv.yaml +++ b/translations/base-sv.yaml @@ -726,6 +726,12 @@ settings: enableColorBlindHelper: title: Färgblint läge description: Aktiverar olika verktyg som låter dig spela spelet om du är färbling. + rotationByBuilding: + title: Rotation by building type + description: >- + Each building type remembers the rotation you last set it to individually. + This may be more comfortable if you frequently switch between placing + different building types. keybindings: title: Tangentbindningar diff --git a/translations/base-tr.yaml b/translations/base-tr.yaml index 532eb3fc..8084ee4a 100644 --- a/translations/base-tr.yaml +++ b/translations/base-tr.yaml @@ -727,6 +727,12 @@ settings: enableColorBlindHelper: title: Color Blind Mode description: Enables various tools which allow to play the game if you are color blind. + rotationByBuilding: + title: Rotation by building type + description: >- + Each building type remembers the rotation you last set it to individually. + This may be more comfortable if you frequently switch between placing + different building types. keybindings: title: Keybindings diff --git a/translations/base-zh-CN.yaml b/translations/base-zh-CN.yaml index 4b2890ae..1e44992c 100644 --- a/translations/base-zh-CN.yaml +++ b/translations/base-zh-CN.yaml @@ -567,7 +567,7 @@ storyRewards: reward_mixer: title: 混合颜色 - desc: 恭喜!你解锁了混合机。在这个建筑中将两个颜色混合在一起(加法混合)。 + desc: The mixer has been unlocked - Combine two colors using additive blending with this building! reward_stacker: title: 堆叠 @@ -587,7 +587,7 @@ storyRewards: reward_miner_chainable: title: 链式开采机 - desc: 恭喜!你解锁了开采机链式变体。它可以将开采出来的资源传递给其他的开采机,使得资源提取更加高效! + desc: You have unlocked the chaining extractor! It can forward its resources to other extractors so you can more efficiently extract resources! reward_underground_belt_tier_2: title: 二级隧道 @@ -595,8 +595,7 @@ storyRewards: reward_splitter_compact: title: 小型合流机 - desc: >- - 恭喜!你解锁了平衡机小型合流机变体。它可以把两个输入合并到一个输出上。 + desc: You have unlocked a compact variant of the balancer - It accepts two inputs and merges them into one! reward_cutter_quad: title: 四向切割机 @@ -612,7 +611,7 @@ storyRewards: reward_storage: title: 仓库 - desc: 恭喜!你解锁了垃圾桶仓库变体。它可以暂时储存一些材料,有容量上限。 + desc: You have unlocked a variant of the trash - It allows to store items up to a given capacity! reward_freeplay: title: 自由模式 @@ -762,6 +761,12 @@ settings: enableColorBlindHelper: title: 色盲模式 description: 提供一些分辨颜色的工具。目前当鼠标移至颜色资源上方时,屏幕上方会显示颜色名称。 + rotationByBuilding: + title: Rotation by building type + description: >- + Each building type remembers the rotation you last set it to individually. + This may be more comfortable if you frequently switch between placing + different building types. keybindings: title: 按键设置 diff --git a/translations/base-zh-TW.yaml b/translations/base-zh-TW.yaml index ef7a522f..973312d0 100644 --- a/translations/base-zh-TW.yaml +++ b/translations/base-zh-TW.yaml @@ -156,7 +156,7 @@ mainMenu: browserWarning: >- 很抱歉, 本遊戲在當前瀏覽器上可能運行緩慢! 使用chrome或者獲取獨立版以得到更好的體驗。 - savegameLevel: 第關/關 + savegameLevel: Level savegameLevelUnknown: 未知關卡 contests: @@ -258,7 +258,7 @@ dialogs: upgradesIntroduction: title: 解鎖建築升級 desc: >- - 你生產過的所有圖形都會被用來升級建築。升級菜單在屏幕右上角。 + 你生產過的所有圖形都會被用來升級建築。升級菜單在屏幕右上角。 不要銷毀你之前建造的工廠! massDeleteConfirm: @@ -343,7 +343,7 @@ ingame: # The notification when completing a level levelCompleteNotification: # is replaced by the actual level, so this gets 'Level 03' for example. - levelTitle: 第關 / 關 + levelTitle: Level completed: 完成 unlockText: 解鎖! buttonNextLevel: 下一關 @@ -529,7 +529,7 @@ buildings: name: 上色機(四向) description: 為圖形的四個角塗上不同的顏色。 mirrored: - name: &painter 上色機 (反向) + name: *painter description: *painter_desc trash: @@ -558,7 +558,7 @@ storyRewards: reward_painter: title: 上色 desc: >- - 上色機已解鎖。和圖形一樣,從顏色礦脈中開採顏色,然後將在上色機中將顏色塗在圖形上。

    PS:我們正在開發色盲模式! + 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 color blind mode in the settings! reward_mixer: title: 混色 @@ -758,6 +758,13 @@ settings: enableColorBlindHelper: title: 色盲模式 description: 如果您是色盲者,啟用了這設定,就可以玩遊戲了。 + + rotationByBuilding: + title: Rotation by building type + description: >- + Each building type remembers the rotation you last set it to individually. + This may be more comfortable if you frequently switch between placing + different building types. keybindings: title: 按鍵設置 hint: >-