diff --git a/.editorconfig b/.editorconfig new file mode 100755 index 00000000..9a3e06f4 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,8 @@ +root = true + +[{src, translations}/*] +end_of_line = crlf +insert_final_newline = true +indent_style = space +indent_size = 4 +charset = utf-8 \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 926180ac..61d54684 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,30 +1,31 @@ FROM node:12 as base +EXPOSE 3001 3005 + WORKDIR /shapez.io -COPY . . - -EXPOSE 3005 -EXPOSE 3001 - -RUN apt-get update \ - && apt-get update \ - && apt-get upgrade -y \ - && apt-get dist-upgrade -y \ - && apt-get install -y --no-install-recommends \ - ffmpeg \ +RUN apt-get update && apt-get install -y --no-install-recommends \ + ffmpeg default-jre \ + && apt-get clean \ && rm -rf /var/lib/apt/lists/* -FROM base as shape_base +COPY package.json yarn.lock ./ +RUN yarn + +COPY gulp ./gulp +WORKDIR /shapez.io/gulp +RUN yarn WORKDIR /shapez.io - -RUN yarn +COPY res ./res +COPY src/html ./src/html +COPY src/css ./src/css +COPY version ./version +COPY sync-translations.js ./ +COPY translations ./translations +COPY src/js ./src/js +COPY res_raw ./res_raw +COPY .git ./.git WORKDIR /shapez.io/gulp - -RUN yarn - -WORKDIR /shapez.io/gulp - ENTRYPOINT ["yarn", "gulp"] diff --git a/gulp/gulpfile.js b/gulp/gulpfile.js index bc98d536..7ac426a1 100644 --- a/gulp/gulpfile.js +++ b/gulp/gulpfile.js @@ -86,8 +86,16 @@ gulp.task("utils.cleanBuildTempFolder", () => { .src(path.join(__dirname, "..", "src", "js", "built-temp"), { read: false, allowEmpty: true }) .pipe($.clean({ force: true })); }); +gulp.task("utils.cleanImageBuildFolder", () => { + return gulp + .src(path.join(__dirname, "res_built"), { read: false, allowEmpty: true }) + .pipe($.clean({ force: true })); +}); -gulp.task("utils.cleanup", gulp.series("utils.cleanBuildFolder", "utils.cleanBuildTempFolder")); +gulp.task( + "utils.cleanup", + gulp.series("utils.cleanBuildFolder", "utils.cleanImageBuildFolder", "utils.cleanBuildTempFolder") +); // Requires no uncomitted files gulp.task("utils.requireCleanWorkingTree", cb => { @@ -234,12 +242,13 @@ gulp.task( "build.standalone.dev", gulp.series( "utils.cleanup", + "imgres.buildAtlas", + "imgres.atlasToJson", "imgres.atlas", "sounds.dev", "imgres.copyImageResources", "imgres.copyNonImageResources", "translations.fullBuild", - "js.standalone-dev", "css.dev", "html.standalone-dev" ) diff --git a/gulp/image-resources.js b/gulp/image-resources.js index 35365c79..33df234d 100644 --- a/gulp/image-resources.js +++ b/gulp/image-resources.js @@ -173,6 +173,8 @@ function gulptasksImageResources($, gulp, buildFolder) { gulp.task( "imgres.allOptimized", gulp.parallel( + "imgres.buildAtlas", + "imgres.atlasToJson", "imgres.atlasOptimized", "imgres.copyNonImageResources", "imgres.copyImageResourcesOptimized" diff --git a/res/ui/building_tutorials/virtual_processor-rotater.png b/res/ui/building_tutorials/virtual_processor-rotater.png index 61f8e866..752d21f8 100644 Binary files a/res/ui/building_tutorials/virtual_processor-rotater.png and b/res/ui/building_tutorials/virtual_processor-rotater.png differ diff --git a/res/ui/interactive_tutorial.noinline/2_1_place_cutter.gif b/res/ui/interactive_tutorial.noinline/2_1_place_cutter.gif new file mode 100644 index 00000000..1678c0b2 Binary files /dev/null and b/res/ui/interactive_tutorial.noinline/2_1_place_cutter.gif differ diff --git a/res/ui/interactive_tutorial.noinline/2_2_place_trash.gif b/res/ui/interactive_tutorial.noinline/2_2_place_trash.gif new file mode 100644 index 00000000..0d60fa9f Binary files /dev/null and b/res/ui/interactive_tutorial.noinline/2_2_place_trash.gif differ diff --git a/res/ui/interactive_tutorial.noinline/2_3_more_cutters.gif b/res/ui/interactive_tutorial.noinline/2_3_more_cutters.gif new file mode 100644 index 00000000..50ce88f9 Binary files /dev/null and b/res/ui/interactive_tutorial.noinline/2_3_more_cutters.gif differ diff --git a/res/ui/interactive_tutorial.noinline/3_1_rectangles.gif b/res/ui/interactive_tutorial.noinline/3_1_rectangles.gif new file mode 100644 index 00000000..418d3123 Binary files /dev/null and b/res/ui/interactive_tutorial.noinline/3_1_rectangles.gif differ diff --git a/res/ui/languages/da.svg b/res/ui/languages/da.svg index ea9d950a..b6ace9ab 100644 --- a/res/ui/languages/da.svg +++ b/res/ui/languages/da.svg @@ -1,36 +1,38 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res_raw/sprites/blueprints/balancer-merger-inverse.png b/res_raw/sprites/blueprints/balancer-merger-inverse.png index db528f32..3940e0a6 100644 Binary files a/res_raw/sprites/blueprints/balancer-merger-inverse.png and b/res_raw/sprites/blueprints/balancer-merger-inverse.png differ diff --git a/res_raw/sprites/blueprints/balancer-merger.png b/res_raw/sprites/blueprints/balancer-merger.png index 3e3c81f7..a9554f33 100644 Binary files a/res_raw/sprites/blueprints/balancer-merger.png and b/res_raw/sprites/blueprints/balancer-merger.png differ diff --git a/res_raw/sprites/blueprints/balancer-splitter-inverse.png b/res_raw/sprites/blueprints/balancer-splitter-inverse.png index 90a429c0..c9c4c4c9 100644 Binary files a/res_raw/sprites/blueprints/balancer-splitter-inverse.png and b/res_raw/sprites/blueprints/balancer-splitter-inverse.png differ diff --git a/res_raw/sprites/blueprints/balancer-splitter.png b/res_raw/sprites/blueprints/balancer-splitter.png index 93488fd9..3d59d05f 100644 Binary files a/res_raw/sprites/blueprints/balancer-splitter.png and b/res_raw/sprites/blueprints/balancer-splitter.png differ diff --git a/res_raw/sprites/blueprints/logic_gate-or.png b/res_raw/sprites/blueprints/logic_gate-or.png index f58c3656..e8b4573a 100644 Binary files a/res_raw/sprites/blueprints/logic_gate-or.png and b/res_raw/sprites/blueprints/logic_gate-or.png differ diff --git a/res_raw/sprites/blueprints/logic_gate-xor.png b/res_raw/sprites/blueprints/logic_gate-xor.png index 97823a7a..9a711fe4 100644 Binary files a/res_raw/sprites/blueprints/logic_gate-xor.png and b/res_raw/sprites/blueprints/logic_gate-xor.png differ diff --git a/res_raw/sprites/blueprints/logic_gate.png b/res_raw/sprites/blueprints/logic_gate.png index de59fa57..5b492203 100644 Binary files a/res_raw/sprites/blueprints/logic_gate.png and b/res_raw/sprites/blueprints/logic_gate.png differ diff --git a/res_raw/sprites/blueprints/underground_belt_entry-tier2.png b/res_raw/sprites/blueprints/underground_belt_entry-tier2.png index 9a4beb66..796dff59 100644 Binary files a/res_raw/sprites/blueprints/underground_belt_entry-tier2.png and b/res_raw/sprites/blueprints/underground_belt_entry-tier2.png differ diff --git a/res_raw/sprites/blueprints/underground_belt_entry.png b/res_raw/sprites/blueprints/underground_belt_entry.png index aa237b6d..f9ad0ba5 100644 Binary files a/res_raw/sprites/blueprints/underground_belt_entry.png and b/res_raw/sprites/blueprints/underground_belt_entry.png differ diff --git a/res_raw/sprites/blueprints/underground_belt_exit-tier2.png b/res_raw/sprites/blueprints/underground_belt_exit-tier2.png index b9c97b75..be78107b 100644 Binary files a/res_raw/sprites/blueprints/underground_belt_exit-tier2.png and b/res_raw/sprites/blueprints/underground_belt_exit-tier2.png differ diff --git a/res_raw/sprites/blueprints/underground_belt_exit.png b/res_raw/sprites/blueprints/underground_belt_exit.png index 760f63a9..9f02504d 100644 Binary files a/res_raw/sprites/blueprints/underground_belt_exit.png and b/res_raw/sprites/blueprints/underground_belt_exit.png differ diff --git a/res_raw/sprites/blueprints/virtual_processor-rotater.png b/res_raw/sprites/blueprints/virtual_processor-rotater.png index cf283717..1cf0e901 100644 Binary files a/res_raw/sprites/blueprints/virtual_processor-rotater.png and b/res_raw/sprites/blueprints/virtual_processor-rotater.png differ diff --git a/res_raw/sprites/buildings/balancer-merger-inverse.png b/res_raw/sprites/buildings/balancer-merger-inverse.png index 1d9bd4c9..ff9c3833 100644 Binary files a/res_raw/sprites/buildings/balancer-merger-inverse.png and b/res_raw/sprites/buildings/balancer-merger-inverse.png differ diff --git a/res_raw/sprites/buildings/balancer-merger.png b/res_raw/sprites/buildings/balancer-merger.png index f0f80a0b..7101c824 100644 Binary files a/res_raw/sprites/buildings/balancer-merger.png and b/res_raw/sprites/buildings/balancer-merger.png differ diff --git a/res_raw/sprites/buildings/balancer-splitter-inverse.png b/res_raw/sprites/buildings/balancer-splitter-inverse.png index 704663c0..4a765fa4 100644 Binary files a/res_raw/sprites/buildings/balancer-splitter-inverse.png and b/res_raw/sprites/buildings/balancer-splitter-inverse.png differ diff --git a/res_raw/sprites/buildings/balancer-splitter.png b/res_raw/sprites/buildings/balancer-splitter.png index d5a72dd4..e8fb62be 100644 Binary files a/res_raw/sprites/buildings/balancer-splitter.png and b/res_raw/sprites/buildings/balancer-splitter.png differ diff --git a/res_raw/sprites/buildings/logic_gate-or.png b/res_raw/sprites/buildings/logic_gate-or.png index 1b9c9e34..f2995a86 100644 Binary files a/res_raw/sprites/buildings/logic_gate-or.png and b/res_raw/sprites/buildings/logic_gate-or.png differ diff --git a/res_raw/sprites/buildings/logic_gate-xor.png b/res_raw/sprites/buildings/logic_gate-xor.png index c7d43a58..3828e4a0 100644 Binary files a/res_raw/sprites/buildings/logic_gate-xor.png and b/res_raw/sprites/buildings/logic_gate-xor.png differ diff --git a/res_raw/sprites/buildings/logic_gate.png b/res_raw/sprites/buildings/logic_gate.png index 2d206aa3..aa0ad6fd 100644 Binary files a/res_raw/sprites/buildings/logic_gate.png and b/res_raw/sprites/buildings/logic_gate.png differ diff --git a/res_raw/sprites/buildings/underground_belt_entry-tier2.png b/res_raw/sprites/buildings/underground_belt_entry-tier2.png index 3a30f4b5..96dd4f66 100644 Binary files a/res_raw/sprites/buildings/underground_belt_entry-tier2.png and b/res_raw/sprites/buildings/underground_belt_entry-tier2.png differ diff --git a/res_raw/sprites/buildings/underground_belt_entry.png b/res_raw/sprites/buildings/underground_belt_entry.png index 3b771f60..c1e4c4e8 100644 Binary files a/res_raw/sprites/buildings/underground_belt_entry.png and b/res_raw/sprites/buildings/underground_belt_entry.png differ diff --git a/res_raw/sprites/buildings/underground_belt_exit-tier2.png b/res_raw/sprites/buildings/underground_belt_exit-tier2.png index 87c59bf3..2ee15c61 100644 Binary files a/res_raw/sprites/buildings/underground_belt_exit-tier2.png and b/res_raw/sprites/buildings/underground_belt_exit-tier2.png differ diff --git a/res_raw/sprites/buildings/underground_belt_exit.png b/res_raw/sprites/buildings/underground_belt_exit.png index c281ed0e..9614ae11 100644 Binary files a/res_raw/sprites/buildings/underground_belt_exit.png and b/res_raw/sprites/buildings/underground_belt_exit.png differ diff --git a/res_raw/sprites/buildings/virtual_processor-rotater.png b/res_raw/sprites/buildings/virtual_processor-rotater.png index 327e8e13..6cad3244 100644 Binary files a/res_raw/sprites/buildings/virtual_processor-rotater.png and b/res_raw/sprites/buildings/virtual_processor-rotater.png differ diff --git a/src/css/ingame_hud/buildings_toolbar.scss b/src/css/ingame_hud/buildings_toolbar.scss index a20626af..54205d64 100644 --- a/src/css/ingame_hud/buildings_toolbar.scss +++ b/src/css/ingame_hud/buildings_toolbar.scss @@ -27,7 +27,7 @@ @include S(border-radius, $globalBorderRadius); @include DarkThemeOverride { - background-color: rgba(darken($darkModeGameBackground, 15), 0.4); + background-color: rgba(darken($darkModeGameBackground, 15), 0.95); } &.secondary { diff --git a/src/css/ingame_hud/pinned_shapes.scss b/src/css/ingame_hud/pinned_shapes.scss index 8ff13fe7..671f5aa5 100644 --- a/src/css/ingame_hud/pinned_shapes.scss +++ b/src/css/ingame_hud/pinned_shapes.scss @@ -17,13 +17,10 @@ grid-template-rows: 1fr 1fr; @include S(margin-bottom, 4px); color: #333438; - // text-shadow: #{D(1px)} #{D(1px)} 0 rgba(0, 10, 20, 0.2); - &.unpinable { - > canvas { - cursor: pointer; - pointer-events: all; - } + &.removable { + cursor: pointer; + pointer-events: all; } > canvas { @@ -31,16 +28,9 @@ @include S(height, 25px); grid-column: 1 / 2; grid-row: 1 / 3; - pointer-events: all; - transition: transform 0.1s ease-in-out; - transform-origin: D(2px) center; - will-change: transform; - position: relative; + pointer-events: none; z-index: 20; - &:hover { - transform: scale(2); - z-index: 21; - } + position: relative; } > .amountLabel, diff --git a/src/css/main.scss b/src/css/main.scss index 5c4686e9..4f6a771e 100644 --- a/src/css/main.scss +++ b/src/css/main.scss @@ -73,8 +73,8 @@ ingame_HUD_KeybindingOverlay, ingame_HUD_Notifications, ingame_HUD_DebugInfo, ingame_HUD_EntityDebugger, -ingame_HUD_InteractiveTutorial, ingame_HUD_TutorialHints, +ingame_HUD_InteractiveTutorial, ingame_HUD_BuildingsToolbar, ingame_HUD_wires_toolbar, ingame_HUD_BlueprintPlacer, diff --git a/src/js/application.js b/src/js/application.js index 1a8ca21f..d9ca7641 100644 --- a/src/js/application.js +++ b/src/js/application.js @@ -29,6 +29,7 @@ import { MobileWarningState } from "./states/mobile_warning"; import { PreloadState } from "./states/preload"; import { SettingsState } from "./states/settings"; import { ShapezGameAnalytics } from "./platform/browser/game_analytics"; +import { RestrictionManager } from "./core/restriction_manager"; /** * @typedef {import("./platform/game_analytics").GameAnalyticsInterface} GameAnalyticsInterface @@ -70,6 +71,9 @@ export class Application { this.inputMgr = new InputDistributor(this); this.backgroundResourceLoader = new BackgroundResourcesLoader(this); + // Restrictions (Like demo etc) + this.restrictionMgr = new RestrictionManager(this); + // Platform dependent stuff /** @type {StorageInterface} */ diff --git a/src/js/core/config.js b/src/js/core/config.js index c01f7c76..8b4c64c2 100644 --- a/src/js/core/config.js +++ b/src/js/core/config.js @@ -1,5 +1,3 @@ -import { queryParamOptions } from "./query_parameters"; - export const IS_DEBUG = G_IS_DEV && typeof window !== "undefined" && @@ -7,13 +5,10 @@ export const IS_DEBUG = (window.location.host.indexOf("localhost:") >= 0 || window.location.host.indexOf("192.168.0.") >= 0) && window.location.search.indexOf("nodebug") < 0; -export const IS_DEMO = queryParamOptions.fullVersion - ? false - : (!G_IS_DEV && !G_IS_STANDALONE) || - (typeof window !== "undefined" && window.location.search.indexOf("demo") >= 0); - export const SUPPORT_TOUCH = false; +export const IS_MAC = navigator.platform.toLowerCase().indexOf("mac") >= 0; + const smoothCanvas = true; export const THIRDPARTY_URLS = { @@ -64,7 +59,7 @@ export const globalConfig = { undergroundBeltMaxTilesByTier: [5, 9], - readerAnalyzeIntervalSeconds: G_IS_DEV ? 3 : 10, + readerAnalyzeIntervalSeconds: 10, buildingSpeeds: { cutter: 1 / 4, @@ -137,3 +132,8 @@ if (G_IS_DEV && globalConfig.debug.renderForTrailer) { if (globalConfig.debug.fastGameEnter) { globalConfig.debug.noArtificialDelays = true; } + +if (G_IS_DEV && globalConfig.debug.noArtificialDelays) { + globalConfig.warmupTimeSecondsFast = 0; + globalConfig.warmupTimeSecondsRegular = 0; +} diff --git a/src/js/core/modal_dialog_elements.js b/src/js/core/modal_dialog_elements.js index 934e2de5..5f0ed59f 100644 --- a/src/js/core/modal_dialog_elements.js +++ b/src/js/core/modal_dialog_elements.js @@ -13,6 +13,17 @@ import { getStringForKeyCode } from "../game/key_action_mapper"; import { createLogger } from "./logging"; import { T } from "../translations"; +/* + * *************************************************** + * + * LEGACY CODE WARNING + * + * This is old code from yorg3.io and needs to be refactored + * @TODO + * + * *************************************************** + */ + const kbEnter = 13; const kbCancel = 27; diff --git a/src/js/core/modal_dialog_forms.js b/src/js/core/modal_dialog_forms.js index 58fd2543..1c5b1986 100644 --- a/src/js/core/modal_dialog_forms.js +++ b/src/js/core/modal_dialog_forms.js @@ -2,6 +2,17 @@ import { BaseItem } from "../game/base_item"; import { ClickDetector } from "./click_detector"; import { Signal } from "./signal"; +/* + * *************************************************** + * + * LEGACY CODE WARNING + * + * This is old code from yorg3.io and needs to be refactored + * @TODO + * + * *************************************************** + */ + export class FormElement { constructor(id, label) { this.id = id; diff --git a/src/js/core/read_write_proxy.js b/src/js/core/read_write_proxy.js index 6d26fa2b..7c96149b 100644 --- a/src/js/core/read_write_proxy.js +++ b/src/js/core/read_write_proxy.js @@ -81,10 +81,6 @@ export class ReadWriteProxy { return this.writeAsync(); } - getCurrentData() { - return this.currentData; - } - /** * * @param {object} obj diff --git a/src/js/core/restriction_manager.js b/src/js/core/restriction_manager.js new file mode 100644 index 00000000..fb34acb8 --- /dev/null +++ b/src/js/core/restriction_manager.js @@ -0,0 +1,155 @@ +/* typehints:start */ +import { Application } from "../application"; +/* typehints:end */ +import { IS_MAC } from "./config"; +import { ExplainedResult } from "./explained_result"; +import { queryParamOptions } from "./query_parameters"; +import { ReadWriteProxy } from "./read_write_proxy"; + +export class RestrictionManager extends ReadWriteProxy { + /** + * @param {Application} app + */ + constructor(app) { + super(app, "restriction-flags.bin"); + + this.currentData = this.getDefaultData(); + } + + // -- RW Proxy Impl + + /** + * @param {any} data + */ + verify(data) { + return ExplainedResult.good(); + } + + /** + */ + getDefaultData() { + return { + version: this.getCurrentVersion(), + savegameV1119Imported: false, + }; + } + + /** + */ + getCurrentVersion() { + return 1; + } + + /** + * @param {any} data + */ + migrate(data) { + // Todo + return ExplainedResult.good(); + } + + initialize() { + return this.readAsync().then(() => { + if (this.currentData.savegameV1119Imported) { + console.warn("Levelunlock is granted to current user due to past savegame"); + } + }); + } + + // -- End RW Proxy Impl + + /** + * Checks if there are any savegames from the 1.1.19 version + */ + onHasLegacySavegamesChanged(has119Savegames = false) { + if (has119Savegames && !this.currentData.savegameV1119Imported) { + this.currentData.savegameV1119Imported = true; + console.warn("Current user now has access to all levels due to 1119 savegame"); + return this.writeAsync(); + } + return Promise.resolve(); + } + + /** + * Returns if the app is currently running as the limited version + * @returns {boolean} + */ + isLimitedVersion() { + if (IS_MAC) { + // On mac, the full version is always active + return false; + } + + if (G_IS_STANDALONE) { + // Standalone is never limited + return false; + } + + if (queryParamOptions.fullVersion) { + // Full version is activated via flag + return false; + } + + if (G_IS_DEV) { + return typeof window !== "undefined" && window.location.search.indexOf("demo") >= 0; + } + + return true; + } + + /** + * Returns if the app markets the standalone version on steam + * @returns {boolean} + */ + getIsStandaloneMarketingActive() { + return this.isLimitedVersion(); + } + + /** + * Returns if exporting the base as a screenshot is possible + * @returns {boolean} + */ + getIsExportingScreenshotsPossible() { + return !this.isLimitedVersion(); + } + + /** + * Returns the maximum number of supported waypoints + * @returns {number} + */ + getMaximumWaypoints() { + return this.isLimitedVersion() ? 2 : 1e20; + } + + /** + * Returns if the user has unlimited savegames + * @returns {boolean} + */ + getHasUnlimitedSavegames() { + return !this.isLimitedVersion(); + } + + /** + * Returns if the app has all settings available + * @returns {boolean} + */ + getHasExtendedSettings() { + return !this.isLimitedVersion(); + } + + /** + * Returns if all upgrades are available + * @returns {boolean} + */ + getHasExtendedUpgrades() { + return !this.isLimitedVersion() || this.currentData.savegameV1119Imported; + } + + /** + * Returns if all levels & freeplay is available + * @returns {boolean} + */ + getHasExtendedLevelsAndFreeplay() { + return !this.isLimitedVersion() || this.currentData.savegameV1119Imported; + } +} diff --git a/src/js/core/rng.js b/src/js/core/rng.js index 9c5c1c43..7a6766cd 100644 --- a/src/js/core/rng.js +++ b/src/js/core/rng.js @@ -108,17 +108,6 @@ export class RandomNumberGenerator { assert(max > min, "rng: max <= min"); return Math.floor(this.next() * (max - min) + min); } - /** - * @param {number} min - * @param {number} max - * @returns {number} Integer in range [min, max] - */ - nextIntRangeInclusive(min, max) { - assert(Number.isFinite(min), "Minimum is no integer"); - assert(Number.isFinite(max), "Maximum is no integer"); - assert(max > min, "rng: max <= min"); - return Math.round(this.next() * (max - min) + min); - } /** * @param {number} min diff --git a/src/js/core/utils.js b/src/js/core/utils.js index 30573c81..98327378 100644 --- a/src/js/core/utils.js +++ b/src/js/core/utils.js @@ -681,3 +681,72 @@ export function fillInLinkIntoTranslation(translation, link) { .replace("", "") .replace("", ""); } + +/** + * Generates a file download + * @param {string} filename + * @param {string} text + */ +export function generateFileDownload(filename, text) { + var element = document.createElement("a"); + element.setAttribute("href", "data:text/plain;charset=utf-8," + encodeURIComponent(text)); + element.setAttribute("download", filename); + + element.style.display = "none"; + document.body.appendChild(element); + + element.click(); + document.body.removeChild(element); +} + +/** + * Starts a file chooser + * @param {string} acceptedType + */ +export function startFileChoose(acceptedType = ".bin") { + var input = document.createElement("input"); + input.type = "file"; + input.accept = acceptedType; + + return new Promise(resolve => { + input.onchange = _ => resolve(input.files[0]); + input.click(); + }); +} + +const romanLiterals = [ + "0", // NULL + "I", + "II", + "III", + "IV", + "V", + "VI", + "VII", + "VIII", + "IX", + "X", + "XI", + "XII", + "XIII", + "XIV", + "XV", + "XVI", + "XVII", + "XVIII", + "XIX", + "XX", +]; + +/** + * + * @param {number} number + * @returns {string} + */ +export function getRomanNumber(number) { + number = Math.max(0, Math.round(number)); + if (number < romanLiterals.length) { + return romanLiterals[number]; + } + return String(number); +} diff --git a/src/js/game/belt_path.js b/src/js/game/belt_path.js index b162f09f..eb55d613 100644 --- a/src/js/game/belt_path.js +++ b/src/js/game/belt_path.js @@ -1111,7 +1111,7 @@ export class BeltPath extends BasicSerializableObject { isFirstItemProcessed = false; this.spacingToFirstItem += clampedProgress; - if (remainingVelocity < 0.01) { + if (remainingVelocity < 1e-7) { break; } } diff --git a/src/js/game/blueprint.js b/src/js/game/blueprint.js index c26fb12f..63989393 100644 --- a/src/js/game/blueprint.js +++ b/src/js/game/blueprint.js @@ -1,13 +1,9 @@ import { globalConfig } from "../core/config"; import { DrawParameters } from "../core/draw_parameters"; -import { createLogger } from "../core/logging"; import { findNiceIntegerValue } from "../core/utils"; import { Vector } from "../core/vector"; import { Entity } from "./entity"; import { GameRoot } from "./root"; -import { blueprintShape } from "./upgrades"; - -const logger = createLogger("blueprint"); export class Blueprint { /** @@ -142,7 +138,7 @@ export class Blueprint { * @param {GameRoot} root */ canAfford(root) { - return root.hubGoals.getShapesStoredByKey(blueprintShape) >= this.getCost(); + return root.hubGoals.getShapesStoredByKey(root.gameMode.getBlueprintShapeKey()) >= this.getCost(); } /** diff --git a/src/js/game/buildings/belt.js b/src/js/game/buildings/belt.js index 562b47d5..84646b19 100644 --- a/src/js/game/buildings/belt.js +++ b/src/js/game/buildings/belt.js @@ -7,6 +7,7 @@ import { BeltComponent } from "../components/belt"; import { Entity } from "../entity"; import { MetaBuilding } from "../meta_building"; import { GameRoot } from "../root"; +import { THEME } from "../theme"; export const arrayBeltVariantToRotation = [enumDirection.top, enumDirection.left, enumDirection.right]; @@ -22,7 +23,7 @@ export class MetaBeltBuilding extends MetaBuilding { } getSilhouetteColor() { - return "#777"; + return THEME.map.chunkOverview.beltColor; } getPlacementSound() { diff --git a/src/js/game/buildings/virtual_processor.js b/src/js/game/buildings/virtual_processor.js index 79d65b22..b4f91762 100644 --- a/src/js/game/buildings/virtual_processor.js +++ b/src/js/game/buildings/virtual_processor.js @@ -110,12 +110,7 @@ export class MetaVirtualProcessorBuilding extends MetaBuilding { pinComp.setSlots([ { pos: new Vector(0, 0), - direction: enumDirection.left, - type: enumPinSlotType.logicalEjector, - }, - { - pos: new Vector(0, 0), - direction: enumDirection.right, + direction: enumDirection.top, type: enumPinSlotType.logicalEjector, }, { diff --git a/src/js/game/camera.js b/src/js/game/camera.js index 8f5b850e..107d1fb4 100644 --- a/src/js/game/camera.js +++ b/src/js/game/camera.js @@ -511,7 +511,11 @@ export class Camera extends BasicSerializableObject { this.clampZoomLevel(); this.desiredZoom = null; - const mousePosition = this.root.app.mousePosition; + let mousePosition = this.root.app.mousePosition; + if (!this.root.app.settings.getAllSettings().zoomToCursor) { + mousePosition = new Vector(this.root.gameWidth / 2, this.root.gameHeight / 2); + } + if (mousePosition) { const worldPos = this.root.camera.screenToWorld(mousePosition); const worldDelta = worldPos.sub(this.center); diff --git a/src/js/game/components/item_ejector.js b/src/js/game/components/item_ejector.js index 4eda52f9..47253b4b 100644 --- a/src/js/game/components/item_ejector.js +++ b/src/js/game/components/item_ejector.js @@ -26,7 +26,7 @@ export class ItemEjectorComponent extends Component { static getSchema() { // The cachedDestSlot, cachedTargetEntity fields are not serialized. return { - slots: types.array( + slots: types.fixedSizeArray( types.structured({ item: types.nullable(typeItemSingleton), progress: types.float, diff --git a/src/js/game/components/wired_pins.js b/src/js/game/components/wired_pins.js index ff339b86..81a6ec62 100644 --- a/src/js/game/components/wired_pins.js +++ b/src/js/game/components/wired_pins.js @@ -31,7 +31,7 @@ export class WiredPinsComponent extends Component { static getSchema() { return { - slots: types.array( + slots: types.fixedSizeArray( types.structured({ value: types.nullable(typeItemSingleton), }) diff --git a/src/js/game/core.js b/src/js/game/core.js index 306643f9..2df8989f 100644 --- a/src/js/game/core.js +++ b/src/js/game/core.js @@ -31,6 +31,7 @@ import { KeyActionMapper } from "./key_action_mapper"; import { GameLogic } from "./logic"; import { MapView } from "./map_view"; import { defaultBuildingVariant } from "./meta_building"; +import { RegularGameMode } from "./modes/regular"; import { ProductionAnalytics } from "./production_analytics"; import { GameRoot } from "./root"; import { ShapeDefinitionManager } from "./shape_definition_manager"; @@ -101,6 +102,9 @@ export class GameCore { // Needs to come first root.dynamicTickrate = new DynamicTickrate(root); + // Init game mode + root.gameMode = new RegularGameMode(root); + // Init classes root.camera = new Camera(root); root.map = new MapView(root); diff --git a/src/js/game/game_mode.js b/src/js/game/game_mode.js new file mode 100644 index 00000000..15403eb5 --- /dev/null +++ b/src/js/game/game_mode.js @@ -0,0 +1,71 @@ +/* typehints:start */ +import { enumHubGoalRewards } from "./tutorial_goals"; +/* typehints:end */ + +import { GameRoot } from "./root"; + +/** @typedef {{ + * shape: string, + * amount: number + * }} UpgradeRequirement */ + +/** @typedef {{ + * required: Array + * improvement?: number, + * excludePrevious?: boolean + * }} TierRequirement */ + +/** @typedef {Array} UpgradeTiers */ + +/** @typedef {{ + * shape: string, + * required: number, + * reward: enumHubGoalRewards, + * throughputOnly?: boolean + * }} LevelDefinition */ + +export class GameMode { + /** + * + * @param {GameRoot} root + */ + constructor(root) { + this.root = root; + } + + /** + * Should return all available upgrades + * @returns {Object} + */ + getUpgrades() { + abstract; + return null; + } + + /** + * Returns the blueprint shape key + * @returns {string} + */ + getBlueprintShapeKey() { + abstract; + return null; + } + + /** + * Returns the goals for all levels including their reward + * @returns {Array} + */ + getLevelDefinitions() { + abstract; + return null; + } + + /** + * Should return whether free play is available or if the game stops + * after the predefined levels + * @returns {boolean} + */ + getIsFreeplayAvailable() { + return true; + } +} diff --git a/src/js/game/hub_goals.js b/src/js/game/hub_goals.js index e01ab868..c9d9494f 100644 --- a/src/js/game/hub_goals.js +++ b/src/js/game/hub_goals.js @@ -1,14 +1,13 @@ -import { globalConfig, IS_DEMO } from "../core/config"; +import { globalConfig } from "../core/config"; import { RandomNumberGenerator } from "../core/rng"; -import { clamp, findNiceIntegerValue, randomChoice, randomInt } from "../core/utils"; +import { clamp } from "../core/utils"; import { BasicSerializableObject, types } from "../savegame/serialization"; import { enumColors } from "./colors"; import { enumItemProcessorTypes } from "./components/item_processor"; import { enumAnalyticsDataSource } from "./production_analytics"; import { GameRoot } from "./root"; import { enumSubShape, ShapeDefinition } from "./shape_definition"; -import { enumHubGoalRewards, tutorialGoals } from "./tutorial_goals"; -import { UPGRADES } from "./upgrades"; +import { enumHubGoalRewards } from "./tutorial_goals"; export class HubGoals extends BasicSerializableObject { static getId() { @@ -23,27 +22,36 @@ export class HubGoals extends BasicSerializableObject { }; } - deserialize(data) { + /** + * + * @param {*} data + * @param {GameRoot} root + */ + deserialize(data, root) { const errorCode = super.deserialize(data); if (errorCode) { return errorCode; } - if (IS_DEMO) { - this.level = Math.min(this.level, tutorialGoals.length); + const levels = root.gameMode.getLevelDefinitions(); + + // If freeplay is not available, clamp the level + if (!root.gameMode.getIsFreeplayAvailable()) { + this.level = Math.min(this.level, levels.length); } // Compute gained rewards for (let i = 0; i < this.level - 1; ++i) { - if (i < tutorialGoals.length) { - const reward = tutorialGoals[i].reward; + if (i < levels.length) { + const reward = levels[i].reward; this.gainedRewards[reward] = (this.gainedRewards[reward] || 0) + 1; } } // Compute upgrade improvements - for (const upgradeId in UPGRADES) { - const tiers = UPGRADES[upgradeId]; + const upgrades = this.root.gameMode.getUpgrades(); + for (const upgradeId in upgrades) { + const tiers = upgrades[upgradeId]; const level = this.upgradeLevels[upgradeId] || 0; let totalImprovement = 1; for (let i = 0; i < level; ++i) { @@ -84,17 +92,16 @@ export class HubGoals extends BasicSerializableObject { */ this.upgradeLevels = {}; - // Reset levels - for (const key in UPGRADES) { - this.upgradeLevels[key] = 0; - } - /** * Stores the improvements for all upgrades * @type {Object} */ this.upgradeImprovements = {}; - for (const key in UPGRADES) { + + // Reset levels first + const upgrades = this.root.gameMode.getUpgrades(); + for (const key in upgrades) { + this.upgradeLevels[key] = 0; this.upgradeImprovements[key] = 1; } @@ -120,7 +127,10 @@ export class HubGoals extends BasicSerializableObject { * @returns {boolean} */ isEndOfDemoReached() { - return IS_DEMO && this.level >= tutorialGoals.length; + return ( + !this.root.gameMode.getIsFreeplayAvailable() && + this.level >= this.root.gameMode.getLevelDefinitions().length + ); } /** @@ -215,8 +225,9 @@ export class HubGoals extends BasicSerializableObject { */ computeNextGoal() { const storyIndex = this.level - 1; - if (storyIndex < tutorialGoals.length) { - const { shape, required, reward, throughputOnly } = tutorialGoals[storyIndex]; + const levels = this.root.gameMode.getLevelDefinitions(); + if (storyIndex < levels.length) { + const { shape, required, reward, throughputOnly } = levels[storyIndex]; this.currentGoal = { /** @type {ShapeDefinition} */ definition: this.root.shapeDefinitionMgr.getShapeFromShortKey(shape), @@ -254,7 +265,7 @@ export class HubGoals extends BasicSerializableObject { * Returns whether we are playing in free-play */ isFreePlay() { - return this.level >= tutorialGoals.length; + return this.level >= this.root.gameMode.getLevelDefinitions().length; } /** @@ -262,7 +273,7 @@ export class HubGoals extends BasicSerializableObject { * @param {string} upgradeId */ canUnlockUpgrade(upgradeId) { - const tiers = UPGRADES[upgradeId]; + const tiers = this.root.gameMode.getUpgrades()[upgradeId]; const currentLevel = this.getUpgradeLevel(upgradeId); if (currentLevel >= tiers.length) { @@ -270,11 +281,6 @@ export class HubGoals extends BasicSerializableObject { return false; } - if (IS_DEMO && currentLevel >= 4) { - // DEMO - return false; - } - if (G_IS_DEV && globalConfig.debug.upgradesNoCost) { return true; } @@ -296,7 +302,7 @@ export class HubGoals extends BasicSerializableObject { */ getAvailableUpgradeCount() { let count = 0; - for (const upgradeId in UPGRADES) { + for (const upgradeId in this.root.gameMode.getUpgrades()) { if (this.canUnlockUpgrade(upgradeId)) { ++count; } @@ -314,7 +320,7 @@ export class HubGoals extends BasicSerializableObject { return false; } - const upgradeTiers = UPGRADES[upgradeId]; + const upgradeTiers = this.root.gameMode.getUpgrades()[upgradeId]; const currentLevel = this.getUpgradeLevel(upgradeId); const tierData = upgradeTiers[currentLevel]; @@ -363,7 +369,7 @@ export class HubGoals extends BasicSerializableObject { if (allowUncolored) { universalColors.push(enumColors.uncolored); } - const index = rng.nextIntRangeInclusive(0, colorWheel.length - 3); + const index = rng.nextIntRange(0, colorWheel.length - 2); const pickedColors = colorWheel.slice(index, index + 3); pickedColors.push(rng.choice(universalColors)); return pickedColors; diff --git a/src/js/game/hud/hud.js b/src/js/game/hud/hud.js index 9033b3cc..a9edffd0 100644 --- a/src/js/game/hud/hud.js +++ b/src/js/game/hud/hud.js @@ -15,7 +15,7 @@ import { HUDKeybindingOverlay } from "./parts/keybinding_overlay"; import { HUDUnlockNotification } from "./parts/unlock_notification"; import { HUDGameMenu } from "./parts/game_menu"; import { HUDShop } from "./parts/shop"; -import { IS_MOBILE, globalConfig, IS_DEMO } from "../../core/config"; +import { IS_MOBILE, globalConfig } from "../../core/config"; import { HUDMassSelector } from "./parts/mass_selector"; import { HUDVignetteOverlay } from "./parts/vignette_overlay"; import { HUDStatistics } from "./parts/statistics"; @@ -45,7 +45,6 @@ import { HUDLeverToggle } from "./parts/lever_toggle"; import { HUDLayerPreview } from "./parts/layer_preview"; import { HUDMinerHighlight } from "./parts/miner_highlight"; import { HUDBetaOverlay } from "./parts/beta_overlay"; -import { HUDPerformanceWarning } from "./parts/performance_warning"; import { HUDStandaloneAdvantages } from "./parts/standalone_advantages"; import { HUDCatMemes } from "./parts/cat_memes"; @@ -88,7 +87,6 @@ export class GameHUD { layerPreview: new HUDLayerPreview(this.root), minerHighlight: new HUDMinerHighlight(this.root), - performanceWarning: new HUDPerformanceWarning(this.root), // Typing hints /* typehints:start */ @@ -116,7 +114,7 @@ export class GameHUD { this.parts.entityDebugger = new HUDEntityDebugger(this.root); } - if (IS_DEMO) { + if (this.root.app.restrictionMgr.getIsStandaloneMarketingActive()) { this.parts.watermark = new HUDWatermark(this.root); this.parts.standaloneAdvantages = new HUDStandaloneAdvantages(this.root); this.parts.catMemes = new HUDCatMemes(this.root); diff --git a/src/js/game/hud/parts/beta_overlay.js b/src/js/game/hud/parts/beta_overlay.js index 1b2fa259..3a515d29 100644 --- a/src/js/game/hud/parts/beta_overlay.js +++ b/src/js/game/hud/parts/beta_overlay.js @@ -7,7 +7,7 @@ export class HUDBetaOverlay extends BaseHUDPart { parent, "ingame_HUD_BetaOverlay", [], - "

UNSTABLE BETA VERSION

Steam Release: 9th October 2020!" + "

UNSTABLE BETA VERSION

Unfinalized & potential buggy content!" ); } diff --git a/src/js/game/hud/parts/blueprint_placer.js b/src/js/game/hud/parts/blueprint_placer.js index 47bf1363..e1040c3b 100644 --- a/src/js/game/hud/parts/blueprint_placer.js +++ b/src/js/game/hud/parts/blueprint_placer.js @@ -1,202 +1,203 @@ -import { DrawParameters } from "../../../core/draw_parameters"; -import { STOP_PROPAGATION } from "../../../core/signal"; -import { TrackedState } from "../../../core/tracked_state"; -import { makeDiv } from "../../../core/utils"; -import { Vector } from "../../../core/vector"; -import { T } from "../../../translations"; -import { enumMouseButton } from "../../camera"; -import { KEYMAPPINGS } from "../../key_action_mapper"; -import { blueprintShape } from "../../upgrades"; -import { BaseHUDPart } from "../base_hud_part"; -import { DynamicDomAttach } from "../dynamic_dom_attach"; -import { Blueprint } from "../../blueprint"; -import { SOUNDS } from "../../../platform/sound"; - -export class HUDBlueprintPlacer extends BaseHUDPart { - createElements(parent) { - const blueprintCostShape = this.root.shapeDefinitionMgr.getShapeFromShortKey(blueprintShape); - const blueprintCostShapeCanvas = blueprintCostShape.generateAsCanvas(80); - - this.costDisplayParent = makeDiv(parent, "ingame_HUD_BlueprintPlacer", [], ``); - - makeDiv(this.costDisplayParent, null, ["label"], T.ingame.blueprintPlacer.cost); - const costContainer = makeDiv(this.costDisplayParent, null, ["costContainer"], ""); - this.costDisplayText = makeDiv(costContainer, null, ["costText"], ""); - costContainer.appendChild(blueprintCostShapeCanvas); - } - - initialize() { - this.root.hud.signals.buildingsSelectedForCopy.add(this.createBlueprintFromBuildings, this); - - /** @type {TypedTrackedState} */ - this.currentBlueprint = new TrackedState(this.onBlueprintChanged, this); - /** @type {Blueprint?} */ - this.lastBlueprintUsed = null; - - const keyActionMapper = this.root.keyMapper; - keyActionMapper.getBinding(KEYMAPPINGS.general.back).add(this.abortPlacement, this); - keyActionMapper.getBinding(KEYMAPPINGS.placement.pipette).add(this.abortPlacement, this); - keyActionMapper.getBinding(KEYMAPPINGS.placement.rotateWhilePlacing).add(this.rotateBlueprint, this); - keyActionMapper.getBinding(KEYMAPPINGS.massSelect.pasteLastBlueprint).add(this.pasteBlueprint, this); - - this.root.camera.downPreHandler.add(this.onMouseDown, this); - this.root.camera.movePreHandler.add(this.onMouseMove, this); - - this.root.hud.signals.selectedPlacementBuildingChanged.add(this.abortPlacement, this); - this.root.signals.editModeChanged.add(this.onEditModeChanged, this); - - this.domAttach = new DynamicDomAttach(this.root, this.costDisplayParent); - this.trackedCanAfford = new TrackedState(this.onCanAffordChanged, this); - } - - abortPlacement() { - if (this.currentBlueprint.get()) { - this.currentBlueprint.set(null); - - return STOP_PROPAGATION; - } - } - - /** - * Called when the layer was changed - * @param {Layer} layer - */ - onEditModeChanged(layer) { - // Check if the layer of the blueprint differs and thus we have to deselect it - const blueprint = this.currentBlueprint.get(); - if (blueprint) { - if (blueprint.layer !== layer) { - this.currentBlueprint.set(null); - } - } - } - - /** - * Called when the blueprint is now affordable or not - * @param {boolean} canAfford - */ - onCanAffordChanged(canAfford) { - this.costDisplayParent.classList.toggle("canAfford", canAfford); - } - - update() { - const currentBlueprint = this.currentBlueprint.get(); - this.domAttach.update(currentBlueprint && currentBlueprint.getCost() > 0); - this.trackedCanAfford.set(currentBlueprint && currentBlueprint.canAfford(this.root)); - } - - /** - * Called when the blueprint was changed - * @param {Blueprint} blueprint - */ - onBlueprintChanged(blueprint) { - if (blueprint) { - this.lastBlueprintUsed = blueprint; - this.costDisplayText.innerText = "" + blueprint.getCost(); - } - } - - /** - * mouse down pre handler - * @param {Vector} pos - * @param {enumMouseButton} button - */ - onMouseDown(pos, button) { - if (button === enumMouseButton.right) { - if (this.currentBlueprint.get()) { - this.abortPlacement(); - return STOP_PROPAGATION; - } - } - - const blueprint = this.currentBlueprint.get(); - if (!blueprint) { - return; - } - - if (!blueprint.canAfford(this.root)) { - this.root.soundProxy.playUiError(); - return; - } - - const worldPos = this.root.camera.screenToWorld(pos); - const tile = worldPos.toTileSpace(); - if (blueprint.tryPlace(this.root, tile)) { - const cost = blueprint.getCost(); - this.root.hubGoals.takeShapeByKey(blueprintShape, cost); - this.root.soundProxy.playUi(SOUNDS.placeBuilding); - } - } - - /** - * Mose move handler - */ - onMouseMove() { - // Prevent movement while blueprint is selected - if (this.currentBlueprint.get()) { - return STOP_PROPAGATION; - } - } - - /** - * Called when an array of bulidings was selected - * @param {Array} uids - */ - createBlueprintFromBuildings(uids) { - if (uids.length === 0) { - return; - } - this.currentBlueprint.set(Blueprint.fromUids(this.root, uids)); - } - - /** - * Attempts to rotate the current blueprint - */ - rotateBlueprint() { - if (this.currentBlueprint.get()) { - if (this.root.keyMapper.getBinding(KEYMAPPINGS.placement.rotateInverseModifier).pressed) { - this.currentBlueprint.get().rotateCcw(); - } else { - this.currentBlueprint.get().rotateCw(); - } - } - } - - /** - * Attempts to paste the last blueprint - */ - pasteBlueprint() { - if (this.lastBlueprintUsed !== null) { - if (this.lastBlueprintUsed.layer !== this.root.currentLayer) { - // Not compatible - this.root.soundProxy.playUiError(); - return; - } - - this.root.hud.signals.pasteBlueprintRequested.dispatch(); - this.currentBlueprint.set(this.lastBlueprintUsed); - } else { - this.root.soundProxy.playUiError(); - } - } - - /** - * - * @param {DrawParameters} parameters - */ - draw(parameters) { - const blueprint = this.currentBlueprint.get(); - if (!blueprint) { - return; - } - const mousePosition = this.root.app.mousePosition; - if (!mousePosition) { - // Not on screen - return; - } - - const worldPos = this.root.camera.screenToWorld(mousePosition); - const tile = worldPos.toTileSpace(); - blueprint.draw(parameters, tile); - } -} +import { DrawParameters } from "../../../core/draw_parameters"; +import { STOP_PROPAGATION } from "../../../core/signal"; +import { TrackedState } from "../../../core/tracked_state"; +import { makeDiv } from "../../../core/utils"; +import { Vector } from "../../../core/vector"; +import { SOUNDS } from "../../../platform/sound"; +import { T } from "../../../translations"; +import { Blueprint } from "../../blueprint"; +import { enumMouseButton } from "../../camera"; +import { KEYMAPPINGS } from "../../key_action_mapper"; +import { BaseHUDPart } from "../base_hud_part"; +import { DynamicDomAttach } from "../dynamic_dom_attach"; + +export class HUDBlueprintPlacer extends BaseHUDPart { + createElements(parent) { + const blueprintCostShape = this.root.shapeDefinitionMgr.getShapeFromShortKey( + this.root.gameMode.getBlueprintShapeKey() + ); + const blueprintCostShapeCanvas = blueprintCostShape.generateAsCanvas(80); + + this.costDisplayParent = makeDiv(parent, "ingame_HUD_BlueprintPlacer", [], ``); + + makeDiv(this.costDisplayParent, null, ["label"], T.ingame.blueprintPlacer.cost); + const costContainer = makeDiv(this.costDisplayParent, null, ["costContainer"], ""); + this.costDisplayText = makeDiv(costContainer, null, ["costText"], ""); + costContainer.appendChild(blueprintCostShapeCanvas); + } + + initialize() { + this.root.hud.signals.buildingsSelectedForCopy.add(this.createBlueprintFromBuildings, this); + + /** @type {TypedTrackedState} */ + this.currentBlueprint = new TrackedState(this.onBlueprintChanged, this); + /** @type {Blueprint?} */ + this.lastBlueprintUsed = null; + + const keyActionMapper = this.root.keyMapper; + keyActionMapper.getBinding(KEYMAPPINGS.general.back).add(this.abortPlacement, this); + keyActionMapper.getBinding(KEYMAPPINGS.placement.pipette).add(this.abortPlacement, this); + keyActionMapper.getBinding(KEYMAPPINGS.placement.rotateWhilePlacing).add(this.rotateBlueprint, this); + keyActionMapper.getBinding(KEYMAPPINGS.massSelect.pasteLastBlueprint).add(this.pasteBlueprint, this); + + this.root.camera.downPreHandler.add(this.onMouseDown, this); + this.root.camera.movePreHandler.add(this.onMouseMove, this); + + this.root.hud.signals.selectedPlacementBuildingChanged.add(this.abortPlacement, this); + this.root.signals.editModeChanged.add(this.onEditModeChanged, this); + + this.domAttach = new DynamicDomAttach(this.root, this.costDisplayParent); + this.trackedCanAfford = new TrackedState(this.onCanAffordChanged, this); + } + + abortPlacement() { + if (this.currentBlueprint.get()) { + this.currentBlueprint.set(null); + + return STOP_PROPAGATION; + } + } + + /** + * Called when the layer was changed + * @param {Layer} layer + */ + onEditModeChanged(layer) { + // Check if the layer of the blueprint differs and thus we have to deselect it + const blueprint = this.currentBlueprint.get(); + if (blueprint) { + if (blueprint.layer !== layer) { + this.currentBlueprint.set(null); + } + } + } + + /** + * Called when the blueprint is now affordable or not + * @param {boolean} canAfford + */ + onCanAffordChanged(canAfford) { + this.costDisplayParent.classList.toggle("canAfford", canAfford); + } + + update() { + const currentBlueprint = this.currentBlueprint.get(); + this.domAttach.update(currentBlueprint && currentBlueprint.getCost() > 0); + this.trackedCanAfford.set(currentBlueprint && currentBlueprint.canAfford(this.root)); + } + + /** + * Called when the blueprint was changed + * @param {Blueprint} blueprint + */ + onBlueprintChanged(blueprint) { + if (blueprint) { + this.lastBlueprintUsed = blueprint; + this.costDisplayText.innerText = "" + blueprint.getCost(); + } + } + + /** + * mouse down pre handler + * @param {Vector} pos + * @param {enumMouseButton} button + */ + onMouseDown(pos, button) { + if (button === enumMouseButton.right) { + if (this.currentBlueprint.get()) { + this.abortPlacement(); + return STOP_PROPAGATION; + } + } + + const blueprint = this.currentBlueprint.get(); + if (!blueprint) { + return; + } + + if (!blueprint.canAfford(this.root)) { + this.root.soundProxy.playUiError(); + return; + } + + const worldPos = this.root.camera.screenToWorld(pos); + const tile = worldPos.toTileSpace(); + if (blueprint.tryPlace(this.root, tile)) { + const cost = blueprint.getCost(); + this.root.hubGoals.takeShapeByKey(this.root.gameMode.getBlueprintShapeKey(), cost); + this.root.soundProxy.playUi(SOUNDS.placeBuilding); + } + } + + /** + * Mose move handler + */ + onMouseMove() { + // Prevent movement while blueprint is selected + if (this.currentBlueprint.get()) { + return STOP_PROPAGATION; + } + } + + /** + * Called when an array of bulidings was selected + * @param {Array} uids + */ + createBlueprintFromBuildings(uids) { + if (uids.length === 0) { + return; + } + this.currentBlueprint.set(Blueprint.fromUids(this.root, uids)); + } + + /** + * Attempts to rotate the current blueprint + */ + rotateBlueprint() { + if (this.currentBlueprint.get()) { + if (this.root.keyMapper.getBinding(KEYMAPPINGS.placement.rotateInverseModifier).pressed) { + this.currentBlueprint.get().rotateCcw(); + } else { + this.currentBlueprint.get().rotateCw(); + } + } + } + + /** + * Attempts to paste the last blueprint + */ + pasteBlueprint() { + if (this.lastBlueprintUsed !== null) { + if (this.lastBlueprintUsed.layer !== this.root.currentLayer) { + // Not compatible + this.root.soundProxy.playUiError(); + return; + } + + this.root.hud.signals.pasteBlueprintRequested.dispatch(); + this.currentBlueprint.set(this.lastBlueprintUsed); + } else { + this.root.soundProxy.playUiError(); + } + } + + /** + * + * @param {DrawParameters} parameters + */ + draw(parameters) { + const blueprint = this.currentBlueprint.get(); + if (!blueprint) { + return; + } + const mousePosition = this.root.app.mousePosition; + if (!mousePosition) { + // Not on screen + return; + } + + const worldPos = this.root.camera.screenToWorld(mousePosition); + const tile = worldPos.toTileSpace(); + blueprint.draw(parameters, tile); + } +} diff --git a/src/js/game/hud/parts/interactive_tutorial.js b/src/js/game/hud/parts/interactive_tutorial.js index ffebc639..b6037eb2 100644 --- a/src/js/game/hud/parts/interactive_tutorial.js +++ b/src/js/game/hud/parts/interactive_tutorial.js @@ -6,6 +6,8 @@ import { DynamicDomAttach } from "../dynamic_dom_attach"; import { TrackedState } from "../../../core/tracked_state"; import { cachebust } from "../../../core/cachebust"; import { T } from "../../../translations"; +import { enumItemProcessorTypes, ItemProcessorComponent } from "../../components/item_processor"; +import { ShapeItem } from "../../items/shape_item"; const tutorialsByLevel = [ // Level 1 @@ -30,6 +32,68 @@ const tutorialsByLevel = [ condition: () => true, }, ], + // Level 2 + [ + // 2.1 place a cutter + { + id: "2_1_place_cutter", + condition: /** @param {GameRoot} root */ root => { + return ( + root.entityMgr + .getAllWithComponent(ItemProcessorComponent) + .filter(e => e.components.ItemProcessor.type === enumItemProcessorTypes.cutter) + .length === 0 + ); + }, + }, + // 2.2 place trash + { + id: "2_2_place_trash", + condition: /** @param {GameRoot} root */ root => { + return ( + root.entityMgr + .getAllWithComponent(ItemProcessorComponent) + .filter(e => e.components.ItemProcessor.type === enumItemProcessorTypes.trash) + .length === 0 + ); + }, + }, + // 2.3 place more cutters + { + id: "2_3_more_cutters", + condition: /** @param {GameRoot} root */ root => { + return ( + root.entityMgr + .getAllWithComponent(ItemProcessorComponent) + .filter(e => e.components.ItemProcessor.type === enumItemProcessorTypes.cutter) + .length < 3 + ); + }, + }, + ], + + // Level 2 + [ + // 3.1. rectangles + { + id: "3_1_rectangles", + condition: /** @param {GameRoot} root */ root => { + return ( + // 4 miners placed above rectangles and 10 delivered + root.hubGoals.getCurrentGoalDelivered() < 10 || + root.entityMgr.getAllWithComponent(MinerComponent).filter(entity => { + const tile = entity.components.StaticMapEntity.origin; + const below = root.map.getLowerLayerContentXY(tile.x, tile.y); + if (below && below.getItemType() === "shape") { + const shape = /** @type {ShapeItem} */ (below).definition.getHash(); + return shape === "RuRuRuRu"; + } + return false; + }).length < 4 + ); + }, + }, + ], ]; export class HUDInteractiveTutorial extends BaseHUDPart { diff --git a/src/js/game/hud/parts/performance_warning.js b/src/js/game/hud/parts/performance_warning.js deleted file mode 100644 index 4875acc8..00000000 --- a/src/js/game/hud/parts/performance_warning.js +++ /dev/null @@ -1,16 +0,0 @@ -import { T } from "../../../translations"; -import { BaseHUDPart } from "../base_hud_part"; - -export class HUDPerformanceWarning extends BaseHUDPart { - initialize() { - this.warningShown = false; - this.root.signals.entityManuallyPlaced.add(this.checkAfterPlace, this); - } - - checkAfterPlace() { - if (!this.warningShown && this.root.entityMgr.entities.length > 10000) { - this.root.hud.parts.dialogs.showInfo(T.dialogs.entityWarning.title, T.dialogs.entityWarning.desc); - this.warningShown = true; - } - } -} diff --git a/src/js/game/hud/parts/pinned_shapes.js b/src/js/game/hud/parts/pinned_shapes.js index 91663c56..542a38b2 100644 --- a/src/js/game/hud/parts/pinned_shapes.js +++ b/src/js/game/hud/parts/pinned_shapes.js @@ -1,12 +1,11 @@ import { ClickDetector } from "../../../core/click_detector"; -import { formatBigNumber, makeDiv, arrayDeleteValue } from "../../../core/utils"; -import { ShapeDefinition } from "../../shape_definition"; -import { BaseHUDPart } from "../base_hud_part"; -import { blueprintShape, UPGRADES } from "../../upgrades"; -import { enumHubGoalRewards } from "../../tutorial_goals"; -import { enumAnalyticsDataSource } from "../../production_analytics"; -import { T } from "../../../translations"; import { globalConfig } from "../../../core/config"; +import { arrayDeleteValue, formatBigNumber, makeDiv } from "../../../core/utils"; +import { T } from "../../../translations"; +import { enumAnalyticsDataSource } from "../../production_analytics"; +import { ShapeDefinition } from "../../shape_definition"; +import { enumHubGoalRewards } from "../../tutorial_goals"; +import { BaseHUDPart } from "../base_hud_part"; /** * Manages the pinned shapes on the left side of the screen @@ -82,7 +81,7 @@ export class HUDPinnedShapes extends BaseHUDPart { updateShapesAfterUpgrade() { for (let i = 0; i < this.pinnedShapes.length; ++i) { const key = this.pinnedShapes[i]; - if (key === blueprintShape) { + if (key === this.root.gameMode.getBlueprintShapeKey()) { // Ignore blueprint shapes continue; } @@ -107,13 +106,14 @@ export class HUDPinnedShapes extends BaseHUDPart { if (key === this.root.hubGoals.currentGoal.definition.getHash()) { return this.root.hubGoals.currentGoal.required; } - if (key === blueprintShape) { + if (key === this.root.gameMode.getBlueprintShapeKey()) { return null; } // Check if this shape is required for any upgrade - for (const upgradeId in UPGRADES) { - const upgradeTiers = UPGRADES[upgradeId]; + const upgrades = this.root.gameMode.getUpgrades(); + for (const upgradeId in upgrades) { + const upgradeTiers = upgrades[upgradeId]; const currentTier = this.root.hubGoals.getUpgradeLevel(upgradeId); const tierHandle = upgradeTiers[currentTier]; @@ -138,7 +138,10 @@ export class HUDPinnedShapes extends BaseHUDPart { * @param {string} key */ isShapePinned(key) { - if (key === this.root.hubGoals.currentGoal.definition.getHash() || key === blueprintShape) { + if ( + key === this.root.hubGoals.currentGoal.definition.getHash() || + key === this.root.gameMode.getBlueprintShapeKey() + ) { // This is a "special" shape which is always pinned return true; } @@ -178,7 +181,7 @@ export class HUDPinnedShapes extends BaseHUDPart { // Pin blueprint shape as well if (this.root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_blueprints)) { this.internalPinShape({ - key: blueprintShape, + key: this.root.gameMode.getBlueprintShapeKey(), canUnpin: false, className: "blueprint", }); @@ -214,11 +217,11 @@ export class HUDPinnedShapes extends BaseHUDPart { let detector = null; if (canUnpin) { - element.classList.add("unpinable"); + element.classList.add("removable"); detector = new ClickDetector(element, { consumeEvents: true, preventDefault: true, - targetOnly: true, + targetOnly: false, }); detector.click.add(() => this.unpinShape(key)); } else { @@ -291,6 +294,7 @@ export class HUDPinnedShapes extends BaseHUDPart { * @param {string} key */ unpinShape(key) { + console.log("unpin", key); arrayDeleteValue(this.pinnedShapes, key); this.rerenderFull(); } @@ -306,7 +310,7 @@ export class HUDPinnedShapes extends BaseHUDPart { return; } - if (key === blueprintShape) { + if (key === this.root.gameMode.getBlueprintShapeKey()) { // Can not pin the blueprint shape return; } diff --git a/src/js/game/hud/parts/sandbox_controller.js b/src/js/game/hud/parts/sandbox_controller.js index f71b87e0..592487ee 100644 --- a/src/js/game/hud/parts/sandbox_controller.js +++ b/src/js/game/hud/parts/sandbox_controller.js @@ -1,9 +1,7 @@ -import { BaseHUDPart } from "../base_hud_part"; import { makeDiv } from "../../../core/utils"; +import { BaseHUDPart } from "../base_hud_part"; import { DynamicDomAttach } from "../dynamic_dom_attach"; -import { blueprintShape, UPGRADES } from "../../upgrades"; import { enumNotificationType } from "./notifications"; -import { tutorialGoals } from "../../tutorial_goals"; export class HUDSandboxController extends BaseHUDPart { createElements(parent) { @@ -75,10 +73,11 @@ export class HUDSandboxController extends BaseHUDPart { } giveBlueprints() { - if (!this.root.hubGoals.storedShapes[blueprintShape]) { - this.root.hubGoals.storedShapes[blueprintShape] = 0; + const shape = this.root.gameMode.getBlueprintShapeKey(); + if (!this.root.hubGoals.storedShapes[shape]) { + this.root.hubGoals.storedShapes[shape] = 0; } - this.root.hubGoals.storedShapes[blueprintShape] += 1e9; + this.root.hubGoals.storedShapes[shape] += 1e9; } maxOutAll() { @@ -89,7 +88,7 @@ export class HUDSandboxController extends BaseHUDPart { } modifyUpgrade(id, amount) { - const upgradeTiers = UPGRADES[id]; + const upgradeTiers = this.root.gameMode.getUpgrades()[id]; const maxLevel = upgradeTiers.length; this.root.hubGoals.upgradeLevels[id] = Math.max( @@ -122,9 +121,10 @@ export class HUDSandboxController extends BaseHUDPart { // Compute gained rewards hubGoals.gainedRewards = {}; + const levels = this.root.gameMode.getLevelDefinitions(); for (let i = 0; i < hubGoals.level - 1; ++i) { - if (i < tutorialGoals.length) { - const reward = tutorialGoals[i].reward; + if (i < levels.length) { + const reward = levels[i].reward; hubGoals.gainedRewards[reward] = (hubGoals.gainedRewards[reward] || 0) + 1; } } diff --git a/src/js/game/hud/parts/screenshot_exporter.js b/src/js/game/hud/parts/screenshot_exporter.js index 59e76c63..dd81f8b6 100644 --- a/src/js/game/hud/parts/screenshot_exporter.js +++ b/src/js/game/hud/parts/screenshot_exporter.js @@ -1,13 +1,13 @@ -import { BaseHUDPart } from "../base_hud_part"; -import { KEYMAPPINGS } from "../../key_action_mapper"; -import { IS_DEMO, globalConfig } from "../../../core/config"; -import { T } from "../../../translations"; -import { createLogger } from "../../../core/logging"; -import { StaticMapEntityComponent } from "../../components/static_map_entity"; -import { Vector } from "../../../core/vector"; import { makeOffscreenBuffer } from "../../../core/buffer_utils"; +import { globalConfig } from "../../../core/config"; import { DrawParameters } from "../../../core/draw_parameters"; +import { createLogger } from "../../../core/logging"; import { Rectangle } from "../../../core/rectangle"; +import { Vector } from "../../../core/vector"; +import { T } from "../../../translations"; +import { StaticMapEntityComponent } from "../../components/static_map_entity"; +import { KEYMAPPINGS } from "../../key_action_mapper"; +import { BaseHUDPart } from "../base_hud_part"; const logger = createLogger("screenshot_exporter"); @@ -19,7 +19,7 @@ export class HUDScreenshotExporter extends BaseHUDPart { } startExport() { - if (IS_DEMO) { + if (!this.root.app.restrictionMgr.getIsExportingScreenshotsPossible()) { this.root.hud.parts.dialogs.showFeatureRestrictionInfo(T.demo.features.exportingBase); return; } @@ -87,7 +87,7 @@ export class HUDScreenshotExporter extends BaseHUDPart { const parameters = new DrawParameters({ context, visibleRect, - desiredAtlasScale: chunkScale, + desiredAtlasScale: 0.25, root: this.root, zoomLevel: chunkScale, }); diff --git a/src/js/game/hud/parts/shop.js b/src/js/game/hud/parts/shop.js index 4a25d16e..96521898 100644 --- a/src/js/game/hud/parts/shop.js +++ b/src/js/game/hud/parts/shop.js @@ -1,9 +1,8 @@ import { ClickDetector } from "../../../core/click_detector"; import { InputReceiver } from "../../../core/input_receiver"; -import { formatBigNumber, makeDiv } from "../../../core/utils"; +import { formatBigNumber, getRomanNumber, makeDiv } from "../../../core/utils"; import { T } from "../../../translations"; import { KeyActionMapper, KEYMAPPINGS } from "../../key_action_mapper"; -import { UPGRADES } from "../../upgrades"; import { BaseHUDPart } from "../base_hud_part"; import { DynamicDomAttach } from "../dynamic_dom_attach"; @@ -21,7 +20,7 @@ export class HUDShop extends BaseHUDPart { this.upgradeToElements = {}; // Upgrades - for (const upgradeId in UPGRADES) { + for (const upgradeId in this.root.gameMode.getUpgrades()) { const handle = {}; handle.requireIndexToElement = []; @@ -59,7 +58,7 @@ export class HUDShop extends BaseHUDPart { rerenderFull() { for (const upgradeId in this.upgradeToElements) { const handle = this.upgradeToElements[upgradeId]; - const upgradeTiers = UPGRADES[upgradeId]; + const upgradeTiers = this.root.gameMode.getUpgrades()[upgradeId]; const currentTier = this.root.hubGoals.getUpgradeLevel(upgradeId); const currentTierMultiplier = this.root.hubGoals.upgradeImprovements[upgradeId]; @@ -68,7 +67,7 @@ export class HUDShop extends BaseHUDPart { // Set tier handle.elemTierLabel.innerText = T.ingame.shop.tier.replace( "", - "" + T.ingame.shop.tierLabels[currentTier] + getRomanNumber(currentTier + 1) ); handle.elemTierLabel.setAttribute("data-tier", currentTier); diff --git a/src/js/game/hud/parts/unlock_notification.js b/src/js/game/hud/parts/unlock_notification.js index 5fea2892..5fa0e3e2 100644 --- a/src/js/game/hud/parts/unlock_notification.js +++ b/src/js/game/hud/parts/unlock_notification.js @@ -1,14 +1,14 @@ import { globalConfig } from "../../../core/config"; import { gMetaBuildingRegistry } from "../../../core/global_registries"; +import { InputReceiver } from "../../../core/input_receiver"; import { makeDiv } from "../../../core/utils"; import { SOUNDS } from "../../../platform/sound"; import { T } from "../../../translations"; import { defaultBuildingVariant } from "../../meta_building"; -import { enumHubGoalRewards, tutorialGoals } from "../../tutorial_goals"; +import { enumHubGoalRewards } from "../../tutorial_goals"; +import { enumHubGoalRewardsToContentUnlocked } from "../../tutorial_goals_mappings"; import { BaseHUDPart } from "../base_hud_part"; import { DynamicDomAttach } from "../dynamic_dom_attach"; -import { enumHubGoalRewardsToContentUnlocked } from "../../tutorial_goals_mappings"; -import { InputReceiver } from "../../../core/input_receiver"; import { enumNotificationType } from "./notifications"; export class HUDUnlockNotification extends BaseHUDPart { @@ -53,7 +53,9 @@ export class HUDUnlockNotification extends BaseHUDPart { showForLevel(level, reward) { this.root.soundProxy.playUi(SOUNDS.levelComplete); - if (level > tutorialGoals.length) { + const levels = this.root.gameMode.getLevelDefinitions(); + // Don't use getIsFreeplay() because we want the freeplay level up to show + if (level > levels.length) { this.root.hud.signals.notification.dispatch( T.ingame.notifications.freeplayLevelComplete.replace("", String(level)), enumNotificationType.success diff --git a/src/js/game/hud/parts/waypoints.js b/src/js/game/hud/parts/waypoints.js index 1aed7df2..a6f37b93 100644 --- a/src/js/game/hud/parts/waypoints.js +++ b/src/js/game/hud/parts/waypoints.js @@ -1,5 +1,5 @@ import { makeOffscreenBuffer } from "../../../core/buffer_utils"; -import { globalConfig, IS_DEMO, THIRDPARTY_URLS } from "../../../core/config"; +import { globalConfig, THIRDPARTY_URLS } from "../../../core/config"; import { DrawParameters } from "../../../core/draw_parameters"; import { Loader } from "../../../core/loader"; import { DialogWithForm } from "../../../core/modal_dialog_elements"; @@ -302,7 +302,7 @@ export class HUDWaypoints extends BaseHUDPart { // Show info that you can have only N markers in the demo, // actually show this *after* entering the name so you want the // standalone even more (I'm evil :P) - if (IS_DEMO && this.waypoints.length > 2) { + if (this.waypoints.length > this.root.app.restrictionMgr.getMaximumWaypoints()) { this.root.hud.parts.dialogs.showFeatureRestrictionInfo( "", T.dialogs.markerDemoLimit.desc diff --git a/src/js/game/hud/parts/wires_toolbar.js b/src/js/game/hud/parts/wires_toolbar.js index e44d7186..5141bbeb 100644 --- a/src/js/game/hud/parts/wires_toolbar.js +++ b/src/js/game/hud/parts/wires_toolbar.js @@ -11,6 +11,7 @@ import { MetaComparatorBuilding } from "../../buildings/comparator"; import { MetaReaderBuilding } from "../../buildings/reader"; import { MetaFilterBuilding } from "../../buildings/filter"; import { MetaDisplayBuilding } from "../../buildings/display"; +import { MetaStorageBuilding } from "../../buildings/storage"; export class HUDWiresToolbar extends HUDBaseToolbar { constructor(root) { @@ -26,6 +27,7 @@ export class HUDWiresToolbar extends HUDBaseToolbar { MetaTransistorBuilding, ], secondaryBuildings: [ + MetaStorageBuilding, MetaReaderBuilding, MetaLeverBuilding, MetaFilterBuilding, diff --git a/src/js/game/key_action_mapper.js b/src/js/game/key_action_mapper.js index 872db1d2..9fa4ffe1 100644 --- a/src/js/game/key_action_mapper.js +++ b/src/js/game/key_action_mapper.js @@ -122,6 +122,7 @@ export const KEYCODE_RMB = 3; * @returns {string} */ export function getStringForKeyCode(code) { + // @todo: Refactor into dictionary switch (code) { case KEYCODE_LMB: return "LMB"; diff --git a/src/js/game/map_chunk_view.js b/src/js/game/map_chunk_view.js index 5bb5d2c4..848afbab 100644 --- a/src/js/game/map_chunk_view.js +++ b/src/js/game/map_chunk_view.js @@ -104,15 +104,17 @@ export class MapChunkView extends MapChunk { }); parameters.context.imageSmoothingEnabled = true; + const resourcesScale = this.root.app.settings.getAllSettings().mapResourcesScale; // Draw patch items - if (this.root.currentLayer === "regular") { + if (this.root.currentLayer === "regular" && resourcesScale > 0.05) { + const diameter = (70 / Math.pow(parameters.zoomLevel, 0.35)) * (0.2 + 2 * resourcesScale); + for (let i = 0; i < this.patches.length; ++i) { const patch = this.patches[i]; if (patch.item.getItemType() === "shape") { const destX = this.x * dims + patch.pos.x * globalConfig.tileSize; const destY = this.y * dims + patch.pos.y * globalConfig.tileSize; - const diameter = 80 / Math.pow(parameters.zoomLevel, 0.35); patch.item.drawItemCenteredClipped(destX, destY, parameters, diameter); } } diff --git a/src/js/game/modes/regular.js b/src/js/game/modes/regular.js new file mode 100644 index 00000000..5dbf50ad --- /dev/null +++ b/src/js/game/modes/regular.js @@ -0,0 +1,480 @@ +import { findNiceIntegerValue } from "../../core/utils"; +import { GameMode } from "../game_mode"; +import { ShapeDefinition } from "../shape_definition"; +import { enumHubGoalRewards } from "../tutorial_goals"; + +const rocketShape = "CbCuCbCu:Sr------:--CrSrCr:CwCwCwCw"; +const finalGameShape = "RuCw--Cw:----Ru--"; +const preparementShape = "CpRpCp--:SwSwSwSw"; +const blueprintShape = "CbCbCbRb:CwCwCwCw"; + +// Tiers need % of the previous tier as requirement too +const tierGrowth = 2.5; + +/** + * Generates all upgrades + * @returns {Object} */ +function generateUpgrades(limitedVersion = false) { + const fixedImprovements = [0.5, 0.5, 1, 1, 2, 1, 1]; + const numEndgameUpgrades = limitedVersion ? 0 : 1000 - fixedImprovements.length - 1; + + function generateInfiniteUnlocks() { + return new Array(numEndgameUpgrades).fill(null).map((_, i) => ({ + required: [ + { shape: preparementShape, amount: 30000 + i * 10000 }, + { shape: finalGameShape, amount: 20000 + i * 5000 }, + { shape: rocketShape, amount: 20000 + i * 5000 }, + ], + excludePrevious: true, + })); + } + + // Fill in endgame upgrades + for (let i = 0; i < numEndgameUpgrades; ++i) { + if (i < 20) { + fixedImprovements.push(0.1); + } else if (i < 50) { + fixedImprovements.push(0.05); + } else if (i < 100) { + fixedImprovements.push(0.025); + } else { + fixedImprovements.push(0.0125); + } + } + + const upgrades = { + belt: [ + { + required: [{ shape: "CuCuCuCu", amount: 30 }], + }, + { + required: [{ shape: "--CuCu--", amount: 500 }], + }, + { + required: [{ shape: "CpCpCpCp", amount: 1000 }], + }, + { + required: [{ shape: "SrSrSrSr:CyCyCyCy", amount: 6000 }], + }, + { + required: [{ shape: "SrSrSrSr:CyCyCyCy:SwSwSwSw", amount: 25000 }], + }, + { + required: [{ shape: preparementShape, amount: 25000 }], + excludePrevious: true, + }, + { + required: [ + { shape: preparementShape, amount: 25000 }, + { shape: finalGameShape, amount: 50000 }, + ], + excludePrevious: true, + }, + ...generateInfiniteUnlocks(), + ], + + miner: [ + { + required: [{ shape: "RuRuRuRu", amount: 300 }], + }, + { + required: [{ shape: "Cu------", amount: 800 }], + }, + { + required: [{ shape: "ScScScSc", amount: 3500 }], + }, + { + required: [{ shape: "CwCwCwCw:WbWbWbWb", amount: 23000 }], + }, + { + required: [{ shape: "CbRbRbCb:CwCwCwCw:WbWbWbWb", amount: 50000 }], + }, + { + required: [{ shape: preparementShape, amount: 25000 }], + excludePrevious: true, + }, + { + required: [ + { shape: preparementShape, amount: 25000 }, + { shape: finalGameShape, amount: 50000 }, + ], + excludePrevious: true, + }, + ...generateInfiniteUnlocks(), + ], + + processors: [ + { + required: [{ shape: "SuSuSuSu", amount: 500 }], + }, + { + required: [{ shape: "RuRu----", amount: 600 }], + }, + { + required: [{ shape: "CgScScCg", amount: 3500 }], + }, + { + required: [{ shape: "CwCrCwCr:SgSgSgSg", amount: 25000 }], + }, + { + required: [{ shape: "WrRgWrRg:CwCrCwCr:SgSgSgSg", amount: 50000 }], + }, + { + required: [{ shape: preparementShape, amount: 25000 }], + excludePrevious: true, + }, + { + required: [ + { shape: preparementShape, amount: 25000 }, + { shape: finalGameShape, amount: 50000 }, + ], + excludePrevious: true, + }, + ...generateInfiniteUnlocks(), + ], + + painting: [ + { + required: [{ shape: "RbRb----", amount: 600 }], + }, + { + required: [{ shape: "WrWrWrWr", amount: 3800 }], + }, + { + required: [{ shape: "RpRpRpRp:CwCwCwCw", amount: 6500 }], + }, + { + required: [{ shape: "WpWpWpWp:CwCwCwCw:WpWpWpWp", amount: 25000 }], + }, + { + required: [{ shape: "WpWpWpWp:CwCwCwCw:WpWpWpWp:CwCwCwCw", amount: 50000 }], + }, + { + required: [{ shape: preparementShape, amount: 25000 }], + excludePrevious: true, + }, + { + required: [ + { shape: preparementShape, amount: 25000 }, + { shape: finalGameShape, amount: 50000 }, + ], + excludePrevious: true, + }, + ...generateInfiniteUnlocks(), + ], + }; + + // Automatically generate tier levels + for (const upgradeId in upgrades) { + const upgradeTiers = upgrades[upgradeId]; + + let currentTierRequirements = []; + for (let i = 0; i < upgradeTiers.length; ++i) { + const tierHandle = upgradeTiers[i]; + tierHandle.improvement = fixedImprovements[i]; + const originalRequired = tierHandle.required.slice(); + + for (let k = currentTierRequirements.length - 1; k >= 0; --k) { + const oldTierRequirement = currentTierRequirements[k]; + if (!tierHandle.excludePrevious) { + tierHandle.required.unshift({ + shape: oldTierRequirement.shape, + amount: oldTierRequirement.amount, + }); + } + } + currentTierRequirements.push( + ...originalRequired.map(req => ({ + amount: req.amount, + shape: req.shape, + })) + ); + currentTierRequirements.forEach(tier => { + tier.amount = findNiceIntegerValue(tier.amount * tierGrowth); + }); + } + } + + // VALIDATE + if (G_IS_DEV) { + for (const upgradeId in upgrades) { + upgrades[upgradeId].forEach(tier => { + tier.required.forEach(({ shape }) => { + try { + ShapeDefinition.fromShortKey(shape); + } catch (ex) { + throw new Error("Invalid upgrade goal: '" + ex + "' for shape" + shape); + } + }); + }); + } + } + + return upgrades; +} + +/** + * Generates the level definitions + * @param {boolean} limitedVersion + */ +export function generateLevelDefinitions(limitedVersion = false) { + const levelDefinitions = [ + // 1 + // Circle + { + shape: "CuCuCuCu", // belts t1 + required: 30, + reward: enumHubGoalRewards.reward_cutter_and_trash, + }, + + // 2 + // Cutter + { + shape: "----CuCu", // + required: 40, + reward: enumHubGoalRewards.no_reward, + }, + + // 3 + // Rectangle + { + shape: "RuRuRuRu", // miners t1 + required: 70, + reward: enumHubGoalRewards.reward_balancer, + }, + + // 4 + { + shape: "RuRu----", // processors t2 + required: 70, + reward: enumHubGoalRewards.reward_rotater, + }, + + // 5 + // Rotater + { + shape: "Cu----Cu", // belts t2 + required: 170, + reward: enumHubGoalRewards.reward_tunnel, + }, + + // 6 + { + shape: "Cu------", // miners t2 + required: 270, + reward: enumHubGoalRewards.reward_painter, + }, + + // 7 + // Painter + { + shape: "CrCrCrCr", // unused + required: 300, + reward: enumHubGoalRewards.reward_rotater_ccw, + }, + + // 8 + { + shape: "RbRb----", // painter t2 + required: 480, + reward: enumHubGoalRewards.reward_mixer, + }, + + // 9 + // Mixing (purple) + { + shape: "CpCpCpCp", // belts t3 + required: 600, + reward: enumHubGoalRewards.reward_merger, + }, + + // 10 + // STACKER: Star shape + cyan + { + shape: "ScScScSc", // miners t3 + required: 800, + reward: enumHubGoalRewards.reward_stacker, + }, + + // 11 + // Chainable miner + { + shape: "CgScScCg", // processors t3 + required: 1000, + reward: enumHubGoalRewards.reward_miner_chainable, + }, + + // 12 + // Blueprints + { + shape: "CbCbCbRb:CwCwCwCw", + required: 1000, + reward: enumHubGoalRewards.reward_blueprints, + }, + + // 13 + // Tunnel Tier 2 + { + shape: "RpRpRpRp:CwCwCwCw", // painting t3 + required: 3800, + reward: enumHubGoalRewards.reward_underground_belt_tier_2, + }, + + // DEMO STOPS HERE + ...(limitedVersion + ? [ + { + shape: "RpRpRpRp:CwCwCwCw", + required: 0, + reward: enumHubGoalRewards.reward_demo_end, + }, + ] + : [ + // 14 + // Belt reader + { + shape: "--Cg----:--Cr----", // unused + required: 16, // Per second! + reward: enumHubGoalRewards.reward_belt_reader, + throughputOnly: true, + }, + + // 15 + // Storage + { + shape: "SrSrSrSr:CyCyCyCy", // unused + required: 10000, + reward: enumHubGoalRewards.reward_storage, + }, + + // 16 + // Quad Cutter + { + shape: "SrSrSrSr:CyCyCyCy:SwSwSwSw", // belts t4 (two variants) + required: 6000, + reward: enumHubGoalRewards.reward_cutter_quad, + }, + + // 17 + // Double painter + { + shape: "CbRbRbCb:CwCwCwCw:WbWbWbWb", // miner t4 (two variants) + required: 20000, + reward: enumHubGoalRewards.reward_painter_double, + }, + + // 18 + // Rotater (180deg) + { + shape: "Sg----Sg:CgCgCgCg:--CyCy--", // unused + required: 20000, + reward: enumHubGoalRewards.reward_rotater_180, + }, + + // 19 + // Compact splitter + { + shape: "CpRpCp--:SwSwSwSw", + required: 25000, + reward: enumHubGoalRewards.reward_splitter, + }, + + // 20 + // WIRES + { + shape: finalGameShape, + required: 25000, + reward: enumHubGoalRewards.reward_wires_painter_and_levers, + }, + + // 21 + // Filter + { + shape: "CrCwCrCw:CwCrCwCr:CrCwCrCw:CwCrCwCr", + required: 25000, + reward: enumHubGoalRewards.reward_filter, + }, + + // 22 + // Constant signal + { + shape: "Cg----Cr:Cw----Cw:Sy------:Cy----Cy", + required: 25000, + reward: enumHubGoalRewards.reward_constant_signal, + }, + + // 23 + // Display + { + shape: "CcSyCcSy:SyCcSyCc:CcSyCcSy", + required: 25000, + reward: enumHubGoalRewards.reward_display, + }, + + // 24 Logic gates + { + shape: "CcRcCcRc:RwCwRwCw:Sr--Sw--:CyCyCyCy", + required: 25000, + reward: enumHubGoalRewards.reward_logic_gates, + }, + + // 25 Virtual Processing + { + shape: "Rg--Rg--:CwRwCwRw:--Rg--Rg", + required: 25000, + reward: enumHubGoalRewards.reward_virtual_processing, + }, + + // 26 Freeplay + { + shape: "CbCuCbCu:Sr------:--CrSrCr:CwCwCwCw", + required: 50000, + reward: enumHubGoalRewards.reward_freeplay, + }, + ]), + ]; + + if (G_IS_DEV) { + levelDefinitions.forEach(({ shape }) => { + try { + ShapeDefinition.fromShortKey(shape); + } catch (ex) { + throw new Error("Invalid tutorial goal: '" + ex + "' for shape" + shape); + } + }); + } + + return levelDefinitions; +} + +const fullVersionUpgrades = generateUpgrades(false); +const demoVersionUpgrades = generateUpgrades(true); + +const fullVersionLevels = generateLevelDefinitions(false); +const demoVersionLevels = generateLevelDefinitions(true); + +export class RegularGameMode extends GameMode { + constructor(root) { + super(root); + } + + getUpgrades() { + return this.root.app.restrictionMgr.getHasExtendedUpgrades() + ? fullVersionUpgrades + : demoVersionUpgrades; + } + + getIsFreeplayAvailable() { + return this.root.app.restrictionMgr.getHasExtendedLevelsAndFreeplay(); + } + + getBlueprintShapeKey() { + return blueprintShape; + } + + getLevelDefinitions() { + return this.root.app.restrictionMgr.getHasExtendedLevelsAndFreeplay() + ? fullVersionLevels + : demoVersionLevels; + } +} diff --git a/src/js/game/root.js b/src/js/game/root.js index dd224dd8..6f1e7c36 100644 --- a/src/js/game/root.js +++ b/src/js/game/root.js @@ -1,221 +1,225 @@ -/* eslint-disable no-unused-vars */ -import { Signal } from "../core/signal"; -import { RandomNumberGenerator } from "../core/rng"; -import { createLogger } from "../core/logging"; - -// Type hints -/* typehints:start */ -import { GameTime } from "./time/game_time"; -import { EntityManager } from "./entity_manager"; -import { GameSystemManager } from "./game_system_manager"; -import { GameHUD } from "./hud/hud"; -import { MapView } from "./map_view"; -import { Camera } from "./camera"; -import { InGameState } from "../states/ingame"; -import { AutomaticSave } from "./automatic_save"; -import { Application } from "../application"; -import { SoundProxy } from "./sound_proxy"; -import { Savegame } from "../savegame/savegame"; -import { GameLogic } from "./logic"; -import { ShapeDefinitionManager } from "./shape_definition_manager"; -import { HubGoals } from "./hub_goals"; -import { BufferMaintainer } from "../core/buffer_maintainer"; -import { ProductionAnalytics } from "./production_analytics"; -import { Entity } from "./entity"; -import { ShapeDefinition } from "./shape_definition"; -import { BaseItem } from "./base_item"; -import { DynamicTickrate } from "./dynamic_tickrate"; -import { KeyActionMapper } from "./key_action_mapper"; -import { Vector } from "../core/vector"; -/* typehints:end */ - -const logger = createLogger("game/root"); - -/** @type {Array} */ -export const layers = ["regular", "wires"]; - -/** - * The game root is basically the whole game state at a given point, - * combining all important classes. We don't have globals, but this - * class is passed to almost all game classes. - */ -export class GameRoot { - /** - * Constructs a new game root - * @param {Application} app - */ - constructor(app) { - this.app = app; - - /** @type {Savegame} */ - this.savegame = null; - - /** @type {InGameState} */ - this.gameState = null; - - /** @type {KeyActionMapper} */ - this.keyMapper = null; - - // Store game dimensions - this.gameWidth = 500; - this.gameHeight = 500; - - // Stores whether the current session is a fresh game (true), or was continued (false) - /** @type {boolean} */ - this.gameIsFresh = true; - - // Stores whether the logic is already initialized - /** @type {boolean} */ - this.logicInitialized = false; - - // Stores whether the game is already initialized, that is, all systems etc have been created - /** @type {boolean} */ - this.gameInitialized = false; - - /** - * Whether a bulk operation is running - */ - this.bulkOperationRunning = false; - - //////// Other properties /////// - - /** @type {Camera} */ - this.camera = null; - - /** @type {HTMLCanvasElement} */ - this.canvas = null; - - /** @type {CanvasRenderingContext2D} */ - this.context = null; - - /** @type {MapView} */ - this.map = null; - - /** @type {GameLogic} */ - this.logic = null; - - /** @type {EntityManager} */ - this.entityMgr = null; - - /** @type {GameHUD} */ - this.hud = null; - - /** @type {GameSystemManager} */ - this.systemMgr = null; - - /** @type {GameTime} */ - this.time = null; - - /** @type {HubGoals} */ - this.hubGoals = null; - - /** @type {BufferMaintainer} */ - this.buffers = null; - - /** @type {AutomaticSave} */ - this.automaticSave = null; - - /** @type {SoundProxy} */ - this.soundProxy = null; - - /** @type {ShapeDefinitionManager} */ - this.shapeDefinitionMgr = null; - - /** @type {ProductionAnalytics} */ - this.productionAnalytics = null; - - /** @type {DynamicTickrate} */ - this.dynamicTickrate = null; - - /** @type {Layer} */ - this.currentLayer = "regular"; - - this.signals = { - // Entities - entityManuallyPlaced: /** @type {TypedSignal<[Entity]>} */ (new Signal()), - entityAdded: /** @type {TypedSignal<[Entity]>} */ (new Signal()), - entityChanged: /** @type {TypedSignal<[Entity]>} */ (new Signal()), - entityGotNewComponent: /** @type {TypedSignal<[Entity]>} */ (new Signal()), - entityComponentRemoved: /** @type {TypedSignal<[Entity]>} */ (new Signal()), - entityQueuedForDestroy: /** @type {TypedSignal<[Entity]>} */ (new Signal()), - entityDestroyed: /** @type {TypedSignal<[Entity]>} */ (new Signal()), - - // Global - resized: /** @type {TypedSignal<[number, number]>} */ (new Signal()), - readyToRender: /** @type {TypedSignal<[]>} */ (new Signal()), - aboutToDestruct: /** @type {TypedSignal<[]>} */ new Signal(), - - // Game Hooks - gameSaved: /** @type {TypedSignal<[]>} */ (new Signal()), // Game got saved - gameRestored: /** @type {TypedSignal<[]>} */ (new Signal()), // Game got restored - - gameFrameStarted: /** @type {TypedSignal<[]>} */ (new Signal()), // New frame - - storyGoalCompleted: /** @type {TypedSignal<[number, string]>} */ (new Signal()), - upgradePurchased: /** @type {TypedSignal<[string]>} */ (new Signal()), - - // Called right after game is initialized - postLoadHook: /** @type {TypedSignal<[]>} */ (new Signal()), - - shapeDelivered: /** @type {TypedSignal<[ShapeDefinition]>} */ (new Signal()), - itemProduced: /** @type {TypedSignal<[BaseItem]>} */ (new Signal()), - - bulkOperationFinished: /** @type {TypedSignal<[]>} */ (new Signal()), - - editModeChanged: /** @type {TypedSignal<[Layer]>} */ (new Signal()), - - // Called to check if an entity can be placed, second parameter is an additional offset. - // Use to introduce additional placement checks - prePlacementCheck: /** @type {TypedSignal<[Entity, Vector]>} */ (new Signal()), - - // Called before actually placing an entity, use to perform additional logic - // for freeing space before actually placing. - freeEntityAreaBeforeBuild: /** @type {TypedSignal<[Entity]>} */ (new Signal()), - }; - - // RNG's - /** @type {Object.>} */ - this.rngs = {}; - - // Work queue - this.queue = { - requireRedraw: false, - }; - } - - /** - * Destructs the game root - */ - destruct() { - logger.log("destructing root"); - this.signals.aboutToDestruct.dispatch(); - - this.reset(); - } - - /** - * Resets the whole root and removes all properties - */ - reset() { - if (this.signals) { - // Destruct all signals - for (let i = 0; i < this.signals.length; ++i) { - this.signals[i].removeAll(); - } - } - - if (this.hud) { - this.hud.cleanup(); - } - if (this.camera) { - this.camera.cleanup(); - } - - // Finally free all properties - for (let prop in this) { - if (this.hasOwnProperty(prop)) { - delete this[prop]; - } - } - } -} +/* eslint-disable no-unused-vars */ +import { Signal } from "../core/signal"; +import { RandomNumberGenerator } from "../core/rng"; +import { createLogger } from "../core/logging"; + +// Type hints +/* typehints:start */ +import { GameTime } from "./time/game_time"; +import { EntityManager } from "./entity_manager"; +import { GameSystemManager } from "./game_system_manager"; +import { GameHUD } from "./hud/hud"; +import { MapView } from "./map_view"; +import { Camera } from "./camera"; +import { InGameState } from "../states/ingame"; +import { AutomaticSave } from "./automatic_save"; +import { Application } from "../application"; +import { SoundProxy } from "./sound_proxy"; +import { Savegame } from "../savegame/savegame"; +import { GameLogic } from "./logic"; +import { ShapeDefinitionManager } from "./shape_definition_manager"; +import { HubGoals } from "./hub_goals"; +import { BufferMaintainer } from "../core/buffer_maintainer"; +import { ProductionAnalytics } from "./production_analytics"; +import { Entity } from "./entity"; +import { ShapeDefinition } from "./shape_definition"; +import { BaseItem } from "./base_item"; +import { DynamicTickrate } from "./dynamic_tickrate"; +import { KeyActionMapper } from "./key_action_mapper"; +import { Vector } from "../core/vector"; +import { GameMode } from "./game_mode"; +/* typehints:end */ + +const logger = createLogger("game/root"); + +/** @type {Array} */ +export const layers = ["regular", "wires"]; + +/** + * The game root is basically the whole game state at a given point, + * combining all important classes. We don't have globals, but this + * class is passed to almost all game classes. + */ +export class GameRoot { + /** + * Constructs a new game root + * @param {Application} app + */ + constructor(app) { + this.app = app; + + /** @type {Savegame} */ + this.savegame = null; + + /** @type {InGameState} */ + this.gameState = null; + + /** @type {KeyActionMapper} */ + this.keyMapper = null; + + // Store game dimensions + this.gameWidth = 500; + this.gameHeight = 500; + + // Stores whether the current session is a fresh game (true), or was continued (false) + /** @type {boolean} */ + this.gameIsFresh = true; + + // Stores whether the logic is already initialized + /** @type {boolean} */ + this.logicInitialized = false; + + // Stores whether the game is already initialized, that is, all systems etc have been created + /** @type {boolean} */ + this.gameInitialized = false; + + /** + * Whether a bulk operation is running + */ + this.bulkOperationRunning = false; + + //////// Other properties /////// + + /** @type {Camera} */ + this.camera = null; + + /** @type {HTMLCanvasElement} */ + this.canvas = null; + + /** @type {CanvasRenderingContext2D} */ + this.context = null; + + /** @type {MapView} */ + this.map = null; + + /** @type {GameLogic} */ + this.logic = null; + + /** @type {EntityManager} */ + this.entityMgr = null; + + /** @type {GameHUD} */ + this.hud = null; + + /** @type {GameSystemManager} */ + this.systemMgr = null; + + /** @type {GameTime} */ + this.time = null; + + /** @type {HubGoals} */ + this.hubGoals = null; + + /** @type {BufferMaintainer} */ + this.buffers = null; + + /** @type {AutomaticSave} */ + this.automaticSave = null; + + /** @type {SoundProxy} */ + this.soundProxy = null; + + /** @type {ShapeDefinitionManager} */ + this.shapeDefinitionMgr = null; + + /** @type {ProductionAnalytics} */ + this.productionAnalytics = null; + + /** @type {DynamicTickrate} */ + this.dynamicTickrate = null; + + /** @type {Layer} */ + this.currentLayer = "regular"; + + /** @type {GameMode} */ + this.gameMode = null; + + this.signals = { + // Entities + entityManuallyPlaced: /** @type {TypedSignal<[Entity]>} */ (new Signal()), + entityAdded: /** @type {TypedSignal<[Entity]>} */ (new Signal()), + entityChanged: /** @type {TypedSignal<[Entity]>} */ (new Signal()), + entityGotNewComponent: /** @type {TypedSignal<[Entity]>} */ (new Signal()), + entityComponentRemoved: /** @type {TypedSignal<[Entity]>} */ (new Signal()), + entityQueuedForDestroy: /** @type {TypedSignal<[Entity]>} */ (new Signal()), + entityDestroyed: /** @type {TypedSignal<[Entity]>} */ (new Signal()), + + // Global + resized: /** @type {TypedSignal<[number, number]>} */ (new Signal()), + readyToRender: /** @type {TypedSignal<[]>} */ (new Signal()), + aboutToDestruct: /** @type {TypedSignal<[]>} */ new Signal(), + + // Game Hooks + gameSaved: /** @type {TypedSignal<[]>} */ (new Signal()), // Game got saved + gameRestored: /** @type {TypedSignal<[]>} */ (new Signal()), // Game got restored + + gameFrameStarted: /** @type {TypedSignal<[]>} */ (new Signal()), // New frame + + storyGoalCompleted: /** @type {TypedSignal<[number, string]>} */ (new Signal()), + upgradePurchased: /** @type {TypedSignal<[string]>} */ (new Signal()), + + // Called right after game is initialized + postLoadHook: /** @type {TypedSignal<[]>} */ (new Signal()), + + shapeDelivered: /** @type {TypedSignal<[ShapeDefinition]>} */ (new Signal()), + itemProduced: /** @type {TypedSignal<[BaseItem]>} */ (new Signal()), + + bulkOperationFinished: /** @type {TypedSignal<[]>} */ (new Signal()), + + editModeChanged: /** @type {TypedSignal<[Layer]>} */ (new Signal()), + + // Called to check if an entity can be placed, second parameter is an additional offset. + // Use to introduce additional placement checks + prePlacementCheck: /** @type {TypedSignal<[Entity, Vector]>} */ (new Signal()), + + // Called before actually placing an entity, use to perform additional logic + // for freeing space before actually placing. + freeEntityAreaBeforeBuild: /** @type {TypedSignal<[Entity]>} */ (new Signal()), + }; + + // RNG's + /** @type {Object.>} */ + this.rngs = {}; + + // Work queue + this.queue = { + requireRedraw: false, + }; + } + + /** + * Destructs the game root + */ + destruct() { + logger.log("destructing root"); + this.signals.aboutToDestruct.dispatch(); + + this.reset(); + } + + /** + * Resets the whole root and removes all properties + */ + reset() { + if (this.signals) { + // Destruct all signals + for (let i = 0; i < this.signals.length; ++i) { + this.signals[i].removeAll(); + } + } + + if (this.hud) { + this.hud.cleanup(); + } + if (this.camera) { + this.camera.cleanup(); + } + + // Finally free all properties + for (let prop in this) { + if (this.hasOwnProperty(prop)) { + delete this[prop]; + } + } + } +} diff --git a/src/js/game/systems/constant_signal.js b/src/js/game/systems/constant_signal.js index 0b2f38da..aaf31a19 100644 --- a/src/js/game/systems/constant_signal.js +++ b/src/js/game/systems/constant_signal.js @@ -12,7 +12,6 @@ import { GameSystemWithFilter } from "../game_system_with_filter"; import { BOOL_FALSE_SINGLETON, BOOL_TRUE_SINGLETON } from "../items/boolean_item"; import { COLOR_ITEM_SINGLETONS } from "../items/color_item"; import { ShapeDefinition } from "../shape_definition"; -import { blueprintShape } from "../upgrades"; export class ConstantSignalSystem extends GameSystemWithFilter { constructor(root) { @@ -61,7 +60,9 @@ export class ConstantSignalSystem extends GameSystemWithFilter { this.root.shapeDefinitionMgr.getShapeItemFromDefinition( this.root.hubGoals.currentGoal.definition ), - this.root.shapeDefinitionMgr.getShapeItemFromShortKey(blueprintShape), + this.root.shapeDefinitionMgr.getShapeItemFromShortKey( + this.root.gameMode.getBlueprintShapeKey() + ), ...this.root.hud.parts.pinnedShapes.pinnedShapes.map(key => this.root.shapeDefinitionMgr.getShapeItemFromShortKey(key) ), diff --git a/src/js/game/systems/hub.js b/src/js/game/systems/hub.js index 2f1f7c05..2002b66e 100644 --- a/src/js/game/systems/hub.js +++ b/src/js/game/systems/hub.js @@ -1,4 +1,4 @@ -import { globalConfig, IS_DEMO } from "../../core/config"; +import { globalConfig } from "../../core/config"; import { smoothenDpi } from "../../core/dpi_manager"; import { DrawParameters } from "../../core/draw_parameters"; import { drawSpriteClipped } from "../../core/draw_utils"; diff --git a/src/js/game/systems/logic_gate.js b/src/js/game/systems/logic_gate.js index 46d040c0..4545a331 100644 --- a/src/js/game/systems/logic_gate.js +++ b/src/js/game/systems/logic_gate.js @@ -154,22 +154,18 @@ export class LogicGateSystem extends GameSystemWithFilter { /** * @param {Array} parameters - * @returns {[BaseItem, BaseItem]} + * @returns {BaseItem} */ compute_ROTATE(parameters) { const item = parameters[0]; if (!item || item.getItemType() !== "shape") { // Not a shape - return [null, null]; + return null; } const definition = /** @type {ShapeItem} */ (item).definition; - const rotatedDefinitionCCW = this.root.shapeDefinitionMgr.shapeActionRotateCCW(definition); const rotatedDefinitionCW = this.root.shapeDefinitionMgr.shapeActionRotateCW(definition); - return [ - this.root.shapeDefinitionMgr.getShapeItemFromDefinition(rotatedDefinitionCCW), - this.root.shapeDefinitionMgr.getShapeItemFromDefinition(rotatedDefinitionCW), - ]; + return this.root.shapeDefinitionMgr.getShapeItemFromDefinition(rotatedDefinitionCW); } /** diff --git a/src/js/game/themes/dark.json b/src/js/game/themes/dark.json index 227d4532..733b7682 100644 --- a/src/js/game/themes/dark.json +++ b/src/js/game/themes/dark.json @@ -32,7 +32,8 @@ }, "chunkOverview": { "empty": "#444856", - "filled": "#646b7d" + "filled": "#646b7d", + "beltColor": "#9096a3" }, "wires": { diff --git a/src/js/game/themes/light.json b/src/js/game/themes/light.json index 47616d82..0c793c26 100644 --- a/src/js/game/themes/light.json +++ b/src/js/game/themes/light.json @@ -33,7 +33,8 @@ "chunkOverview": { "empty": "#a6afbb", - "filled": "#c5ccd6" + "filled": "#c5ccd6", + "beltColor": "#777" }, "wires": { diff --git a/src/js/game/tutorial_goals.js b/src/js/game/tutorial_goals.js index f7b56ffe..84634b0a 100644 --- a/src/js/game/tutorial_goals.js +++ b/src/js/game/tutorial_goals.js @@ -1,7 +1,3 @@ -import { IS_DEMO } from "../core/config"; -import { ShapeDefinition } from "./shape_definition"; -import { finalGameShape } from "./upgrades"; - /** * Don't forget to also update tutorial_goals_mappings.js as well as the translations! * @enum {string} @@ -40,229 +36,3 @@ export const enumHubGoalRewards = { no_reward: "no_reward", no_reward_freeplay: "no_reward_freeplay", }; - -export const tutorialGoals = [ - // 1 - // Circle - { - shape: "CuCuCuCu", // belts t1 - required: 30, - reward: enumHubGoalRewards.reward_cutter_and_trash, - }, - - // 2 - // Cutter - { - shape: "----CuCu", // - required: 40, - reward: enumHubGoalRewards.no_reward, - }, - - // 3 - // Rectangle - { - shape: "RuRuRuRu", // miners t1 - required: 70, - reward: enumHubGoalRewards.reward_balancer, - }, - - // 4 - { - shape: "RuRu----", // processors t2 - required: 70, - reward: enumHubGoalRewards.reward_rotater, - }, - - // 5 - // Rotater - { - shape: "Cu----Cu", // belts t2 - required: 170, - reward: enumHubGoalRewards.reward_tunnel, - }, - - // 6 - { - shape: "Cu------", // miners t2 - required: 270, - reward: enumHubGoalRewards.reward_painter, - }, - - // 7 - // Painter - { - shape: "CrCrCrCr", // unused - required: 300, - reward: enumHubGoalRewards.reward_rotater_ccw, - }, - - // 8 - { - shape: "RbRb----", // painter t2 - required: 480, - reward: enumHubGoalRewards.reward_mixer, - }, - - // 9 - // Mixing (purple) - { - shape: "CpCpCpCp", // belts t3 - required: 600, - reward: enumHubGoalRewards.reward_merger, - }, - - // 10 - // STACKER: Star shape + cyan - { - shape: "ScScScSc", // miners t3 - required: 800, - reward: enumHubGoalRewards.reward_stacker, - }, - - // 11 - // Chainable miner - { - shape: "CgScScCg", // processors t3 - required: 1000, - reward: enumHubGoalRewards.reward_miner_chainable, - }, - - // 12 - // Blueprints - { - shape: "CbCbCbRb:CwCwCwCw", - required: 1000, - reward: enumHubGoalRewards.reward_blueprints, - }, - - // 13 - // Tunnel Tier 2 - { - shape: "RpRpRpRp:CwCwCwCw", // painting t3 - required: 3800, - reward: enumHubGoalRewards.reward_underground_belt_tier_2, - }, - - // DEMO STOPS HERE - ...(IS_DEMO - ? [ - { - shape: "RpRpRpRp:CwCwCwCw", - required: 0, - reward: enumHubGoalRewards.reward_demo_end, - }, - ] - : [ - // 14 - // Belt reader - { - shape: "--Cg----:--Cr----", // unused - required: 16, // Per second! - reward: enumHubGoalRewards.reward_belt_reader, - throughputOnly: true, - }, - - // 15 - // Storage - { - shape: "SrSrSrSr:CyCyCyCy", // unused - required: 10000, - reward: enumHubGoalRewards.reward_storage, - }, - - // 16 - // Quad Cutter - { - shape: "SrSrSrSr:CyCyCyCy:SwSwSwSw", // belts t4 (two variants) - required: 6000, - reward: enumHubGoalRewards.reward_cutter_quad, - }, - - // 17 - // Double painter - { - shape: "CbRbRbCb:CwCwCwCw:WbWbWbWb", // miner t4 (two variants) - required: 20000, - reward: enumHubGoalRewards.reward_painter_double, - }, - - // 18 - // Rotater (180deg) - { - shape: "Sg----Sg:CgCgCgCg:--CyCy--", // unused - required: 20000, - reward: enumHubGoalRewards.reward_rotater_180, - }, - - // 19 - // Compact splitter - { - shape: "CpRpCp--:SwSwSwSw", - required: 25000, - reward: enumHubGoalRewards.reward_splitter, - }, - - // 20 - // WIRES - { - shape: finalGameShape, - required: 25000, - reward: enumHubGoalRewards.reward_wires_painter_and_levers, - }, - - // 21 - // Filter - { - shape: "CrCwCrCw:CwCrCwCr:CrCwCrCw:CwCrCwCr", - required: 25000, - reward: enumHubGoalRewards.reward_filter, - }, - - // 22 - // Constant signal - { - shape: "Cg----Cr:Cw----Cw:Sy------:Cy----Cy", - required: 25000, - reward: enumHubGoalRewards.reward_constant_signal, - }, - - // 23 - // Display - { - shape: "CcSyCcSy:SyCcSyCc:CcSyCcSy", - required: 25000, - reward: enumHubGoalRewards.reward_display, - }, - - // 24 Logic gates - { - shape: "CcRcCcRc:RwCwRwCw:Sr--Sw--:CyCyCyCy", - required: 25000, - reward: enumHubGoalRewards.reward_logic_gates, - }, - - // 25 Virtual Processing - { - shape: "Rg--Rg--:CwRwCwRw:--Rg--Rg", - required: 25000, - reward: enumHubGoalRewards.reward_virtual_processing, - }, - - // 26 Freeplay - { - shape: "CbCuCbCu:Sr------:--CrSrCr:CwCwCwCw", - required: 50000, - reward: enumHubGoalRewards.reward_freeplay, - }, - ]), -]; - -if (G_IS_DEV) { - tutorialGoals.forEach(({ shape }) => { - try { - ShapeDefinition.fromShortKey(shape); - } catch (ex) { - throw new Error("Invalid tutorial goal: '" + ex + "' for shape" + shape); - } - }); -} diff --git a/src/js/game/upgrades.js b/src/js/game/upgrades.js deleted file mode 100644 index db8997a1..00000000 --- a/src/js/game/upgrades.js +++ /dev/null @@ -1,212 +0,0 @@ -import { IS_DEMO } from "../core/config"; -import { findNiceIntegerValue } from "../core/utils"; -import { ShapeDefinition } from "./shape_definition"; - -export const preparementShape = "CpRpCp--:SwSwSwSw"; -export const finalGameShape = "RuCw--Cw:----Ru--"; -export const rocketShape = "CbCuCbCu:Sr------:--CrSrCr:CwCwCwCw"; -export const blueprintShape = "CbCbCbRb:CwCwCwCw"; - -const fixedImprovements = [0.5, 0.5, 1, 1, 2, 1, 1]; - -const numEndgameUpgrades = !IS_DEMO ? 20 - fixedImprovements.length - 1 : 0; - -function generateEndgameUpgrades() { - return new Array(numEndgameUpgrades).fill(null).map((_, i) => ({ - required: [ - { shape: preparementShape, amount: 30000 + i * 10000 }, - { shape: finalGameShape, amount: 20000 + i * 5000 }, - { shape: rocketShape, amount: 20000 + i * 5000 }, - ], - excludePrevious: true, - })); -} - -for (let i = 0; i < numEndgameUpgrades; ++i) { - fixedImprovements.push(0.1); -} - -/** @typedef {{ - * shape: string, - * amount: number - * }} UpgradeRequirement */ - -/** @typedef {{ - * required: Array - * improvement?: number, - * excludePrevious?: boolean - * }} TierRequirement */ - -/** @typedef {Array} UpgradeTiers */ - -/** @type {Object} */ -export const UPGRADES = { - belt: [ - { - required: [{ shape: "CuCuCuCu", amount: 60 }], - }, - { - required: [{ shape: "--CuCu--", amount: 500 }], - }, - { - required: [{ shape: "CpCpCpCp", amount: 1000 }], - }, - { - required: [{ shape: "SrSrSrSr:CyCyCyCy", amount: 6000 }], - }, - { - required: [{ shape: "SrSrSrSr:CyCyCyCy:SwSwSwSw", amount: 25000 }], - }, - { - required: [{ shape: preparementShape, amount: 25000 }], - excludePrevious: true, - }, - { - required: [ - { shape: preparementShape, amount: 25000 }, - { shape: finalGameShape, amount: 50000 }, - ], - excludePrevious: true, - }, - ...generateEndgameUpgrades(), - ], - - miner: [ - { - required: [{ shape: "RuRuRuRu", amount: 300 }], - }, - { - required: [{ shape: "Cu------", amount: 800 }], - }, - { - required: [{ shape: "ScScScSc", amount: 3500 }], - }, - { - required: [{ shape: "CwCwCwCw:WbWbWbWb", amount: 23000 }], - }, - { - required: [{ shape: "CbRbRbCb:CwCwCwCw:WbWbWbWb", amount: 50000 }], - }, - { - required: [{ shape: preparementShape, amount: 25000 }], - excludePrevious: true, - }, - { - required: [ - { shape: preparementShape, amount: 25000 }, - { shape: finalGameShape, amount: 50000 }, - ], - excludePrevious: true, - }, - ...generateEndgameUpgrades(), - ], - - processors: [ - { - required: [{ shape: "SuSuSuSu", amount: 500 }], - }, - { - required: [{ shape: "RuRu----", amount: 600 }], - }, - { - required: [{ shape: "CgScScCg", amount: 3500 }], - }, - { - required: [{ shape: "CwCrCwCr:SgSgSgSg", amount: 25000 }], - }, - { - required: [{ shape: "WrRgWrRg:CwCrCwCr:SgSgSgSg", amount: 50000 }], - }, - { - required: [{ shape: preparementShape, amount: 25000 }], - excludePrevious: true, - }, - { - required: [ - { shape: preparementShape, amount: 25000 }, - { shape: finalGameShape, amount: 50000 }, - ], - excludePrevious: true, - }, - ...generateEndgameUpgrades(), - ], - - painting: [ - { - required: [{ shape: "RbRb----", amount: 600 }], - }, - { - required: [{ shape: "WrWrWrWr", amount: 3800 }], - }, - { - required: [{ shape: "RpRpRpRp:CwCwCwCw", amount: 6500 }], - }, - { - required: [{ shape: "WpWpWpWp:CwCwCwCw:WpWpWpWp", amount: 25000 }], - }, - { - required: [{ shape: "WpWpWpWp:CwCwCwCw:WpWpWpWp:CwCwCwCw", amount: 50000 }], - }, - { - required: [{ shape: preparementShape, amount: 25000 }], - excludePrevious: true, - }, - { - required: [ - { shape: preparementShape, amount: 25000 }, - { shape: finalGameShape, amount: 50000 }, - ], - excludePrevious: true, - }, - ...generateEndgameUpgrades(), - ], -}; - -// Tiers need % of the previous tier as requirement too -const tierGrowth = 2.5; - -// Automatically generate tier levels -for (const upgradeId in UPGRADES) { - const upgradeTiers = UPGRADES[upgradeId]; - - let currentTierRequirements = []; - for (let i = 0; i < upgradeTiers.length; ++i) { - const tierHandle = upgradeTiers[i]; - tierHandle.improvement = fixedImprovements[i]; - const originalRequired = tierHandle.required.slice(); - - for (let k = currentTierRequirements.length - 1; k >= 0; --k) { - const oldTierRequirement = currentTierRequirements[k]; - if (!tierHandle.excludePrevious) { - tierHandle.required.unshift({ - shape: oldTierRequirement.shape, - amount: oldTierRequirement.amount, - }); - } - } - currentTierRequirements.push( - ...originalRequired.map(req => ({ - amount: req.amount, - shape: req.shape, - })) - ); - currentTierRequirements.forEach(tier => { - tier.amount = findNiceIntegerValue(tier.amount * tierGrowth); - }); - } -} - -// VALIDATE -if (G_IS_DEV) { - for (const upgradeId in UPGRADES) { - UPGRADES[upgradeId].forEach(tier => { - tier.required.forEach(({ shape }) => { - try { - ShapeDefinition.fromShortKey(shape); - } catch (ex) { - throw new Error("Invalid upgrade goal: '" + ex + "' for shape" + shape); - } - }); - }); - } -} diff --git a/src/js/languages.js b/src/js/languages.js index c46c3e88..46ff8af9 100644 --- a/src/js/languages.js +++ b/src/js/languages.js @@ -1,113 +1,120 @@ -/** - * @type {Object} - */ -export const LANGUAGES = { - "en": { - name: "English", - data: null, - code: "en", - region: "", - }, - "de": { - name: "Deutsch", - data: require("./built-temp/base-de.json"), - code: "de", - region: "", - }, - "fr": { - name: "Français", - data: require("./built-temp/base-fr.json"), - code: "fr", - region: "", - }, - "ja": { - name: "日本語", - data: require("./built-temp/base-ja.json"), - code: "ja", - region: "", - }, - "pt-PT": { - name: "Português (Portugal)", - data: require("./built-temp/base-pt-PT.json"), - code: "pt", - region: "PT", - }, - "pt-BR": { - name: "Português (Brasil)", - data: require("./built-temp/base-pt-BR.json"), - code: "pt", - region: "BR", - }, - "ru": { - name: "Русский", - data: require("./built-temp/base-ru.json"), - code: "ru", - region: "", - }, - "cs": { - name: "Čeština", - data: require("./built-temp/base-cz.json"), - code: "cs", - region: "", - }, - "es-419": { - name: "Español", - data: require("./built-temp/base-es.json"), - code: "es", - region: "", - }, - "pl": { - name: "Polski", - data: require("./built-temp/base-pl.json"), - code: "pl", - region: "", - }, - "kor": { - name: "한국어", - data: require("./built-temp/base-kor.json"), - code: "kor", - region: "", - }, - "nl": { - name: "Nederlands", - data: require("./built-temp/base-nl.json"), - code: "nl", - region: "", - }, - "no": { - name: "Norsk", - data: require("./built-temp/base-no.json"), - code: "no", - region: "", - }, - - "tr": { - name: "Türkçe", - data: require("./built-temp/base-tr.json"), - code: "tr", - 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"), - code: "sv", - region: "", - }, -}; +/** + * @type {Object} + */ +export const LANGUAGES = { + "en": { + name: "English", + data: null, + code: "en", + region: "", + }, + "de": { + name: "Deutsch", + data: require("./built-temp/base-de.json"), + code: "de", + region: "", + }, + "fr": { + name: "Français", + data: require("./built-temp/base-fr.json"), + code: "fr", + region: "", + }, + "ja": { + name: "日本語", + data: require("./built-temp/base-ja.json"), + code: "ja", + region: "", + }, + "pt-PT": { + name: "Português (Portugal)", + data: require("./built-temp/base-pt-PT.json"), + code: "pt", + region: "PT", + }, + "pt-BR": { + name: "Português (Brasil)", + data: require("./built-temp/base-pt-BR.json"), + code: "pt", + region: "BR", + }, + "ru": { + name: "Русский", + data: require("./built-temp/base-ru.json"), + code: "ru", + region: "", + }, + "cs": { + name: "Čeština", + data: require("./built-temp/base-cz.json"), + code: "cs", + region: "", + }, + "es-419": { + name: "Español", + data: require("./built-temp/base-es.json"), + code: "es", + region: "", + }, + "pl": { + name: "Polski", + data: require("./built-temp/base-pl.json"), + code: "pl", + region: "", + }, + "kor": { + name: "한국어", + data: require("./built-temp/base-kor.json"), + code: "kor", + region: "", + }, + "nl": { + name: "Nederlands", + data: require("./built-temp/base-nl.json"), + code: "nl", + region: "", + }, + "no": { + name: "Norsk", + data: require("./built-temp/base-no.json"), + code: "no", + region: "", + }, + + "tr": { + name: "Türkçe", + data: require("./built-temp/base-tr.json"), + code: "tr", + 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"), + code: "sv", + region: "", + }, + + "da": { + name: "Dansk", + data: require("./built-temp/base-da.json"), + code: "da", + region: "", + }, +}; diff --git a/src/js/platform/browser/game_analytics.js b/src/js/platform/browser/game_analytics.js index ea23509b..a3947be6 100644 --- a/src/js/platform/browser/game_analytics.js +++ b/src/js/platform/browser/game_analytics.js @@ -1,14 +1,12 @@ import { globalConfig } from "../../core/config"; import { createLogger } from "../../core/logging"; +import { queryParamOptions } from "../../core/query_parameters"; +import { BeltComponent } from "../../game/components/belt"; +import { StaticMapEntityComponent } from "../../game/components/static_map_entity"; 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"; -import { queryParamOptions } from "../../core/query_parameters"; const logger = createLogger("game_analytics"); @@ -190,23 +188,26 @@ export class ShapezGameAnalytics extends GameAnalyticsInterface { /** * Returns true if the shape is interesting + * @param {GameRoot} root * @param {string} key */ - isInterestingShape(key) { - if (key === blueprintShape) { + isInterestingShape(root, key) { + if (key === root.gameMode.getBlueprintShapeKey()) { return true; } // Check if its a story goal - for (let i = 0; i < tutorialGoals.length; ++i) { - if (key === tutorialGoals[i].shape) { + const levels = root.gameMode.getLevelDefinitions(); + for (let i = 0; i < levels.length; ++i) { + if (key === levels[i].shape) { return true; } } // Check if its required to unlock an upgrade - for (const upgradeKey in UPGRADES) { - const upgradeTiers = UPGRADES[upgradeKey]; + const upgrades = root.gameMode.getUpgrades(); + for (const upgradeKey in upgrades) { + const upgradeTiers = upgrades[upgradeKey]; for (let i = 0; i < upgradeTiers.length; ++i) { const tier = upgradeTiers[i]; const required = tier.required; @@ -226,7 +227,9 @@ export class ShapezGameAnalytics extends GameAnalyticsInterface { * @param {GameRoot} root */ generateGameDump(root) { - const shapeIds = Object.keys(root.hubGoals.storedShapes).filter(this.isInterestingShape.bind(this)); + const shapeIds = Object.keys(root.hubGoals.storedShapes).filter(key => + this.isInterestingShape(root, key) + ); let shapes = {}; for (let i = 0; i < shapeIds.length; ++i) { shapes[shapeIds[i]] = root.hubGoals.storedShapes[shapeIds[i]]; diff --git a/src/js/platform/browser/wrapper.js b/src/js/platform/browser/wrapper.js index 56705025..232a743b 100644 --- a/src/js/platform/browser/wrapper.js +++ b/src/js/platform/browser/wrapper.js @@ -1,214 +1,202 @@ -import { globalConfig, IS_DEMO, IS_MOBILE } from "../../core/config"; -import { createLogger } from "../../core/logging"; -import { queryParamOptions } from "../../core/query_parameters"; -import { clamp } from "../../core/utils"; -import { GamedistributionAdProvider } from "../ad_providers/gamedistribution"; -import { NoAdProvider } from "../ad_providers/no_ad_provider"; -import { PlatformWrapperInterface } from "../wrapper"; -import { StorageImplBrowser } from "./storage"; -import { StorageImplBrowserIndexedDB } from "./storage_indexed_db"; - -const logger = createLogger("platform/browser"); - -export class PlatformWrapperImplBrowser extends PlatformWrapperInterface { - initialize() { - this.recaptchaTokenCallback = null; - - this.embedProvider = { - id: "shapezio-website", - adProvider: NoAdProvider, - iframed: false, - externalLinks: true, - iogLink: true, - unlimitedSavegames: IS_DEMO ? false : true, - showDemoBadge: IS_DEMO, - }; - - if (!G_IS_STANDALONE && queryParamOptions.embedProvider) { - const providerId = queryParamOptions.embedProvider; - this.embedProvider.iframed = true; - this.embedProvider.iogLink = false; - - switch (providerId) { - case "armorgames": { - this.embedProvider.id = "armorgames"; - break; - } - - case "iogames.space": { - this.embedProvider.id = "iogames.space"; - this.embedProvider.iogLink = true; - this.embedProvider.unlimitedSavegames = true; - this.embedProvider.showDemoBadge = false; - break; - } - - case "miniclip": { - this.embedProvider.id = "miniclip"; - break; - } - - case "gamedistribution": { - this.embedProvider.id = "gamedistribution"; - this.embedProvider.externalLinks = false; - this.embedProvider.adProvider = GamedistributionAdProvider; - break; - } - - case "kongregate": { - this.embedProvider.id = "kongregate"; - break; - } - - case "crazygames": { - this.embedProvider.id = "crazygames"; - break; - } - - default: { - logger.error("Got unsupported embed provider:", providerId); - } - } - } - - logger.log("Embed provider:", this.embedProvider.id); - - return this.detectStorageImplementation() - .then(() => this.initializeAdProvider()) - .then(() => super.initialize()); - } - - detectStorageImplementation() { - return new Promise(resolve => { - logger.log("Detecting storage"); - - if (!window.indexedDB) { - logger.log("Indexed DB not supported"); - this.app.storage = new StorageImplBrowser(this.app); - resolve(); - return; - } - - // Try accessing the indexedb - let request; - try { - request = window.indexedDB.open("indexeddb_feature_detection", 1); - } catch (ex) { - logger.warn("Error while opening indexed db:", ex); - this.app.storage = new StorageImplBrowser(this.app); - resolve(); - return; - } - request.onerror = err => { - logger.log("Indexed DB can *not* be accessed: ", err); - logger.log("Using fallback to local storage"); - this.app.storage = new StorageImplBrowser(this.app); - resolve(); - }; - request.onsuccess = () => { - logger.log("Indexed DB *can* be accessed"); - this.app.storage = new StorageImplBrowserIndexedDB(this.app); - resolve(); - }; - }); - } - - getHasUnlimitedSavegames() { - return this.embedProvider.unlimitedSavegames; - } - - getShowDemoBadges() { - return this.embedProvider.showDemoBadge; - } - - getId() { - return "browser@" + this.embedProvider.id; - } - - getUiScale() { - if (IS_MOBILE) { - return 1; - } - - const avgDims = Math.min(this.app.screenWidth, this.app.screenHeight); - return clamp((avgDims / 1000.0) * 1.9, 0.1, 10); - } - - getSupportsRestart() { - return true; - } - - getTouchPanStrength() { - return IS_MOBILE ? 1 : 0.5; - } - - openExternalLink(url, force = false) { - logger.log("Opening external:", url); - if (force || this.embedProvider.externalLinks) { - window.open(url); - } else { - // Do nothing - alert( - "This platform does not allow opening external links. You can play on https://shapez.io directly to open them.\n\nClicked Link: " + - url - ); - } - } - - performRestart() { - logger.log("Performing restart"); - window.location.reload(true); - } - - /** - * Detects if there is an adblocker installed - * @returns {Promise} - */ - detectAdblock() { - return Promise.race([ - new Promise(resolve => { - // If the request wasn't blocked within a very short period of time, this means - // the adblocker is not active and the request was actually made -> ignore it then - setTimeout(() => resolve(false), 30); - }), - new Promise(resolve => { - fetch("https://googleads.g.doubleclick.net/pagead/id", { - method: "HEAD", - mode: "no-cors", - }) - .then(res => { - resolve(false); - }) - .catch(err => { - resolve(true); - }); - }), - ]); - } - - initializeAdProvider() { - if (G_IS_DEV && !globalConfig.debug.testAds) { - logger.log("Ads disabled in local environment"); - return Promise.resolve(); - } - - // First, detect adblocker - return this.detectAdblock().then(hasAdblocker => { - if (hasAdblocker) { - logger.log("Adblock detected"); - return; - } - - const adProvider = this.embedProvider.adProvider; - this.app.adProvider = new adProvider(this.app); - return this.app.adProvider.initialize().catch(err => { - logger.error("Failed to initialize ad provider, disabling ads:", err); - this.app.adProvider = new NoAdProvider(this.app); - }); - }); - } - - exitApp() { - // Can not exit app - } -} +import { globalConfig, IS_MOBILE } from "../../core/config"; +import { createLogger } from "../../core/logging"; +import { queryParamOptions } from "../../core/query_parameters"; +import { clamp } from "../../core/utils"; +import { GamedistributionAdProvider } from "../ad_providers/gamedistribution"; +import { NoAdProvider } from "../ad_providers/no_ad_provider"; +import { PlatformWrapperInterface } from "../wrapper"; +import { StorageImplBrowser } from "./storage"; +import { StorageImplBrowserIndexedDB } from "./storage_indexed_db"; + +const logger = createLogger("platform/browser"); + +export class PlatformWrapperImplBrowser extends PlatformWrapperInterface { + initialize() { + this.recaptchaTokenCallback = null; + + this.embedProvider = { + id: "shapezio-website", + adProvider: NoAdProvider, + iframed: false, + externalLinks: true, + iogLink: true, + }; + + if (!G_IS_STANDALONE && queryParamOptions.embedProvider) { + const providerId = queryParamOptions.embedProvider; + this.embedProvider.iframed = true; + this.embedProvider.iogLink = false; + + switch (providerId) { + case "armorgames": { + this.embedProvider.id = "armorgames"; + break; + } + + case "iogames.space": { + this.embedProvider.id = "iogames.space"; + this.embedProvider.iogLink = true; + break; + } + + case "miniclip": { + this.embedProvider.id = "miniclip"; + break; + } + + case "gamedistribution": { + this.embedProvider.id = "gamedistribution"; + this.embedProvider.externalLinks = false; + this.embedProvider.adProvider = GamedistributionAdProvider; + break; + } + + case "kongregate": { + this.embedProvider.id = "kongregate"; + break; + } + + case "crazygames": { + this.embedProvider.id = "crazygames"; + break; + } + + default: { + logger.error("Got unsupported embed provider:", providerId); + } + } + } + + logger.log("Embed provider:", this.embedProvider.id); + + return this.detectStorageImplementation() + .then(() => this.initializeAdProvider()) + .then(() => super.initialize()); + } + + detectStorageImplementation() { + return new Promise(resolve => { + logger.log("Detecting storage"); + + if (!window.indexedDB) { + logger.log("Indexed DB not supported"); + this.app.storage = new StorageImplBrowser(this.app); + resolve(); + return; + } + + // Try accessing the indexedb + let request; + try { + request = window.indexedDB.open("indexeddb_feature_detection", 1); + } catch (ex) { + logger.warn("Error while opening indexed db:", ex); + this.app.storage = new StorageImplBrowser(this.app); + resolve(); + return; + } + request.onerror = err => { + logger.log("Indexed DB can *not* be accessed: ", err); + logger.log("Using fallback to local storage"); + this.app.storage = new StorageImplBrowser(this.app); + resolve(); + }; + request.onsuccess = () => { + logger.log("Indexed DB *can* be accessed"); + this.app.storage = new StorageImplBrowserIndexedDB(this.app); + resolve(); + }; + }); + } + + getId() { + return "browser@" + this.embedProvider.id; + } + + getUiScale() { + if (IS_MOBILE) { + return 1; + } + + const avgDims = Math.min(this.app.screenWidth, this.app.screenHeight); + return clamp((avgDims / 1000.0) * 1.9, 0.1, 10); + } + + getSupportsRestart() { + return true; + } + + getTouchPanStrength() { + return IS_MOBILE ? 1 : 0.5; + } + + openExternalLink(url, force = false) { + logger.log("Opening external:", url); + if (force || this.embedProvider.externalLinks) { + window.open(url); + } else { + // Do nothing + alert( + "This platform does not allow opening external links. You can play on https://shapez.io directly to open them.\n\nClicked Link: " + + url + ); + } + } + + performRestart() { + logger.log("Performing restart"); + window.location.reload(true); + } + + /** + * Detects if there is an adblocker installed + * @returns {Promise} + */ + detectAdblock() { + return Promise.race([ + new Promise(resolve => { + // If the request wasn't blocked within a very short period of time, this means + // the adblocker is not active and the request was actually made -> ignore it then + setTimeout(() => resolve(false), 30); + }), + new Promise(resolve => { + fetch("https://googleads.g.doubleclick.net/pagead/id", { + method: "HEAD", + mode: "no-cors", + }) + .then(res => { + resolve(false); + }) + .catch(err => { + resolve(true); + }); + }), + ]); + } + + initializeAdProvider() { + if (G_IS_DEV && !globalConfig.debug.testAds) { + logger.log("Ads disabled in local environment"); + return Promise.resolve(); + } + + // First, detect adblocker + return this.detectAdblock().then(hasAdblocker => { + if (hasAdblocker) { + logger.log("Adblock detected"); + return; + } + + const adProvider = this.embedProvider.adProvider; + this.app.adProvider = new adProvider(this.app); + return this.app.adProvider.initialize().catch(err => { + logger.error("Failed to initialize ad provider, disabling ads:", err); + this.app.adProvider = new NoAdProvider(this.app); + }); + }); + } + + exitApp() { + // Can not exit app + } +} diff --git a/src/js/platform/electron/wrapper.js b/src/js/platform/electron/wrapper.js index 69bc9695..941aff44 100644 --- a/src/js/platform/electron/wrapper.js +++ b/src/js/platform/electron/wrapper.js @@ -1,65 +1,57 @@ -import { PlatformWrapperImplBrowser } from "../browser/wrapper"; -import { getIPCRenderer } from "../../core/utils"; -import { createLogger } from "../../core/logging"; -import { StorageImplElectron } from "./storage"; -import { PlatformWrapperInterface } from "../wrapper"; - -const logger = createLogger("electron-wrapper"); - -export class PlatformWrapperImplElectron extends PlatformWrapperImplBrowser { - initialize() { - this.app.storage = new StorageImplElectron(this); - return PlatformWrapperInterface.prototype.initialize.call(this); - } - - getId() { - return "electron"; - } - - getSupportsRestart() { - return true; - } - - openExternalLink(url) { - logger.log(this, "Opening external:", url); - window.open(url, "about:blank"); - } - - getSupportsAds() { - return false; - } - - getHasUnlimitedSavegames() { - return true; - } - - getShowDemoBadges() { - return false; - } - - performRestart() { - logger.log(this, "Performing restart"); - window.location.reload(true); - } - - initializeAdProvider() { - return Promise.resolve(); - } - - getSupportsFullscreen() { - return true; - } - - setFullscreen(flag) { - getIPCRenderer().send("set-fullscreen", flag); - } - - getSupportsAppExit() { - return true; - } - - exitApp() { - logger.log(this, "Sending app exit signal"); - getIPCRenderer().send("exit-app"); - } -} +import { PlatformWrapperImplBrowser } from "../browser/wrapper"; +import { getIPCRenderer } from "../../core/utils"; +import { createLogger } from "../../core/logging"; +import { StorageImplElectron } from "./storage"; +import { PlatformWrapperInterface } from "../wrapper"; + +const logger = createLogger("electron-wrapper"); + +export class PlatformWrapperImplElectron extends PlatformWrapperImplBrowser { + initialize() { + this.app.storage = new StorageImplElectron(this); + return PlatformWrapperInterface.prototype.initialize.call(this); + } + + getId() { + return "electron"; + } + + getSupportsRestart() { + return true; + } + + openExternalLink(url) { + logger.log(this, "Opening external:", url); + window.open(url, "about:blank"); + } + + getSupportsAds() { + return false; + } + + performRestart() { + logger.log(this, "Performing restart"); + window.location.reload(true); + } + + initializeAdProvider() { + return Promise.resolve(); + } + + getSupportsFullscreen() { + return true; + } + + setFullscreen(flag) { + getIPCRenderer().send("set-fullscreen", flag); + } + + getSupportsAppExit() { + return true; + } + + exitApp() { + logger.log(this, "Sending app exit signal"); + getIPCRenderer().send("exit-app"); + } +} diff --git a/src/js/platform/sound.js b/src/js/platform/sound.js index 51dca8d4..9d5a8461 100644 --- a/src/js/platform/sound.js +++ b/src/js/platform/sound.js @@ -6,7 +6,7 @@ import { GameRoot } from "../game/root"; import { newEmptyMap, clamp } from "../core/utils"; import { createLogger } from "../core/logging"; -import { globalConfig, IS_DEMO } from "../core/config"; +import { globalConfig } from "../core/config"; const logger = createLogger("sound"); @@ -29,7 +29,9 @@ export const SOUNDS = { }; export const MUSIC = { - theme: IS_DEMO ? "theme-short" : "theme-full", + // The theme always depends on the standalone only, even if running the full + // version in the browser + theme: G_IS_STANDALONE ? "theme-full" : "theme-short", menu: "menu", }; diff --git a/src/js/platform/wrapper.js b/src/js/platform/wrapper.js index 9c35a8e4..f80c2fd6 100644 --- a/src/js/platform/wrapper.js +++ b/src/js/platform/wrapper.js @@ -1,142 +1,131 @@ -/* typehints:start */ -import { Application } from "../application"; -/* typehints:end */ - -import { IS_MOBILE } from "../core/config"; - -export class PlatformWrapperInterface { - constructor(app) { - /** @type {Application} */ - this.app = app; - } - - /** @returns {string} */ - getId() { - abstract; - return "unknown-platform"; - } - - /** - * Returns the UI scale, called on every resize - * @returns {number} */ - getUiScale() { - return 1; - } - - /** @returns {boolean} */ - getSupportsRestart() { - abstract; - return false; - } - - /** - * Whether the user has unlimited savegames - */ - getHasUnlimitedSavegames() { - return true; - } - - getShowDemoBadges() { - return false; - } - - /** - * Returns the strength of touch pans with the mouse - */ - getTouchPanStrength() { - return 1; - } - - /** @returns {Promise} */ - initialize() { - document.documentElement.classList.add("p-" + this.getId()); - return Promise.resolve(); - } - - /** - * Should initialize the apps ad provider in case supported - * @returns {Promise} - */ - initializeAdProvider() { - return Promise.resolve(); - } - - /** - * Should return the minimum supported zoom level - * @returns {number} - */ - getMinimumZoom() { - return 0.1 * this.getScreenScale(); - } - - /** - * Should return the maximum supported zoom level - * @returns {number} - */ - getMaximumZoom() { - return 3.5 * this.getScreenScale(); - } - - getScreenScale() { - return Math.min(window.innerWidth, window.innerHeight) / 1024.0; - } - - /** - * Should return if this platform supports ads at all - */ - getSupportsAds() { - return false; - } - - /** - * Attempt to open an external url - * @param {string} url - * @param {boolean=} force Whether to always open the url even if not allowed - */ - openExternalLink(url, force = false) { - abstract; - } - - /** - * Attempt to restart the app - */ - performRestart() { - abstract; - } - - /** - * Returns whether this platform supports a toggleable fullscreen - */ - getSupportsFullscreen() { - return false; - } - - /** - * Should set the apps fullscreen state to the desired state - * @param {boolean} flag - */ - setFullscreen(flag) { - abstract; - } - - /** - * Returns whether this platform supports quitting the app - */ - getSupportsAppExit() { - return false; - } - - /** - * Attempts to quit the app - */ - exitApp() { - abstract; - } - - /** - * Whether this platform supports a keyboard - */ - getSupportsKeyboard() { - return !IS_MOBILE; - } -} +/* typehints:start */ +import { Application } from "../application"; +/* typehints:end */ + +import { IS_MOBILE } from "../core/config"; + +export class PlatformWrapperInterface { + constructor(app) { + /** @type {Application} */ + this.app = app; + } + + /** @returns {string} */ + getId() { + abstract; + return "unknown-platform"; + } + + /** + * Returns the UI scale, called on every resize + * @returns {number} */ + getUiScale() { + return 1; + } + + /** @returns {boolean} */ + getSupportsRestart() { + abstract; + return false; + } + + /** + * Returns the strength of touch pans with the mouse + */ + getTouchPanStrength() { + return 1; + } + + /** @returns {Promise} */ + initialize() { + document.documentElement.classList.add("p-" + this.getId()); + return Promise.resolve(); + } + + /** + * Should initialize the apps ad provider in case supported + * @returns {Promise} + */ + initializeAdProvider() { + return Promise.resolve(); + } + + /** + * Should return the minimum supported zoom level + * @returns {number} + */ + getMinimumZoom() { + return 0.1 * this.getScreenScale(); + } + + /** + * Should return the maximum supported zoom level + * @returns {number} + */ + getMaximumZoom() { + return 3.5 * this.getScreenScale(); + } + + getScreenScale() { + return Math.min(window.innerWidth, window.innerHeight) / 1024.0; + } + + /** + * Should return if this platform supports ads at all + */ + getSupportsAds() { + return false; + } + + /** + * Attempt to open an external url + * @param {string} url + * @param {boolean=} force Whether to always open the url even if not allowed + */ + openExternalLink(url, force = false) { + abstract; + } + + /** + * Attempt to restart the app + */ + performRestart() { + abstract; + } + + /** + * Returns whether this platform supports a toggleable fullscreen + */ + getSupportsFullscreen() { + return false; + } + + /** + * Should set the apps fullscreen state to the desired state + * @param {boolean} flag + */ + setFullscreen(flag) { + abstract; + } + + /** + * Returns whether this platform supports quitting the app + */ + getSupportsAppExit() { + return false; + } + + /** + * Attempts to quit the app + */ + exitApp() { + abstract; + } + + /** + * Whether this platform supports a keyboard + */ + getSupportsKeyboard() { + return !IS_MOBILE; + } +} diff --git a/src/js/profile/application_settings.js b/src/js/profile/application_settings.js index ace30eff..061e3ead 100644 --- a/src/js/profile/application_settings.js +++ b/src/js/profile/application_settings.js @@ -6,8 +6,7 @@ import { ReadWriteProxy } from "../core/read_write_proxy"; import { BoolSetting, EnumSetting, RangeSetting, BaseSetting } from "./setting_types"; import { createLogger } from "../core/logging"; import { ExplainedResult } from "../core/explained_result"; -import { THEMES, THEME, applyGameTheme } from "../game/theme"; -import { IS_DEMO } from "../core/config"; +import { THEMES, applyGameTheme } from "../game/theme"; import { T } from "../translations"; import { LANGUAGES } from "../languages"; @@ -187,7 +186,9 @@ export const allApplicationSettings = [ app.platformWrapper.setFullscreen(value); } }, - !IS_DEMO + /** + * @param {Application} app + */ app => app.restrictionMgr.getHasExtendedSettings() ), new BoolSetting( @@ -215,7 +216,9 @@ export const allApplicationSettings = [ applyGameTheme(id); document.documentElement.setAttribute("data-theme", id); }, - enabled: !IS_DEMO, + enabledCb: /** + * @param {Application} app + */ app => app.restrictionMgr.getHasExtendedSettings(), }), new EnumSetting("autosaveInterval", { @@ -255,6 +258,7 @@ export const allApplicationSettings = [ new BoolSetting("enableMousePan", enumCategories.advanced, (app, value) => {}), new BoolSetting("alwaysMultiplace", enumCategories.advanced, (app, value) => {}), + new BoolSetting("zoomToCursor", enumCategories.advanced, (app, value) => {}), new BoolSetting("clearCursorOnDeleteWhilePlacing", enumCategories.advanced, (app, value) => {}), new BoolSetting("enableTunnelSmartplace", enumCategories.advanced, (app, value) => {}), new BoolSetting("vignette", enumCategories.userInterface, (app, value) => {}), @@ -263,6 +267,7 @@ export const allApplicationSettings = [ new BoolSetting("rotationByBuilding", enumCategories.advanced, (app, value) => {}), new BoolSetting("displayChunkBorders", enumCategories.advanced, (app, value) => {}), new BoolSetting("pickMinerOnPatch", enumCategories.advanced, (app, value) => {}), + new RangeSetting("mapResourcesScale", enumCategories.advanced, () => null), new EnumSetting("refreshRate", { options: refreshRateOptions, @@ -271,7 +276,9 @@ export const allApplicationSettings = [ category: enumCategories.performance, restartRequired: false, changeCb: (app, id) => {}, - enabled: !IS_DEMO, + enabledCb: /** + * @param {Application} app + */ app => app.restrictionMgr.getHasExtendedSettings(), }), new BoolSetting("lowQualityMapResources", enumCategories.performance, (app, value) => {}), @@ -317,6 +324,8 @@ class SettingsStorage { this.disableTileGrid = false; this.lowQualityTextures = false; this.simplifiedBelts = false; + this.zoomToCursor = true; + this.mapResourcesScale = 0.5; /** * @type {Object.} @@ -355,7 +364,7 @@ export class ApplicationSettings extends ReadWriteProxy { * @returns {SettingsStorage} */ getAllSettings() { - return this.getCurrentData().settings; + return this.currentData.settings; } /** @@ -527,7 +536,7 @@ export class ApplicationSettings extends ReadWriteProxy { } getCurrentVersion() { - return 28; + return 30; } /** @param {{settings: SettingsStorage, version: number}} data */ @@ -660,6 +669,16 @@ export class ApplicationSettings extends ReadWriteProxy { data.version = 28; } + if (data.version < 29) { + data.settings.zoomToCursor = true; + data.version = 29; + } + + if (data.version < 30) { + data.settings.mapResourcesScale = 0.5; + data.version = 30; + } + return ExplainedResult.good(); } } diff --git a/src/js/profile/setting_types.js b/src/js/profile/setting_types.js index 7d07ca99..4df02892 100644 --- a/src/js/profile/setting_types.js +++ b/src/js/profile/setting_types.js @@ -7,19 +7,30 @@ import { T } from "../translations"; const logger = createLogger("setting_types"); +/* + * *************************************************** + * + * LEGACY CODE WARNING + * + * This is old code from yorg3.io and needs to be refactored + * @TODO + * + * *************************************************** + */ + export class BaseSetting { /** * * @param {string} id * @param {string} categoryId * @param {function(Application, any):void} changeCb - * @param {boolean} enabled + * @param {function(Application) : boolean=} enabledCb */ - constructor(id, categoryId, changeCb, enabled) { + constructor(id, categoryId, changeCb, enabledCb = null) { this.id = id; this.categoryId = categoryId; this.changeCb = changeCb; - this.enabled = enabled; + this.enabledCb = enabledCb; /** @type {Application} */ this.app = null; @@ -39,6 +50,7 @@ export class BaseSetting { } /** + * Binds all parameters * @param {Application} app * @param {HTMLElement} element * @param {any} dialogs @@ -49,19 +61,37 @@ export class BaseSetting { this.dialogs = dialogs; } - getHtml() { + /** + * Returns the HTML for this setting + * @param {Application} app + */ + getHtml(app) { abstract; return ""; } + /** + * Returns whether this setting is enabled and available + * @param {Application} app + */ + getIsAvailable(app) { + return this.enabledCb ? this.enabledCb(app) : true; + } + syncValueToElement() { abstract; } + /** + * Attempts to modify the setting + */ modify() { abstract; } + /** + * Shows the dialog that a restart is required + */ showRestartRequiredDialog() { const { restart } = this.dialogs.showInfo( T.dialogs.restartRequired.title, @@ -74,6 +104,7 @@ export class BaseSetting { } /** + * Validates the set value * @param {any} value * @returns {boolean} */ @@ -96,10 +127,10 @@ export class EnumSetting extends BaseSetting { iconPrefix = null, changeCb = null, magicValue = null, - enabled = true, + enabledCb = null, } ) { - super(id, category, changeCb, enabled); + super(id, category, changeCb, enabledCb); this.options = options; this.valueGetter = valueGetter; @@ -110,10 +141,14 @@ export class EnumSetting extends BaseSetting { this.magicValue = magicValue; } - getHtml() { + /** + * @param {Application} app + */ + getHtml(app) { + const available = this.getIsAvailable(app); return ` -
- ${this.enabled ? "" : `${T.demo.settingNotAvailable}`} +
+ ${available ? "" : `${T.demo.settingNotAvailable}`}
@@ -180,14 +215,18 @@ export class EnumSetting extends BaseSetting { } export class BoolSetting extends BaseSetting { - constructor(id, category, changeCb = null, enabled = true) { - super(id, category, changeCb, enabled); + constructor(id, category, changeCb = null, enabledCb = null) { + super(id, category, changeCb, enabledCb); } - getHtml() { + /** + * @param {Application} app + */ + getHtml(app) { + const available = this.getIsAvailable(app); return ` -
- ${this.enabled ? "" : `${T.demo.settingNotAvailable}`} +
+ ${available ? "" : `${T.demo.settingNotAvailable}`}
@@ -226,13 +265,13 @@ export class RangeSetting extends BaseSetting { id, category, changeCb = null, - enabled = true, defaultValue = 1.0, minValue = 0, maxValue = 1.0, - stepSize = 0.0001 + stepSize = 0.0001, + enabledCb = null ) { - super(id, category, changeCb, enabled); + super(id, category, changeCb, enabledCb); this.defaultValue = defaultValue; this.minValue = minValue; @@ -240,10 +279,14 @@ export class RangeSetting extends BaseSetting { this.stepSize = stepSize; } - getHtml() { + /** + * @param {Application} app + */ + getHtml(app) { + const available = this.getIsAvailable(app); return ` -
- ${this.enabled ? "" : `${T.demo.settingNotAvailable}`} +
+ ${available ? "" : `${T.demo.settingNotAvailable}`}
diff --git a/src/js/savegame/savegame_manager.js b/src/js/savegame/savegame_manager.js index 52f9dc14..ed31dbcf 100644 --- a/src/js/savegame/savegame_manager.js +++ b/src/js/savegame/savegame_manager.js @@ -40,13 +40,6 @@ export class SavegameManager extends ReadWriteProxy { return 1002; } - /** - * @returns {SavegamesData} - */ - getCurrentData() { - return super.getCurrentData(); - } - verify(data) { // TODO / FIXME!!!! return ExplainedResult.good(); @@ -96,6 +89,14 @@ export class SavegameManager extends ReadWriteProxy { return new Savegame(this.app, { internalId, metaDataRef: metadata }); } + /** + * Returns if this manager has any savegame of a 1.1.19 version, which + * enables all levels + */ + getHasAnyLegacySavegames() { + return this.currentData.savegames.some(savegame => savegame.version === 1005 || savegame.level > 14); + } + /** * Deletes a savegame * @param {SavegameMetadata} game @@ -149,7 +150,9 @@ export class SavegameManager extends ReadWriteProxy { }); this.currentData.savegames.push(metaData); - this.sortSavegames(); + + // Notice: This is async and happening in the background + this.updateAfterSavegamesChanged(); return new Savegame(this.app, { internalId: id, @@ -157,8 +160,16 @@ export class SavegameManager extends ReadWriteProxy { }); } + /** + * Attempts to import a savegame + * @param {object} data + */ importSavegame(data) { const savegame = this.createNewSavegame(); + + // Track legacy savegames + const isOldSavegame = data.version < 1006; + const migrationResult = savegame.migrate(data); if (migrationResult.isBad()) { return Promise.reject("Failed to migrate: " + migrationResult.reason); @@ -170,7 +181,19 @@ export class SavegameManager extends ReadWriteProxy { return Promise.reject("Verification failed: " + verification.result); } - return savegame.writeSavegameAndMetadata().then(() => this.sortSavegames()); + return savegame + .writeSavegameAndMetadata() + .then(() => this.updateAfterSavegamesChanged()) + .then(() => this.app.restrictionMgr.onHasLegacySavegamesChanged(isOldSavegame)); + } + + /** + * Hook after the savegames got changed + */ + updateAfterSavegamesChanged() { + return this.sortSavegames() + .then(() => this.writeAsync()) + .then(() => this.app.restrictionMgr.onHasLegacySavegamesChanged(this.getHasAnyLegacySavegames())); } /** @@ -219,7 +242,7 @@ export class SavegameManager extends ReadWriteProxy { if (G_IS_DEV && globalConfig.debug.disableSavegameWrite) { return Promise.resolve(); } - return this.sortSavegames().then(() => this.writeAsync()); + return this.updateAfterSavegamesChanged(); }); } } diff --git a/src/js/savegame/savegame_serializer.js b/src/js/savegame/savegame_serializer.js index 552bc35c..c1247225 100644 --- a/src/js/savegame/savegame_serializer.js +++ b/src/js/savegame/savegame_serializer.js @@ -130,7 +130,7 @@ export class SavegameSerializer { errorReason = errorReason || root.time.deserialize(savegame.time); errorReason = errorReason || root.camera.deserialize(savegame.camera); errorReason = errorReason || root.map.deserialize(savegame.map); - errorReason = errorReason || root.hubGoals.deserialize(savegame.hubGoals); + errorReason = errorReason || root.hubGoals.deserialize(savegame.hubGoals, root); errorReason = errorReason || root.hud.parts.pinnedShapes.deserialize(savegame.pinnedShapes); errorReason = errorReason || root.hud.parts.waypoints.deserialize(savegame.waypoints); errorReason = errorReason || this.internal.deserializeEntityArray(root, savegame.entities); diff --git a/src/js/savegame/schemas/1006.js b/src/js/savegame/schemas/1006.js index 29f2c64f..79226772 100644 --- a/src/js/savegame/schemas/1006.js +++ b/src/js/savegame/schemas/1006.js @@ -19,7 +19,6 @@ import { getCodeFromBuildingData } from "../../game/building_codes.js"; import { StaticMapEntityComponent } from "../../game/components/static_map_entity.js"; import { Entity } from "../../game/entity.js"; import { defaultBuildingVariant, MetaBuilding } from "../../game/meta_building.js"; -import { finalGameShape } from "../../game/upgrades.js"; import { SavegameInterface_V1005 } from "./1005.js"; const schema = require("./1006.json"); @@ -152,7 +151,8 @@ export class SavegameInterface_V1006 extends SavegameInterface_V1005 { stored[shapeKey] = rebalance(stored[shapeKey]); } - stored[finalGameShape] = 0; + // Reset final game shape + stored["RuCw--Cw:----Ru--"] = 0; // Reduce goals if (dump.hubGoals.currentGoal) { @@ -248,7 +248,7 @@ export class SavegameInterface_V1006 extends SavegameInterface_V1005 { if (components.Storage) { // @ts-ignore components.Storage = { - storedCount: 0, + storedCount: rebalance(components.Storage.storedCount), storedItem: null, }; } diff --git a/src/js/savegame/serialization.js b/src/js/savegame/serialization.js index 9f998a0f..801b26ab 100644 --- a/src/js/savegame/serialization.js +++ b/src/js/savegame/serialization.js @@ -1,344 +1,351 @@ -import { createLogger } from "../core/logging"; -import { - BaseDataType, - TypeArray, - TypeBoolean, - TypeClass, - TypeClassData, - TypeClassFromMetaclass, - TypeClassId, - TypeEntity, - TypeEntityWeakref, - TypeEnum, - TypeFixedClass, - TypeInteger, - TypeKeyValueMap, - TypeMetaClass, - TypeNullable, - TypeNumber, - TypePair, - TypePositiveInteger, - TypePositiveNumber, - TypeString, - TypeStructuredObject, - TypeVector, -} from "./serialization_data_types"; - -const logger = createLogger("serialization"); - -// Schema declarations -export const types = { - int: new TypeInteger(), - uint: new TypePositiveInteger(), - float: new TypeNumber(), - ufloat: new TypePositiveNumber(), - string: new TypeString(), - entity: new TypeEntity(), - weakEntityRef: new TypeEntityWeakref(), - vector: new TypeVector(), - tileVector: new TypeVector(), - bool: new TypeBoolean(), - - /** - * @param {BaseDataType} wrapped - */ - nullable(wrapped) { - return new TypeNullable(wrapped); - }, - - /** - * @param {FactoryTemplate<*>|SingletonFactoryTemplate<*>} registry - */ - classId(registry) { - return new TypeClassId(registry); - }, - /** - * @param {BaseDataType} valueType - * @param {boolean=} includeEmptyValues - */ - keyValueMap(valueType, includeEmptyValues = true) { - return new TypeKeyValueMap(valueType, includeEmptyValues); - }, - - /** - * @param {Object} values - */ - enum(values) { - return new TypeEnum(values); - }, - - /** - * @param {FactoryTemplate<*>} registry - * @param {(GameRoot, any) => object=} resolver - */ - obj(registry, resolver = null) { - return new TypeClass(registry, resolver); - }, - - /** - * @param {FactoryTemplate<*>} registry - */ - objData(registry) { - return new TypeClassData(registry); - }, - - /** - * @param {typeof BasicSerializableObject} cls - */ - knownType(cls) { - return new TypeFixedClass(cls); - }, - - /** - * @param {BaseDataType} innerType - */ - array(innerType) { - return new TypeArray(innerType); - }, - - /** - * @param {SingletonFactoryTemplate<*>} innerType - */ - classRef(registry) { - return new TypeMetaClass(registry); - }, - - /** - * @param {Object.} descriptor - */ - structured(descriptor) { - return new TypeStructuredObject(descriptor); - }, - - /** - * @param {BaseDataType} a - * @param {BaseDataType} b - */ - pair(a, b) { - return new TypePair(a, b); - }, - - /** - * @param {typeof BasicSerializableObject} classHandle - * @param {SingletonFactoryTemplate<*>} registry - */ - classWithMetaclass(classHandle, registry) { - return new TypeClassFromMetaclass(classHandle, registry); - }, -}; - -/** - * A full schema declaration - * @typedef {Object.} Schema - */ - -const globalSchemaCache = {}; - -/* dev:start */ -const classnamesCache = {}; -/* dev:end*/ - -export class BasicSerializableObject { - /* dev:start */ - /** - * Fixes typeof DerivedComponent is not assignable to typeof Component, compiled out - * in non-dev builds - */ - constructor(...args) {} - - /* dev:end */ - - static getId() { - abstract; - } - - /** - * Should return the serialization schema - * @returns {Schema} - */ - static getSchema() { - return {}; - } - - // Implementation - /** @returns {Schema} */ - static getCachedSchema() { - const id = this.getId(); - - /* dev:start */ - assert( - classnamesCache[id] === this || classnamesCache[id] === undefined, - "Class name taken twice: " + id + " (from " + this.name + ")" - ); - classnamesCache[id] = this; - /* dev:end */ - - const entry = globalSchemaCache[id]; - if (entry) { - return entry; - } - - const schema = this.getSchema(); - globalSchemaCache[id] = schema; - return schema; - } - - /** @returns {object} */ - serialize() { - return serializeSchema( - this, - /** @type {typeof BasicSerializableObject} */ (this.constructor).getCachedSchema() - ); - } - - /** - * @param {any} data - * @param {import("./savegame_serializer").GameRoot} root - * @returns {string|void} - */ - deserialize(data, root = null) { - return deserializeSchema( - this, - /** @type {typeof BasicSerializableObject} */ (this.constructor).getCachedSchema(), - data, - null, - root - ); - } - - /** @returns {string|void} */ - static verify(data) { - return verifySchema(this.getCachedSchema(), data); - } -} - -/** - * Serializes an object using the given schema, mergin with the given properties - * @param {object} obj The object to serialize - * @param {Schema} schema The schema to use - * @param {object=} mergeWith Any additional properties to merge with the schema, useful for super calls - * @returns {object} Serialized data object - */ -export function serializeSchema(obj, schema, mergeWith = {}) { - for (const key in schema) { - if (!obj.hasOwnProperty(key)) { - logger.error("Invalid schema, property", key, "does not exist on", obj, "(schema=", schema, ")"); - assert( - obj.hasOwnProperty(key), - "serialization: invalid schema, property does not exist on object: " + key - ); - } - if (!schema[key]) { - assert(false, "Invalid schema (bad key '" + key + "'): " + JSON.stringify(schema)); - } - - if (G_IS_DEV) { - try { - mergeWith[key] = schema[key].serialize(obj[key]); - } catch (ex) { - logger.error( - "Serialization of", - obj, - "failed on key '" + key + "' ->", - ex, - "(schema was", - schema, - ")" - ); - throw ex; - } - } else { - mergeWith[key] = schema[key].serialize(obj[key]); - } - } - return mergeWith; -} - -/** - * Deserializes data into an object - * @param {object} obj The object to store the deserialized data into - * @param {Schema} schema The schema to use - * @param {object} data The serialized data - * @param {string|void|null=} baseclassErrorResult Convenience, if this is a string error code, do nothing and return it - * @param {import("../game/root").GameRoot=} root Optional game root reference - * @returns {string|void} String error code or nothing on success - */ -export function deserializeSchema(obj, schema, data, baseclassErrorResult = null, root) { - if (baseclassErrorResult) { - return baseclassErrorResult; - } - - if (!data) { - logger.error("Got 'NULL' data for", obj, "and schema", schema, "!"); - return "Got null data"; - } - - for (const key in schema) { - if (!data.hasOwnProperty(key)) { - logger.error("Data", data, "does not contain", key, "(schema:", schema, ")"); - return "Missing key in schema: " + key + " of class " + obj.constructor.name; - } - if (!schema[key].allowNull() && (data[key] === null || data[key] === undefined)) { - logger.error("Data", data, "has null value for", key, "(schema:", schema, ")"); - return "Non-nullable entry is null: " + key + " of class " + obj.constructor.name; - } - - const errorStatus = schema[key].deserializeWithVerify(data[key], obj, key, obj.root || root); - if (errorStatus) { - logger.error( - "Deserialization failed with error '" + errorStatus + "' on object", - obj, - "and key", - key, - "(root? =", - obj.root ? "y" : "n", - ")" - ); - return errorStatus; - } - } -} - -/** - * Verifies stored data using the given schema - * @param {Schema} schema The schema to use - * @param {object} data The data to verify - * @returns {string|void} String error code or nothing on success - */ -export function verifySchema(schema, data) { - for (const key in schema) { - if (!data.hasOwnProperty(key)) { - logger.error("Data", data, "does not contain", key, "(schema:", schema, ")"); - return "verify: missing key required by schema in stored data: " + key; - } - if (!schema[key].allowNull() && (data[key] === null || data[key] === undefined)) { - logger.error("Data", data, "has null value for", key, "(schema:", schema, ")"); - return "verify: non-nullable entry is null: " + key; - } - - const errorStatus = schema[key].verifySerializedValue(data[key]); - if (errorStatus) { - logger.error(errorStatus); - return "verify: " + errorStatus; - } - } -} - -/** - * Extends a schema by adding the properties from the new schema to the existing base schema - * @param {Schema} base - * @param {Schema} newOne - * @returns {Schema} - */ -export function extendSchema(base, newOne) { - /** @type {Schema} */ - const result = Object.assign({}, base); - for (const key in newOne) { - if (result.hasOwnProperty(key)) { - logger.error("Extend schema got duplicate key:", key); - continue; - } - result[key] = newOne[key]; - } - return result; -} +import { createLogger } from "../core/logging"; +import { + BaseDataType, + TypeArray, + TypeBoolean, + TypeClass, + TypeClassData, + TypeClassFromMetaclass, + TypeClassId, + TypeEntity, + TypeEntityWeakref, + TypeEnum, + TypeFixedClass, + TypeInteger, + TypeKeyValueMap, + TypeMetaClass, + TypeNullable, + TypeNumber, + TypePair, + TypePositiveInteger, + TypePositiveNumber, + TypeString, + TypeStructuredObject, + TypeVector, +} from "./serialization_data_types"; + +const logger = createLogger("serialization"); + +// Schema declarations +export const types = { + int: new TypeInteger(), + uint: new TypePositiveInteger(), + float: new TypeNumber(), + ufloat: new TypePositiveNumber(), + string: new TypeString(), + entity: new TypeEntity(), + weakEntityRef: new TypeEntityWeakref(), + vector: new TypeVector(), + tileVector: new TypeVector(), + bool: new TypeBoolean(), + + /** + * @param {BaseDataType} wrapped + */ + nullable(wrapped) { + return new TypeNullable(wrapped); + }, + + /** + * @param {FactoryTemplate<*>|SingletonFactoryTemplate<*>} registry + */ + classId(registry) { + return new TypeClassId(registry); + }, + /** + * @param {BaseDataType} valueType + * @param {boolean=} includeEmptyValues + */ + keyValueMap(valueType, includeEmptyValues = true) { + return new TypeKeyValueMap(valueType, includeEmptyValues); + }, + + /** + * @param {Object} values + */ + enum(values) { + return new TypeEnum(values); + }, + + /** + * @param {FactoryTemplate<*>} registry + * @param {(GameRoot, any) => object=} resolver + */ + obj(registry, resolver = null) { + return new TypeClass(registry, resolver); + }, + + /** + * @param {FactoryTemplate<*>} registry + */ + objData(registry) { + return new TypeClassData(registry); + }, + + /** + * @param {typeof BasicSerializableObject} cls + */ + knownType(cls) { + return new TypeFixedClass(cls); + }, + + /** + * @param {BaseDataType} innerType + */ + array(innerType) { + return new TypeArray(innerType); + }, + + /** + * @param {BaseDataType} innerType + */ + fixedSizeArray(innerType) { + return new TypeArray(innerType, true); + }, + + /** + * @param {SingletonFactoryTemplate<*>} innerType + */ + classRef(registry) { + return new TypeMetaClass(registry); + }, + + /** + * @param {Object.} descriptor + */ + structured(descriptor) { + return new TypeStructuredObject(descriptor); + }, + + /** + * @param {BaseDataType} a + * @param {BaseDataType} b + */ + pair(a, b) { + return new TypePair(a, b); + }, + + /** + * @param {typeof BasicSerializableObject} classHandle + * @param {SingletonFactoryTemplate<*>} registry + */ + classWithMetaclass(classHandle, registry) { + return new TypeClassFromMetaclass(classHandle, registry); + }, +}; + +/** + * A full schema declaration + * @typedef {Object.} Schema + */ + +const globalSchemaCache = {}; + +/* dev:start */ +const classnamesCache = {}; +/* dev:end*/ + +export class BasicSerializableObject { + /* dev:start */ + /** + * Fixes typeof DerivedComponent is not assignable to typeof Component, compiled out + * in non-dev builds + */ + constructor(...args) {} + + /* dev:end */ + + static getId() { + abstract; + } + + /** + * Should return the serialization schema + * @returns {Schema} + */ + static getSchema() { + return {}; + } + + // Implementation + /** @returns {Schema} */ + static getCachedSchema() { + const id = this.getId(); + + /* dev:start */ + assert( + classnamesCache[id] === this || classnamesCache[id] === undefined, + "Class name taken twice: " + id + " (from " + this.name + ")" + ); + classnamesCache[id] = this; + /* dev:end */ + + const entry = globalSchemaCache[id]; + if (entry) { + return entry; + } + + const schema = this.getSchema(); + globalSchemaCache[id] = schema; + return schema; + } + + /** @returns {object} */ + serialize() { + return serializeSchema( + this, + /** @type {typeof BasicSerializableObject} */ (this.constructor).getCachedSchema() + ); + } + + /** + * @param {any} data + * @param {import("./savegame_serializer").GameRoot} root + * @returns {string|void} + */ + deserialize(data, root = null) { + return deserializeSchema( + this, + /** @type {typeof BasicSerializableObject} */ (this.constructor).getCachedSchema(), + data, + null, + root + ); + } + + /** @returns {string|void} */ + static verify(data) { + return verifySchema(this.getCachedSchema(), data); + } +} + +/** + * Serializes an object using the given schema, mergin with the given properties + * @param {object} obj The object to serialize + * @param {Schema} schema The schema to use + * @param {object=} mergeWith Any additional properties to merge with the schema, useful for super calls + * @returns {object} Serialized data object + */ +export function serializeSchema(obj, schema, mergeWith = {}) { + for (const key in schema) { + if (!obj.hasOwnProperty(key)) { + logger.error("Invalid schema, property", key, "does not exist on", obj, "(schema=", schema, ")"); + assert( + obj.hasOwnProperty(key), + "serialization: invalid schema, property does not exist on object: " + key + ); + } + if (!schema[key]) { + assert(false, "Invalid schema (bad key '" + key + "'): " + JSON.stringify(schema)); + } + + if (G_IS_DEV) { + try { + mergeWith[key] = schema[key].serialize(obj[key]); + } catch (ex) { + logger.error( + "Serialization of", + obj, + "failed on key '" + key + "' ->", + ex, + "(schema was", + schema, + ")" + ); + throw ex; + } + } else { + mergeWith[key] = schema[key].serialize(obj[key]); + } + } + return mergeWith; +} + +/** + * Deserializes data into an object + * @param {object} obj The object to store the deserialized data into + * @param {Schema} schema The schema to use + * @param {object} data The serialized data + * @param {string|void|null=} baseclassErrorResult Convenience, if this is a string error code, do nothing and return it + * @param {import("../game/root").GameRoot=} root Optional game root reference + * @returns {string|void} String error code or nothing on success + */ +export function deserializeSchema(obj, schema, data, baseclassErrorResult = null, root) { + if (baseclassErrorResult) { + return baseclassErrorResult; + } + + if (!data) { + logger.error("Got 'NULL' data for", obj, "and schema", schema, "!"); + return "Got null data"; + } + + for (const key in schema) { + if (!data.hasOwnProperty(key)) { + logger.error("Data", data, "does not contain", key, "(schema:", schema, ")"); + return "Missing key in schema: " + key + " of class " + obj.constructor.name; + } + if (!schema[key].allowNull() && (data[key] === null || data[key] === undefined)) { + logger.error("Data", data, "has null value for", key, "(schema:", schema, ")"); + return "Non-nullable entry is null: " + key + " of class " + obj.constructor.name; + } + + const errorStatus = schema[key].deserializeWithVerify(data[key], obj, key, obj.root || root); + if (errorStatus) { + logger.error( + "Deserialization failed with error '" + errorStatus + "' on object", + obj, + "and key", + key, + "(root? =", + obj.root ? "y" : "n", + ")" + ); + return errorStatus; + } + } +} + +/** + * Verifies stored data using the given schema + * @param {Schema} schema The schema to use + * @param {object} data The data to verify + * @returns {string|void} String error code or nothing on success + */ +export function verifySchema(schema, data) { + for (const key in schema) { + if (!data.hasOwnProperty(key)) { + logger.error("Data", data, "does not contain", key, "(schema:", schema, ")"); + return "verify: missing key required by schema in stored data: " + key; + } + if (!schema[key].allowNull() && (data[key] === null || data[key] === undefined)) { + logger.error("Data", data, "has null value for", key, "(schema:", schema, ")"); + return "verify: non-nullable entry is null: " + key; + } + + const errorStatus = schema[key].verifySerializedValue(data[key]); + if (errorStatus) { + logger.error(errorStatus); + return "verify: " + errorStatus; + } + } +} + +/** + * Extends a schema by adding the properties from the new schema to the existing base schema + * @param {Schema} base + * @param {Schema} newOne + * @returns {Schema} + */ +export function extendSchema(base, newOne) { + /** @type {Schema} */ + const result = Object.assign({}, base); + for (const key in newOne) { + if (result.hasOwnProperty(key)) { + logger.error("Extend schema got duplicate key:", key); + continue; + } + result[key] = newOne[key]; + } + return result; +} diff --git a/src/js/savegame/serialization_data_types.js b/src/js/savegame/serialization_data_types.js index 9fb53bb8..9d3b689f 100644 --- a/src/js/savegame/serialization_data_types.js +++ b/src/js/savegame/serialization_data_types.js @@ -1,1292 +1,1295 @@ -/* typehints:start */ -import { GameRoot } from "../game/root"; -import { BasicSerializableObject } from "./serialization"; -/* typehints:end */ - -import { Vector } from "../core/vector"; -import { round4Digits } from "../core/utils"; -export const globalJsonSchemaDefs = {}; - -/** - * - * @param {import("./serialization").Schema} schema - */ -export function schemaToJsonSchema(schema) { - const jsonSchema = { - type: "object", - additionalProperties: false, - required: [], - properties: {}, - }; - - for (const key in schema) { - const subSchema = schema[key].getAsJsonSchema(); - jsonSchema.required.push(key); - jsonSchema.properties[key] = subSchema; - } - - return jsonSchema; -} - -/** - * Helper function to create a json schema object - * @param {any} properties - */ -function schemaObject(properties) { - return { - type: "object", - required: Object.keys(properties).slice(), - additionalProperties: false, - properties, - }; -} - -/** - * Base serialization data type - */ -export class BaseDataType { - /** - * Serializes a given raw value - * @param {any} value - */ - serialize(value) { - abstract; - return {}; - } - - /** - * Verifies a given serialized value - * @param {any} value - * @returns {string|void} String error code or null on success - */ - verifySerializedValue(value) {} - - /** - * Deserializes a serialized value into the target object under the given key - * @param {any} value - * @param {GameRoot} root - * @param {object} targetObject - * @param {string|number} targetKey - * @returns {string|void} String error code or null on success - */ - deserialize(value, targetObject, targetKey, root) { - abstract; - } - - /** - * Returns the json schema - */ - getAsJsonSchema() { - const key = this.getCacheKey(); - const schema = this.getAsJsonSchemaUncached(); - - if (!globalJsonSchemaDefs[key]) { - // schema.$id = key; - globalJsonSchemaDefs[key] = schema; - } - - return { - $ref: "#/definitions/" + key, - }; - } - - /** - * INTERNAL Should return the json schema representation - */ - getAsJsonSchemaUncached() { - abstract; - } - - /** - * Returns whether null values are okay - * @returns {boolean} - */ - allowNull() { - return false; - } - - // Helper methods - - /** - * Deserializes a serialized value, but performs integrity checks before - * @param {any} value - * @param {GameRoot} root - * @param {object} targetObject - * @param {string|number} targetKey - * @returns {string|void} String error code or null on success - */ - deserializeWithVerify(value, targetObject, targetKey, root) { - const errorCode = this.verifySerializedValue(value); - if (errorCode) { - return ( - "serialization verify failed: " + - errorCode + - " [value " + - JSON.stringify(value).substr(0, 100) + - "]" - ); - } - return this.deserialize(value, targetObject, targetKey, root); - } - - /** - * Should return a cacheable key - */ - getCacheKey() { - abstract; - return ""; - } -} - -export class TypeInteger extends BaseDataType { - serialize(value) { - assert(Number.isInteger(value), "Type integer got non integer for serialize: " + value); - return value; - } - - /** - * @see BaseDataType.deserialize - * @param {any} value - * @param {GameRoot} root - * @param {object} targetObject - * @param {string|number} targetKey - * @returns {string|void} String error code or null on success - */ - deserialize(value, targetObject, targetKey, root) { - targetObject[targetKey] = value; - } - - getAsJsonSchemaUncached() { - return { - type: "integer", - }; - } - - verifySerializedValue(value) { - if (!Number.isInteger(value)) { - return "Not a valid number"; - } - } - - getCacheKey() { - return "int"; - } -} - -export class TypePositiveInteger extends BaseDataType { - serialize(value) { - assert(Number.isInteger(value), "Type integer got non integer for serialize: " + value); - assert(value >= 0, "value < 0: " + value); - return value; - } - - /** - * @see BaseDataType.deserialize - * @param {any} value - * @param {GameRoot} root - * @param {object} targetObject - * @param {string|number} targetKey - * @returns {string|void} String error code or null on success - */ - deserialize(value, targetObject, targetKey, root) { - targetObject[targetKey] = value; - } - - getAsJsonSchemaUncached() { - return { - type: "integer", - minimum: 0, - }; - } - - verifySerializedValue(value) { - if (!Number.isInteger(value)) { - return "Not a valid number"; - } - if (value < 0) { - return "Negative value for positive integer"; - } - } - - getCacheKey() { - return "uint"; - } -} - -export class TypeBoolean extends BaseDataType { - serialize(value) { - assert(value === true || value === false, "Type bool got non bool for serialize: " + value); - return value; - } - - /** - * @see BaseDataType.deserialize - * @param {any} value - * @param {GameRoot} root - * @param {object} targetObject - * @param {string|number} targetKey - * @returns {string|void} String error code or null on success - */ - deserialize(value, targetObject, targetKey, root) { - targetObject[targetKey] = value; - } - - getAsJsonSchemaUncached() { - return { - type: "boolean", - }; - } - - verifySerializedValue(value) { - if (value !== true && value !== false) { - return "Not a boolean"; - } - } - - getCacheKey() { - return "bool"; - } -} - -export class TypeString extends BaseDataType { - serialize(value) { - assert(typeof value === "string", "Type string got non string for serialize: " + value); - return value; - } - - /** - * @see BaseDataType.deserialize - * @param {any} value - * @param {GameRoot} root - * @param {object} targetObject - * @param {string|number} targetKey - * @returns {string|void} String error code or null on success - */ - deserialize(value, targetObject, targetKey, root) { - targetObject[targetKey] = value; - } - getAsJsonSchemaUncached() { - return { - type: "string", - }; - } - - verifySerializedValue(value) { - if (typeof value !== "string") { - return "Not a valid string"; - } - } - - getCacheKey() { - return "string"; - } -} - -export class TypeVector extends BaseDataType { - serialize(value) { - assert(value instanceof Vector, "Type vector got non vector for serialize: " + value); - return { - x: round4Digits(value.x), - y: round4Digits(value.y), - }; - } - - getAsJsonSchemaUncached() { - return schemaObject({ - x: { - type: "number", - }, - y: { - type: "number", - }, - }); - } - - /** - * @see BaseDataType.deserialize - * @param {any} value - * @param {GameRoot} root - * @param {object} targetObject - * @param {string|number} targetKey - * @returns {string|void} String error code or null on success - */ - deserialize(value, targetObject, targetKey, root) { - targetObject[targetKey] = new Vector(value.x, value.y); - } - - verifySerializedValue(value) { - if (!Number.isFinite(value.x) || !Number.isFinite(value.y)) { - return "Not a valid vector, missing x/y or bad data type"; - } - } - - getCacheKey() { - return "vector"; - } -} - -export class TypeTileVector extends BaseDataType { - serialize(value) { - assert(value instanceof Vector, "Type vector got non vector for serialize: " + value); - assert(Number.isInteger(value.x) && value.x > 0, "Invalid tile x:" + value.x); - assert(Number.isInteger(value.y) && value.y > 0, "Invalid tile x:" + value.y); - return { x: value.x, y: value.y }; - } - - getAsJsonSchemaUncached() { - return schemaObject({ - x: { - type: "integer", - minimum: 0, - maximum: 256, - }, - y: { - type: "integer", - minimum: 0, - maximum: 256, - }, - }); - } - - /** - * @see BaseDataType.deserialize - * @param {any} value - * @param {GameRoot} root - * @param {object} targetObject - * @param {string|number} targetKey - * @returns {string|void} String error code or null on success - */ - deserialize(value, targetObject, targetKey, root) { - targetObject[targetKey] = new Vector(value.x, value.y); - } - - verifySerializedValue(value) { - if (!Number.isInteger(value.x) || !Number.isInteger(value.y)) { - return "Not a valid tile vector, missing x/y or bad data type"; - } - if (value.x < 0 || value.y < 0) { - return "Invalid tile vector, x or y < 0"; - } - } - - getCacheKey() { - return "tilevector"; - } -} - -export class TypeNumber extends BaseDataType { - serialize(value) { - assert(Number.isFinite(value), "Type number got non number for serialize: " + value); - assert(!Number.isNaN(value), "Value is nan: " + value); - return round4Digits(value); - } - - getAsJsonSchemaUncached() { - return { - type: "number", - }; - } - - /** - * @see BaseDataType.deserialize - * @param {any} value - * @param {GameRoot} root - * @param {object} targetObject - * @param {string|number} targetKey - * @returns {string|void} String error code or null on success - */ - deserialize(value, targetObject, targetKey, root) { - targetObject[targetKey] = value; - } - - verifySerializedValue(value) { - if (!Number.isFinite(value)) { - return "Not a valid number: " + value; - } - } - - getCacheKey() { - return "float"; - } -} - -export class TypePositiveNumber extends BaseDataType { - serialize(value) { - assert(Number.isFinite(value), "Type number got non number for serialize: " + value); - assert(value >= 0, "Postitive number got negative value: " + value); - return round4Digits(value); - } - - /** - * @see BaseDataType.deserialize - * @param {any} value - * @param {GameRoot} root - * @param {object} targetObject - * @param {string|number} targetKey - * @returns {string|void} String error code or null on success - */ - deserialize(value, targetObject, targetKey, root) { - targetObject[targetKey] = value; - } - - getAsJsonSchemaUncached() { - return { - type: "number", - minimum: 0, - }; - } - - verifySerializedValue(value) { - if (!Number.isFinite(value)) { - return "Not a valid number: " + value; - } - if (value < 0) { - return "Positive number got negative value: " + value; - } - } - - getCacheKey() { - return "ufloat"; - } -} - -export class TypeEnum extends BaseDataType { - /** - * @param {Object.} enumeration - */ - constructor(enumeration = {}) { - super(); - this.availableValues = Object.values(enumeration); - } - - serialize(value) { - assert(this.availableValues.indexOf(value) >= 0, "Unknown value: " + value); - return value; - } - - /** - * @see BaseDataType.deserialize - * @param {any} value - * @param {GameRoot} root - * @param {object} targetObject - * @param {string|number} targetKey - * @returns {string|void} String error code or null on success - */ - deserialize(value, targetObject, targetKey, root) { - targetObject[targetKey] = value; - } - - getAsJsonSchemaUncached() { - return { - type: "string", - enum: this.availableValues, - }; - } - - verifySerializedValue(value) { - if (this.availableValues.indexOf(value) < 0) { - return "Unknown enum value: " + value; - } - } - - getCacheKey() { - return "enum." + this.availableValues.join(","); - } -} - -export class TypeEntity extends BaseDataType { - serialize(value) { - // assert(value instanceof Entity, "Not a valid entity ref: " + value); - assert(value.uid, "Entity has no uid yet"); - assert(!value.destroyed, "Entity already destroyed"); - assert(!value.queuedForDestroy, "Entity queued for destroy"); - - return value.uid; - } - - getAsJsonSchemaUncached() { - return { - type: "integer", - minimum: 0, - }; - } - - /** - * @see BaseDataType.deserialize - * @param {any} value - * @param {GameRoot} root - * @param {object} targetObject - * @param {string|number} targetKey - * @returns {string|void} String error code or null on success - */ - deserialize(value, targetObject, targetKey, root) { - const entity = root.entityMgr.findByUid(value); - if (!entity) { - return "Entity not found by uid: " + value; - } - targetObject[targetKey] = entity; - } - - verifySerializedValue(value) { - if (!Number.isFinite(value)) { - return "Not a valid uuid: " + value; - } - } - - getCacheKey() { - return "entity"; - } -} - -export class TypeEntityWeakref extends BaseDataType { - serialize(value) { - if (value === null) { - return null; - } - - // assert(value instanceof Entity, "Not a valid entity ref (weak): " + value); - assert(value.uid, "Entity has no uid yet"); - if (value.destroyed || value.queuedForDestroy) { - return null; - } - return value.uid; - } - - /** - * @see BaseDataType.deserialize - * @param {any} value - * @param {GameRoot} root - * @param {object} targetObject - * @param {string|number} targetKey - * @returns {string|void} String error code or null on success - */ - deserialize(value, targetObject, targetKey, root) { - if (value === null) { - targetObject[targetKey] = null; - return; - } - const entity = root.entityMgr.findByUid(value, false); - targetObject[targetKey] = entity; - } - - getAsJsonSchemaUncached() { - return { - type: ["null", "integer"], - minimum: 0, - }; - } - - allowNull() { - return true; - } - - verifySerializedValue(value) { - if (value !== null && !Number.isFinite(value)) { - return "Not a valid uuid: " + value; - } - } - - getCacheKey() { - return "entity-weakref"; - } -} - -export class TypeClass extends BaseDataType { - /** - * - * @param {FactoryTemplate<*>} registry - * @param {(GameRoot, object) => object} customResolver - */ - constructor(registry, customResolver = null) { - super(); - this.registry = registry; - this.customResolver = customResolver; - } - - serialize(value) { - assert(typeof value === "object", "Not a class instance: " + value); - return { - $: value.constructor.getId(), - data: value.serialize(), - }; - } - - getAsJsonSchemaUncached() { - const options = []; - const entries = this.registry.getEntries(); - for (let i = 0; i < entries.length; ++i) { - const entry = entries[i]; - - options.push( - schemaObject({ - $: { - type: "string", - // @ts-ignore - enum: [entry.getId()], - }, - // @ts-ignore - data: schemaToJsonSchema(entry.getCachedSchema()), - }) - ); - } - - return { oneOf: options }; - } - - /** - * @see BaseDataType.deserialize - * @param {any} value - * @param {GameRoot} root - * @param {object} targetObject - * @param {string|number} targetKey - * @returns {string|void} String error code or null on success - */ - deserialize(value, targetObject, targetKey, root) { - let instance; - - if (this.customResolver) { - instance = this.customResolver(root, value); - if (!instance) { - return "Failed to call custom resolver"; - } - } else { - const instanceClass = this.registry.findById(value.$); - if (!instanceClass || !instanceClass.prototype) { - return "Invalid class id (runtime-err): " + value.$ + "->" + instanceClass; - } - instance = Object.create(instanceClass.prototype); - const errorState = instance.deserialize(value.data); - if (errorState) { - return errorState; - } - } - targetObject[targetKey] = instance; - } - - verifySerializedValue(value) { - if (!value) { - return "Got null data"; - } - - if (!this.registry.hasId(value.$)) { - return "Invalid class id: " + value.$ + " (factory is " + this.registry.getId() + ")"; - } - } - - getCacheKey() { - return "class." + this.registry.getId(); - } -} - -export class TypeClassData extends BaseDataType { - /** - * - * @param {FactoryTemplate<*>} registry - */ - constructor(registry) { - super(); - this.registry = registry; - } - - serialize(value) { - assert(typeof value === "object", "Not a class instance: " + value); - return value.serialize(); - } - - getAsJsonSchemaUncached() { - const options = []; - const entries = this.registry.getEntries(); - for (let i = 0; i < entries.length; ++i) { - const entry = entries[i]; - options.push( - schemaToJsonSchema(/** @type {typeof BasicSerializableObject} */ (entry).getCachedSchema()) - ); - } - return { oneOf: options }; - } - - /** - * @see BaseDataType.deserialize - * @param {any} value - * @param {GameRoot} root - * @param {object} targetObject - * @param {string|number} targetKey - * @returns {string|void} String error code or null on success - */ - deserialize(value, targetObject, targetKey, root) { - assert(false, "can not deserialize class data of type " + this.registry.getId()); - } - - verifySerializedValue(value) { - if (!value) { - return "Got null data"; - } - } - - getCacheKey() { - return "class." + this.registry.getId(); - } -} - -export class TypeClassFromMetaclass extends BaseDataType { - /** - * - * @param {typeof BasicSerializableObject} classHandle - * @param {SingletonFactoryTemplate<*>} registry - */ - constructor(classHandle, registry) { - super(); - this.registry = registry; - this.classHandle = classHandle; - } - - serialize(value) { - assert(typeof value === "object", "Not a class instance: " + value); - return { - $: value.getMetaclass().getId(), - data: value.serialize(), - }; - } - - getAsJsonSchemaUncached() { - // const options = []; - const ids = this.registry.getAllIds(); - - return { - $: { - type: "string", - enum: ids, - }, - data: schemaToJsonSchema(this.classHandle.getCachedSchema()), - }; - } - - /** - * @see BaseDataType.deserialize - * @param {any} value - * @param {GameRoot} root - * @param {object} targetObject - * @param {string|number} targetKey - * @returns {string|void} String error code or null on success - */ - deserialize(value, targetObject, targetKey, root) { - const metaClassInstance = this.registry.findById(value.$); - if (!metaClassInstance || !metaClassInstance.prototype) { - return "Invalid meta class id (runtime-err): " + value.$ + "->" + metaClassInstance; - } - - const instanceClass = metaClassInstance.getInstanceClass(); - const instance = Object.create(instanceClass.prototype); - const errorState = instance.deserialize(value.data); - if (errorState) { - return errorState; - } - targetObject[targetKey] = instance; - } - - verifySerializedValue(value) { - if (!value) { - return "Got null data"; - } - - if (!this.registry.hasId(value.$)) { - return "Invalid class id: " + value.$ + " (factory is " + this.registry.getId() + ")"; - } - } - - getCacheKey() { - return "classofmetaclass." + this.registry.getId(); - } -} - -export class TypeMetaClass extends BaseDataType { - /** - * - * @param {SingletonFactoryTemplate<*>} registry - */ - constructor(registry) { - super(); - this.registry = registry; - } - - serialize(value) { - return value.getId(); - } - - /** - * @see BaseDataType.deserialize - * @param {any} value - * @param {GameRoot} root - * @param {object} targetObject - * @param {string|number} targetKey - * @returns {string|void} String error code or null on success - */ - deserialize(value, targetObject, targetKey, root) { - const instanceClass = this.registry.findById(value); - if (!instanceClass) { - return "Invalid class id (runtime-err): " + value; - } - targetObject[targetKey] = instanceClass; - } - - getAsJsonSchemaUncached() { - return { - type: "string", - enum: this.registry.getAllIds(), - }; - } - - verifySerializedValue(value) { - if (!value) { - return "Got null data"; - } - - if (typeof value !== "string") { - return "Got non string data"; - } - - if (!this.registry.hasId(value)) { - return "Invalid class id: " + value + " (factory is " + this.registry.getId() + ")"; - } - } - - getCacheKey() { - return "metaclass." + this.registry.getId(); - } -} - -export class TypeArray extends BaseDataType { - /** - * @param {BaseDataType} innerType - */ - constructor(innerType) { - super(); - this.innerType = innerType; - } - - serialize(value) { - assert(Array.isArray(value), "Not an array"); - const result = new Array(value.length); - for (let i = 0; i < value.length; ++i) { - result[i] = this.innerType.serialize(value[i]); - } - return result; - } - - /** - * @see BaseDataType.deserialize - * @param {any} value - * @param {GameRoot} root - * @param {object} targetObject - * @param {string|number} targetKey - * @returns {string|void} String error code or null on success - */ - deserialize(value, targetObject, targetKey, root) { - let destination = targetObject[targetKey]; - if (!destination) { - targetObject[targetKey] = destination = new Array(value.length); - } - - for (let i = 0; i < value.length; ++i) { - const errorStatus = this.innerType.deserializeWithVerify(value[i], destination, i, root); - if (errorStatus) { - return errorStatus; - } - } - } - - getAsJsonSchemaUncached() { - return { - type: "array", - items: this.innerType.getAsJsonSchema(), - }; - } - - verifySerializedValue(value) { - if (!Array.isArray(value)) { - return "Not an array: " + value; - } - } - - getCacheKey() { - return "array." + this.innerType.getCacheKey(); - } -} - -export class TypeFixedClass extends BaseDataType { - /** - * - * @param {typeof BasicSerializableObject} baseclass - */ - constructor(baseclass) { - super(); - this.baseclass = baseclass; - } - - serialize(value) { - assert(value instanceof this.baseclass, "Not a valid class instance"); - return value.serialize(); - } - - /** - * @see BaseDataType.deserialize - * @param {any} value - * @param {GameRoot} root - * @param {object} targetObject - * @param {string|number} targetKey - * @returns {string|void} String error code or null on success - */ - deserialize(value, targetObject, targetKey, root) { - const instance = Object.create(this.baseclass.prototype); - const errorState = instance.deserialize(value); - if (errorState) { - return "Failed to deserialize class: " + errorState; - } - targetObject[targetKey] = instance; - } - - getAsJsonSchemaUncached() { - this.baseclass.getSchema(); - this.baseclass.getCachedSchema(); - return schemaToJsonSchema(this.baseclass.getCachedSchema()); - } - - verifySerializedValue(value) { - if (!value) { - return "Got null data"; - } - } - - getCacheKey() { - return "fixedclass." + this.baseclass.getId(); - } -} - -export class TypeKeyValueMap extends BaseDataType { - /** - * @param {BaseDataType} valueType - * @param {boolean=} includeEmptyValues - */ - constructor(valueType, includeEmptyValues = true) { - super(); - this.valueType = valueType; - this.includeEmptyValues = includeEmptyValues; - } - - serialize(value) { - assert(typeof value === "object", "not an object"); - let result = {}; - for (const key in value) { - const serialized = this.valueType.serialize(value[key]); - if (!this.includeEmptyValues && typeof serialized === "object") { - if ( - serialized.$ && - typeof serialized.data === "object" && - Object.keys(serialized.data).length === 0 - ) { - continue; - } else if (Object.keys(serialized).length === 0) { - continue; - } - } - - result[key] = serialized; - } - return result; - } - - /** - * @see BaseDataType.deserialize - * @param {any} value - * @param {GameRoot} root - * @param {object} targetObject - * @param {string|number} targetKey - * @returns {string|void} String error code or null on success - */ - deserialize(value, targetObject, targetKey, root) { - let result = {}; - for (const key in value) { - const errorCode = this.valueType.deserializeWithVerify(value[key], result, key, root); - if (errorCode) { - return errorCode; - } - } - targetObject[targetKey] = result; - } - - getAsJsonSchemaUncached() { - return { - type: "object", - additionalProperties: this.valueType.getAsJsonSchema(), - }; - } - - verifySerializedValue(value) { - if (typeof value !== "object") { - return "KV map is not an object"; - } - } - - getCacheKey() { - return "kvmap." + this.valueType.getCacheKey(); - } -} - -export class TypeClassId extends BaseDataType { - /** - * @param {FactoryTemplate<*>|SingletonFactoryTemplate<*>} registry - */ - constructor(registry) { - super(); - this.registry = registry; - } - - serialize(value) { - assert(typeof value === "string", "Not a valid string"); - assert(this.registry.hasId(value), "Id " + value + " not found in registry"); - return value; - } - - /** - * @see BaseDataType.deserialize - * @param {any} value - * @param {GameRoot} root - * @param {object} targetObject - * @param {string|number} targetKey - * @returns {string|void} String error code or null on success - */ - deserialize(value, targetObject, targetKey, root) { - targetObject[targetKey] = value; - } - - getAsJsonSchemaUncached() { - return { - type: "string", - enum: this.registry.getAllIds(), - }; - } - - verifySerializedValue(value) { - if (typeof value !== "string") { - return "Not a valid registry id key: " + value; - } - if (!this.registry.hasId(value)) { - return "Id " + value + " not known to registry"; - } - } - - getCacheKey() { - return "classid." + this.registry.getId(); - } -} - -export class TypePair extends BaseDataType { - /** - * @param {BaseDataType} type1 - * @param {BaseDataType} type2 - */ - constructor(type1, type2) { - super(); - assert(type1 && type1 instanceof BaseDataType, "bad first type given for pair"); - assert(type2 && type2 instanceof BaseDataType, "bad second type given for pair"); - this.type1 = type1; - this.type2 = type2; - } - - serialize(value) { - assert(Array.isArray(value), "pair: not an array"); - assert(value.length === 2, "pair: length != 2"); - return [this.type1.serialize(value[0]), this.type2.serialize(value[1])]; - } - - /** - * @see BaseDataType.deserialize - * @param {any} value - * @param {GameRoot} root - * @param {object} targetObject - * @param {string|number} targetKey - * @returns {string|void} String error code or null on success - */ - deserialize(value, targetObject, targetKey, root) { - const result = [undefined, undefined]; - - let errorCode = this.type1.deserialize(value[0], result, 0, root); - if (errorCode) { - return errorCode; - } - errorCode = this.type2.deserialize(value[1], result, 1, root); - if (errorCode) { - return errorCode; - } - - targetObject[targetKey] = result; - } - - getAsJsonSchemaUncached() { - return { - type: "array", - minLength: 2, - maxLength: 2, - items: [this.type1.getAsJsonSchema(), this.type2.getAsJsonSchema()], - }; - } - - verifySerializedValue(value) { - if (!Array.isArray(value)) { - return "Pair is not an array"; - } - if (value.length !== 2) { - return "Pair length != 2"; - } - let errorCode = this.type1.verifySerializedValue(value[0]); - if (errorCode) { - return errorCode; - } - errorCode = this.type2.verifySerializedValue(value[1]); - if (errorCode) { - return errorCode; - } - } - - getCacheKey() { - return "pair.(" + this.type1.getCacheKey() + "," + this.type2.getCacheKey + ")"; - } -} - -export class TypeNullable extends BaseDataType { - /** - * @param {BaseDataType} wrapped - */ - constructor(wrapped) { - super(); - this.wrapped = wrapped; - } - - serialize(value) { - if (value === null || value === undefined) { - return null; - } - return this.wrapped.serialize(value); - } - - /** - * @see BaseDataType.deserialize - * @param {any} value - * @param {GameRoot} root - * @param {object} targetObject - * @param {string|number} targetKey - * @returns {string|void} String error code or null on success - */ - deserialize(value, targetObject, targetKey, root) { - if (value === null || value === undefined) { - targetObject[targetKey] = null; - return; - } - return this.wrapped.deserialize(value, targetObject, targetKey, root); - } - - verifySerializedValue(value) { - if (value === null) { - return; - } - return this.wrapped.verifySerializedValue(value); - } - - getAsJsonSchemaUncached() { - return { - oneOf: [ - { - type: "null", - }, - this.wrapped.getAsJsonSchema(), - ], - }; - } - - allowNull() { - return true; - } - - getCacheKey() { - return "nullable." + this.wrapped.getCacheKey(); - } -} - -export class TypeStructuredObject extends BaseDataType { - /** - * @param {Object.} descriptor - */ - constructor(descriptor) { - super(); - this.descriptor = descriptor; - } - - serialize(value) { - assert(typeof value === "object", "not an object"); - let result = {}; - for (const key in this.descriptor) { - // assert(value.hasOwnProperty(key), "Serialization: Object does not have", key, "property!"); - result[key] = this.descriptor[key].serialize(value[key]); - } - return result; - } - - /** - * @see BaseDataType.deserialize - * @param {any} value - * @param {GameRoot} root - * @param {object} targetObject - * @param {string|number} targetKey - * @returns {string|void} String error code or null on success - */ - deserialize(value, targetObject, targetKey, root) { - let target = targetObject[targetKey]; - if (!target) { - targetObject[targetKey] = target = {}; - } - - for (const key in value) { - const valueType = this.descriptor[key]; - const errorCode = valueType.deserializeWithVerify(value[key], target, key, root); - if (errorCode) { - return errorCode; - } - } - } - - getAsJsonSchemaUncached() { - let properties = {}; - for (const key in this.descriptor) { - properties[key] = this.descriptor[key].getAsJsonSchema(); - } - - return { - type: "object", - required: Object.keys(this.descriptor), - properties, - }; - } - - verifySerializedValue(value) { - if (typeof value !== "object") { - return "structured object is not an object"; - } - for (const key in this.descriptor) { - if (!value.hasOwnProperty(key)) { - return "structured object is missing key " + key; - } - const subError = this.descriptor[key].verifySerializedValue(value[key]); - if (subError) { - return "structured object::" + subError; - } - } - } - - getCacheKey() { - let props = []; - for (const key in this.descriptor) { - props.push(key + "=" + this.descriptor[key].getCacheKey()); - } - return "structured[" + props.join(",") + "]"; - } -} +/* typehints:start */ +import { GameRoot } from "../game/root"; +import { BasicSerializableObject } from "./serialization"; +/* typehints:end */ + +import { Vector } from "../core/vector"; +import { round4Digits } from "../core/utils"; +export const globalJsonSchemaDefs = {}; + +/** + * + * @param {import("./serialization").Schema} schema + */ +export function schemaToJsonSchema(schema) { + const jsonSchema = { + type: "object", + additionalProperties: false, + required: [], + properties: {}, + }; + + for (const key in schema) { + const subSchema = schema[key].getAsJsonSchema(); + jsonSchema.required.push(key); + jsonSchema.properties[key] = subSchema; + } + + return jsonSchema; +} + +/** + * Helper function to create a json schema object + * @param {any} properties + */ +function schemaObject(properties) { + return { + type: "object", + required: Object.keys(properties).slice(), + additionalProperties: false, + properties, + }; +} + +/** + * Base serialization data type + */ +export class BaseDataType { + /** + * Serializes a given raw value + * @param {any} value + */ + serialize(value) { + abstract; + return {}; + } + + /** + * Verifies a given serialized value + * @param {any} value + * @returns {string|void} String error code or null on success + */ + verifySerializedValue(value) {} + + /** + * Deserializes a serialized value into the target object under the given key + * @param {any} value + * @param {GameRoot} root + * @param {object} targetObject + * @param {string|number} targetKey + * @returns {string|void} String error code or null on success + */ + deserialize(value, targetObject, targetKey, root) { + abstract; + } + + /** + * Returns the json schema + */ + getAsJsonSchema() { + const key = this.getCacheKey(); + const schema = this.getAsJsonSchemaUncached(); + + if (!globalJsonSchemaDefs[key]) { + // schema.$id = key; + globalJsonSchemaDefs[key] = schema; + } + + return { + $ref: "#/definitions/" + key, + }; + } + + /** + * INTERNAL Should return the json schema representation + */ + getAsJsonSchemaUncached() { + abstract; + } + + /** + * Returns whether null values are okay + * @returns {boolean} + */ + allowNull() { + return false; + } + + // Helper methods + + /** + * Deserializes a serialized value, but performs integrity checks before + * @param {any} value + * @param {GameRoot} root + * @param {object} targetObject + * @param {string|number} targetKey + * @returns {string|void} String error code or null on success + */ + deserializeWithVerify(value, targetObject, targetKey, root) { + const errorCode = this.verifySerializedValue(value); + if (errorCode) { + return ( + "serialization verify failed: " + + errorCode + + " [value " + + (JSON.stringify(value) || "").substr(0, 100) + + "]" + ); + } + return this.deserialize(value, targetObject, targetKey, root); + } + + /** + * Should return a cacheable key + */ + getCacheKey() { + abstract; + return ""; + } +} + +export class TypeInteger extends BaseDataType { + serialize(value) { + assert(Number.isInteger(value), "Type integer got non integer for serialize: " + value); + return value; + } + + /** + * @see BaseDataType.deserialize + * @param {any} value + * @param {GameRoot} root + * @param {object} targetObject + * @param {string|number} targetKey + * @returns {string|void} String error code or null on success + */ + deserialize(value, targetObject, targetKey, root) { + targetObject[targetKey] = value; + } + + getAsJsonSchemaUncached() { + return { + type: "integer", + }; + } + + verifySerializedValue(value) { + if (!Number.isInteger(value)) { + return "Not a valid number"; + } + } + + getCacheKey() { + return "int"; + } +} + +export class TypePositiveInteger extends BaseDataType { + serialize(value) { + assert(Number.isInteger(value), "Type integer got non integer for serialize: " + value); + assert(value >= 0, "value < 0: " + value); + return value; + } + + /** + * @see BaseDataType.deserialize + * @param {any} value + * @param {GameRoot} root + * @param {object} targetObject + * @param {string|number} targetKey + * @returns {string|void} String error code or null on success + */ + deserialize(value, targetObject, targetKey, root) { + targetObject[targetKey] = value; + } + + getAsJsonSchemaUncached() { + return { + type: "integer", + minimum: 0, + }; + } + + verifySerializedValue(value) { + if (!Number.isInteger(value)) { + return "Not a valid number"; + } + if (value < 0) { + return "Negative value for positive integer"; + } + } + + getCacheKey() { + return "uint"; + } +} + +export class TypeBoolean extends BaseDataType { + serialize(value) { + assert(value === true || value === false, "Type bool got non bool for serialize: " + value); + return value; + } + + /** + * @see BaseDataType.deserialize + * @param {any} value + * @param {GameRoot} root + * @param {object} targetObject + * @param {string|number} targetKey + * @returns {string|void} String error code or null on success + */ + deserialize(value, targetObject, targetKey, root) { + targetObject[targetKey] = value; + } + + getAsJsonSchemaUncached() { + return { + type: "boolean", + }; + } + + verifySerializedValue(value) { + if (value !== true && value !== false) { + return "Not a boolean"; + } + } + + getCacheKey() { + return "bool"; + } +} + +export class TypeString extends BaseDataType { + serialize(value) { + assert(typeof value === "string", "Type string got non string for serialize: " + value); + return value; + } + + /** + * @see BaseDataType.deserialize + * @param {any} value + * @param {GameRoot} root + * @param {object} targetObject + * @param {string|number} targetKey + * @returns {string|void} String error code or null on success + */ + deserialize(value, targetObject, targetKey, root) { + targetObject[targetKey] = value; + } + getAsJsonSchemaUncached() { + return { + type: "string", + }; + } + + verifySerializedValue(value) { + if (typeof value !== "string") { + return "Not a valid string"; + } + } + + getCacheKey() { + return "string"; + } +} + +export class TypeVector extends BaseDataType { + serialize(value) { + assert(value instanceof Vector, "Type vector got non vector for serialize: " + value); + return { + x: round4Digits(value.x), + y: round4Digits(value.y), + }; + } + + getAsJsonSchemaUncached() { + return schemaObject({ + x: { + type: "number", + }, + y: { + type: "number", + }, + }); + } + + /** + * @see BaseDataType.deserialize + * @param {any} value + * @param {GameRoot} root + * @param {object} targetObject + * @param {string|number} targetKey + * @returns {string|void} String error code or null on success + */ + deserialize(value, targetObject, targetKey, root) { + targetObject[targetKey] = new Vector(value.x, value.y); + } + + verifySerializedValue(value) { + if (!Number.isFinite(value.x) || !Number.isFinite(value.y)) { + return "Not a valid vector, missing x/y or bad data type"; + } + } + + getCacheKey() { + return "vector"; + } +} + +export class TypeTileVector extends BaseDataType { + serialize(value) { + assert(value instanceof Vector, "Type vector got non vector for serialize: " + value); + assert(Number.isInteger(value.x) && value.x > 0, "Invalid tile x:" + value.x); + assert(Number.isInteger(value.y) && value.y > 0, "Invalid tile x:" + value.y); + return { x: value.x, y: value.y }; + } + + getAsJsonSchemaUncached() { + return schemaObject({ + x: { + type: "integer", + minimum: 0, + maximum: 256, + }, + y: { + type: "integer", + minimum: 0, + maximum: 256, + }, + }); + } + + /** + * @see BaseDataType.deserialize + * @param {any} value + * @param {GameRoot} root + * @param {object} targetObject + * @param {string|number} targetKey + * @returns {string|void} String error code or null on success + */ + deserialize(value, targetObject, targetKey, root) { + targetObject[targetKey] = new Vector(value.x, value.y); + } + + verifySerializedValue(value) { + if (!Number.isInteger(value.x) || !Number.isInteger(value.y)) { + return "Not a valid tile vector, missing x/y or bad data type"; + } + if (value.x < 0 || value.y < 0) { + return "Invalid tile vector, x or y < 0"; + } + } + + getCacheKey() { + return "tilevector"; + } +} + +export class TypeNumber extends BaseDataType { + serialize(value) { + assert(Number.isFinite(value), "Type number got non number for serialize: " + value); + assert(!Number.isNaN(value), "Value is nan: " + value); + return round4Digits(value); + } + + getAsJsonSchemaUncached() { + return { + type: "number", + }; + } + + /** + * @see BaseDataType.deserialize + * @param {any} value + * @param {GameRoot} root + * @param {object} targetObject + * @param {string|number} targetKey + * @returns {string|void} String error code or null on success + */ + deserialize(value, targetObject, targetKey, root) { + targetObject[targetKey] = value; + } + + verifySerializedValue(value) { + if (!Number.isFinite(value)) { + return "Not a valid number: " + value; + } + } + + getCacheKey() { + return "float"; + } +} + +export class TypePositiveNumber extends BaseDataType { + serialize(value) { + assert(Number.isFinite(value), "Type number got non number for serialize: " + value); + assert(value >= 0, "Postitive number got negative value: " + value); + return round4Digits(value); + } + + /** + * @see BaseDataType.deserialize + * @param {any} value + * @param {GameRoot} root + * @param {object} targetObject + * @param {string|number} targetKey + * @returns {string|void} String error code or null on success + */ + deserialize(value, targetObject, targetKey, root) { + targetObject[targetKey] = value; + } + + getAsJsonSchemaUncached() { + return { + type: "number", + minimum: 0, + }; + } + + verifySerializedValue(value) { + if (!Number.isFinite(value)) { + return "Not a valid number: " + value; + } + if (value < 0) { + return "Positive number got negative value: " + value; + } + } + + getCacheKey() { + return "ufloat"; + } +} + +export class TypeEnum extends BaseDataType { + /** + * @param {Object.} enumeration + */ + constructor(enumeration = {}) { + super(); + this.availableValues = Object.values(enumeration); + } + + serialize(value) { + assert(this.availableValues.indexOf(value) >= 0, "Unknown value: " + value); + return value; + } + + /** + * @see BaseDataType.deserialize + * @param {any} value + * @param {GameRoot} root + * @param {object} targetObject + * @param {string|number} targetKey + * @returns {string|void} String error code or null on success + */ + deserialize(value, targetObject, targetKey, root) { + targetObject[targetKey] = value; + } + + getAsJsonSchemaUncached() { + return { + type: "string", + enum: this.availableValues, + }; + } + + verifySerializedValue(value) { + if (this.availableValues.indexOf(value) < 0) { + return "Unknown enum value: " + value; + } + } + + getCacheKey() { + return "enum." + this.availableValues.join(","); + } +} + +export class TypeEntity extends BaseDataType { + serialize(value) { + // assert(value instanceof Entity, "Not a valid entity ref: " + value); + assert(value.uid, "Entity has no uid yet"); + assert(!value.destroyed, "Entity already destroyed"); + assert(!value.queuedForDestroy, "Entity queued for destroy"); + + return value.uid; + } + + getAsJsonSchemaUncached() { + return { + type: "integer", + minimum: 0, + }; + } + + /** + * @see BaseDataType.deserialize + * @param {any} value + * @param {GameRoot} root + * @param {object} targetObject + * @param {string|number} targetKey + * @returns {string|void} String error code or null on success + */ + deserialize(value, targetObject, targetKey, root) { + const entity = root.entityMgr.findByUid(value); + if (!entity) { + return "Entity not found by uid: " + value; + } + targetObject[targetKey] = entity; + } + + verifySerializedValue(value) { + if (!Number.isFinite(value)) { + return "Not a valid uuid: " + value; + } + } + + getCacheKey() { + return "entity"; + } +} + +export class TypeEntityWeakref extends BaseDataType { + serialize(value) { + if (value === null) { + return null; + } + + // assert(value instanceof Entity, "Not a valid entity ref (weak): " + value); + assert(value.uid, "Entity has no uid yet"); + if (value.destroyed || value.queuedForDestroy) { + return null; + } + return value.uid; + } + + /** + * @see BaseDataType.deserialize + * @param {any} value + * @param {GameRoot} root + * @param {object} targetObject + * @param {string|number} targetKey + * @returns {string|void} String error code or null on success + */ + deserialize(value, targetObject, targetKey, root) { + if (value === null) { + targetObject[targetKey] = null; + return; + } + const entity = root.entityMgr.findByUid(value, false); + targetObject[targetKey] = entity; + } + + getAsJsonSchemaUncached() { + return { + type: ["null", "integer"], + minimum: 0, + }; + } + + allowNull() { + return true; + } + + verifySerializedValue(value) { + if (value !== null && !Number.isFinite(value)) { + return "Not a valid uuid: " + value; + } + } + + getCacheKey() { + return "entity-weakref"; + } +} + +export class TypeClass extends BaseDataType { + /** + * + * @param {FactoryTemplate<*>} registry + * @param {(GameRoot, object) => object} customResolver + */ + constructor(registry, customResolver = null) { + super(); + this.registry = registry; + this.customResolver = customResolver; + } + + serialize(value) { + assert(typeof value === "object", "Not a class instance: " + value); + return { + $: value.constructor.getId(), + data: value.serialize(), + }; + } + + getAsJsonSchemaUncached() { + const options = []; + const entries = this.registry.getEntries(); + for (let i = 0; i < entries.length; ++i) { + const entry = entries[i]; + + options.push( + schemaObject({ + $: { + type: "string", + // @ts-ignore + enum: [entry.getId()], + }, + // @ts-ignore + data: schemaToJsonSchema(entry.getCachedSchema()), + }) + ); + } + + return { oneOf: options }; + } + + /** + * @see BaseDataType.deserialize + * @param {any} value + * @param {GameRoot} root + * @param {object} targetObject + * @param {string|number} targetKey + * @returns {string|void} String error code or null on success + */ + deserialize(value, targetObject, targetKey, root) { + let instance; + + if (this.customResolver) { + instance = this.customResolver(root, value); + if (!instance) { + return "Failed to call custom resolver"; + } + } else { + const instanceClass = this.registry.findById(value.$); + if (!instanceClass || !instanceClass.prototype) { + return "Invalid class id (runtime-err): " + value.$ + "->" + instanceClass; + } + instance = Object.create(instanceClass.prototype); + const errorState = instance.deserialize(value.data); + if (errorState) { + return errorState; + } + } + targetObject[targetKey] = instance; + } + + verifySerializedValue(value) { + if (!value) { + return "Got null data"; + } + + if (!this.registry.hasId(value.$)) { + return "Invalid class id: " + value.$ + " (factory is " + this.registry.getId() + ")"; + } + } + + getCacheKey() { + return "class." + this.registry.getId(); + } +} + +export class TypeClassData extends BaseDataType { + /** + * + * @param {FactoryTemplate<*>} registry + */ + constructor(registry) { + super(); + this.registry = registry; + } + + serialize(value) { + assert(typeof value === "object", "Not a class instance: " + value); + return value.serialize(); + } + + getAsJsonSchemaUncached() { + const options = []; + const entries = this.registry.getEntries(); + for (let i = 0; i < entries.length; ++i) { + const entry = entries[i]; + options.push( + schemaToJsonSchema(/** @type {typeof BasicSerializableObject} */ (entry).getCachedSchema()) + ); + } + return { oneOf: options }; + } + + /** + * @see BaseDataType.deserialize + * @param {any} value + * @param {GameRoot} root + * @param {object} targetObject + * @param {string|number} targetKey + * @returns {string|void} String error code or null on success + */ + deserialize(value, targetObject, targetKey, root) { + assert(false, "can not deserialize class data of type " + this.registry.getId()); + } + + verifySerializedValue(value) { + if (!value) { + return "Got null data"; + } + } + + getCacheKey() { + return "class." + this.registry.getId(); + } +} + +export class TypeClassFromMetaclass extends BaseDataType { + /** + * + * @param {typeof BasicSerializableObject} classHandle + * @param {SingletonFactoryTemplate<*>} registry + */ + constructor(classHandle, registry) { + super(); + this.registry = registry; + this.classHandle = classHandle; + } + + serialize(value) { + assert(typeof value === "object", "Not a class instance: " + value); + return { + $: value.getMetaclass().getId(), + data: value.serialize(), + }; + } + + getAsJsonSchemaUncached() { + // const options = []; + const ids = this.registry.getAllIds(); + + return { + $: { + type: "string", + enum: ids, + }, + data: schemaToJsonSchema(this.classHandle.getCachedSchema()), + }; + } + + /** + * @see BaseDataType.deserialize + * @param {any} value + * @param {GameRoot} root + * @param {object} targetObject + * @param {string|number} targetKey + * @returns {string|void} String error code or null on success + */ + deserialize(value, targetObject, targetKey, root) { + const metaClassInstance = this.registry.findById(value.$); + if (!metaClassInstance || !metaClassInstance.prototype) { + return "Invalid meta class id (runtime-err): " + value.$ + "->" + metaClassInstance; + } + + const instanceClass = metaClassInstance.getInstanceClass(); + const instance = Object.create(instanceClass.prototype); + const errorState = instance.deserialize(value.data); + if (errorState) { + return errorState; + } + targetObject[targetKey] = instance; + } + + verifySerializedValue(value) { + if (!value) { + return "Got null data"; + } + + if (!this.registry.hasId(value.$)) { + return "Invalid class id: " + value.$ + " (factory is " + this.registry.getId() + ")"; + } + } + + getCacheKey() { + return "classofmetaclass." + this.registry.getId(); + } +} + +export class TypeMetaClass extends BaseDataType { + /** + * + * @param {SingletonFactoryTemplate<*>} registry + */ + constructor(registry) { + super(); + this.registry = registry; + } + + serialize(value) { + return value.getId(); + } + + /** + * @see BaseDataType.deserialize + * @param {any} value + * @param {GameRoot} root + * @param {object} targetObject + * @param {string|number} targetKey + * @returns {string|void} String error code or null on success + */ + deserialize(value, targetObject, targetKey, root) { + const instanceClass = this.registry.findById(value); + if (!instanceClass) { + return "Invalid class id (runtime-err): " + value; + } + targetObject[targetKey] = instanceClass; + } + + getAsJsonSchemaUncached() { + return { + type: "string", + enum: this.registry.getAllIds(), + }; + } + + verifySerializedValue(value) { + if (!value) { + return "Got null data"; + } + + if (typeof value !== "string") { + return "Got non string data"; + } + + if (!this.registry.hasId(value)) { + return "Invalid class id: " + value + " (factory is " + this.registry.getId() + ")"; + } + } + + getCacheKey() { + return "metaclass." + this.registry.getId(); + } +} + +export class TypeArray extends BaseDataType { + /** + * @param {BaseDataType} innerType + */ + constructor(innerType, fixedSize = false) { + super(); + this.fixedSize = fixedSize; + this.innerType = innerType; + } + + serialize(value) { + assert(Array.isArray(value), "Not an array"); + const result = new Array(value.length); + for (let i = 0; i < value.length; ++i) { + result[i] = this.innerType.serialize(value[i]); + } + return result; + } + + /** + * @see BaseDataType.deserialize + * @param {any} value + * @param {GameRoot} root + * @param {object} targetObject + * @param {string|number} targetKey + * @returns {string|void} String error code or null on success + */ + deserialize(value, targetObject, targetKey, root) { + let destination = targetObject[targetKey]; + if (!destination) { + targetObject[targetKey] = destination = new Array(value.length); + } + + const size = this.fixedSize ? Math.min(value.length, destination.length) : value.length; + + for (let i = 0; i < size; ++i) { + const errorStatus = this.innerType.deserializeWithVerify(value[i], destination, i, root); + if (errorStatus) { + return errorStatus; + } + } + } + + getAsJsonSchemaUncached() { + return { + type: "array", + items: this.innerType.getAsJsonSchema(), + }; + } + + verifySerializedValue(value) { + if (!Array.isArray(value)) { + return "Not an array: " + value; + } + } + + getCacheKey() { + return "array." + this.innerType.getCacheKey(); + } +} + +export class TypeFixedClass extends BaseDataType { + /** + * + * @param {typeof BasicSerializableObject} baseclass + */ + constructor(baseclass) { + super(); + this.baseclass = baseclass; + } + + serialize(value) { + assert(value instanceof this.baseclass, "Not a valid class instance"); + return value.serialize(); + } + + /** + * @see BaseDataType.deserialize + * @param {any} value + * @param {GameRoot} root + * @param {object} targetObject + * @param {string|number} targetKey + * @returns {string|void} String error code or null on success + */ + deserialize(value, targetObject, targetKey, root) { + const instance = Object.create(this.baseclass.prototype); + const errorState = instance.deserialize(value); + if (errorState) { + return "Failed to deserialize class: " + errorState; + } + targetObject[targetKey] = instance; + } + + getAsJsonSchemaUncached() { + this.baseclass.getSchema(); + this.baseclass.getCachedSchema(); + return schemaToJsonSchema(this.baseclass.getCachedSchema()); + } + + verifySerializedValue(value) { + if (!value) { + return "Got null data"; + } + } + + getCacheKey() { + return "fixedclass." + this.baseclass.getId(); + } +} + +export class TypeKeyValueMap extends BaseDataType { + /** + * @param {BaseDataType} valueType + * @param {boolean=} includeEmptyValues + */ + constructor(valueType, includeEmptyValues = true) { + super(); + this.valueType = valueType; + this.includeEmptyValues = includeEmptyValues; + } + + serialize(value) { + assert(typeof value === "object", "not an object"); + let result = {}; + for (const key in value) { + const serialized = this.valueType.serialize(value[key]); + if (!this.includeEmptyValues && typeof serialized === "object") { + if ( + serialized.$ && + typeof serialized.data === "object" && + Object.keys(serialized.data).length === 0 + ) { + continue; + } else if (Object.keys(serialized).length === 0) { + continue; + } + } + + result[key] = serialized; + } + return result; + } + + /** + * @see BaseDataType.deserialize + * @param {any} value + * @param {GameRoot} root + * @param {object} targetObject + * @param {string|number} targetKey + * @returns {string|void} String error code or null on success + */ + deserialize(value, targetObject, targetKey, root) { + let result = {}; + for (const key in value) { + const errorCode = this.valueType.deserializeWithVerify(value[key], result, key, root); + if (errorCode) { + return errorCode; + } + } + targetObject[targetKey] = result; + } + + getAsJsonSchemaUncached() { + return { + type: "object", + additionalProperties: this.valueType.getAsJsonSchema(), + }; + } + + verifySerializedValue(value) { + if (typeof value !== "object") { + return "KV map is not an object"; + } + } + + getCacheKey() { + return "kvmap." + this.valueType.getCacheKey(); + } +} + +export class TypeClassId extends BaseDataType { + /** + * @param {FactoryTemplate<*>|SingletonFactoryTemplate<*>} registry + */ + constructor(registry) { + super(); + this.registry = registry; + } + + serialize(value) { + assert(typeof value === "string", "Not a valid string"); + assert(this.registry.hasId(value), "Id " + value + " not found in registry"); + return value; + } + + /** + * @see BaseDataType.deserialize + * @param {any} value + * @param {GameRoot} root + * @param {object} targetObject + * @param {string|number} targetKey + * @returns {string|void} String error code or null on success + */ + deserialize(value, targetObject, targetKey, root) { + targetObject[targetKey] = value; + } + + getAsJsonSchemaUncached() { + return { + type: "string", + enum: this.registry.getAllIds(), + }; + } + + verifySerializedValue(value) { + if (typeof value !== "string") { + return "Not a valid registry id key: " + value; + } + if (!this.registry.hasId(value)) { + return "Id " + value + " not known to registry"; + } + } + + getCacheKey() { + return "classid." + this.registry.getId(); + } +} + +export class TypePair extends BaseDataType { + /** + * @param {BaseDataType} type1 + * @param {BaseDataType} type2 + */ + constructor(type1, type2) { + super(); + assert(type1 && type1 instanceof BaseDataType, "bad first type given for pair"); + assert(type2 && type2 instanceof BaseDataType, "bad second type given for pair"); + this.type1 = type1; + this.type2 = type2; + } + + serialize(value) { + assert(Array.isArray(value), "pair: not an array"); + assert(value.length === 2, "pair: length != 2"); + return [this.type1.serialize(value[0]), this.type2.serialize(value[1])]; + } + + /** + * @see BaseDataType.deserialize + * @param {any} value + * @param {GameRoot} root + * @param {object} targetObject + * @param {string|number} targetKey + * @returns {string|void} String error code or null on success + */ + deserialize(value, targetObject, targetKey, root) { + const result = [undefined, undefined]; + + let errorCode = this.type1.deserialize(value[0], result, 0, root); + if (errorCode) { + return errorCode; + } + errorCode = this.type2.deserialize(value[1], result, 1, root); + if (errorCode) { + return errorCode; + } + + targetObject[targetKey] = result; + } + + getAsJsonSchemaUncached() { + return { + type: "array", + minLength: 2, + maxLength: 2, + items: [this.type1.getAsJsonSchema(), this.type2.getAsJsonSchema()], + }; + } + + verifySerializedValue(value) { + if (!Array.isArray(value)) { + return "Pair is not an array"; + } + if (value.length !== 2) { + return "Pair length != 2"; + } + let errorCode = this.type1.verifySerializedValue(value[0]); + if (errorCode) { + return errorCode; + } + errorCode = this.type2.verifySerializedValue(value[1]); + if (errorCode) { + return errorCode; + } + } + + getCacheKey() { + return "pair.(" + this.type1.getCacheKey() + "," + this.type2.getCacheKey + ")"; + } +} + +export class TypeNullable extends BaseDataType { + /** + * @param {BaseDataType} wrapped + */ + constructor(wrapped) { + super(); + this.wrapped = wrapped; + } + + serialize(value) { + if (value === null || value === undefined) { + return null; + } + return this.wrapped.serialize(value); + } + + /** + * @see BaseDataType.deserialize + * @param {any} value + * @param {GameRoot} root + * @param {object} targetObject + * @param {string|number} targetKey + * @returns {string|void} String error code or null on success + */ + deserialize(value, targetObject, targetKey, root) { + if (value === null || value === undefined) { + targetObject[targetKey] = null; + return; + } + return this.wrapped.deserialize(value, targetObject, targetKey, root); + } + + verifySerializedValue(value) { + if (value === null) { + return; + } + return this.wrapped.verifySerializedValue(value); + } + + getAsJsonSchemaUncached() { + return { + oneOf: [ + { + type: "null", + }, + this.wrapped.getAsJsonSchema(), + ], + }; + } + + allowNull() { + return true; + } + + getCacheKey() { + return "nullable." + this.wrapped.getCacheKey(); + } +} + +export class TypeStructuredObject extends BaseDataType { + /** + * @param {Object.} descriptor + */ + constructor(descriptor) { + super(); + this.descriptor = descriptor; + } + + serialize(value) { + assert(typeof value === "object", "not an object"); + let result = {}; + for (const key in this.descriptor) { + // assert(value.hasOwnProperty(key), "Serialization: Object does not have", key, "property!"); + result[key] = this.descriptor[key].serialize(value[key]); + } + return result; + } + + /** + * @see BaseDataType.deserialize + * @param {any} value + * @param {GameRoot} root + * @param {object} targetObject + * @param {string|number} targetKey + * @returns {string|void} String error code or null on success + */ + deserialize(value, targetObject, targetKey, root) { + let target = targetObject[targetKey]; + if (!target) { + targetObject[targetKey] = target = {}; + } + + for (const key in value) { + const valueType = this.descriptor[key]; + const errorCode = valueType.deserializeWithVerify(value[key], target, key, root); + if (errorCode) { + return errorCode; + } + } + } + + getAsJsonSchemaUncached() { + let properties = {}; + for (const key in this.descriptor) { + properties[key] = this.descriptor[key].getAsJsonSchema(); + } + + return { + type: "object", + required: Object.keys(this.descriptor), + properties, + }; + } + + verifySerializedValue(value) { + if (typeof value !== "object") { + return "structured object is not an object"; + } + for (const key in this.descriptor) { + if (!value.hasOwnProperty(key)) { + return "structured object is missing key " + key; + } + const subError = this.descriptor[key].verifySerializedValue(value[key]); + if (subError) { + return "structured object::" + subError; + } + } + } + + getCacheKey() { + let props = []; + for (const key in this.descriptor) { + props.push(key + "=" + this.descriptor[key].getCacheKey()); + } + return "structured[" + props.join(",") + "]"; + } +} diff --git a/src/js/savegame/serializer_internal.js b/src/js/savegame/serializer_internal.js index fa02a437..c75cebad 100644 --- a/src/js/savegame/serializer_internal.js +++ b/src/js/savegame/serializer_internal.js @@ -80,7 +80,10 @@ export class SerializerInternal { for (const componentId in data) { if (!entity.components[componentId]) { if (G_IS_DEV && !globalConfig.debug.disableSlowAsserts) { - logger.warn("Entity no longer has component:", componentId); + // @ts-ignore + if (++window.componentWarningsShown < 100) { + logger.warn("Entity no longer has component:", componentId); + } } continue; } diff --git a/src/js/states/ingame.js b/src/js/states/ingame.js index 2dd2db76..316c536c 100644 --- a/src/js/states/ingame.js +++ b/src/js/states/ingame.js @@ -285,14 +285,10 @@ export class InGameState extends GameState { */ stage7Warmup() { if (this.switchStage(stages.s7_warmup)) { - if (G_IS_DEV && globalConfig.debug.noArtificialDelays) { - this.warmupTimeSeconds = 0.05; + if (this.creationPayload.fastEnter) { + this.warmupTimeSeconds = globalConfig.warmupTimeSecondsFast; } else { - if (this.creationPayload.fastEnter) { - this.warmupTimeSeconds = globalConfig.warmupTimeSecondsFast; - } else { - this.warmupTimeSeconds = globalConfig.warmupTimeSecondsRegular; - } + this.warmupTimeSeconds = globalConfig.warmupTimeSecondsRegular; } } } diff --git a/src/js/states/keybindings.js b/src/js/states/keybindings.js index b68626c7..a01629f1 100644 --- a/src/js/states/keybindings.js +++ b/src/js/states/keybindings.js @@ -1,179 +1,173 @@ -import { TextualGameState } from "../core/textual_game_state"; -import { SOUNDS } from "../platform/sound"; -import { T } from "../translations"; -import { KEYMAPPINGS, getStringForKeyCode } from "../game/key_action_mapper"; -import { Dialog } from "../core/modal_dialog_elements"; -import { IS_DEMO } from "../core/config"; - -export class KeybindingsState extends TextualGameState { - constructor() { - super("KeybindingsState"); - } - - getStateHeaderTitle() { - return T.keybindings.title; - } - - getMainContentHTML() { - return ` - -
- ${T.keybindings.hint} - - -
- -
- -
- `; - } - - onEnter() { - const keybindingsElem = this.htmlElement.querySelector(".keybindings"); - - this.trackClicks(this.htmlElement.querySelector(".resetBindings"), this.resetBindings); - - for (const category in KEYMAPPINGS) { - const categoryDiv = document.createElement("div"); - categoryDiv.classList.add("category"); - keybindingsElem.appendChild(categoryDiv); - - const labelDiv = document.createElement("strong"); - labelDiv.innerText = T.keybindings.categoryLabels[category]; - labelDiv.classList.add("categoryLabel"); - categoryDiv.appendChild(labelDiv); - - for (const keybindingId in KEYMAPPINGS[category]) { - const mapped = KEYMAPPINGS[category][keybindingId]; - - const elem = document.createElement("div"); - elem.classList.add("entry"); - elem.setAttribute("data-keybinding", keybindingId); - categoryDiv.appendChild(elem); - - const title = document.createElement("span"); - title.classList.add("title"); - title.innerText = T.keybindings.mappings[keybindingId]; - elem.appendChild(title); - - const mappingDiv = document.createElement("span"); - mappingDiv.classList.add("mapping"); - elem.appendChild(mappingDiv); - - const editBtn = document.createElement("button"); - editBtn.classList.add("styledButton", "editKeybinding"); - - const resetBtn = document.createElement("button"); - resetBtn.classList.add("styledButton", "resetKeybinding"); - - if (mapped.builtin) { - editBtn.classList.add("disabled"); - resetBtn.classList.add("disabled"); - } else { - this.trackClicks(editBtn, () => this.editKeybinding(keybindingId)); - this.trackClicks(resetBtn, () => this.resetKeybinding(keybindingId)); - } - elem.appendChild(editBtn); - elem.appendChild(resetBtn); - } - } - this.updateKeybindings(); - } - - editKeybinding(id) { - // if (IS_DEMO) { - // this.dialogs.showFeatureRestrictionInfo(T.demo.features.customizeKeybindings); - // return; - // } - - const dialog = new Dialog({ - app: this.app, - title: T.dialogs.editKeybinding.title, - contentHTML: T.dialogs.editKeybinding.desc, - buttons: ["cancel:good"], - type: "info", - }); - - dialog.inputReciever.keydown.add(({ keyCode, shift, alt, event }) => { - if (keyCode === 27) { - this.dialogs.closeDialog(dialog); - return; - } - - if (event) { - event.preventDefault(); - } - - if (event.target && event.target.tagName === "BUTTON" && keyCode === 1) { - return; - } - - if ( - // Enter - keyCode === 13 - ) { - // Ignore builtins - return; - } - - this.app.settings.updateKeybindingOverride(id, keyCode); - - this.dialogs.closeDialog(dialog); - this.updateKeybindings(); - }); - - dialog.inputReciever.backButton.add(() => {}); - this.dialogs.internalShowDialog(dialog); - - this.app.sound.playUiSound(SOUNDS.dialogOk); - } - - updateKeybindings() { - const overrides = this.app.settings.getKeybindingOverrides(); - for (const category in KEYMAPPINGS) { - for (const keybindingId in KEYMAPPINGS[category]) { - const mapped = KEYMAPPINGS[category][keybindingId]; - - const container = this.htmlElement.querySelector("[data-keybinding='" + keybindingId + "']"); - assert(container, "Container for keybinding not found: " + keybindingId); - - let keyCode = mapped.keyCode; - if (overrides[keybindingId]) { - keyCode = overrides[keybindingId]; - } - - const mappingDiv = container.querySelector(".mapping"); - mappingDiv.innerHTML = getStringForKeyCode(keyCode); - mappingDiv.classList.toggle("changed", !!overrides[keybindingId]); - - const resetBtn = container.querySelector("button.resetKeybinding"); - resetBtn.classList.toggle("disabled", mapped.builtin || !overrides[keybindingId]); - } - } - } - - resetKeybinding(id) { - this.app.settings.resetKeybindingOverride(id); - this.updateKeybindings(); - } - - resetBindings() { - const { reset } = this.dialogs.showWarning( - T.dialogs.resetKeybindingsConfirmation.title, - T.dialogs.resetKeybindingsConfirmation.desc, - ["cancel:good", "reset:bad"] - ); - - reset.add(() => { - this.app.settings.resetKeybindingOverrides(); - this.updateKeybindings(); - - this.dialogs.showInfo(T.dialogs.keybindingsResetOk.title, T.dialogs.keybindingsResetOk.desc); - }); - } - - getDefaultPreviousState() { - return "SettingsState"; - } -} +import { Dialog } from "../core/modal_dialog_elements"; +import { TextualGameState } from "../core/textual_game_state"; +import { getStringForKeyCode, KEYMAPPINGS } from "../game/key_action_mapper"; +import { SOUNDS } from "../platform/sound"; +import { T } from "../translations"; + +export class KeybindingsState extends TextualGameState { + constructor() { + super("KeybindingsState"); + } + + getStateHeaderTitle() { + return T.keybindings.title; + } + + getMainContentHTML() { + return ` + +
+ ${T.keybindings.hint} + + +
+ +
+ +
+ `; + } + + onEnter() { + const keybindingsElem = this.htmlElement.querySelector(".keybindings"); + + this.trackClicks(this.htmlElement.querySelector(".resetBindings"), this.resetBindings); + + for (const category in KEYMAPPINGS) { + const categoryDiv = document.createElement("div"); + categoryDiv.classList.add("category"); + keybindingsElem.appendChild(categoryDiv); + + const labelDiv = document.createElement("strong"); + labelDiv.innerText = T.keybindings.categoryLabels[category]; + labelDiv.classList.add("categoryLabel"); + categoryDiv.appendChild(labelDiv); + + for (const keybindingId in KEYMAPPINGS[category]) { + const mapped = KEYMAPPINGS[category][keybindingId]; + + const elem = document.createElement("div"); + elem.classList.add("entry"); + elem.setAttribute("data-keybinding", keybindingId); + categoryDiv.appendChild(elem); + + const title = document.createElement("span"); + title.classList.add("title"); + title.innerText = T.keybindings.mappings[keybindingId]; + elem.appendChild(title); + + const mappingDiv = document.createElement("span"); + mappingDiv.classList.add("mapping"); + elem.appendChild(mappingDiv); + + const editBtn = document.createElement("button"); + editBtn.classList.add("styledButton", "editKeybinding"); + + const resetBtn = document.createElement("button"); + resetBtn.classList.add("styledButton", "resetKeybinding"); + + if (mapped.builtin) { + editBtn.classList.add("disabled"); + resetBtn.classList.add("disabled"); + } else { + this.trackClicks(editBtn, () => this.editKeybinding(keybindingId)); + this.trackClicks(resetBtn, () => this.resetKeybinding(keybindingId)); + } + elem.appendChild(editBtn); + elem.appendChild(resetBtn); + } + } + this.updateKeybindings(); + } + + editKeybinding(id) { + const dialog = new Dialog({ + app: this.app, + title: T.dialogs.editKeybinding.title, + contentHTML: T.dialogs.editKeybinding.desc, + buttons: ["cancel:good"], + type: "info", + }); + + dialog.inputReciever.keydown.add(({ keyCode, shift, alt, event }) => { + if (keyCode === 27) { + this.dialogs.closeDialog(dialog); + return; + } + + if (event) { + event.preventDefault(); + } + + if (event.target && event.target.tagName === "BUTTON" && keyCode === 1) { + return; + } + + if ( + // Enter + keyCode === 13 + ) { + // Ignore builtins + return; + } + + this.app.settings.updateKeybindingOverride(id, keyCode); + + this.dialogs.closeDialog(dialog); + this.updateKeybindings(); + }); + + dialog.inputReciever.backButton.add(() => {}); + this.dialogs.internalShowDialog(dialog); + + this.app.sound.playUiSound(SOUNDS.dialogOk); + } + + updateKeybindings() { + const overrides = this.app.settings.getKeybindingOverrides(); + for (const category in KEYMAPPINGS) { + for (const keybindingId in KEYMAPPINGS[category]) { + const mapped = KEYMAPPINGS[category][keybindingId]; + + const container = this.htmlElement.querySelector("[data-keybinding='" + keybindingId + "']"); + assert(container, "Container for keybinding not found: " + keybindingId); + + let keyCode = mapped.keyCode; + if (overrides[keybindingId]) { + keyCode = overrides[keybindingId]; + } + + const mappingDiv = container.querySelector(".mapping"); + mappingDiv.innerHTML = getStringForKeyCode(keyCode); + mappingDiv.classList.toggle("changed", !!overrides[keybindingId]); + + const resetBtn = container.querySelector("button.resetKeybinding"); + resetBtn.classList.toggle("disabled", mapped.builtin || !overrides[keybindingId]); + } + } + } + + resetKeybinding(id) { + this.app.settings.resetKeybindingOverride(id); + this.updateKeybindings(); + } + + resetBindings() { + const { reset } = this.dialogs.showWarning( + T.dialogs.resetKeybindingsConfirmation.title, + T.dialogs.resetKeybindingsConfirmation.desc, + ["cancel:good", "reset:bad"] + ); + + reset.add(() => { + this.app.settings.resetKeybindingOverrides(); + this.updateKeybindings(); + + this.dialogs.showInfo(T.dialogs.keybindingsResetOk.title, T.dialogs.keybindingsResetOk.desc); + }); + } + + getDefaultPreviousState() { + return "SettingsState"; + } +} diff --git a/src/js/states/main_menu.js b/src/js/states/main_menu.js index 0d186ffd..fa177874 100644 --- a/src/js/states/main_menu.js +++ b/src/js/states/main_menu.js @@ -1,21 +1,23 @@ -import { GameState } from "../core/game_state"; import { cachebust } from "../core/cachebust"; -import { A_B_TESTING_LINK_TYPE, globalConfig, IS_DEMO, THIRDPARTY_URLS } from "../core/config"; +import { A_B_TESTING_LINK_TYPE, globalConfig, THIRDPARTY_URLS } from "../core/config"; +import { GameState } from "../core/game_state"; +import { DialogWithForm } from "../core/modal_dialog_elements"; +import { FormElementInput } from "../core/modal_dialog_forms"; +import { ReadWriteProxy } from "../core/read_write_proxy"; import { - makeDiv, - makeButtonElement, formatSecondsToTimeAgo, - waitNextFrame, + generateFileDownload, isSupportedBrowser, makeButton, + makeButtonElement, + makeDiv, removeAllChildren, + startFileChoose, + waitNextFrame, } from "../core/utils"; -import { ReadWriteProxy } from "../core/read_write_proxy"; import { HUDModalDialogs } from "../game/hud/parts/modal_dialogs"; -import { T } from "../translations"; import { getApplicationSettingById } from "../profile/application_settings"; -import { FormElementInput } from "../core/modal_dialog_forms"; -import { DialogWithForm } from "../core/modal_dialog_elements"; +import { T } from "../translations"; const trim = require("trim"); @@ -24,23 +26,6 @@ const trim = require("trim"); * @typedef {import("../profile/setting_types").EnumSetting} EnumSetting */ -/** - * Generates a file download - * @param {string} filename - * @param {string} text - */ -function generateFileDownload(filename, text) { - var element = document.createElement("a"); - element.setAttribute("href", "data:text/plain;charset=utf-8," + encodeURIComponent(text)); - element.setAttribute("download", filename); - - element.style.display = "none"; - document.body.appendChild(element); - - element.click(); - document.body.removeChild(element); -} - export class MainMenuState extends GameState { constructor() { super("MainMenuState"); @@ -49,18 +34,16 @@ export class MainMenuState extends GameState { getInnerHTML() { const bannerHtml = `

${T.demoBanners.title}

-

${T.demoBanners.intro}

- Get the shapez.io standalone! `; - return ` + const showDemoBadges = this.app.restrictionMgr.getIsStandaloneMarketingActive(); + return `
- ${ G_IS_STANDALONE || G_IS_DEV ? ` @@ -74,17 +57,14 @@ export class MainMenuState extends GameState { - - -
- +
- ${IS_DEMO ? `
${bannerHtml}
` : ""} + ${showDemoBadges ? `
${bannerHtml}
` : ""}
@@ -95,12 +75,9 @@ export class MainMenuState extends GameState { }
- -
-
`; } + /** + * Asks the user to import a savegame + */ requestImportSavegame() { if ( - IS_DEMO && this.app.savegameMgr.getSavegamesMetaData().length > 0 && - !this.app.platformWrapper.getHasUnlimitedSavegames() + !this.app.restrictionMgr.getHasUnlimitedSavegames() ) { this.app.analytics.trackUiClick("importgame_slot_limit_show"); this.showSavegameSlotLimit(); return; } - var input = document.createElement("input"); - input.type = "file"; - input.accept = ".bin"; - - input.onchange = e => { - const file = input.files[0]; + // Create a 'fake' file-input to accept savegames + startFileChoose(".bin").then(file => { if (file) { + const closeLoader = this.dialogs.showLoadingDialog(); waitNextFrame().then(() => { this.app.analytics.trackUiClick("import_savegame"); - const closeLoader = this.dialogs.showLoadingDialog(); const reader = new FileReader(); reader.addEventListener("load", event => { const contents = event.target.result; @@ -194,8 +168,7 @@ export class MainMenuState extends GameState { reader.readAsText(file, "utf-8"); }); } - }; - input.click(); + }); } onBackButton() { @@ -347,20 +320,23 @@ export class MainMenuState extends GameState { }); optionSelected.add(value => { - this.app.settings.updateLanguage(value); - if (setting.restartRequired) { - if (this.app.platformWrapper.getSupportsRestart()) { - this.app.platformWrapper.performRestart(); - } else { - this.dialogs.showInfo(T.dialogs.restartRequired.title, T.dialogs.restartRequired.text, [ - "ok:good", - ]); + this.app.settings.updateLanguage(value).then(() => { + if (setting.restartRequired) { + if (this.app.platformWrapper.getSupportsRestart()) { + this.app.platformWrapper.performRestart(); + } else { + this.dialogs.showInfo( + T.dialogs.restartRequired.title, + T.dialogs.restartRequired.text, + ["ok:good"] + ); + } } - } - if (setting.changeCb) { - setting.changeCb(this.app, value); - } + if (setting.changeCb) { + setting.changeCb(this.app, value); + } + }); // Update current icon this.htmlElement.querySelector("button.languageChoose").setAttribute("data-languageIcon", value); @@ -557,9 +533,8 @@ export class MainMenuState extends GameState { onPlayButtonClicked() { if ( - IS_DEMO && this.app.savegameMgr.getSavegamesMetaData().length > 0 && - !this.app.platformWrapper.getHasUnlimitedSavegames() + !this.app.restrictionMgr.getHasUnlimitedSavegames() ) { this.app.analytics.trackUiClick("startgame_slot_limit_show"); this.showSavegameSlotLimit(); diff --git a/src/js/states/preload.js b/src/js/states/preload.js index b35b369d..c1746da6 100644 --- a/src/js/states/preload.js +++ b/src/js/states/preload.js @@ -145,6 +145,11 @@ export class PreloadState extends GameState { this.app.backgroundResourceLoader.startLoading(); }) + .then(() => this.setStatus("Initializing restrictions")) + .then(() => { + return this.app.restrictionMgr.initialize(); + }) + .then(() => this.setStatus("Initializing savegame")) .then(() => { return this.app.savegameMgr.initialize().catch(err => { diff --git a/src/js/states/settings.js b/src/js/states/settings.js index 5e22492a..36dee5d8 100644 --- a/src/js/states/settings.js +++ b/src/js/states/settings.js @@ -1,169 +1,169 @@ -import { TextualGameState } from "../core/textual_game_state"; -import { formatSecondsToTimeAgo } from "../core/utils"; -import { allApplicationSettings, enumCategories } from "../profile/application_settings"; -import { T } from "../translations"; - -export class SettingsState extends TextualGameState { - constructor() { - super("SettingsState"); - } - - getStateHeaderTitle() { - return T.settings.title; - } - - getMainContentHTML() { - return ` - - - -
- ${this.getSettingsHtml()} -
- - `; - } - - getCategoryButtonsHtml() { - return Object.keys(enumCategories) - .map(key => enumCategories[key]) - .map( - category => - ` - - ` - ) - .join(""); - } - - getSettingsHtml() { - const categoriesHTML = {}; - - Object.keys(enumCategories).forEach(key => { - const catName = enumCategories[key]; - categoriesHTML[catName] = `
`; - }); - - for (let i = 0; i < allApplicationSettings.length; ++i) { - const setting = allApplicationSettings[i]; - - categoriesHTML[setting.categoryId] += setting.getHtml(); - } - - return Object.keys(categoriesHTML) - .map(k => categoriesHTML[k] + "
") - .join(""); - } - - renderBuildText() { - const labelVersion = this.htmlElement.querySelector(".buildVersion"); - const lastBuildMs = new Date().getTime() - G_BUILD_TIME; - const lastBuildText = formatSecondsToTimeAgo(lastBuildMs / 1000.0); - - const version = T.settings.versionBadges[G_APP_ENVIRONMENT]; - - labelVersion.innerHTML = ` - - ${G_BUILD_VERSION} @ ${version} @ ${G_BUILD_COMMIT_HASH} - - - ${T.settings.buildDate.replace("", lastBuildText)}
-
`; - } - - onEnter(payload) { - this.renderBuildText(); - this.trackClicks(this.htmlElement.querySelector(".about"), this.onAboutClicked, { - preventDefault: false, - }); - - const keybindingsButton = this.htmlElement.querySelector(".editKeybindings"); - - if (keybindingsButton) { - this.trackClicks(keybindingsButton, this.onKeybindingsClicked, { preventDefault: false }); - } - - this.initSettings(); - this.initCategoryButtons(); - - this.htmlElement.querySelector(".category").classList.add("active"); - this.htmlElement.querySelector(".categoryButton").classList.add("active"); - } - - setActiveCategory(category) { - const previousCategory = this.htmlElement.querySelector(".category.active"); - const previousCategoryButton = this.htmlElement.querySelector(".categoryButton.active"); - - if (previousCategory.getAttribute("data-category") == category) { - return; - } - - previousCategory.classList.remove("active"); - previousCategoryButton.classList.remove("active"); - - const newCategory = this.htmlElement.querySelector("[data-category='" + category + "']"); - const newCategoryButton = this.htmlElement.querySelector("[data-category-btn='" + category + "']"); - - newCategory.classList.add("active"); - newCategoryButton.classList.add("active"); - } - - initSettings() { - allApplicationSettings.forEach(setting => { - /** @type {HTMLElement} */ - const element = this.htmlElement.querySelector("[data-setting='" + setting.id + "']"); - setting.bind(this.app, element, this.dialogs); - setting.syncValueToElement(); - this.trackClicks( - element, - () => { - setting.modify(); - }, - { preventDefault: false } - ); - }); - } - - initCategoryButtons() { - Object.keys(enumCategories).forEach(key => { - const category = enumCategories[key]; - const button = this.htmlElement.querySelector("[data-category-btn='" + category + "']"); - this.trackClicks( - button, - () => { - this.setActiveCategory(category); - }, - { preventDefault: false } - ); - }); - } - - onAboutClicked() { - this.moveToStateAddGoBack("AboutState"); - } - - onKeybindingsClicked() { - this.moveToStateAddGoBack("KeybindingsState"); - } -} +import { TextualGameState } from "../core/textual_game_state"; +import { formatSecondsToTimeAgo } from "../core/utils"; +import { allApplicationSettings, enumCategories } from "../profile/application_settings"; +import { T } from "../translations"; + +export class SettingsState extends TextualGameState { + constructor() { + super("SettingsState"); + } + + getStateHeaderTitle() { + return T.settings.title; + } + + getMainContentHTML() { + return ` + + + +
+ ${this.getSettingsHtml()} +
+ + `; + } + + getCategoryButtonsHtml() { + return Object.keys(enumCategories) + .map(key => enumCategories[key]) + .map( + category => + ` + + ` + ) + .join(""); + } + + getSettingsHtml() { + const categoriesHTML = {}; + + Object.keys(enumCategories).forEach(key => { + const catName = enumCategories[key]; + categoriesHTML[catName] = `
`; + }); + + for (let i = 0; i < allApplicationSettings.length; ++i) { + const setting = allApplicationSettings[i]; + + categoriesHTML[setting.categoryId] += setting.getHtml(this.app); + } + + return Object.keys(categoriesHTML) + .map(k => categoriesHTML[k] + "
") + .join(""); + } + + renderBuildText() { + const labelVersion = this.htmlElement.querySelector(".buildVersion"); + const lastBuildMs = new Date().getTime() - G_BUILD_TIME; + const lastBuildText = formatSecondsToTimeAgo(lastBuildMs / 1000.0); + + const version = T.settings.versionBadges[G_APP_ENVIRONMENT]; + + labelVersion.innerHTML = ` + + ${G_BUILD_VERSION} @ ${version} @ ${G_BUILD_COMMIT_HASH} + + + ${T.settings.buildDate.replace("", lastBuildText)}
+
`; + } + + onEnter(payload) { + this.renderBuildText(); + this.trackClicks(this.htmlElement.querySelector(".about"), this.onAboutClicked, { + preventDefault: false, + }); + + const keybindingsButton = this.htmlElement.querySelector(".editKeybindings"); + + if (keybindingsButton) { + this.trackClicks(keybindingsButton, this.onKeybindingsClicked, { preventDefault: false }); + } + + this.initSettings(); + this.initCategoryButtons(); + + this.htmlElement.querySelector(".category").classList.add("active"); + this.htmlElement.querySelector(".categoryButton").classList.add("active"); + } + + setActiveCategory(category) { + const previousCategory = this.htmlElement.querySelector(".category.active"); + const previousCategoryButton = this.htmlElement.querySelector(".categoryButton.active"); + + if (previousCategory.getAttribute("data-category") == category) { + return; + } + + previousCategory.classList.remove("active"); + previousCategoryButton.classList.remove("active"); + + const newCategory = this.htmlElement.querySelector("[data-category='" + category + "']"); + const newCategoryButton = this.htmlElement.querySelector("[data-category-btn='" + category + "']"); + + newCategory.classList.add("active"); + newCategoryButton.classList.add("active"); + } + + initSettings() { + allApplicationSettings.forEach(setting => { + /** @type {HTMLElement} */ + const element = this.htmlElement.querySelector("[data-setting='" + setting.id + "']"); + setting.bind(this.app, element, this.dialogs); + setting.syncValueToElement(); + this.trackClicks( + element, + () => { + setting.modify(); + }, + { preventDefault: false } + ); + }); + } + + initCategoryButtons() { + Object.keys(enumCategories).forEach(key => { + const category = enumCategories[key]; + const button = this.htmlElement.querySelector("[data-category-btn='" + category + "']"); + this.trackClicks( + button, + () => { + this.setActiveCategory(category); + }, + { preventDefault: false } + ); + }); + } + + onAboutClicked() { + this.moveToStateAddGoBack("AboutState"); + } + + onKeybindingsClicked() { + this.moveToStateAddGoBack("KeybindingsState"); + } +} diff --git a/translations/README.md b/translations/README.md index 596da8d8..020c7ca6 100644 --- a/translations/README.md +++ b/translations/README.md @@ -33,6 +33,7 @@ The base language is English and can be found [here](base-en.yaml). - [Ukrainian](base-uk.yaml) - [Indonesian](base-ind.yaml) - [Serbian](base-sr.yaml) +- [Czech](base-cz.yaml) (If you want to translate into a new language, see below!) diff --git a/translations/base-ar.yaml b/translations/base-ar.yaml index 7029bf28..0244395c 100644 --- a/translations/base-ar.yaml +++ b/translations/base-ar.yaml @@ -1,20 +1,17 @@ steamPage: - shortText: shapez.io is a game about building factories to automate the creation - and processing of increasingly complex shapes across an infinitely - expanding map. + shortText: لعبة شيبز (أشكال) هي لعبة تدور حول بناء مصانع وتوصيلها حتى تقوم بشكل + .آلي بصناعة أشكال مختلفة تزداد تعقيدا في خريطة لانهائية. discordLinkShort: Official Discord intro: >- - Shapez.io is a relaxed game in which you have to build factories for the - automated production of geometric shapes. + لعبة شيبز (أشكال) هي لعبة مريحة تقوم فيها ببناء مصانع ووتشغيلها آليا + لصناعة أشكال هندسية. - As the level increases, the shapes become more and more complex, and you have to spread out on the infinite map. + مع التقدم في المستوى، تزداد الأشكال تعقيداً، فيتوجب عليك التوسع في الخريطة اللانهائية، وذلك ليس كافياً للتقدم في مستوى اللعبة حيث عليك صناعة المزيد بأضعاف مضاعفة لتلبية الطلب، الشيء الوحيد الذي يمكنه مساعدتك هو التوسع. - And as if that wasn't enough, you also have to produce exponentially more to satisfy the demands - the only thing that helps is scaling! + بينما في البداية تقوم بصناعة أشكال مختلفة، تتطلب منك المراحل المتقدمة تلوين هذه الأشكال، حيث يتوجب عليك استخراج وخلط الألوان. - While you only process shapes at the beginning, you have to color them later - for this you have to extract and mix colors! - - Buying the game on Steam gives you access to the full version, but you can also play a demo on shapez.io first and decide later! - title_advantages: Standalone Advantages + عند شراءك اللعبة على ستيم (Steam) تحصل على الإصدار الكامل للعبة، ولكن يمكن أيضاً لعبة نسخة تجريبية على موقع shapez.io ثم يمكنك القرار لاحقا + title_advantages: ميزات نسخة الحاسوب advantages: - 12 New Level for a total of 26 levels - 18 New Buildings for a fully automated factory! @@ -199,11 +196,6 @@ dialogs: renameSavegame: title: Rename Savegame desc: You can rename your savegame here. - entityWarning: - title: Performance Warning - desc: You have placed a lot of buildings, this is just a friendly reminder that - the game can not handle an endless count of buildings - So try to - keep your factories compact! ingame: keybindingsOverlay: moveMap: Move @@ -259,27 +251,6 @@ ingame: title: Upgrades buttonUnlock: Upgrade tier: Tier - tierLabels: - - I - - II - - III - - IV - - V - - VI - - VII - - VIII - - IX - - X - - XI - - XII - - XIII - - XIV - - XV - - XVI - - XVII - - XVIII - - XIX - - XX maximumLevel: MAXIMUM LEVEL (Speed x) statistics: title: Statistics @@ -303,10 +274,6 @@ ingame: playtime: Playtime buildingsPlaced: Buildings beltsPlaced: Belts - buttons: - continue: Continue - settings: Settings - menu: Return to menu tutorialHints: title: Need help? showHint: Show hint @@ -676,7 +643,7 @@ storyRewards: title: Balancer desc: The multifunctional balancer has been unlocked - It can be used to build bigger factories by splitting and merging - items onto multiple belts!

+ items onto multiple belts! reward_merger: title: Compact Merger desc: You have unlocked a merger variant of the @@ -893,6 +860,14 @@ settings: title: Enable Mouse Pan description: Allows to move the map by moving the cursor to the edges of the screen. The speed depends on the Movement Speed setting. + zoomToCursor: + title: Zoom towards Cursor + description: If activated the zoom will happen in the direction of your mouse + position, otherwise in the middle of the screen. + mapResourcesScale: + title: Map Resources Size + description: Controls the size of the shapes on the map overview (when zooming + out). rangeSliderPercentage: % keybindings: title: Keybindings diff --git a/translations/base-cat.yaml b/translations/base-cat.yaml index 88478494..af78d514 100644 --- a/translations/base-cat.yaml +++ b/translations/base-cat.yaml @@ -94,7 +94,6 @@ mainMenu: helpTranslate: Ajuda a traduir-lo! madeBy: Creat per browserWarning: >- - Disculpa, però el joc funcionarà lent al teu navegador! Aconsegueix el joc complet o descarrega't chrome per una millor experiència. savegameLevel: Nivell savegameLevelUnknown: Nivell desconegut @@ -205,11 +204,6 @@ dialogs: renameSavegame: title: Canviar el nom. desc: Canviar el nom de la partida guardada. - entityWarning: - title: Alerta de rendiment - desc: Has col·locat molts edificis, això és una alerta amistosa de que el joc no - pot suportar edificis infinits, així que intenta mantenir els teus - dissenys minimament compactes! ingame: keybindingsOverlay: moveMap: Moure @@ -265,27 +259,6 @@ ingame: title: Millores buttonUnlock: Millorar tier: Nivell - tierLabels: - - I - - II - - III - - IV - - V - - VI - - VII - - VIII - - IX - - X - - XI - - XII - - XIII - - XIV - - XV - - XVI - - XVII - - XVIII - - XIX - - XX maximumLevel: NIVELL MÀXIM (Velocitat x) statistics: title: Estadístiques @@ -310,10 +283,6 @@ ingame: playtime: Temps de joc buildingsPlaced: Edificis beltsPlaced: Cintes transportadores - buttons: - continue: Continuar - settings: Configuració - menu: Tornar al menú tutorialHints: title: Necessites ajuda? showHint: Mostrar pista @@ -692,9 +661,9 @@ storyRewards: completa - fora del web! reward_balancer: title: Equilibrador - desc: Has desbloquejat el multifuncional equilibrador! Pot - ésser emprat per construir fàbriques més grans dividint i - fusionant ítems a múltiples cintes!

+ desc: The multifunctional balancer has been unlocked - It can + be used to build bigger factories by splitting and merging + items onto multiple belts! reward_merger: title: Fusionador Compacte desc: Has desbloquejat una variant fusionadora de @@ -918,6 +887,14 @@ settings: description: Permet moure el mapa quan mous el cursor a les vores de la pantalla. La velocitat de moviment depèn de la configuració de Velocitat de Moviment. + zoomToCursor: + title: Zoom towards Cursor + description: If activated the zoom will happen in the direction of your mouse + position, otherwise in the middle of the screen. + mapResourcesScale: + title: Map Resources Size + description: Controls the size of the shapes on the map overview (when zooming + out). rangeSliderPercentage: % keybindings: title: Combinacions de tecles diff --git a/translations/base-cz.yaml b/translations/base-cz.yaml index 611c975a..f94fa0c7 100644 --- a/translations/base-cz.yaml +++ b/translations/base-cz.yaml @@ -1,10 +1,9 @@ steamPage: shortText: shapez.io je hra o stavbě továren pro automatizaci výroby a kombinování čím dál složitějších tvarů na nekonečné mapě. - discordLinkShort: Official Discord + discordLinkShort: Oficiální Discord intro: >- - Shapez.io je relaxační hra, ve které musíte stavět továrny pro - automatizaci výroby geometrických tvarů. + Shapez.io je relaxační hra, ve které musíte stavět továrny pro automatizaci výroby geometrických tvarů. Jak se zvyšuje úroveň, tvary se stávají stále složitějšími a vy se musíte rozšířit po nekonečné mapě. @@ -13,36 +12,36 @@ steamPage: Zatímco tvary zpracováváte pouze na začátku, musíte je později obarvit - k tomu musíte těžit a míchat barvy! Koupením hry na platformě Steam vám dá přístup k plné verzi hry, ale také můžete hrát demo verzi na shapez.io a potom se můžete rozhodnou jestli hru koupíte! - title_advantages: Samostatné výhody + title_advantages: Výhody samostatné verze hry advantages: - 12 Nových úrovní celkem 26 úrovní - 18 Nových budov pro plně automatizovanou továrnu! - 20 vylepšení pro mnoho hodin zábavy! - Wires Update pro zcela nové rozměry! - Dark Mode! - - Neomezené Savegames + - Neomezený počet uložených her - Neomezené značky - Podpořte mě! ❤️ title_future: Plánovaný kontent planned: - - Blueprintová knihovna (Samostatně exkluzivní) + - Blueprintová knihovna - Steam Achievements - - Puzzle Mode - - Minnimapa - - Mody - - Sandbox mode + - Puzzle Mód + - Minimapa + - Módy + - Sandbox Mód - ... a o hodně víc! title_open_source: Tato hra je open source! title_links: Odkazy links: - discord: Officiální Discord + discord: Oficiální Discord roadmap: Roadmap subreddit: Subreddit source_code: Source code (GitHub) translate: Pomozte přeložit hru! - text_open_source: |- - Kdokoli může přispět, aktivně se zapojit do komunity, - pokusit se zkontrolovat všechny návrhy a vzít v úvahu zpětnou vazbu + text_open_source: >- + Kdokoli může přispět, jsem aktivně zapojený do komunity, + pokouším se zkontrolovat všechny návrhy a vzít v úvahu zpětnou vazbu všude, kde je to možné. Nezapomeňte se podívat na můj trello board, kde najdete kompletní plán! @@ -105,8 +104,8 @@ dialogs: later: Později restart: Restart reset: Reset - getStandalone: Získejte Plnou verzi - deleteGame: Vím co dělám + getStandalone: Získejte plnou verzi + deleteGame: Vím, co dělám viewUpdate: Zobrazit aktualizaci showUpgrades: Zobrazit vylepšení showKeybindings: Zobrazit klávesové zkratky @@ -194,15 +193,11 @@ dialogs: editSignal: title: Nastavte signál descItems: "Vyberte předdefinovanou položku:" - descShortKey: ... nebo zadejte krátký klíč tvaru (který - můžete vygenerovat zde) + descShortKey: ... nebo zadejte krátký klíč tvaru (který můžete + vygenerovat zde) renameSavegame: title: Přejmenovat uloženou hru desc: Zde můžeš přejmenovat svoji uloženou hru. - entityWarning: - title: Varování výkonu - desc: Umístili jste spoustu budov, to je jen přátelská připomínka. Hra nezvládne - nekonečný počet budov - zkuste udržet své továrny kompaktní! ingame: keybindingsOverlay: moveMap: Posun mapy @@ -248,27 +243,6 @@ ingame: title: Vylepšení buttonUnlock: Vylepšit tier: Úroveň - tierLabels: - - I - - II - - III - - IV - - V - - VI - - VII - - VIII - - IX - - X - - XI - - XII - - XIII - - XIV - - XV - - XVI - - XVII - - XVIII - - XIX - - XX maximumLevel: MAXIMÁLNÍ ÚROVEŇ (Rychlost x) statistics: title: Statistiky @@ -291,10 +265,6 @@ ingame: playtime: Herní čas buildingsPlaced: Budovy beltsPlaced: Pásy - buttons: - continue: Pokračovat - settings: Nastavení - menu: Návrat do menu tutorialHints: title: Potřebujete pomoct? showHint: Zobrazit nápovědu @@ -345,7 +315,7 @@ ingame: desc: Kliknutím sem zobrazíte výhody Steam verze! get_on_steam: Získejte na steamu standaloneAdvantages: - title: Získejte plnou verzy! + title: Získejte plnou verzi! no_thanks: Ne, děkuji! points: levels: @@ -399,10 +369,10 @@ buildings: miner: default: name: Extraktor - description: Umístěte na náleziště tvaru nebo barvy pro zahájení těžby. + description: Umístěte na naleziště tvaru nebo barvy pro zahájení těžby. chainable: name: Extraktor (Navazující) - description: Umístěte na náleziště tvaru nebo barvy pro zahájení těžby. Lze + description: Umístěte na naleziště tvaru nebo barvy pro zahájení těžby. Lze zapojit po skupinách. underground_belt: default: @@ -450,9 +420,9 @@ buildings: description: Obarví tvary z levých vstupů barvou z horního vstupu. quad: name: Barvič (čtyřnásobný) - description: Umožnuje obarvit každou čtvrtinu tvaru individuálně. Jen - čtvrtiny se vstupy barev s logickým signálem na vrstvě kabelů - budou obarveny! + description: Umožnuje obarvit každou čtvrtinu tvaru individuálně. Jen čtvrtiny + se vstupy barev s logickým signálem na vrstvě + kabelů budou obarveny! mirrored: name: Barvič description: Obarví celý tvar v levém vstupu barvou z pravého vstupu. @@ -470,7 +440,7 @@ buildings: balancer: default: name: Vyvažovač - description: Multifunkční - Rozděluje vstupy do výstupy. + description: Multifunkční - Rovnoměrně rozděluje vstupy na výstupech. merger: name: Spojovač (kompaktní) description: Spojí dva pásy do jednoho. @@ -486,8 +456,8 @@ buildings: storage: default: name: Sklad - description: Skladuje přebytečné věci až do naplnění kapacity. Může být použit na - skladování surovin navíc. + description: Skladuje přebytečné věci až do naplnění kapacity. Může být použit + na skladování surovin navíc. wire_tunnel: default: name: Křížení kabelů @@ -535,7 +505,7 @@ buildings: reader: default: name: Čtečka pásů - description: Umožňuje měřit průměrnou propustnost pásu. Výstup čte poslední + description: Umožňuje změřit průměrnou propustnost pásu. Výstup čte poslední položku ve vrstvě kabelů. analyzer: default: @@ -568,17 +538,16 @@ buildings: item_producer: default: name: Výrobník předmětů - description: Dostupný pouze v sandboxovém módu, vydává daný signál z - vrstvy kabelů na běžnou vrstvu. + description: Dostupný pouze v sandboxovém módu, vydává daný signál z vrstvy + kabelů na běžnou vrstvu. storyRewards: reward_cutter_and_trash: title: Řezání tvarů - desc: Právě jste odemkli pilu, která řeže tvary - svisle na poloviny bez ohledu na její + desc: Právě jste odemkli pilu, která řeže tvary svisle na + poloviny bez ohledu na její orientaci!

Nezapoměňte se zbavit zbytku tvarů, jinak - se vám produkce zasekne - za tímto účelem - jsem vám dal koš, který smaže - vše, co do něj vložíte! + se vám produkce zasekne - za tímto účelem jsem vám + dal koš, který smaže vše, co do něj vložíte! reward_rotater: title: Otáčení desc: Rotor byl právě odemčen! Otáčí tvary po směru hodinových @@ -601,8 +570,8 @@ storyRewards: reward_splitter: title: Rozřazování/Spojování pásu desc: Právě jste odemkli rozdělovací variantu - vyvažovače - Přijímá jeden vstup a rozdělí ho - na dva! + vyvažovače - Přijímá jeden vstup a rozdělí ho na + dva! reward_tunnel: title: Tunel desc: Tunel byl právě odemčen - Umožňuje vézt suroviny pod @@ -615,9 +584,9 @@ storyRewards: reward_miner_chainable: title: Napojovací extraktor desc: "Právě jste odemkli napojovací extraktor! Může - předat své zdroje ostatním extraktorům, čímž - můžete efektivněji těžit více zdrojů!

PS: Starý - extraktor bude od teď nahrazen ve vašem panelu nástrojů!" + předat své zdroje ostatním extraktorům, čímž můžete + efektivněji těžit více zdrojů!

PS: Starý extraktor bude od + teď nahrazen ve vašem panelu nástrojů!" reward_underground_belt_tier_2: title: Tunel II. úrovně desc: Odemknuli jste tunel II. úrovně - Má delší @@ -633,17 +602,17 @@ storyRewards: barvy! reward_storage: title: Sklad - desc: Právě jste odemkli sklad - Umožnuje skladovat přebytečné věci - až do naplnění kapacity!

Dává prioritu levému - výstupu, takže ho také můžete použít jako průtokovou bránu! + desc: Právě jste odemkli sklad - Umožnuje skladovat přebytečné + věci až do naplnění kapacity!

Dává prioritu levému výstupu, + takže ho také můžete použít jako průtokovou bránu! reward_freeplay: title: Volná hra desc: Zvládli jste to! Odemkli jste mód volné hry! To znamená, - budou od teď náhodně generovány!

- Vzhledem k tomu, že Hub nadále potřebuje propustnost - , především doporučuji postavit továrnu, která automaticky - doručí požadovaný tvar!

Hub vysílá požadovaný - tvar na vrstvu kabelů, takže jediné co musíte udělat, je analyzovat tvar a + budou od teď náhodně generovány!

Vzhledem k + tomu, že Hub nadále potřebuje propustnost , + především doporučuji postavit továrnu, která automaticky doručí + požadovaný tvar!

Hub vysílá požadovaný tvar na vrstvu + kabelů, takže jediné co musíte udělat, je analyzovat tvar a automaticky nastavit svou továrnu dle této analýzy. reward_blueprints: title: Plány @@ -663,66 +632,67 @@ storyRewards: desc: Gratuluji! Mimochodem, více obsahu najdete v plné verzi! reward_balancer: title: Vyvažovač - desc: Multifunkční vyvažovač byl odemknut - Může - být použit ke zvětšení vašich továren rozdělováním a spojováním - předmětů na několik pásu!

+ desc: The multifunctional balancer has been unlocked - It can + be used to build bigger factories by splitting and merging + items onto multiple belts! reward_merger: title: Kompaktní spojovač desc: Právě jste odemkli spojovací variantu - vyvažovače - Přijímá dva vstupy a spojí je - do jednoho! + vyvažovače - Přijímá dva vstupy a spojí je do + jednoho! reward_belt_reader: title: Čtečka pásů - desc: Právě jste odemkli čtečku pásů! Umožnuje vám - změřit propustnost pásu.

A počkejte na odemčení - kabelů - později to bude velmi užitečné! + desc: Právě jste odemkli čtečku pásů! Umožnuje vám změřit + propustnost pásu.

A počkejte na odemčení kabelů - později to + bude velmi užitečné! reward_rotater_180: title: Rotor (180°) - desc: Právě jste odemkli 180 stupňoví rotor! - Umožňuje - vám otáčet tvar o 180 stupňů! + desc: Právě jste odemkli 180 stupňoví rotor! - Umožňuje vám + otáčet tvar o 180 stupňů! reward_display: title: Display - desc: "Právě jste odemkli Display - Připojte signál ve - vrstvě kabelů pro vizualizaci!

PS: Všimli jste si, že čtečka - pásů a sklad vysílájí jejich poslední přečtěný předmět? Zkuste ho ukázat na + desc: "Právě jste odemkli Display - Připojte signál ve vrstvě + kabelů pro vizualizaci!

PS: Všimli jste si, že čtečka pásů a + sklad vysílájí jejich poslední přečtěný předmět? Zkuste ho ukázat na displeji!" reward_constant_signal: title: Konstantní signál - desc: Právě jste odemkli konstantní signál na vrstvě - kabelů! Tohle je například užitečné pro připojení k filtrům předmětů - .

Konstantní signál může vysílat - tvar, barvu nebo - logickou hodnotu (1 / 0). + desc: Právě jste odemkli konstantní signál na vrstvě kabelů! + Tohle je například užitečné pro připojení k filtrům + předmětů .

Konstantní signál může vysílat + tvar, barvu nebo logickou + hodnotu (1 / 0). reward_logic_gates: title: Logické brány - desc: Právě jste odemkli logické brány! Nemusíte být zrovna nadšení, - ale ve skutečnosti je to celkem cool!

S těmito bránami - můžete propočítat AND, OR, XOR a NOT operace.

Jako + desc: Právě jste odemkli logické brány! Nemusíte být zrovna + nadšení, ale ve skutečnosti je to celkem cool!

S těmito + bránami můžete propočítat AND, OR, XOR a NOT operace.

Jako bonus navíc vám také zpřístupním tranzistor! reward_virtual_processing: title: Virtuální zpracování desc: Právě jsem zpřístupnil spoustu nových budov, které vám umožní - simulovat výrobu různých tvarů!

Můžete - teď také simulovat pilu, rotor, kombinátor a mnoho dalšího na vrstvě kabelů! - Nadále máte tři možnosti, jak pokračovat ve hře:

- - Postavit automatickou továrnu k vytvoření jakéhokoliv - tvaru požadovaného Hubem (Doporučuji to alespoň vyzkoušet!).

- Postavit - něco zajímavého s použitím kabelů.

- Pokračovat ve hře - pravidelně.

Bez ohledu na tvou volbu, nezapomeň si svou hru užít! + simulovat výrobu různých tvarů!

Můžete teď + také simulovat pilu, rotor, kombinátor a mnoho dalšího na vrstvě + kabelů! Nadále máte tři možnosti, jak pokračovat ve hře:

- + Postavit automatickou továrnu k vytvoření + jakéhokoliv tvaru požadovaného Hubem (Doporučuji to alespoň + vyzkoušet!).

- Postavit něco zajímavého s použitím + kabelů.

- Pokračovat ve hře pravidelně.

Bez ohledu + na tvou volbu, nezapomeň si svou hru užít! reward_wires_painter_and_levers: title: Kabely a čtyřnásobný barvič desc: "Právě jste odemkli vrstvu kabelů: Je to samostatná vrstva navíc oproti běžné vrstvě a představuje spoustu nových možností!

Do začátku jsem zpřístupnil čtyřnásobný - barvič - Připojte vstupy, které byste chtěli obarvit - na vrstvě kabelů!

Pro přepnutí mezi vrstvami stiskněte klávesu + barvič
- Připojte vstupy, které byste chtěli obarvit na + vrstvě kabelů!

Pro přepnutí mezi vrstvami stiskněte klávesu E." reward_filter: title: Filtr předmětů desc: Právě jste odemkli filtr předmětů! Nasměruje předměty buď - na horní nebo pravý výstup podle toho, zda se shodují - nebo neshodují se signálem na vrstvě kabelů.

Také můžete vyslat logickou hodnotu - (1 / 0) pro zapnutí nebo kompletní vypnutí filtru. + na horní nebo pravý výstup podle toho, zda se shodují nebo neshodují + se signálem na vrstvě kabelů.

Také můžete vyslat logickou + hodnotu (1 / 0) pro zapnutí nebo kompletní vypnutí filtru. reward_demo_end: title: Konec demo verze desc: Právě jste dosáhli konce demo verze! @@ -732,7 +702,7 @@ settings: general: Obecné userInterface: Uživatelské rozhraní advanced: Rozšířené - performance: Performance + performance: Výkon versionBadges: dev: Vývojová verze staging: Testovací verze @@ -742,7 +712,7 @@ settings: uiScale: title: Škálování UI description: Změní velikost uživatelského rozhraní. Rozhraní se bude stále - přizpůsobovoat rozlišení vaší obrazovky, toto nastavení pouze + přizpůsobovat rozlišení vaší obrazovky, toto nastavení pouze mění jeho škálu. scales: super_small: Velmi malé @@ -751,7 +721,7 @@ settings: large: Velké huge: Obrovské scrollWheelSensitivity: - title: Citlivost přibížení + title: Citlivost přiblížení description: Změní citlivost přiblížení (kolečkem myši nebo trackpadem). sensitivity: super_slow: Hodně pomalé @@ -801,8 +771,8 @@ settings: slow: Pomalá regular: Normální fast: Rychlá - super_fast: Hodně Rychlá - extremely_fast: Extrémně Rychlá + super_fast: Hodně rychlá + extremely_fast: Extrémně rychlá enableTunnelSmartplace: title: Chytré tunely description: Pokládání tunelů po zapnutí bude samo odstraňovat nepotřebné pásy. @@ -829,7 +799,7 @@ settings: koeficient. V opačném případě zobrazí popis a obrázek. disableCutDeleteWarnings: title: Zakázat upozornění o vyjmutí nebo odstranění - description: Deaktivujte varovná dialogová okna vyvolaná při vymutí/mazání více + description: Deaktivujte varovná dialogová okna vyvolaná při vyjmutí/mazání více než 100 entit. enableColorBlindHelper: title: Režim pro barvoslepé @@ -837,8 +807,8 @@ settings: barvoslepí. rotationByBuilding: title: Rotace dle typu budov - description: Každý typ budovy si zapamatuje poslední rotaci, na kterou jste je individuálně - nastavili. Tohle může být pohodlnější pokud často + description: Každý typ budovy si zapamatuje poslední rotaci, na kterou jste je + individuálně nastavili. Tohle může být pohodlnější pokud často přepínáte mezi pokládáním budov různých typů. soundVolume: title: Hlasitost zvuků @@ -848,40 +818,48 @@ settings: description: Nastavte hlasitost hudby lowQualityMapResources: title: Nižší kvalita zdrojů na mapě - description: Zjednoduší vykreslování zdrojů na mapě při přiblížení pro - zlepšení výkonu. Také to zlepšuje vzhled hry, takže neváhejte toto nastavení - vyzkoušet! + description: Zjednoduší vykreslování zdrojů na mapě při přiblížení pro zlepšení + výkonu. Také to zlepšuje vzhled hry, takže neváhejte toto + nastavení vyzkoušet! disableTileGrid: title: Vypnout mřížku description: Vypnutí mřížky částic může pomoct s výkonem. Toto nastavení zlepšuje vzhled hry! clearCursorOnDeleteWhilePlacing: title: Uvolní kurzor při kliknutím pravým tlačitkem - description: Povoleno dle výchozího nastavení, uvolní kurzor pokaždé co kliknete pravým tlačítkem, - když máte budovu vybranou pro pokládání. Při vypnutí, - můžete smazat budovy při kliknutí pravým tlačikem spolu s položením dalších - budov. + description: Povoleno dle výchozího nastavení, uvolní kurzor pokaždé co kliknete + pravým tlačítkem, když máte budovu vybranou pro pokládání. Při + vypnutí, můžete smazat budovy při kliknutí pravým tlačikem spolu + s položením dalších budov. lowQualityTextures: title: Nižší kvalita textur (Horší vzhled) description: Používá nižší kvalitu textur pro zlepšení výkonu. Toto nastavení zhorší vzhled hry! displayChunkBorders: title: Zobrazit hranice oblastí - description: Hra je rozdělena na oblasti 16x16 částic. Pokud je toto nastavení povolené, - zobrazí se hranice těchto oblastí. + description: Hra je rozdělena na oblasti 16x16 částic. Pokud je toto nastavení + povolené, zobrazí se hranice těchto oblastí. pickMinerOnPatch: title: Vybrat extraktor na naležistě zdrojů - description: Povoleno dle výchozího nastavení, vybere extraktor, pokud použijete kapátko pro - kliknutí na nalezistě zdrojů. + description: Povoleno dle výchozího nastavení, vybere extraktor, pokud použijete + kapátko pro kliknutí na nalezistě zdrojů. simplifiedBelts: title: Zjednodušené pásy (Horší vzhled) - description: Nevykresluje předměty na pásech, pokud nad nimi nepřejíždíte kurzorem, pro ušetření - výkonu. Nedoporučuji hrát s tímto nastavením, pokud - opravdu nepotřebujete ušetřit výkon. + description: Nevykresluje předměty na pásech, pokud nad nimi nepřejíždíte + kurzorem, pro ušetření výkonu. Nedoporučuji hrát s tímto + nastavením, pokud opravdu nepotřebujete ušetřit výkon. enableMousePan: title: Posouvání myší description: Umožnuje posouvání po mapě, pokud myší přejedete na okraj obrazovky. Rychlost žáleží na nastavení rychlosti pohybu. + zoomToCursor: + title: Zoom towards Cursor + description: If activated the zoom will happen in the direction of your mouse + position, otherwise in the middle of the screen. + mapResourcesScale: + title: Map Resources Size + description: Controls the size of the shapes on the map overview (when zooming + out). rangeSliderPercentage: % keybindings: title: Klávesové zkratky @@ -965,7 +943,7 @@ about: Pokud se chceš na hře podílet, podívej se na shapez.io na githubu.

- Tato hra by nebyla ani možná bez skvělé Discord komunity okolo Tobiasových her - Vážně by ses měl přijít mrknout na Discord server!

+ Tato hra by nebyla možná ani bez skvělé Discord komunity okolo Tobiasových her - Vážně by ses měl přijít mrknout na Discord server!

Soundtrack udělal Peppsen - Je úžasnej.

@@ -994,17 +972,19 @@ tips: - Symetrie je klíčová! - Můžete proplétat různé úrovně tunelů. - Snažte se postavit kompaktní továrny - vyplatí se to! - - Barvič má zrcadlově otočenou variantu, kterou můžete vybrat klávesou T + - Barvič má zrcadlově otočenou variantu, kterou můžete vybrat klávesou + T - Užití správné kombinace vylepšení maximalizuje efektivitu. - Na maximální úrovní, 5 extraktorů zaplní jeden celý pás. - Nezapomeňte na tunely! - Pro plnou efektivitu nemusíte rozdělovat předměty rovnoměrně. - - Držení klávesy SHIFT spolu s pásy aktivuje plánovač pásy, který vám snadno umožní - postavit dlouhé řady pásů. + - Držení klávesy SHIFT spolu s pásy aktivuje plánovač pásy, který vám + snadno umožní postavit dlouhé řady pásů. - Pily řežou vždy svisle, bez ohledu na jejich orientaci. - Smícháním všech 3 barev získáte bílou barvu. - Sklad preferuje levý výstup. - - Investujte čas pro vytvoření opakovatelných designů - ulehčí vám to pozdější expanzy! + - Investujte čas pro vytvoření opakovatelných designů - ulehčí vám to + pozdější expanzy! - Držení klávesy CTRL umožnuje postavit více budov stejného typu. - Můžete podržet klávesu ALT k obrácení směru pokládaných pásů. - Efektivita je klíčová! @@ -1015,29 +995,36 @@ tips: - Plánujte dopředu, abyste předešli vzniku velkého chaosu! - Neodstraňujte své staré továrny! Budete je potřebovat pro další vylepšení. - Před vyhledáním pomoci zkuste sami porazit úroveň 20! - - Snažte se věci nekomplikovat, zůstaňtě u jednoduchých designů a dostanete se daleko. - - Možná budete muset použít stejné továrny i v budoucnu. Vytvořte své továrny takovým stylem, - abyste je mohli použít i v dalších případech. - - V nektěrých případech můžete najít celý požadovaný tvar bez nutnosti jeho výroby s pomocí - kombinátorů. + - Snažte se věci nekomplikovat, zůstaňtě u jednoduchých designů a dostanete + se daleko. + - Možná budete muset použít stejné továrny i v budoucnu. Vytvořte své + továrny takovým stylem, abyste je mohli použít i v dalších případech. + - V nektěrých případech můžete najít celý požadovaný tvar bez nutnosti jeho + výroby s pomocí kombinátorů. - Celý tvar typu mlýnu se na mapě nikdy nevyskytne. - Obarvěte své tvary před řezáním pro zvýšení efektivity. - S moduly, prostor je pouze vnímáním; starost pro smrtelníky. - - Vytvořtě si samostatnou továrnu jen na plány (blueprinty). Jsou důležité pro moduly. + - Vytvořtě si samostatnou továrnu jen na plány (blueprinty). Jsou důležité + pro moduly. - Podívejte se zblízka na míchač barev, a vaše otázky budou odpovězeny. - Použijte klávesu CTRL a myš pro označení oblasti. - - Pokud stavíte příliš blízko Hubu, v budoucnu můžete narazit na problémy s dalšími projekty. - - Ikona připínáčku vedle každého tvaru vám umožnuje připnout tvar, čímž se vám bude neustále zobrazovat vlevo na obrazovce. + - Pokud stavíte příliš blízko Hubu, v budoucnu můžete narazit na problémy s + dalšími projekty. + - Ikona připínáčku vedle každého tvaru vám umožnuje připnout tvar, čímž se + vám bude neustále zobrazovat vlevo na obrazovce. - Smíchejte všechny základní barvy pro vytvoření bílé barvy! - - Vaše mapa je nekonečná, nesnažte se postavit továrnu na malinkém prostoru, rozšiřte se do okolí! + - Vaše mapa je nekonečná, nesnažte se postavit továrnu na malinkém prostoru, + rozšiřte se do okolí! - Neváhejte vyzkoušet hru Factorio! Je to má oblíbená hra. - - Rozebírač funguje po směru hodinových ručiček, počínaje pravým horním rohem! + - Rozebírač funguje po směru hodinových ručiček, počínaje pravým horním + rohem! - V hlavním menu můžete stáhnout své uložené hry! - - Tato hra má spoustu užitečných klávesových zkratek! Určitě si je projděte v - nastavení. + - Tato hra má spoustu užitečných klávesových zkratek! Určitě si je projděte + v nastavení. - Tato hra má spoustu nastavení, určitě si je projděte! - Značka Hubu má vedle sebe malý kompas, který ukazuje směr k Hubu! - - Pro vyčistění pásů, vyjměte budovy z prostoru a pak je zkopírujte zpět na stejné místo. + - Pro vyčistění pásů, vyjměte budovy z prostoru a pak je zkopírujte zpět na + stejné místo. - Stisknutím F4 zobrazíte FPS a rychlost ticků. - Stisknutím F4 dvakrát zobrazíte častici myši a kamery. - Můžete kliknout na připínáček vlevo vedle připnutého tvaru k jeho odepnutí. diff --git a/translations/base-da.yaml b/translations/base-da.yaml index e36b438c..6b592d27 100644 --- a/translations/base-da.yaml +++ b/translations/base-da.yaml @@ -201,11 +201,6 @@ dialogs: renameSavegame: title: Rename Savegame desc: You can rename your savegame here. - entityWarning: - title: Performance Warning - desc: You have placed a lot of buildings, this is just a friendly reminder that - the game can not handle an endless count of buildings - So try to - keep your factories compact! ingame: keybindingsOverlay: moveMap: Bevæg dig @@ -261,27 +256,6 @@ ingame: title: Opgraderinger buttonUnlock: Opgrader tier: Trin - tierLabels: - - I - - II - - III - - IV - - V - - VI - - VII - - VIII - - IX - - X - - XI - - XII - - XIII - - XIV - - XV - - XVI - - XVII - - XVIII - - XIX - - XX maximumLevel: HØJESTE NIVEAU (Speed x) statistics: title: Statistikker @@ -305,10 +279,6 @@ ingame: playtime: Spilletid buildingsPlaced: Bygninger beltsPlaced: Bælter - buttons: - continue: Fortsæt - settings: Indstillinger - menu: Hovedmenu tutorialHints: title: Har du brug for hjælp? showHint: Vis hint @@ -678,7 +648,7 @@ storyRewards: title: Balancer desc: The multifunctional balancer has been unlocked - It can be used to build bigger factories by splitting and merging - items onto multiple belts!

+ items
onto multiple belts! reward_merger: title: Compact Merger desc: You have unlocked a merger variant of the @@ -898,6 +868,14 @@ settings: title: Enable Mouse Pan description: Allows to move the map by moving the cursor to the edges of the screen. The speed depends on the Movement Speed setting. + zoomToCursor: + title: Zoom towards Cursor + description: If activated the zoom will happen in the direction of your mouse + position, otherwise in the middle of the screen. + mapResourcesScale: + title: Map Resources Size + description: Controls the size of the shapes on the map overview (when zooming + out). rangeSliderPercentage: % keybindings: title: Keybindings diff --git a/translations/base-de.yaml b/translations/base-de.yaml index 1487517e..346f5353 100644 --- a/translations/base-de.yaml +++ b/translations/base-de.yaml @@ -1,15 +1,16 @@ ---- steamPage: shortText: In shapez.io nutzt du die vorhandenen Ressourcen, um mit deinen Maschinen durch Kombination immer komplexere Formen zu erschaffen. discordLinkShort: Offizieller Discord intro: >- Du magst Automatisierungsspiele? Dann bist du hier genau richtig! - shapez.io ist ein ruhiges Spiel, in dem du Fabriken zur automatisierten Produktion von geometrischen Formen bauen musst. - Mit steigendem Level werden die Formen immer komplexer, und du musst dich auf der unendlich großen Karte ausbreiten. - Das ist noch nicht alles, denn du musst exponentiell mehr produzieren, um die Anforderungen zu erfüllen - Da hilft nur skalieren! - Während du am Anfang nur Formen verarbeitest, musst du diese später auch einfärben - Dafür musst du Farben extrahieren und mischen! - Der Kauf des Spiels auf Steam gibt dir Zugriff auf die Vollversion, aber du kannst auch zuerst die Demo auf shapez.io spielen und dich später entscheiden! + + shapez.io ist ein entspanntes Spiel, in dem du Fabriken zur + automatisierten Produktion von geometrischen Formen bauen musst. + + Mit steigendem Level werden die Formen immer komplexer, und du musst dich auf der unendlich großen Karte ausbreiten. Das ist noch nicht alles, denn du musst exponentiell mehr produzieren, um die Anforderungen zu erfüllen - Da hilft nur skalieren! + + Während du am Anfang nur Formen verarbeitest, musst du diese später auch einfärben - Dafür musst du Farben extrahieren und mischen! Der Kauf des Spiels auf Steam gibt dir Zugriff auf die Vollversion, aber du kannst auch zuerst die Demo auf shapez.io spielen und dich später entscheiden! title_advantages: Vorteile der Vollversion advantages: - 12 Neue Level für insgesamt 26 Level @@ -30,10 +31,9 @@ steamPage: - Sandkastenmodus - ... und noch viel mehr! title_open_source: Dieses Spiel ist quelloffen! - text_open_source: >- - Jeder kann etwas zum Spiel beitragen! Ich engagiere mich aktiv in der - Community und versuche alle Vorschläge zu berücksichtigen. - Die vollständige Roadmap findet ihr auf meinem Trello-Board! + text_open_source: Jeder kann etwas zum Spiel beitragen! Ich engagiere mich aktiv + in der Community und versuche alle Vorschläge zu berücksichtigen. Die + vollständige Roadmap findet ihr auf meinem Trello-Board! title_links: Links links: discord: Offizieller Discord @@ -44,7 +44,7 @@ steamPage: global: loading: Laden error: Fehler - thousandsDivider: "." + thousandsDivider: . decimalSeparator: "," suffix: thousands: k @@ -86,7 +86,7 @@ mainMenu: discordLink: Offizieller Discord Server helpTranslate: Hilf beim Übersetzen! madeBy: Ein Spiel von - browserWarning: Sorry, aber das Spiel wird in deinem Browser langsam laufen! + browserWarning: Sorry, aber das Spiel wird in deinem Browser langsamer laufen! Kaufe die Vollversion oder verwende Google Chrome für die beste Erfahrung! savegameLevel: Level @@ -117,7 +117,8 @@ dialogs: confirmSavegameDelete: title: Löschen bestätigen text: Bist du sicher, dass du folgenden Speicherstand löschen willst?

- '' auf Level

Das kann nicht rückgängig gemacht werden! + '' auf Level

Das kann nicht + rückgängig gemacht werden! savegameDeletionError: title: Löschen fehlgeschlagen text: "Das Löschen des Speicherstands ist fehlgeschlagen:" @@ -147,22 +148,20 @@ dialogs: desc: "Hier sind die Änderungen, seitdem du das letzte Mal gespielt hast:" upgradesIntroduction: title: Upgrades freischalten - desc: >- - Viele deiner Formen können noch benutzt werden, um Upgrades freizuschalten + desc: Viele deiner Formen können noch benutzt werden, um Upgrades freizuschalten - Zerstöre deine alten Fabriken nicht! Den Upgrade-Tab findest du oben rechts im Bildschirm. massDeleteConfirm: title: Löschen bestätigen - desc: Du löscht viele Gebäude ( um genau zu sein)! Bist du dir - sicher? + desc: Du löscht viele Gebäude ( um genau zu sein)! Bist du dir sicher? massCutConfirm: title: Ausschneiden bestätigen - desc: Du schneidest viele Gebäude aus ( um genau zu sein)! Bist du - dir sicher? + desc: Du schneidest viele Gebäude aus ( um genau zu sein)! Bist du dir + sicher? massCutInsufficientConfirm: title: Ausschneiden bestätigen - desc: Du wirst dir das Einfügen nicht leisten können! Bist du sicher, dass du trotzdem - Ausschneiden möchtest? + desc: Du wirst dir das Einfügen nicht leisten können! Bist du sicher, dass du + trotzdem Ausschneiden möchtest? blueprintsNotUnlocked: title: Noch nicht freigeschaltet desc: Blaupausen werden erst in Level 12 freigeschaltet! @@ -173,17 +172,20 @@ dialogs: vereinfachen und beschleunigen. Hier sind ein paar Beispiele, aber prüfe am besten die Tastenbelegung-Einstellungen!

- STRG + Ziehen: Wähle Areal aus.
+ STRG + Ziehen: Wähle Bereich aus.
UMSCH: Halten, um mehrere Gebäude zu platzieren.
- ALT: Invertiere die Platzierungsrichtung der Förderbänder.
+ ALT: Invertiere die Platzierungsrichtung der Fließbänder.
createMarker: title: Neuer Marker titleEdit: Marker bearbeiten - desc: Gib ihm einen griffigen Namen. Du kannst auch den Kurz-Code einer Form eingeben (Welchen du hier generieren kannst). + desc: Gib ihm einen griffigen Namen. Du kannst auch den + Kurz-Code einer Form eingeben (Welchen du + hier generieren kannst). editSignal: title: Signal setzen descItems: "Wähle ein vordefiniertes Item:" - descShortKey: ... oder gib den Kurz-Code einer Form an (Welchen du hier generieren kannst). + descShortKey: ... oder gib den Kurz-Code einer Form an (Welchen + du hier generieren kannst). markerDemoLimit: desc: Du kannst nur 2 Marker in der Demo benutzen. Hole dir die Vollversion, um unendlich viele Marker zu erstellen! @@ -195,9 +197,6 @@ dialogs: renameSavegame: title: Speicherstand umbenennen desc: Hier kannst du deinen Speicherstand umbenennen. - entityWarning: - title: Leistungswarnung - desc: Du hast eine Menge Gebäude platziert. Das hier ist nur ein freundlicher Hinweis, dass das Spiel nicht mit unendlich vielen Gebäuden umgehen kann. Halte deine Fabriken kompakt! ingame: keybindingsOverlay: moveMap: Bewegen @@ -253,27 +252,6 @@ ingame: title: Upgrades buttonUnlock: Upgrade tier: Stufe - tierLabels: - - I - - II - - III - - IV - - V - - VI - - VII - - VIII - - IX - - X - - XI - - XII - - XIII - - XIV - - XV - - XVI - - XVII - - XVIII - - XIX - - XX maximumLevel: MAXIMALE STUFE (Geschw. x) statistics: title: Statistiken @@ -295,7 +273,7 @@ ingame: settingsMenu: playtime: Spielzeit buildingsPlaced: Gebäude - beltsPlaced: Förderbänder + beltsPlaced: Fließbänder tutorialHints: title: Brauchst du Hilfe? showHint: Hinweis @@ -319,11 +297,11 @@ ingame: hints: 1_1_extractor: Platziere einen Extrahierer auf der Kreisform, um sie zu extrahieren! - 1_2_conveyor: "Verbinde den Extrahierer mit einem Fließband - und schließe ihn am Hub an!

Tipp: Drücke und + 1_2_conveyor: "Verbinde den Extrahierer mit einem Fließband und + schließe ihn am Hub an!

Tipp: Drücke und ziehe das Fließband mit der Maus!" 1_3_expand: "Dies ist KEIN Idle-Game! Baue mehr Extrahierer und - Förderbänder, um das Ziel schneller zu erreichen.

Tipp: + Fließbänder, um das Ziel schneller zu erreichen.

Tipp: Halte UMSCH, um mehrere Gebäude zu platzieren und nutze R, um sie zu rotieren." connectedMiners: @@ -334,7 +312,6 @@ ingame: title: Demo-Version desc: Klicke hier, um die Vorteile der Vollversion zu sehen! get_on_steam: Zur Vollversion - standaloneAdvantages: title: Vorteile der Vollversion no_thanks: Nein, danke! @@ -355,7 +332,7 @@ ingame: title: ∞ Marker desc: Verliere nie den Überblick! wires: - title: Wires + title: Wires-Ebene desc: Eine ganz neue Dimension! darkmode: title: Dark-Mode @@ -365,7 +342,7 @@ ingame: desc: Ich entwickle in meiner Freizeit! shopUpgrades: belt: - name: Förderbänder, Verteiler & Tunnel + name: Fließbänder, Verteiler & Tunnel description: Geschw. x → x miner: name: Extrahierer @@ -397,28 +374,29 @@ buildings: underground_belt: default: name: Tunnel - description: Erlaubt dir, Formen und Farbe unter Gebäuden und Förderbändern + description: Erlaubt dir, Formen und Farbe unter Gebäuden und Fließbändern durchzuleiten. tier2: name: Tunnel Stufe II - description: Erlaubt dir, Formen und Farbe unter Gebäuden und Förderbändern + description: Erlaubt dir, Formen und Farbe unter Gebäuden und Fließbändern durchzuleiten. Höhere Reichweite. balancer: default: name: Verteiler - description: Multifunktional - Verteilt alle Eingänge gleichmäßig auf die Ausgänge. + description: Multifunktional - Verteilt alle Eingänge gleichmäßig auf die + Ausgänge. merger: name: Kombinierer (kompakt) - description: Kombiniert zwei Fließbänder zu einem. + description: Kombiniert zwei Eingänge gleichmäßig zu einem Ausgang. merger-inverse: name: Kombinierer (kompakt) - description: Kombiniert zwei Fließbänder zu einem. + description: Kombiniert zwei Eingänge gleichmäßig zu einem Ausgang. splitter: name: Aufteiler (kompakt) - description: Teilt ein Fließband in zwei. + description: Teilt einen Eingang gleichmäßig in zwei Ausgänge auf. splitter-inverse: name: Aufteiler (kompakt) - description: Teilt ein Fließband in zwei. + description: Teilt einen Eingang gleichmäßig in zwei Ausgänge auf. cutter: default: name: Schneider @@ -463,8 +441,8 @@ buildings: quad: name: Färber (vierfach) description: Erlaubt dir, jeden Quadranten der Form individuell zu färben. Nur - Quadranten mit einem wahren Signal auf der Wires-Ebene - werden angemalt! + Quadranten mit einem wahren Signal (Form, Farbe + oder "1") auf der Wires-Ebene werden angemalt! trash: default: name: Mülleimer @@ -473,16 +451,20 @@ buildings: storage: default: name: Speicher - description: - Speichert überschüssige Gegenstände, bis zu einer bestimmten Kapazität. Priorisiert den linken - Ausgang und kann als Überlauftor verwendet werden. + description: Speichert überschüssige Gegenstände, bis zu einer bestimmten + Kapazität. Priorisiert den linken Ausgang und kann als + Überlauftor verwendet werden. wire: default: name: Signalkabel - description: Erlaubt den Transport von Signalen. Das sind Items, Farben oder Wahrheitswerte (1 oder 0). Unterschiedlich gefärbte Kabel verbinden sich nicht. + description: Erlaubt den Transport von Signalen. Das sind Items, Farben oder + Wahrheitswerte (1 oder 0). Unterschiedlich gefärbte Kabel + verbinden sich nicht. second: name: Signalkabel - description: Erlaubt den Transport von Signalen. Das sind Items, Farben oder Wahrheitswerte (1 oder 0). Unterschiedlich gefärbte Kabel verbinden sich nicht. + description: Erlaubt den Transport von Signalen. Das sind Items, Farben oder + Wahrheitswerte (1 oder 0). Unterschiedlich gefärbte Kabel + verbinden sich nicht. wire_tunnel: default: name: Kabelkreuzung @@ -490,102 +472,108 @@ buildings: constant_signal: default: name: Signalgeber - description: Sendet ein konstantes Signal. Du wählst zwischen Formen, Farben oder + description: Sendet ein konstantes Signal. Du wählst zwischen Formen, Farben und Wahrheitswerten (1 oder 0). lever: default: name: Schalter - description: - Sendet einen Wahrheitswert (1 oder 0) auf der Wires-Ebene abhängig von seiner Stellung, - welcher dann z.B. zur Steuerung eines Filters verwendet werden kann. + description: Sendet einen Wahrheitswert (1 oder 0) auf der Wires-Ebene abhängig + von seiner Stellung, welcher dann z.B. zur Steuerung eines + Filters verwendet werden kann. logic_gate: default: - name: UND Gatter - description: Gibt eine "1" aus, wenn beide Eingänge wahr sind (wahr bedeutet Form, - Farbe oder "1"). + name: UND-Gatter + description: Gibt eine "1" aus, wenn beide Eingänge wahr (Form, Farbe oder "1") + sind. not: - name: NICHT Gatter - description: Gibt eine "1" aus, wenn der Eingang nicht wahr ist (wahr bedeutet Form, - Farbe oder "1"). + name: NICHT-Gatter + description: Gibt eine "1" aus, wenn beide Eingänge nicht wahr (kein Signal oder + "0") sind. xor: - name: XOR Gatter - description: Gibt eine "1" aus, wenn einer der Eingänge wahr ist, aber nicht beide - (wahr bedeutet Form, Farbe oder "1"). + name: XODER-Gatter + description: Gibt eine "1" aus, wenn genau einer der Eingänge wahr (Form, Farbe + oder "1") ist. or: - name: ODER Gatter - description: Gibt eine "1" aus, wenn einer der Eingänge wahr ist (wahr bedeutet Form, Farbe oder "1"). + name: ODER-Gatter + description: Gibt eine "1" aus, wenn mindestens einer der Eingänge wahr (Form, + Farbe oder "1") ist. transistor: default: name: Transistor - description: Leitet den unteren Eingang weiter, wenn der seitliche Eingang wahr ist (eine Form, - Farbe oder "1"). + description: Lässt den unteren Eingang passieren, wenn der seitliche Eingang + wahr (Form, Farbe oder "1") ist. mirrored: name: Transistor - description: Leitet den unteren Eingang weiter, wenn der seitliche Eingang wahr ist (eine Form, - Farbe oder "1"). + description: Lässt den unteren Eingang passieren, wenn der seitliche Eingang + wahr (Form, Farbe oder "1") ist. filter: default: - name: Filter - description: Verbinde ein Signal, um alle passenden Gegenstände nach oben zu leiten und die - restlichen nach rechts. Kann auch mit Wahrheitswerten gesteuert werden. + name: Itemfilter + description: Lässt nur die Items durch, die dem eingespeisten Signal entsprechen + und sortiert die restlichen aus. Mit Wahrheitswerten + konfiguriert fungiert er als Schranke. display: default: name: Anzeige - description: Verbinde ein Signal, um es auf der Anzeige darzustellen. Es kann eine Form, - Farbe oder ein Wahrheitswert sein. + description: Verbinde ein Signal, um es auf der Anzeige darzustellen. Es kann + eine Form, Farbe oder ein Wahrheitswert sein. reader: default: name: Fließbandkontrolle - description: - Ermöglicht es, den durchschnittlichen Durchsatz des Fließbandes zu messen. Gibt den letzten - Gegenstand auf der Wires-Ebene aus (sobald freigeschaltet). + description: Misst den gemittelten Durchsatz des Fließbandes. Gibt zuätzlich den + zuletzt passierten Gegenstand auf der Wires-Ebene aus (sobald + freigeschaltet). analyzer: default: name: Formanalyse - description: Analysiert den oberen rechten Quadranten der untersten Schicht der Form - und gibt seine Form und Farbe zurück. + description: Analysiert den oberen rechten Quadranten der untersten Schicht + einer Form und gibt sowohl Form als auch Farbe aus. comparator: default: name: Vergleich - description: Gibt eine "1" zurück, wenn beide Signale genau gleich sind. Kann Formen, Farben und Wahrheitswerte vergleichen. + description: Gibt eine "1" zurück, wenn beide Signale genau gleich sind. Kann + Formen, Farben und Wahrheitswerte vergleichen. virtual_processor: default: name: Virtueller Schneider description: Schneidet die Form virtuell in zwei Hälften. rotater: name: Virtueller Rotierer - description: Dreht die Form virtuell, sowohl im als auch gegen den Uhrzeigersinn. + description: Dreht die Form virtuell im Uhrzeigersinn. unstacker: name: Virtueller Entstapler description: Extrahiert virtuell die oberste Ebene nach rechts und die - die restlichen Ebenen nach links. + restlichen Ebenen nach links. stacker: name: Virtueller Stapler description: Stapelt virtuell die rechte Form auf die linke. painter: name: Virtueller Färber - description: Färbt virtuell die Form vom unteren Eingang mit der Farbe aus dem rechten Eingang. + description: Färbt virtuell die Form vom unteren Eingang mit der Farbe aus dem + rechten Eingang. item_producer: default: name: Item-Produzent - description: Nur im Sandkastenmodus verfügbar. Gibt das Signal aus der Wires-Ebene auf der regulären Ebene aus. + description: Nur im Sandkastenmodus verfügbar. Gibt das Signal aus der + Wires-Ebene als Item aus. storyRewards: reward_cutter_and_trash: title: Formen zerschneiden - desc: - Du hast gerade den Schneider freigeschaltet, der Formen in zwei Hälften schneidet, - von oben nach unten, unabhängig der Orientierung!

Achte darauf, den Abfall loszuwerden, oder - er wird verstopfen und blockieren - Zu diesem Zweck habe ich dir den Mülleimer gegeben, der - alles entsorgt, was du hineintust! + desc: Du hast gerade den Schneider freigeschaltet, der Formen + in zwei Hälften schneidet, von oben nach unten, unabhängig der + Orientierung!

Achte darauf, den Abfall loszuwerden, oder + er wird verstopfen und blockieren - Zu diesem Zweck habe ich + dir den Mülleimer gegeben, der alles entsorgt, was + du hineintust! reward_rotater: title: Rotieren desc: Der Rotierer wurde freigeschaltet! Er rotiert Formen im Uhrzeigersinn um 90 Grad. reward_painter: title: Färben - desc: "Der Färber wurde freigeschaltet. Extrahiere ein paar - Farben (genauso wie bei Formen) und färbe damit eine Form im - Färber.

PS: Falls du Farbenblind bist, gibt es einen + desc: "Der Färber wurde freigeschaltet! Extrahiere ein paar + Farben (genauso wie bei Formen) und lasse damit eine Form im Färber + bemalen.

PS: Falls du farbenblind bist, gibt es einen Modus für Farbenblinde in den Einstellungen!" reward_mixer: title: Farben mischen @@ -601,11 +589,11 @@ storyRewards: reward_balancer: title: Verteiler desc: Der multifunktionale Verteiler wurde freigeschaltet! Er kann - benutzt werden, um größere Fabriken zu bauen, indem Items auf Fließbänder aufgeteilt oder zusammengelegt werden! + benutzt werden, um größere Fabriken zu bauen, indem Fließbänder aufgeteilt oder zusammengelegt werden! reward_tunnel: title: Tunnel desc: Der Tunnel wurde freigeschaltet! Du kannst Items nun - unter Gebäuden oder Förderbändern hindurchleiten. + unter Gebäuden oder Fließbändern hindurchleiten. reward_rotater_ccw: title: Gegen UZS Rotieren desc: Du hast eine zweite Variante des Rotierers @@ -614,10 +602,10 @@ storyRewards: verschiedene Varianten zuzugreifen
. reward_miner_chainable: title: Extrahierer (Kette) - desc: >- - Du hast den Kettenextrahierer freigeschaltet! Er kann - seine Resourcen an andere Extrahierer weiterleiten!

- PS: Die alten Extrahierer wurde jetzt in deiner Symbolleiste ersetzt! + desc: "Du hast den Kettenextrahierer freigeschaltet! Er kann + seine Ressourcen an andere Extrahierer + weiterleiten.

PS: Der alte Extrahierer + wurde jetzt in deiner Symbolleiste ersetzt!" reward_underground_belt_tier_2: title: Tunnel Stufe II desc: Du hast eine neue Variante des Tunnels freigeschaltet! @@ -625,23 +613,27 @@ storyRewards: beide Tunnel miteinander mischen. reward_merger: title: Kompakter Kombinierer - desc: Du hast eine kompakte Variante des Verteilers - freigeschaltet! Der Kombinierer vereint zwei Eingäge zu einem Ausgang. + desc: Du hast eine kompakte Variante des + Verteilers freigeschaltet! Der Kombinierer vereint + zwei Eingänge zu einem Ausgang. reward_splitter: title: Kompakter Aufteiler - desc: Du hast eine kompakte Variante des Verteilers - freigeschaltet! Der Aufteiler spaltet einen Eingang in zwei Aufgänge auf. + desc: Du hast eine kompakte Variante des + Verteilers freigeschaltet! Der Aufteiler spaltet + einen Eingang in zwei Aufgänge auf. reward_belt_reader: title: Fließbandkontrolle - desc: Du hast nun die Fließbandkontrolle freigeschaltet! Damit kannst du dir - den Durchsatz eines Fließbandes anzeigen lassen.

Wenn du Stromkabel freischaltest, - wird er um eine sehr nützliche Funktion ergänzt! + desc: Du hast nun die Fließbandkontrolle freigeschaltet! Damit + kannst du dir den Durchsatz eines Fließbandes anzeigen + lassen.

Wenn du Stromkabel freischaltest, wird er um eine + sehr nützliche Funktion ergänzt! reward_cutter_quad: - title: Schneider (4-fach) + title: Schneider (vierfach) desc: Du hast eine neue Variante des Schneiders freigeschaltet! - Damit kannst du Formen in alle vier Teile zerschneiden. + Damit kannst du Formen in alle vier Teile + zerschneiden. reward_painter_double: - title: Färber (2-fach) + title: Färber (zweifach) desc: Du hast eine neue Variante des Färbers freigeschaltet! Hiermit kannst du zwei Formen auf einmal färben und verbrauchst nur eine Farbe. @@ -649,62 +641,72 @@ storyRewards: title: Lager desc: Du hast das Lager freigeschaltet! Es erlaubt dir, Gegenstände bis zu einer bestimmten Kapazität zu speichern!

- Es priorisiert den linken Ausgang, also kannst du es auch als Überlauftor benutzen! + Es priorisiert den linken Ausgang, also kannst du es auch als + Überlauftor benutzen! reward_blueprints: title: Blaupausen desc: Jetzt kannst du Teile deiner Fabrik kopieren und - einfügen! Wähle ein Areal aus (Halte STRG und ziehe mit + einfügen! Wähle einen Bereich aus (Halte STRG und ziehe mit deiner Maus) und drücke 'C', um zu kopieren.

Einfügen ist nicht kostenlos! Du musst Blaupausenformen produzieren, um die Kopierkosten - zu decken (Welche du gerade produziert hast)! + zu decken (Welche du gerade produziert hast). reward_rotater_180: title: Rotierer (180°) - desc: Du hast eine weitere Variante des Rotierers freigeschaltet! Mit ihm - kannst du Formen um 180° drehen (Überraschung! :D). + desc: Du hast eine weitere Variante des Rotierers + freigeschaltet! Mit ihm kannst du Formen um 180° drehen + (Überraschung! :D). reward_wires_painter_and_levers: - title: Wires & Quad Painter - desc: "You just unlocked the Wires Layer: It is a separate - layer on top of the regular layer and introduces a lot of new - mechanics!

For the beginning I unlocked you the Quad - Painter - Connect the slots you would like to paint with on - the wires layer!

To switch to the wires layer, press - E." + title: Wires-Ebene & vierfacher Färber + desc: Du hast soeben die Wires-Ebene freigeschaltet! Diese + separate Ebene befindet sich unter deinen Gebäuden und gibt dir + viele neue Möglichkeiten.

Für den Anfang bekommst du einen + vierfachen Färber. Schließe die Eingänge, mit denen + du die Quadranten färben möchtest, an ein Signalkabel auf der + Wires-Ebene an!

Mit E wechselst du zwischen + den Ebenen. reward_filter: - title: Item Filter - desc: You unlocked the Item Filter! It will route items either - to the top or the right output depending on whether they match the - signal from the wires layer or not.

You can also pass in a - boolean signal (1 / 0) to entirely activate or disable it. + title: Itemfilter + desc: Du hast den Itemfilter freigeschaltet! Items, die dem + eingespeisten Signal entsprechen, lässt er passieren. Den Rest + zweigt er nach rechts ab.

Du kannst z.B. nach Farbe und Form + sortieren oder mit Wahrheitswerten (1 oder 0) Items nur auf + Knopfdruck passieren lassen. reward_display: - title: Display - desc: "You have unlocked the Display - Connect a signal on the - wires layer to visualize it!

PS: Did you notice the belt - reader and storage output their last read item? Try showing it on a - display!" + title: Anzeige + desc: Du hast die Anzeige freigeschaltet! Jedes eingespeiste + Signal kann damit dargestellt werden.

Apropos, ist dir schon + aufgefallen, dass Fließbandkontrolle und Lager das zuletzt passierte + Item ausgeben können? Lasse es dir doch gleich mal anzeigen! reward_constant_signal: - title: Constant Signal - desc: You unlocked the constant signal building on the wires - layer! This is useful to connect it to item filters - for example.

The constant signal can emit a - shape, color or - boolean (1 / 0). + title: Signalgeber + desc: Du hast den Signalgeber freigeschaltet! Du kannst das + Gebäude auf der Wires-Ebene platzieren und z.B. mit einem + Itemfilter verbinden.

Der Signalgeber kann + dir Formen, Farben oder + Wahrheitswerte (1 oder 0) zur Verfügung stellen. reward_logic_gates: - title: Logic Gates - desc: You unlocked logic gates! You don't have to be excited - about this, but it's actually super cool!

With those gates - you can now compute AND, OR, XOR and NOT operations.

As a - bonus on top I also just gave you a transistor! + title: Logikgatter + desc: >- + Du hast nun eine Reihe an Logikgattern freigeschaltet! Das muss dich jetzt nicht + nervös machen, die Funktionsweise ist simpel und ziemlich cool.

+ Mit Logikgattern kannst du UND-, ODER-, XODER- und NICHT-Operationen ausführen.

+ Als Sahnehäubchen obendrauf stelle ich dir noch einen Transistor zur Verfügung. + Houston, wir sind Turing-vollständig! reward_virtual_processing: - title: Virtual Processing - desc: I just gave a whole bunch of new buildings which allow you to - simulate the processing of shapes!

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

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

- Build - something cool with wires.

- Continue to play - regulary.

Whatever you choose, remember to have fun! + title: Virtuelle Verarbeitung + desc: "Du hast gerade eine Menge neue Gebäude freigeschaltet! Mit ihnen kannst + du die Verarbeitung von Formen komplett + simulieren.

Das beinhaltet einen Schneider, + Rotierer, Stapler und mehr für die Wires-Ebene! Damit bleiben dir ab + jetzt drei Optionen offen:

- Ich empfehle dir, eine + automatisierte Maschine zu bauen, die jede + beliebige Form am Hub abgreift und herstellt. (Probiere es + wenigstens!)

- Werde kreativ und lasse dir etwas Cooles + einfallen, das du auf der Wires-Ebene umsetzen kannst. (Und teile es + auf dem Discord!)

- Spiele dich weiter durch die Level. Auf + deine Art!

Das Wichstigste an deiner Entscheidung ist: + Vergiss nicht, dabei Spaß zu haben!" no_reward: title: Nächstes Level desc: "Dieses Level hat dir keine Belohnung gegeben, aber im Nächsten gibt es @@ -716,12 +718,14 @@ storyRewards: desc: Du hast das nächste Level freigeschaltet! reward_freeplay: title: Freies Spiel - desc: Du hast es geschafft! Du hast den Freispiel-Modus freigeschaltet! Das bedeutet, - dass die abzuliefernden Formen jetzt zufällig erzeugt werden!

- Da der Hub ab jetzt einen bestimmten Durchsatz benötigt, empfehle ich dringend, eine Maschine zu bauen, - die automatisch die gewünschte Form liefert!

- Der Hub gibt die gewünschte Form auf der Wires-Ebene aus. Also musst du sie nur analysieren und - basierend darauf automatisch deine Fabrik konfigurieren. + desc: Du hast es geschafft! Du bist im Freispiel-Modus + angekommen! Das bedeutet, dass die abzuliefernden Formen jetzt + zufällig erzeugt werden!

Da der Hub ab + jetzt einen bestimmten Durchsatz benötigt, empfehle + ich dringend, eine Maschine zu bauen, die automatisch die gewünschte + Form liefert!

Der Hub gibt die gewünschte Form auf der + Wires-Ebene aus. Also musst du sie nur analysieren und basierend + darauf automatisch deine Fabrik konfigurieren. reward_demo_end: title: Ende der Demo desc: Du bist am Ende der Demo angekommen! @@ -830,10 +834,9 @@ settings: die den Einstieg erleichtern sollen. enableTunnelSmartplace: title: Intelligente Tunnel - description: Aktiviert das automatische Entfernen von überflüssigen - Förderbändern bei der Platzierung von Tunneln. Außerdem - funktioniert das Ziehen von Tunneln und überschüssige werden - ebenfalls entfernt. + description: Aktiviert das automatische Entfernen von überflüssigen Fließbändern + bei der Platzierung von Tunneln. Außerdem funktioniert das + Ziehen von Tunneln und überschüssige werden ebenfalls entfernt. vignette: title: Vignette description: Aktiviert den Vignetteneffekt, der den Rand des Bildschirms @@ -854,43 +857,55 @@ settings: mehr als 100 Feldern angezeigt wird. lowQualityMapResources: title: Minimalistische Ressourcen - description: Vereinfacht die Darstellung der Ressourcen auf der hereingezoomten Karte - zur Verbesserung der Leistung. Die Darstellung ist übersichtlicher, also probiere - es ruhig aus! + description: Vereinfacht die Darstellung der Ressourcen auf der hereingezoomten + Karte zur Verbesserung der Leistung. Die Darstellung ist + übersichtlicher, also probiere es ruhig aus! disableTileGrid: title: Gitter deaktivieren - description: Das Deaktivieren des Gitters kann deine Leistung verbessern. Außerdem vereinfacht - es die Darstellung! + description: Das Deaktivieren des Gitters kann deine Leistung verbessern. + Außerdem vereinfacht es die Darstellung! clearCursorOnDeleteWhilePlacing: title: Abwählen mit Rechtsklick - description: Standardmäßig eingeschaltet, wählt es das aktuelle, zur Platzierung ausgewählte Gebäude - ab, wenn du die rechte Masutaste drückst. Wenn du es abschaltest, kannst du mit der rechten - Maustaste Gebäude löschen, während du im Platzierungsmodus bist. + description: Standardmäßig eingeschaltet, wählt es das aktuelle, zur Platzierung + ausgewählte Gebäude ab, wenn du die rechte Masutaste drückst. + Wenn du es abschaltest, kannst du mit der rechten Maustaste + Gebäude löschen, während du im Platzierungsmodus bist. lowQualityTextures: title: Niedrige Texturqualität (Unschön) description: Das Spiel verwendet eine niedrigere Auflösung bei den Texturen. Allerdings leidet die Grafik des Spiels sehr darunter! displayChunkBorders: title: Chunk-Ränder anzeigen - description: Das Spiel ist in Blöcke (Chunks) aus je 16x16 Feldern aufgeteilt. Diese Einstellung - lässt dich die Grenzen zwischen den Chunks anzeigen. - + description: Das Spiel ist in Blöcke (Chunks) aus je 16x16 Feldern aufgeteilt. + Diese Einstellung lässt dich die Grenzen zwischen den Chunks + anzeigen. pickMinerOnPatch: title: Automatisch Extrahierer auswählen - description: Standardmäßig eingeschaltet, wählst du automatisch den Extrahierer, wenn du mit - der Pipette auf einen Ressourcenfleck zeigst + description: Standardmäßig eingeschaltet, wählst du automatisch den Extrahierer, + wenn du mit der Pipette auf einen Ressourcenfleck zeigst simplifiedBelts: - title: Minimalistische Förderbänder (Unschön) - description: Zur Verbesserung der Leistung werden die Items auf Förderbändern nur angezeigt, - wenn du deine Maus darüber bewegst. Hier leidet sowohl die Grafik, also auch dein - Spielerlebnis. Benutze die Funktion nur, wenn du auf die Leistung wirklich angewiesen bist! + title: Minimalistische Fließbänder (Unschön) + description: Zur Verbesserung der Leistung werden die Items auf Fließbändern nur + angezeigt, wenn du deine Maus darüber bewegst. Hier leidet + sowohl die Grafik, also auch dein Spielerlebnis. Benutze die + Funktion nur, wenn du auf die Leistung wirklich angewiesen bist! enableMousePan: title: Scrollen am Bildschirmrand - description: Damit kannst du dich über die Karte bewegen, indem du deinen Mauszeiger am - Bildschirmrand platzierst. Die Geschwindigkeit stimmt dabei mit den Tasten überein. + description: Damit kannst du dich über die Karte bewegen, indem du deinen + Mauszeiger am Bildschirmrand platzierst. Die Geschwindigkeit + stimmt dabei mit den Tasten überein. + zoomToCursor: + title: Zoom towards Cursor + description: If activated the zoom will happen in the direction of your mouse + position, otherwise in the middle of the screen. + mapResourcesScale: + title: Map Resources Size + description: Controls the size of the shapes on the map overview (when zooming + out). keybindings: title: Tastenbelegung - hint: "Tipp: Benutze STRG, UMSCH and ALT! Sie aktivieren verschiedene Platzierungsoptionen." + hint: "Tipp: Benutze STRG, UMSCH and ALT! Sie aktivieren verschiedene + Platzierungsoptionen." resetKeybindings: Tastenbelegung zurücksetzen categoryLabels: general: Anwendung @@ -962,16 +977,19 @@ keybindings: placeInverse: Automatische Fließbandorientierung invertieren about: title: Über dieses Spiel - body: >- - Dieses Spiel ist quelloffen (Open Source) und wurde von Tobias Springer - (das bin ich!) entwickelt.

- Wenn du etwas zum Spiel beitragen möchtest, dann schaue dir shapez.io auf GitHub an.

- Das Spiel wurde erst durch die großartige Discord-Community um meine Spiele möglich gemacht. - Komm doch einfach mal auf dem Discord-Server vorbei!

- Der Soundtrack wurde von Peppsen komponiert! Klasse Typ.

- Abschließend möchte ich meinem Kumpel Niklas danken! - Ohne unsere etlichen gemeinsamen Stunden in Factorio wäre dieses Projekt nie zustande gekommen. + (das bin ich!) entwickelt.

Wenn du etwas zum Spiel beitragen + möchtest, dann schaue dir shapez.io auf GitHub an.

Das Spiel wurde + erst durch die großartige Discord-Community um meine Spiele möglich + gemacht. Komm doch einfach mal auf dem Discord-Server vorbei!

Der Soundtrack wurde + von Peppsen komponiert! Klasse Typ.

Abschließend + möchte ich meinem Kumpel Niklas danken! Ohne unsere etlichen gemeinsamen + Stunden in Factorio wäre dieses Projekt nie zustande gekommen. changelog: title: Änderungen demo: @@ -984,41 +1002,57 @@ demo: settingNotAvailable: Nicht verfügbar in der Demo. tips: - Der Hub akzeptiert alle Formen, nicht nur die aktuell geforderten! - - Stelle sicher, dass deine Fabriken modular sind. Es zahlt sich irgendwann aus! + - Stelle sicher, dass deine Fabriken modular sind. Es zahlt sich irgendwann + aus! - Baue nicht zu nah am Hub, sonst entsteht ein riesiges Chaos! - - Wenn der Stapler nicht die richtige Form ausspuckt, wechsle doch mal die Eingänge. + - Wenn der Stapler nicht die richtige Form ausspuckt, wechsle doch mal die + Eingänge. - Du kannst mit R die Richtung des Bandplaners umkehren. - - Halte STRG, um die Förderbänder ohne automatische Orientierung zu platzieren. - - Die Verhältnisse der Maschinen bleiben gleich, wenn du die Upgrades gleichmäßig kaufst. + - Halte STRG, um die Fließbänder ohne automatische Orientierung zu + platzieren. + - Die Verhältnisse der Maschinen bleiben gleich, wenn du die Upgrades + gleichmäßig kaufst. - Serielle Ausführung ist effizienter als parallele. - Für viele Gebäude wirst du im Spielverlauf neue Varianten freischalten! - - Du kanst T drücken, um auf andere Varianten des Gebäudes zu wechseln. + - Du kanst T drücken, um auf andere Varianten des Gebäudes zu + wechseln. - Symmetrie ist der Schlüssel! - Du kannst verschiedene Arten von Tunneln miteinander verweben. - Versuche kompakte Fabriken zu bauen. Es zahlt sich aus! - - Der Färber hat eine spiegelverkehrte Variante, die du mit T auswählen kannst. + - Der Färber hat eine spiegelverkehrte Variante, die du mit T + auswählen kannst. - Das richtige Verhältnis der Gebäude maximiert die Effizienz. - - Auf der gleichen Upgrade-Stufe genügen 5 Extrahierer für ein ganzes Fließband. + - Auf der gleichen Upgrade-Stufe genügen 5 Extrahierer für ein ganzes + Fließband. - Vergiss die Tunnel nicht! - Für maximale Effizienz musst du die Items nicht gleichmässig aufteilen. - - Das Halten von UMSCH aktiviert den Bandplaner, der lange Förderbänder ganz einfach platziert. + - Das Halten von UMSCH aktiviert den Bandplaner, der lange + Fließbänder ganz einfach platziert. - Schneider teilen die Form immer vertikal, unabhängig von der Orientierung. - Weiß erhälst du aus der Kombination aller 3 Grundfarben. - Das Lager gibt Items immer zuerst am linken Ausgang ab. - Es lohnt sich, Zeit in den Bau von wiederverwendbaren Designs zu stecken! - Das Halten von STRG ermöglicht dir, mehrere Gebäude zu platzieren. - - Du kanst ALT gedrückt halten, um die Richtung der Förderbänder umzukehren. + - Du kanst ALT gedrückt halten, um die Richtung der Fließbänder + umzukehren. - Effizienz ist entscheidend! - Abbaubare Formen werden komplexer, je weiter sie vom Hub entfernt sind. - - Gebäude haben eine limitierte Geschwindigkeit. Teile die Last zwischen mehreren auf. + - Gebäude haben eine limitierte Geschwindigkeit. Teile die Last zwischen + mehreren auf. - Benutze Aufteiler, um deine Effizienz zu maximieren. - - Organisation ist wichtig! Verheddere dich nicht in einem Gewirr aus Förderbändern. - - Plane vorher und lasse dir Platz für Reserven, oder es gibt ein riesiges Chaos! - - Lösche deine alten Fabriken nicht! Du benötigst sie um Upgrades freizuschalten. + - Organisation ist wichtig! Verheddere dich nicht in einem Gewirr aus + Fließbändern. + - Plane vorher und lasse dir Platz für Reserven, oder es gibt ein riesiges + Chaos! + - Lösche deine alten Fabriken nicht! Du benötigst sie um Upgrades + freizuschalten. - Versuche Level 20 alleine zu meistern, bevor du nach Hilfe suchst! - - Mache es dir nicht zu kompliziert! Auch mit einfachen Konzepten kommst du hier sehr weit. - - Manche Fabriken musst du später wiederverwenden. Also baue sie so, damit du genau das kannst. - - Manchmal kannst du die gewünschte Form auf der Karte finden, ohne sie herstellen zu müssen. + - Mache es dir nicht zu kompliziert! Auch mit einfachen Konzepten kommst du + hier sehr weit. + - Manche Fabriken musst du später wiederverwenden. Also baue sie so, damit + du genau das kannst. + - Manchmal kannst du die gewünschte Form auf der Karte finden, ohne sie + herstellen zu müssen. - Vollständige Windmühlen werden nicht natürlich generiert. - Färbe deine Formen vor dem Schneiden! Das geht viel schneller. - Mit Modulen wird Platz nur noch zum Begriff; eine Sorge für Sterbliche. @@ -1026,16 +1060,21 @@ tips: - Schau dir den Farbmischer genauer an und du wirst deine Antwort finden. - Benutze STRG + Rechtsklick, um einen Bereich zu selektieren. - Zu nahe am Hub zu bauen, kann späteren Projekten im Weg stehen. - - Die Reißzwecke neben Formen in der Upgrade-Liste lässt sie dich am Bildschirm anheften. + - Die Reißzwecke neben Formen in der Upgrade-Liste lässt sie dich am + Bildschirm anheften. - Mische alle drei Grundfarben, um Weiß zu erhalten! - Du hast eine unendlich grosse Karte, nutze den Platz und expandiere! - Probier auch mal Factorio! Es ist mein Lieblingsspiel. - Der Vierfachschneider schneidet im Uhrzeigersinn von oben rechts beginnend! - Du kannst deine Speicherstände im Hauptmenü herunterladen! - - Diese Spiel hat viele nützliche Tastenbelegungen! Schau sie dir in den Einstellungen an. + - Diese Spiel hat viele nützliche Tastenbelegungen! Schau sie dir in den + Einstellungen an. - Dieses Spiel hat eine Menge Einstellungen, schaue sie dir einmal an! - - Die Richtung zu deinem Hub ist oben rechts mit einer kleinen Kompassnadel markiert! - - Um alle Förderbänder zu leeren, schneide den Bereich aus und füge ihn auf der selben Position wieder ein. + - Die Richtung zu deinem Hub ist oben rechts mit einer kleinen Kompassnadel + markiert! + - Um alle Fließbänder zu leeren, schneide den Bereich aus und füge ihn auf + der selben Position wieder ein. - Drücke F4 um deine FPS und Tickrate anzuzeigen. - - Drücke doppelt F4 um den Standort des Mauszeigers und der Kamera zu bestimmen. + - Drücke doppelt F4 um den Standort des Mauszeigers und der Kamera zu + bestimmen. - Du kannst die angehefteten Formen am linken Rand wieder entfernen. diff --git a/translations/base-el.yaml b/translations/base-el.yaml index a9b567ec..57910757 100644 --- a/translations/base-el.yaml +++ b/translations/base-el.yaml @@ -209,11 +209,6 @@ dialogs: renameSavegame: title: Rename Savegame desc: You can rename your savegame here. - entityWarning: - title: Performance Warning - desc: You have placed a lot of buildings, this is just a friendly reminder that - the game can not handle an endless count of buildings - So try to - keep your factories compact! ingame: keybindingsOverlay: moveMap: Κίνηση @@ -259,27 +254,6 @@ ingame: title: Αναβαθμίσεις buttonUnlock: Αναβάθμιση tier: Βαθμίδα - tierLabels: - - I - - II - - III - - IV - - V - - VI - - VII - - VIII - - IX - - X - - XI - - XII - - XIII - - XIV - - XV - - XVI - - XVII - - XVIII - - XIX - - XX maximumLevel: ΜΕΓΙΣΤΟ ΕΠΙΠΕΔΟ (Ταχύτητα x) statistics: title: Στατιστικά @@ -303,10 +277,6 @@ ingame: playtime: Χρόνος που έπαιξες buildingsPlaced: Κτήρια beltsPlaced: Ιμάντες - buttons: - continue: Συνέχεια - settings: Ρυθμίσεις - menu: Επιστροφή στο μενού tutorialHints: title: Χρειάζεσε βοήθεια; showHint: Εμφάνιση υπόδειξης @@ -700,7 +670,7 @@ storyRewards: title: Balancer desc: The multifunctional balancer has been unlocked - It can be used to build bigger factories by splitting and merging - items onto multiple belts!

+ items onto multiple belts! reward_merger: title: Compact Merger desc: You have unlocked a merger variant of the @@ -922,6 +892,14 @@ settings: title: Enable Mouse Pan description: Allows to move the map by moving the cursor to the edges of the screen. The speed depends on the Movement Speed setting. + zoomToCursor: + title: Zoom towards Cursor + description: If activated the zoom will happen in the direction of your mouse + position, otherwise in the middle of the screen. + mapResourcesScale: + title: Map Resources Size + description: Controls the size of the shapes on the map overview (when zooming + out). rangeSliderPercentage: % keybindings: title: Συνδιασμοί πλήκτρων diff --git a/translations/base-en.yaml b/translations/base-en.yaml index 86bc8c90..a0754ba9 100644 --- a/translations/base-en.yaml +++ b/translations/base-en.yaml @@ -272,10 +272,6 @@ dialogs: title: Rename Savegame desc: You can rename your savegame here. - entityWarning: - title: Performance Warning - desc: You have placed a lot of buildings, this is just a friendly reminder that the game can not handle an endless number of buildings - try to keep your factories compact! - ingame: # This is shown in the top left corner and displays useful keybindings in # every situation @@ -355,10 +351,6 @@ ingame: # Gets replaced to e.g. "Tier IX" tier: Tier - # The roman number for each tier - tierLabels: - [I, II, III, IV, V, VI, VII, VIII, IX, X, XI, XII, XIII, XIV, XV, XVI, XVII, XVIII, XIX, XX] - maximumLevel: MAXIMUM LEVEL (Speed x) # The "Statistics" window @@ -422,6 +414,22 @@ ingame: 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. + 2_1_place_cutter: >- + Now place a Cutter to cut the circles in two halves!

+ PS: The cutter always cuts from top to bottom regardless of its orientation. + + 2_2_place_trash: >- + The cutter can clog and stall!

+ Use a trash to get rid of the currently (!) not needed waste. + + 2_3_more_cutters: >- + Good job! Now place 2 more cutters to speed up this slow process!

+ PS: Use the 0-9 hotkeys to access buildings faster! + + 3_1_rectangles: >- + Now let's extract some rectangles! Build 4 extractors and connect them to the hub.

+ PS: Hold SHIFT while dragging a belt to activate the belt planner! + # Connected miners connectedMiners: one_miner: 1 Extractor @@ -676,7 +684,7 @@ buildings: rotater: name: Virtual Rotater - description: Virtually rotates the shape, both clockwise and counter-clockwise. + description: Virtually rotates the shape clockwise. unstacker: name: Virtual Unstacker @@ -1033,6 +1041,16 @@ settings: description: >- Allows panning the map by moving the cursor to the edges of the screen. The scroll speed depends on the Movement Speed setting. + zoomToCursor: + title: Zoom towards Cursor + description: >- + If activated the zoom will happen in the direction of your mouse position, otherwise in the middle of the screen. + + mapResourcesScale: + title: Map Resources Size + description: >- + Controls the size of the shapes on the map overview (when zooming out). + keybindings: title: Keybindings hint: >- diff --git a/translations/base-es.yaml b/translations/base-es.yaml index 110d1e8c..0d26afcd 100644 --- a/translations/base-es.yaml +++ b/translations/base-es.yaml @@ -2,29 +2,29 @@ steamPage: shortText: shapez.io es un juego sobre construir fábricas para automatizar la creación y combinación de figuras cada vez más complejas en un mapa infinito. - discordLinkShort: Official Discord + discordLinkShort: Discord oficial intro: >- - Shapez.io is a relaxed game in which you have to build factories for the - automated production of geometric shapes. + Shapez.io es un juego tranquilo en el que tienes que construir fábricas + para la producción automatizada de formas geométricas. - As the level increases, the shapes become more and more complex, and you have to spread out on the infinite map. + A medida que el nivel aumenta, las formas se vuelven más y más complejas, y tienes que extenderte en un mapa infinito. - And as if that wasn't enough, you also have to produce exponentially more to satisfy the demands - the only thing that helps is scaling! + Y por si fuera poco, también tienes que producir exponencialmente más para satisfacer las demandas - ¡lo único que ayuda es escalar! - While you only process shapes at the beginning, you have to color them later - for this you have to extract and mix colors! + Mientras que sólo procesas formas al principio, tienes que colorearlas después - ¡para ello tienes que extraer y mezclar los colores! - Buying the game on Steam gives you access to the full version, but you can also play a demo on shapez.io first and decide later! - title_advantages: Standalone Advantages + Comprando el juego en Steam tienes acceso a la versión completa, ¡pero también puedes jugar una demo en shapez.io primero y decidir después! + title_advantages: Ventajas del juego advantages: - - 12 New Level for a total of 26 levels - - 18 New Buildings for a fully automated factory! + - 12 nuevos niveles de un total de 26 niveles + - 18 nuevos edificios ¡para una fábrica totalmente automatizada! - 20 Upgrade Tiers for many hours of fun! - Wires Update for an entirely new dimension! - - Dark Mode! - - Unlimited Savegames - - Unlimited Markers + - Modo oscuro! + - Partidad guardadas ilimitadas + - Marcadores ilimitados - Support me! ❤️ - title_future: Planned Content + title_future: Contenido futuro planned: - Blueprint Library (Standalone Exclusive) - Steam Achievements @@ -204,11 +204,6 @@ dialogs: renameSavegame: title: Rename Savegame desc: You can rename your savegame here. - entityWarning: - title: Performance Warning - desc: You have placed a lot of buildings, this is just a friendly reminder that - the game can not handle an endless count of buildings - So try to - keep your factories compact! ingame: keybindingsOverlay: moveMap: Mover @@ -264,27 +259,6 @@ ingame: title: Mejoras buttonUnlock: Mejorar tier: Nivel - tierLabels: - - I - - II - - III - - IV - - V - - VI - - VII - - VIII - - IX - - X - - XI - - XII - - XIII - - XIV - - XV - - XVI - - XVII - - XVIII - - XIX - - XX maximumLevel: NIVEL MÁXIMO (Velocidad x) statistics: title: Estadísticas @@ -308,10 +282,6 @@ ingame: playtime: Tiempo de juego buildingsPlaced: Edificios beltsPlaced: Cintas transportadoras - buttons: - continue: Continuar - settings: Opciones - menu: Volver al menú principal tutorialHints: title: ¿Necesitas ayuda? showHint: Mostrar pista @@ -690,7 +660,7 @@ storyRewards: title: Balancer desc: The multifunctional balancer has been unlocked - It can be used to build bigger factories by splitting and merging - items onto multiple belts!

+ items onto multiple belts! reward_merger: title: Compact Merger desc: You have unlocked a merger variant of the @@ -910,6 +880,14 @@ settings: title: Enable Mouse Pan description: Allows to move the map by moving the cursor to the edges of the screen. The speed depends on the Movement Speed setting. + zoomToCursor: + title: Zoom towards Cursor + description: If activated the zoom will happen in the direction of your mouse + position, otherwise in the middle of the screen. + mapResourcesScale: + title: Map Resources Size + description: Controls the size of the shapes on the map overview (when zooming + out). rangeSliderPercentage: % keybindings: title: Atajos de teclado diff --git a/translations/base-fi.yaml b/translations/base-fi.yaml index 3c708224..337f36ef 100644 --- a/translations/base-fi.yaml +++ b/translations/base-fi.yaml @@ -201,11 +201,6 @@ dialogs: renameSavegame: title: Rename Savegame desc: You can rename your savegame here. - entityWarning: - title: Performance Warning - desc: You have placed a lot of buildings, this is just a friendly reminder that - the game can not handle an endless count of buildings - So try to - keep your factories compact! ingame: keybindingsOverlay: moveMap: Liiku @@ -261,27 +256,6 @@ ingame: title: Päivitykset buttonUnlock: Päivitä tier: Taso - tierLabels: - - I - - II - - III - - IV - - V - - VI - - VII - - VIII - - IX - - X - - XI - - XII - - XIII - - XIV - - XV - - XVI - - XVII - - XVIII - - XIX - - XX maximumLevel: SUURIN TASO (Nopeus x) statistics: title: Tilastot @@ -305,10 +279,6 @@ ingame: playtime: Peliaika buildingsPlaced: Rakennukset beltsPlaced: Liukuhihnat - buttons: - continue: Jatka - settings: Asetukset - menu: Palaa valikkoon tutorialHints: title: Tarvitsetko apua? showHint: Näytä vihje @@ -682,7 +652,7 @@ storyRewards: title: Balancer desc: The multifunctional balancer has been unlocked - It can be used to build bigger factories by splitting and merging - items onto multiple belts!

+ items onto multiple belts! reward_merger: title: Compact Merger desc: You have unlocked a merger variant of the @@ -900,6 +870,14 @@ settings: title: Enable Mouse Pan description: Allows to move the map by moving the cursor to the edges of the screen. The speed depends on the Movement Speed setting. + zoomToCursor: + title: Zoom towards Cursor + description: If activated the zoom will happen in the direction of your mouse + position, otherwise in the middle of the screen. + mapResourcesScale: + title: Map Resources Size + description: Controls the size of the shapes on the map overview (when zooming + out). rangeSliderPercentage: % keybindings: title: Pikanäppäimet diff --git a/translations/base-fr.yaml b/translations/base-fr.yaml index b4065df9..bcc2f61b 100644 --- a/translations/base-fr.yaml +++ b/translations/base-fr.yaml @@ -47,7 +47,7 @@ steamPage: global: loading: Chargement error: Erreur - thousandsDivider: " " + thousandsDivider:   decimalSeparator: "," suffix: thousands: k @@ -176,16 +176,15 @@ dialogs: usines. En voici quelques-uns, n’hésitez pas à aller découvrir les raccourcis !

CTRL + glisser : Sélectionne une zone à - copier / supprimer.
MAJ : Laissez - appuyé pour placer plusieurs fois le même bâtiment.
ALT : Inverse l’orientation des convoyeurs - placés.
' + copier / supprimer.
MAJ : + Laissez appuyé pour placer plusieurs fois le même bâtiment.
+ ALT : Inverse l’orientation des + convoyeurs placés.
' createMarker: title: Nouvelle balise titleEdit: Modifier cette balise - desc: Donnez-lui un nom. Vous pouvez aussi inclure le raccourci - d’une forme (que vous pouvez générer ici). + desc: Give it a meaningful name, you can also include a short + key of a shape (Which you can generate here) editSignal: title: Définir le signal descItems: "Choisissez un objet prédéfini :" @@ -202,11 +201,6 @@ dialogs: renameSavegame: title: Renommer la sauvegarde desc: Vous pouvez renommer la sauvegarde ici. - entityWarning: - title: Avertissement sur les performances - desc: Vous avez placé beaucoup de bâtiments. Ceci est juste un rappel amical que - le jeu ne peut pas gérer un nombre infini de bâtiments, alors - essayez de garder vos usines compactes ! ingame: keybindingsOverlay: moveMap: Déplacer @@ -262,27 +256,6 @@ ingame: title: Améliorations buttonUnlock: Améliorer tier: Niveau  - tierLabels: - - I - - II - - III - - IV - - V - - VI - - VII - - VIII - - IX - - X - - XI - - XII - - XIII - - XIV - - XV - - XVI - - XVII - - XVIII - - XIX - - XX maximumLevel: NIVEAU MAX (Vitesse ×) statistics: title: Statistiques @@ -306,10 +279,6 @@ ingame: playtime: Temps de jeu buildingsPlaced: Bâtiments beltsPlaced: Convoyeurs - buttons: - continue: Continuer - settings: Options - menu: Retourner au menu tutorialHints: title: Besoin d’aide ? showHint: Indice @@ -319,8 +288,8 @@ ingame: waypoints: waypoints: Balise hub: Centre - description: Cliquez sur une balise pour vous y rendre, clic-droit pour - la supprimer.

Appuyez sur pour créer une balise + description: Cliquez sur une balise pour vous y rendre, clic-droit pour la + supprimer.

Appuyez sur pour créer une balise sur la vue actuelle, ou clic-droit pour en créer une sur l’endroit pointé. creationSuccessNotification: La balise a été créée. @@ -597,11 +566,12 @@ buildings: storyRewards: reward_cutter_and_trash: title: Découpage de formes - desc: Vous avez débloqué le découpeur. Il coupe des formes en - deux de haut en bas quelle que soit son - orientation !

Assurez-vous de vous débarrasser des déchets, - sinon gare au blocage. À cet effet, je mets à votre - disposition la poubelle, qui détruit tout ce que vous y mettez ! + desc: You just unlocked the cutter, which cuts shapes in half + from top to bottom regardless of its + orientation!

Be sure to get rid of the waste, or + otherwise it will clog and stall - For this purpose + I have given you the trash, which destroys + everything you put into it! reward_rotater: title: Rotation desc: Le pivoteur a été débloqué ! Il pivote les formes de 90 @@ -627,10 +597,9 @@ storyRewards: placée au-dessus de la forme de gauche. reward_balancer: title: Répartiteur - desc: Le répartiteur multifonctionnel a été débloqué. Il peut - être utilisé pour construire de plus grandes usines en - distribuant équitablement et rassemblant les formes - entre plusieurs convoyeurs !

+ desc: The multifunctional balancer has been unlocked - It can + be used to build bigger factories by splitting and merging + items onto multiple belts! reward_tunnel: title: Tunnel desc: Le tunnel a été débloqué. Vous pouvez maintenant faire @@ -654,12 +623,14 @@ storyRewards: les deux variantes de tunnels ! reward_merger: title: Fusionneur compact - desc: Vous avez débloqué une variante du répartiteur. Il - accepte deux entrées et les fusionne en un seul convoyeur ! + desc: You have unlocked a merger variant of the + balancer - It accepts two inputs and merges them + into one belt! reward_splitter: title: Répartiteur compact - desc: Vous avez débloqué une variante compacte du répartiteur — - Il accepte une seule entrée et la divise en deux sorties ! + desc: You have unlocked a splitter variant of the + balancer - It accepts one input and splits them + into two! reward_belt_reader: title: Lecteur de débit desc: Vous avez débloqué le lecteur de débit ! Il vous permet @@ -733,17 +704,14 @@ storyRewards: transistor !" reward_virtual_processing: title: Traitement virtuel - desc: Je viens de vous donner tout un tas de nouveaux bâtiments qui vous - permettent de simuler le traitement des - formes !

Vous pouvez maintenant simuler un - découpeur, un pivoteur, un combineur et plus encore sur le calque de - câblage !

Avec ça, vous avez trois possibilités pour - continuer le jeu :

- Construire une machine - automatisée pour fabriquer n’importe quelle forme demandée - par le centre (je conseille d’essayer !).

- Construire - quelque chose de cool avec des câbles.

- Continuer à jouer - normalement.

Dans tous les cas, l’important c’est de - s’amuser ! + desc: I just gave a whole bunch of new buildings which allow you to + simulate the processing of shapes!

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

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

- Build + something cool with wires.

- Continue to play + regulary.

Whatever you choose, remember to have fun! no_reward: title: Niveau suivant desc: "Ce niveau n’a pas de récompense mais le prochain, si !

PS : Ne @@ -934,6 +902,14 @@ settings: description: Permet de déplacer la carte en déplaçant le curseur sur les bords de l’écran. La vitesse dépend du réglage de la vitesse de déplacement. + zoomToCursor: + title: Zoom towards Cursor + description: If activated the zoom will happen in the direction of your mouse + position, otherwise in the middle of the screen. + mapResourcesScale: + title: Map Resources Size + description: Controls the size of the shapes on the map overview (when zooming + out). keybindings: title: Contrôles hint: "Astuce : N’oubliez pas d’utiliser CTRL, MAJ et ALT ! Ces touches activent @@ -1083,8 +1059,7 @@ tips: Planifiez vos usines pour qu’elles soient réutilisables. - Parfois, vous pouvez trouver une forme nécessaire sur la carte sans la créer avec des combineurs. - - Les formes en hélice complètes ne peuvent jamais apparaître - naturellement. + - Les formes en hélice complètes ne peuvent jamais apparaître naturellement. - Colorez vos formes avant de les découper pour une efficacité maximale. - Avec les modules, l’espace n’est qu’une perception ; une préoccupation pour les hommes mortels. diff --git a/translations/base-hr.yaml b/translations/base-hr.yaml index 70759c8e..62fde359 100644 --- a/translations/base-hr.yaml +++ b/translations/base-hr.yaml @@ -198,11 +198,6 @@ dialogs: renameSavegame: title: Rename Savegame desc: You can rename your savegame here. - entityWarning: - title: Performance Warning - desc: You have placed a lot of buildings, this is just a friendly reminder that - the game can not handle an endless count of buildings - So try to - keep your factories compact! ingame: keybindingsOverlay: moveMap: Kretanje @@ -258,27 +253,6 @@ ingame: title: Nadogradnje buttonUnlock: Nadogradi tier: Razina - tierLabels: - - I - - II - - III - - IV - - V - - VI - - VII - - VIII - - IX - - X - - XI - - XII - - XIII - - XIV - - XV - - XVI - - XVII - - XVIII - - XIX - - XX maximumLevel: MAKSIMALNI LEVEL (Brzina x) statistics: title: Statistika @@ -302,10 +276,6 @@ ingame: playtime: Vrijeme igranja buildingsPlaced: Građevine beltsPlaced: Trake - buttons: - continue: Nastavi - settings: Postavke - menu: Vrati se u glavni izbornik tutorialHints: title: Trebaš pomoć? showHint: Savjet za gradnju @@ -670,7 +640,7 @@ storyRewards: title: Balancer desc: The multifunctional balancer has been unlocked - It can be used to build bigger factories by splitting and merging - items onto multiple belts!

+ items onto multiple belts! reward_merger: title: Compact Merger desc: You have unlocked a merger variant of the @@ -885,6 +855,14 @@ settings: title: Enable Mouse Pan description: Allows to move the map by moving the cursor to the edges of the screen. The speed depends on the Movement Speed setting. + zoomToCursor: + title: Zoom towards Cursor + description: If activated the zoom will happen in the direction of your mouse + position, otherwise in the middle of the screen. + mapResourcesScale: + title: Map Resources Size + description: Controls the size of the shapes on the map overview (when zooming + out). rangeSliderPercentage: % keybindings: title: Tipka diff --git a/translations/base-hu.yaml b/translations/base-hu.yaml index 86e9cbcd..68b242dd 100644 --- a/translations/base-hu.yaml +++ b/translations/base-hu.yaml @@ -204,11 +204,6 @@ dialogs: renameSavegame: title: Rename Savegame desc: You can rename your savegame here. - entityWarning: - title: Performance Warning - desc: You have placed a lot of buildings, this is just a friendly reminder that - the game can not handle an endless count of buildings - So try to - keep your factories compact! ingame: keybindingsOverlay: moveMap: Mozgatás @@ -264,27 +259,6 @@ ingame: title: Fejlesztések buttonUnlock: Fejlesztés tier: . Szint - tierLabels: - - I - - II - - III - - IV - - V - - VI - - VII - - VIII - - IX - - X - - XI - - XII - - XIII - - XIV - - XV - - XVI - - XVII - - XVIII - - XIX - - XX maximumLevel: LEGMAGASABB SZINT (x Sebesség) statistics: title: Statisztikák @@ -308,10 +282,6 @@ ingame: playtime: Játékidő buildingsPlaced: Épületek száma beltsPlaced: Futószalagok hossza - buttons: - continue: Folytatás - settings: Beállítások - menu: Vissza a menübe tutorialHints: title: Segítségre van szükséged? showHint: Segítség mutatása @@ -684,7 +654,7 @@ storyRewards: title: Balancer desc: The multifunctional balancer has been unlocked - It can be used to build bigger factories by splitting and merging - items onto multiple belts!

+ items onto multiple belts! reward_merger: title: Compact Merger desc: You have unlocked a merger variant of the @@ -901,6 +871,14 @@ settings: title: Enable Mouse Pan description: Allows to move the map by moving the cursor to the edges of the screen. The speed depends on the Movement Speed setting. + zoomToCursor: + title: Zoom towards Cursor + description: If activated the zoom will happen in the direction of your mouse + position, otherwise in the middle of the screen. + mapResourcesScale: + title: Map Resources Size + description: Controls the size of the shapes on the map overview (when zooming + out). rangeSliderPercentage: % keybindings: title: Keybindings diff --git a/translations/base-ind.yaml b/translations/base-ind.yaml index 8d99ca99..659c8e29 100644 --- a/translations/base-ind.yaml +++ b/translations/base-ind.yaml @@ -2,59 +2,56 @@ steamPage: shortText: Shapez.io adalah game tentang membangun pabrik untuk mengotomatiskan pembuatan dan pemrosesan bentuk-bentuk yang semakin kompleks di peta yang meluas tanpa batas. - discordLinkShort: Official Discord + discordLinkShort: Discord Resmi intro: >- - Shapez.io is a relaxed game in which you have to build factories for the - automated production of geometric shapes. + Anda suka game otomasi? Maka anda berada di tempat yang tepat! - As the level increases, the shapes become more and more complex, and you have to spread out on the infinite map. + Shapez.io adalah game santai dimana anda harus membuat pabrik untuk mengotomatiskan produksi bentuk-bentuk geometris. Semakin meningkatnya level, bentuk-bentuknya menjadi lebih kompleks, dan anda perlu meluas di peta yang tak terbatas. - And as if that wasn't enough, you also have to produce exponentially more to satisfy the demands - the only thing that helps is scaling! + Dan jita itu tidak cukup, anda juga perlu untuk memproduksi secara ekxponensial untuk memenuhkan kebutuhan - hal yang membantu hanyalah memperbesar pabrik! Walaupun anda hanya memproses bentuk di awal, nantinya anda harus memberinya warna - dengan mengekstrak dan mencampur warna! - While you only process shapes at the beginning, you have to color them later - for this you have to extract and mix colors! - - Buying the game on Steam gives you access to the full version, but you can also play a demo on shapez.io first and decide later! - title_advantages: Standalone Advantages + Membeli game ini di Steam memberikan anda akses ke versi lengkap, namun anda juga dapat mencoba demo dan memutuskan nanti! + title_advantages: Keuntungan versi lengkap advantages: - - 12 New Level for a total of 26 levels - - 18 New Buildings for a fully automated factory! - - 20 Upgrade Tiers for many hours of fun! - - Wires Update for an entirely new dimension! - - Dark Mode! - - Unlimited Savegames - - Unlimited Markers - - Support me! ❤️ - title_future: Planned Content + - 12 Level Baru dengan total 26 level + - 18 Bangunan Baru untuk membuat pabrik yang otomatis sepenuhnya! + - 20 Tingkat Upgrade untuk keseruan berjam-jam! + - Update Kabel untuk dimensi yang benar-benar berbeda! + - Mode Gelap! + - Data Simpanan Tidak Terbatas + - Penanda Tidak Terbatas + - Dukung saya! ❤️ + title_future: Konten Terencana planned: - - Blueprint Library (Standalone Exclusive) + - Perpustakaan Cetak Biru (Eksklusif Versi Lengkap) - Steam Achievements - - Puzzle Mode - - Minimap + - Mode Puzzle + - Peta Kecil - Mods - - Sandbox mode - - ... and a lot more! - title_open_source: This game is open source! + - Mode Sandbox + - ... dan masih banyak lagi! + title_open_source: Game ini open source! title_links: Links links: - discord: Official Discord - roadmap: Roadmap + discord: Discord Resmi + roadmap: Peta Jalan subreddit: Subreddit source_code: Source code (GitHub) - translate: Help translate + translate: Bantu menterjemahkan text_open_source: >- - Anybody can contribute, I'm actively involved in the community and - attempt to review all suggestions and take feedback into consideration - where possible. + Semua orang bisa berpartisipasi, saya aktif terlibat dalam komunitas dan + mencoba untuk meninjau semua saran dan mempertimbangkan segala umpan balik + jika memungkinkan. - Be sure to check out my trello board for the full roadmap! + Pastikan untuk memeriksa papan trello saya untuk peta jalan lengkapnya! global: loading: Sedang memuat error: Terjadi kesalahan thousandsDivider: "," decimalSeparator: . suffix: - thousands: K - millions: J + thousands: rb + millions: Jt billions: M trillions: T infinite: tak terhingga @@ -67,8 +64,8 @@ global: xHoursAgo: jam yang lalu oneDayAgo: sehari yang lalu xDaysAgo: hari yang lalu - secondsShort: det - minutesAndSecondsShort: m det + secondsShort: dtk + minutesAndSecondsShort: m dtk hoursAndMinutesShort: j m xMinutes: menit keys: @@ -80,10 +77,10 @@ global: space: SPACE demoBanners: title: Versi Demo - intro: Dapatkan versi penuh untuk membuka semua fitur! + intro: Dapatkan versi lengkap untuk membuka semua fitur! mainMenu: play: Mulai Permainan - continue: Lanjutkan Permainan + continue: Lanjutkan newGame: Permainan Baru changelog: Catatan Perubahan subreddit: Reddit @@ -93,7 +90,7 @@ mainMenu: helpTranslate: Bantu Terjemahkan! madeBy: Dibuat oleh browserWarning: Maaf, tetapi permainan ini biasanya lambat pada perambah - (browser) Anda! Dapatkan versi penuh atau unduh Chrome untuk pengalaman + (browser) Anda! Dapatkan versi lengkap atau unduh Chrome untuk pengalaman sepenuhnya. savegameLevel: Level savegameLevelUnknown: Level tidak diketahui @@ -106,7 +103,7 @@ dialogs: later: Nanti restart: Mulai Ulang reset: Setel Ulang - getStandalone: Dapatkan Versi Penuh + getStandalone: Dapatkan Versi Lengkap deleteGame: Saya tahu apa yang saya lakukan viewUpdate: Tampilkan Pembaruan showUpgrades: Tunjukkan Tingkatan @@ -122,9 +119,9 @@ dialogs: text: "Gagal memuat data simpanan Anda:" confirmSavegameDelete: title: Konfirmasi Penghapusan - text: Are you sure you want to delete the following game?

- '' at level

This can not be - undone! + text: Apakah anda yakin ingin menghapus game berikut?

+ '' pada level

+ Hal ini tak dapat diulang! savegameDeletionError: title: Gagal Menghapus text: "Gagal untuk menghapus data simpanan:" @@ -145,12 +142,12 @@ dialogs: featureRestriction: title: Versi Demo desc: Anda mencoba untuk mengakses fitur () yang tidak tersedia pada - versi demo. Pertimbangkan untuk mendapatkan versi penuh untuk + versi demo. Pertimbangkan untuk mendapatkan versi lengkap untuk pengalaman sepenuhnya! oneSavegameLimit: title: Penyimpanan Permainan Terbatas desc: Anda hanya dapat memiliki satu simpanan permainan dalam versi demo. Harap - hapus yang telah ada atau dapatkan versi penuh! + hapus yang telah ada atau dapatkan versi lengkap! updateSummary: title: Pembaruan Baru! desc: "Berikut perubahan-perubahan yang telah dibuat sejak Anda main terakhir @@ -188,10 +185,10 @@ dialogs: createMarker: title: Penanda Baru titleEdit: Sunting Penanda - desc: Give it a meaningful name, you can also include a short - key of a shape (Which you can generate here) + desc: Berikan nama yang berguna, anda juga bisa memasukkan kunci + pintas dari sebuah bentuk (Yang bisa anda buat sendiri disini) markerDemoLimit: - desc: Anda hanya dapat memuat dua penanda pada versi demo. Dapatkan versi penuh + desc: Anda hanya dapat memuat dua penanda pada versi demo. Dapatkan versi lengkap untuk penanda-penanda tak terhingga! exportScreenshotWarning: title: Ekspor Tangkapan Layar @@ -199,18 +196,16 @@ dialogs: layar. Harap ketahui bahwa ini bisa menjadi lambat untuk pangkalan pusat yang besar dan bahkan dapat membuat permainan Anda mogok! editSignal: - title: Set Signal - descItems: "Choose a pre-defined item:" - descShortKey: ... or enter the short key of a shape (Which you - can generate here) + title: Atur Tanda + descItems: "Pilih item yang telah ditentukan sebelumnya:" + descShortKey: ... atau masukkan kunci pintas dari bentuk (Yang bisa anda buat sendiri disini) renameSavegame: - title: Rename Savegame - desc: You can rename your savegame here. + title: Ganti Nama Data Simpanan + desc: Anda bisa mengganti nama data simpanan di sini. entityWarning: - title: Performance Warning - desc: You have placed a lot of buildings, this is just a friendly reminder that - the game can not handle an endless count of buildings - So try to - keep your factories compact! + title: Peringatan Kapasitas + desc: Anda telah membangun banyak bangunan, ini hanya sebuah peringatan kecil bahwa + game ini tidak dapat menangani jumlah bangunan yang tak terbatas - Jadi cobalah untuk membangun pabrik yang rapat. ingame: keybindingsOverlay: moveMap: Pindahkan @@ -249,8 +244,8 @@ ingame: speed: Kecepatan range: Rentang storage: Penyimpanan - oneItemPerSecond: satu artikel / detik - itemsPerSecond: artikel / detik + oneItemPerSecond: satu item / detik + itemsPerSecond: item / detik itemsPerSecondDouble: (x2) tiles: ubin levelCompleteNotification: @@ -261,7 +256,7 @@ ingame: notifications: newUpgrade: Tingkatan baru tersedia! gameSaved: Permainan Anda telah disimpan. - freeplayLevelComplete: Level has been completed! + freeplayLevelComplete: Level telah selesai! shop: title: Tingkatan-tingkatan buttonUnlock: Tingkatkan @@ -305,9 +300,9 @@ ingame: Anda. noShapesProduced: Sejauh ini belum diproduksi. shapesDisplayUnits: - second: / s + second: / dtk minute: / m - hour: / h + hour: / j settingsMenu: playtime: Waktu bermain buildingsPlaced: Bangunan @@ -325,7 +320,7 @@ ingame: waypoints: waypoints: Penanda hub: PUSAT - description: Klik tombol kiri tetikus pada penanda untuk melompat kepadanya, + description: Klik tombol kiri mouse pada penanda untuk melompat kepadanya, klik tombol kanan untuk menghapusnya.

Tekan untuk membuat penanda dari sudut pandang saat ini, atau klik tombol kanan untuk membuat penanda pada lokasi yang @@ -342,49 +337,49 @@ ingame: lingkaran untuk mengekstrak bentuk tersebut! 1_2_conveyor: "Hubungkan ekstraktor dengan sabuk konveyor ke pusat pangkalan Anda!

Kiat: Klik dan - seret sabuk konveyor dengan tetikus!" + seret sabuk konveyor dengan mouse!" 1_3_expand: "Ini BUKAN permainan menganggur! Bangun lebih banyak ekstraktor dan sabuk konveyor untuk menyelesaikan obyektif dengan lebih cepat.

Kiat: Tahan - tombolSHIFT untuk meletakkan beberapa + tombol SHIFT untuk meletakkan beberapa ekstraktor, dan gunakan tombol R untuk memutar." connectedMiners: - one_miner: 1 Miner - n_miners: Miners - limited_items: Limited to + one_miner: 1 Ekstraktor + n_miners: Ekstraktor + limited_items: Terbatas hingga watermark: - title: Demo version - desc: Click here to see the Steam version advantages! - get_on_steam: Get on steam + title: Versi Demo + desc: Klik disini untuk melihat keunggulan pada versi Steam! + get_on_steam: Dapatkan di Steam standaloneAdvantages: - title: Get the full version! - no_thanks: No, thanks! + title: Dapatkan versi lengkap! + no_thanks: Tidak, terima kasih! points: levels: - title: 12 New Levels - desc: For a total of 26 levels! + title: 12 Level Baru + desc: Dengan total 26 level! buildings: - title: 18 New Buildings - desc: Fully automate your factory! + title: 18 Bangunan Baru + desc: Untuk membuat pabrik yang otomatis sepenuhnya! savegames: - title: ∞ Savegames - desc: As many as your heart desires! + title: ∞ Data Simpanan + desc: Sebanyak yang anda mau! upgrades: - title: 20 Upgrade Tiers - desc: This demo version has only 5! + title: 20 Tingkatan Upgrade + desc: Versi demo ini hanya punya 5! markers: - title: ∞ Markers - desc: Never get lost in your factory! + title: ∞ Penanda + desc: Anda tidak akan tersesat di pabrik anda! wires: - title: Wires - desc: An entirely new dimension! + title: Kabel + desc: Sebuah dimensi yang benar-benar berbeda! darkmode: - title: Dark Mode - desc: Stop hurting your eyes! + title: Mode Gelap + desc: Berhenti merusak matamu! support: - title: Support me - desc: I develop it in my spare time! + title: Dukung saya + desc: Saya membuat ini di waktu luang! shopUpgrades: belt: name: Sabuk konveyor, Pembagi Arus & Terowongan @@ -394,29 +389,29 @@ shopUpgrades: description: Kecepatan x → x processors: name: Memotong, Memutar & Menyusun - description: Speed x → x + description: Kecepatan x → x painting: - name: Mencampur & Mencat + name: Mencampur & Mengecat description: Kecepatan x → x buildings: hub: deliver: Kirim toUnlock: untuk membuka levelShortcut: LVL - endOfDemo: End of Demo + endOfDemo: Akhir dari Demo belt: default: name: Sabuk Konveyor - description: Mengangkut artikel-artikel, tahan dan seret untuk meletakkan + description: Mengangkut sumber daya, tahan dan seret untuk meletakkan beberapa. wire: default: - name: Kawat Energi - description: Memungkinkan anda untuk mengangkut energi. + name: Kabel + description: Memungkinkan anda untuk mengangkut Energi. second: - name: Wire - description: Transfers signals, which can be items, colors or booleans (1 / 0). - Different colored wires do not connect. + name: Kabel + description: Mentransfer sinyal, dapat berupa bentuk, warna, atau boolean (1 atau 0). + Kabel dengan warna berbeda tidak akan menyambung. miner: default: name: Ekstraktor @@ -455,188 +450,181 @@ buildings: name: Pemutar (Berlawanan Arah Jarum Jam) description: Memutar bentuk berlawanan arah jarum jam sebesar 90 derajat. rotate180: - name: Rotate (180) - description: Rotates shapes by 180 degrees. + name: Pemutar (180 derajat) + description: Memutar bentuk searah jarum jam sebesar 180 derajat. stacker: default: - name: Penyusun - description: Menggabungkan kedua artikel. Apabila mereka tidak dapat - digabungkan, artikel kanan akan diletakkan diatas artikel kiri. + name: Penumpuk + description: Menumpukkan kedua bentuk. Apabila mereka tidak dapat + digabungkan, bentuk kanan akan diletakkan diatas bentuk kiri. mixer: default: name: Pencampur Warna description: Mencampurkan dua warna menggunakan campuran aditif. painter: default: - name: Pencat - description: Mencat keseluruhan bentuk dari input kiri dengan warna dari input + name: Pengecat + description: Mengecat keseluruhan bentuk dari input kiri dengan warna dari input atas. mirrored: - name: Pencat - description: Mencat keseluruhan bentuk dari input kiri dengan warna dari input + name: Pengecat + description: Mengecat keseluruhan bentuk dari input kiri dengan warna dari input atas. double: - name: Pencat (Ganda) - description: Mencat bentuk-bentuk dari input kiri dengan warna dari input atas. + name: Pengecat (Ganda) + description: Mengecat bentuk-bentuk dari input kiri dengan warna dari input atas. quad: - name: Pencat (Empat Bagian) - description: Allows you to color each quadrant of the shape individually. Only - slots with a truthy signal on the wires layer - will be painted! + name: Pengecat (Empat Bagian) + description: Memungkinkan anda untuk mengecat tiap kuadrannya masing - masing pada bentuk. Hanya menyambung dengan + sinyal yang benar pada lapisan kabel yang akan dicat! trash: default: name: Tong Sampah description: Menerima input dari semua sisi dan menghancurkannya. Selamanya. balancer: default: - name: Balancer - description: Multifunctional - Evenly distributes all inputs onto all outputs. + name: Penyeimbang + description: Multifungsional - Mendistribusikan seluruh input secara merata ke seluruh output. merger: - name: Merger (compact) - description: Merges two conveyor belts into one. + name: Penggabung Sederhana + description: Menggabungkan dua sabuk konveyor menjadi satu. merger-inverse: - name: Merger (compact) - description: Merges two conveyor belts into one. + name: Penggabung Sederhana + description: Menggabungkan dua sabuk konveyor menjadi satu. splitter: - name: Splitter (compact) - description: Splits one conveyor belt into two. + name: Pemisah Sederhana + description: Memisahkan satu sabuk konveyor menjadi dua. splitter-inverse: - name: Splitter (compact) - description: Splits one conveyor belt into two. + name: Pemisah Sederhana + description: Memisahkan satu sabuk konveyor menjadi dua. storage: default: - name: Storage - description: Stores excess items, up to a given capacity. Prioritizes the left - output and can be used as an overflow gate. + name: Tempat Penyimpanan + description: Menyumpan bentuk yang berlebihan, hingga kapasitas yang tertentu. Memprioritaskan output dari kiri wire_tunnel: default: - name: Wire Crossing - description: Allows to cross two wires without connecting them. + name: Penyebrangan Kabel + description: Memungkinkan untuk menyebrangkan 2 kabel tanpa menyambungkannya. constant_signal: default: - name: Constant Signal - description: Emits a constant signal, which can be either a shape, color or - boolean (1 / 0). + name: Sinyal Konstan + description: Mengeluarkan sinyal yang konstan, dapat berupa bentuk, warna atau boolean (1 atau 0). lever: default: - name: Switch - description: Can be toggled to emit a boolean signal (1 / 0) on the wires layer, - which can then be used to control for example an item filter. + name: Saklar + description: Dapat diubah untuk mengeluarkan sinyal boolean (1 atau 0) pada lapisan kabel, + yang bisa digunakan untuk mengontrol seperti penyaring. logic_gate: default: - name: AND Gate - description: Emits a boolean "1" if both inputs are truthy. (Truthy means shape, - color or boolean "1") + name: Gerbang AND + description: Mengeluarkan boolean "1" jika kedua input adalah benar. (Benar berarti sebuah bentuk, + warna atau boolean "1") not: - name: NOT Gate - description: Emits a boolean "1" if the input is not truthy. (Truthy means - shape, color or boolean "1") + name: Gerbang NOT + description: Mengeluarkan boolean "1" jika input adalah tidak benar. (Benar berarti sebuah bentuk, + warna atau boolean "1") xor: - name: XOR Gate - description: Emits a boolean "1" if one of the inputs is truthy, but not both. - (Truthy means shape, color or boolean "1") + name: Gerbang XOR + description: Mengeluarkan boolean "1" jika kedua input adalah benar, namun bukan keduanya. + (Benar berarti sebuah bentuk, warna atau boolean "1") or: - name: OR Gate - description: Emits a boolean "1" if one of the inputs is truthy. (Truthy means - shape, color or boolean "1") + name: Gerbang OR + description: Mengeluarkan boolean "1" jika satu input adalah benar. (Benar berarti sebuah bentuk, + warna atau boolean "1") transistor: default: name: Transistor - description: Forwards the bottom input if the side input is truthy (a shape, - color or "1"). + description: Melanjutkan sinyal dari input bawah jika input samping adalah benar (sebuah bentuk, + warna atau boolean "1") mirrored: name: Transistor - description: Forwards the bottom input if the side input is truthy (a shape, - color or "1"). + description: Melanjutkan sinyal dari input bawah jika input samping adalah benar (sebuah bentuk, + warna atau boolean "1") filter: default: name: Filter - description: Connect a signal to route all matching items to the top and the - remaining to the right. Can be controlled with boolean signals - too. + description: Hubungkan sebuah sinyal untuk merutekan semua benda yang cocok ke atas dan + sisanya ke kanan. Dapat juga dikontrol dengan sinyal boolean display: default: - name: Display - description: Connect a signal to show it on the display - It can be a shape, - color or boolean. + name: Layar + description: Hubungkan dengan sebuah sinyal untuk ditunjukkan pada layar - Dapat berupa bentuk, + warna atau boolean. reader: default: - name: Belt Reader - description: Allows to measure the average belt throughput. Outputs the last - read item on the wires layer (once unlocked). + name: Pembaca Sabuk Konveyor + description: Memungkinkan untuk mengukur rata-rata benda yang melewati sabuk konveyor. Mengeluarkan output benda terakhir + yang dilewati pada lapisan kabel (Setelah terbuka). analyzer: default: - name: Shape Analyzer - description: Analyzes the top right quadrant of the lowest layer of the shape - and returns its shape and color. + name: Penganalisa bentuk + description: Menganalisa perempat bentuk pada kanan atas dan lapisan terbawah + lalu mengeluarkan bentuk dan warnanya. comparator: default: - name: Compare - description: Returns boolean "1" if both signals are exactly equal. Can compare - shapes, items and booleans. + name: Pembanding + description: Mengeluarkan boolean "1" jika kedua sinya adalah sama. Dapat membandingkan + Bentuk, warna dan boolean. virtual_processor: default: - name: Virtual Cutter - description: Virtually cuts the shape into two halves. + name: Pemotong Virtual + description: Memotong bentuk secara virtual menjadi dua bagian. rotater: - name: Virtual Rotater - description: Virtually rotates the shape, both clockwise and counter-clockwise. + name: Pemutar Virtual + description: Memutar bentuk secara virtual, searah jarum jam dan tidak searah jarum jam. unstacker: - name: Virtual Unstacker - description: Virtually extracts the topmost layer to the right output and the - remaining ones to the left. + name: Pemisah Tumpukan Virtual + description: Memisahkan lapisan teratas secara virtual ke output kanan dan + sisanya ke output kiri. stacker: - name: Virtual Stacker - description: Virtually stacks the right shape onto the left. + name: Penumpuk Virtual + description: Menumpuk bentuk kanan ke bentuk kiri secara virtual. painter: - name: Virtual Painter - description: Virtually paints the shape from the bottom input with the shape on - the right input. + name: Pengecat Virtual + description: Mengecat bentuk dari input bawah dengan warna + dari input kanan. item_producer: default: - name: Item Producer - description: Available in sandbox mode only, outputs the given signal from the - wires layer on the regular layer. + name: Pembuat Bentuk + description: Hanya tersedia di dalam mode sandbox , Mengeluarkan sinyal yang diberikan dari + lapisan kabel ke lapisan biasa. storyRewards: reward_cutter_and_trash: title: Memotong Bentuk - desc: You just unlocked the cutter, which cuts shapes in half - from top to bottom regardless of its - orientation!

Be sure to get rid of the waste, or - otherwise it will clog and stall - For this purpose - I have given you the trash, which destroys - everything you put into it! + desc: Pemotong telah dibuka, yang dapat memotong bentuk menjadi dua + secara vertikal apapun + orientasinya!

Pastikan untuk membuang sisanya, jika + tidak ini dapat menghambat dan memperlambat - karena ini + anda diberikan Tong sampah, yang menghapus + semua yang anda masukkan! reward_rotater: title: Memutar desc: Pemutar telah dibuka! Ia memutar bentuk-bentuk searah jarum jam sebesar 90 derajat. reward_painter: - title: Pengecatan - desc: "Pencat telah dibuka – Ekstraksi beberapa warna (seperti - yang Anda lakukan dengan bentuk) dan kemudian kombinasikan dengan - bentuk di dalam pencat untuk mewarnai mereka!

Catatan: + title: Mengecat + desc: "Pengecat telah dibuka – Ekstraksi beberapa warna (seperti yang Anda lakukan dengan bentuk) dan kemudian kombinasikan dengan bentuk di dalam pengecat untuk mewarnai mereka!

Catatan: Apabila Anda buta warna, terdapat mode buta warna di dalam pengaturan!" reward_mixer: - title: Pencampuran Warna + title: Mencampur Warna desc: Pencampur telah dibuka – Kombinasikan dua warna menggunakan pencampuran aditif dengan bangunan ini! reward_stacker: - title: Penyusun + title: Menyusun desc: Anda sekarang dapat mengombinasikan bentuk-bentuk dengan penyusun! Kedua input akan dikombinasikan, dan apabila mereka dapat diletakan disebelah satu sama lain, mereka akan terpadukan. Apabila tidak dapat, input kanan akan diletakkan diatas input kiri! reward_splitter: - title: Pembagi/Penggabung - desc: You have unlocked a splitter variant of the - balancer - It accepts one input and splits them - into two! + title: Membagi + desc: Anda telah membuka varian pembagi dari + penyeimbang - Menerima satu input dan membaginya menjadi 2! reward_tunnel: title: Terowongan desc: Terowongan telah dibuka – Sekarang Anda dapat memindahkan - artikel-artikel melalui terowongan di bawah sabuk-sabuk konveyor dan + bentuk-bentuk melalui terowongan di bawah sabuk-sabuk konveyor dan bangungan-bangunan dengannya! reward_rotater_ccw: title: Memutar Berlawanan Arah Jarum Jam @@ -645,11 +633,8 @@ storyRewards: membangunnya, pilih pemutar dan tekan 'T' to memilih varian! reward_miner_chainable: - title: Merantai Ekstraktor - desc: "You have unlocked the chained extractor! It can - forward its resources to other extractors so you - can more efficiently extract resources!

PS: The old - extractor has been replaced in your toolbar now!" + title: Ekstraktor Merantai + desc: "Anda telah membuka Ekstraktor (Berantai)! Ia dapat mengoper sumber daya ke ekstraktor depannya sehingga anda dapat mengekstrak sumber daya denga lebih efisien!

NB: Ekstraktor yang lama sudah diganti pada toolbar anda sekarang!" reward_underground_belt_tier_2: title: Terowongan Tingkat II desc: Anda telah membuka varian baru terowongan - Ia memiliki @@ -662,23 +647,22 @@ storyRewards: daripada hanya dua bagian! reward_painter_double: title: Pengecatan Ganda - desc: Anda telah membuka varian dari pencat - Ia bekerja - seperti pencat biasa namun dapat memproses dua bentuk + desc: Anda telah membuka varian dari pengecat - Ia bekerja + seperti pengecat biasa namun dapat memproses dua bentuk sekaligus mengonsumsi hanya satu warna daripada dua! reward_storage: - title: Penyangga Penyimpanan - desc: You have unlocked the storage building - It allows you to - store items up to a given capacity!

It priorities the left - output, so you can also use it as an overflow gate! + title: Tempat Penyimpanan + desc: Anda telah membuka Tempat Penyimpanan - Ia memungkinkan anda untuk + menyimpan item hingga kapasitas tertentu!

Ia mengutamakan output kiri, sehingga anda dapat menggunakannya sebagai gerbang luapan! reward_freeplay: title: Permainan Bebas - desc: You did it! You unlocked the free-play mode! This means - that shapes are now randomly generated!

- Since the hub will require a throughput from now - on, I highly recommend to build a machine which automatically - delivers the requested shape!

The HUB outputs the requested - shape on the wires layer, so all you have to do is to analyze it and - automatically configure your factory based on that. + desc: Anda berhasil! Anda telah membuka mode permainan bebas! Ini artinya + bentuk-bentuk akan dibuat secara acak!

+ Karena pusat pangkalan akan membutuhkan penghasilan dari sekarang, + Saya sangat menyarankan untuk membangun mesin yang secara otomatis + mengirim bentuk yang diminta!

Pusat pangkalan mengeluarkan bentuk + yang diminta pada lapisan kabel, jadi yang harus anda lakukan adalah menganalisa dan + membangun pabrik secara otomatis berdasarkan itu. reward_blueprints: title: Cetak Biru desc: Anda sekarang dapat menyalin dan meletakkan bagian dari @@ -699,30 +683,26 @@ storyRewards: desc: Selamat! Omong-omong, lebih banyak konten telah direncanakan untuk versi penuh! reward_balancer: - title: Balancer - desc: The multifunctional balancer has been unlocked - It can - be used to build bigger factories by splitting and merging - items onto multiple belts!

+ title: Penyeimbang + desc: Penyeimbang yang multifungsional telah terbuka - Ia dapat + digunakan untuk membuat pabrik yang lebih besar dengan cara + memisahkan atau menggabungkan item ke beberapa sabuk konveyor!

reward_merger: - title: Compact Merger - desc: You have unlocked a merger variant of the - balancer - It accepts two inputs and merges them - into one belt! + title: Penggabung Sederhana + desc: Anda telah membuka varianpenggabung dari + penyeimbang - Ia menerima dua input dan menggabungkannya dalam satu sabuk konveyor! reward_belt_reader: - title: Belt reader - desc: You have now unlocked the belt reader! It allows you to - measure the throughput of a belt.

And wait until you unlock - wires - then it gets really useful! + title: Pembaca Sabuk Konveyor + desc: Anda telah membuka pembaca sabuk konveyor! Ini memungkinkan anda untuk + mengukur penghasilan dalam sebuah sabuk konveyor.

Dan tunggu sampai anda membuka + kabel - maka ini akan sangat berguna! reward_rotater_180: - title: Rotater (180 degrees) - desc: You just unlocked the 180 degress rotater! - It allows - you to rotate a shape by 180 degress (Surprise! :D) + title: Pemutar (180 derajat) + desc: Anda telah membuka pemutar 180 derajat! - Ini memungkinkan + anda untuk memutar bentuk dalam 180 derajat (Kejutan! :D) reward_display: - title: Display - desc: "You have unlocked the Display - Connect a signal on the - wires layer to visualize it!

PS: Did you notice the belt - reader and storage output their last read item? Try showing it on a - display!" + title: Layar + desc: "Anda baru saja membuka Layar - Hubungkan sebuah sinyal dalam lapisan kabel untuk memvisualisasikannya!

NB: Apakah anda memperhatikan pembaca sabuk dan penyimpanan mengeluarkan item bacaan terakhir? Coba tampilkan pada layar!" reward_constant_signal: title: Constant Signal desc: You unlocked the constant signal building on the wires @@ -830,7 +810,7 @@ settings: fullscreen: title: Layar penuh description: Direkomendasikan untuk bermain dengan layar penuh untuk mendapatkan - pengalaman terbaik. Hanya tersedia dalam versi penuh. + pengalaman terbaik. Hanya tersedia dalam versi lengkap. soundsMuted: title: Bisukan suara description: Apabila diaktifkan, membisukan semua efek suara. @@ -966,7 +946,7 @@ keybindings: rotater: Pemutar stacker: Penyusun mixer: Pencampur Warna - painter: Pencat + painter: Pengecat trash: Tong Sampah wire: Kawat Energi pipette: Pipet diff --git a/translations/base-it.yaml b/translations/base-it.yaml index d1c61c43..3864c2a9 100644 --- a/translations/base-it.yaml +++ b/translations/base-it.yaml @@ -204,11 +204,6 @@ dialogs: renameSavegame: title: Rinomina salvataggio. desc: Qui puoi cambiare il nome del salvataggio. - entityWarning: - title: Attento alle prestazioni - desc: Hai posizionato molti edifici, questo messaggio serve a ricordarti che il - gioco non può gestire una quantità infinita di edifici. Quindi cerca - di mantenere le tue fabbriche compatte! ingame: keybindingsOverlay: moveMap: Sposta @@ -254,27 +249,6 @@ ingame: title: Miglioramenti buttonUnlock: Sblocca tier: Grado - tierLabels: - - I - - II - - III - - IV - - V - - VI - - VII - - VIII - - IX - - X - - XI - - XII - - XIII - - XIV - - XV - - XVI - - XVII - - XVIII - - XIX - - XX maximumLevel: GRADO MASSIMO (Velocità x) statistics: title: Statistiche @@ -298,10 +272,6 @@ ingame: playtime: Tempo di gioco buildingsPlaced: Edifici beltsPlaced: Nastri - buttons: - continue: Continua - settings: Impostazioni - menu: Torna al menù tutorialHints: title: Serve aiuto? showHint: Mostra indizio @@ -914,6 +884,14 @@ settings: description: Consente di spostare la mappa spostando il mouse verso il bordo dello schermo. La velocità dipende dall'impostazione Velocità di movimento. + zoomToCursor: + title: Zoom towards Cursor + description: If activated the zoom will happen in the direction of your mouse + position, otherwise in the middle of the screen. + mapResourcesScale: + title: Map Resources Size + description: Controls the size of the shapes on the map overview (when zooming + out). rangeSliderPercentage: % keybindings: title: Comandi diff --git a/translations/base-ja.yaml b/translations/base-ja.yaml index 67372906..f4b52b30 100644 --- a/translations/base-ja.yaml +++ b/translations/base-ja.yaml @@ -1,4 +1,3 @@ ---- steamPage: shortText: shapez.ioは無限のマップ内で様々な"形"を資源とし、段々と複雑になっていく形の作成や合成の自動化を目指して工場を構築するゲームです。 discordLinkShort: 公式Discord @@ -20,7 +19,6 @@ steamPage: - セーブ数の上限がなくなります。 - マップマーカー数の上限がなくなります。 - 私をサポートできる!❤️ - title_future: 計画中の要素 planned: - ブループリント (スタンドアロン版専用) @@ -31,12 +29,9 @@ steamPage: - サンドボックスモード - ……あともっとたくさんの要素! title_open_source: このゲームはオープンソースです! - - text_open_source: >- + text_open_source: |- 誰でも参加することができます。私はコミュニティに積極的に参加し、すべての提案を確認し、可能な場合はフィードバックしようとしています。 - 完全なロードマップについては、Trello boardを確認してください! - title_links: リンク links: discord: 公式Discord @@ -44,12 +39,11 @@ steamPage: subreddit: Subreddit source_code: ソースコード(GitHub) translate: 翻訳を助けてください! - global: loading: ロード中 error: エラー thousandsDivider: "," - decimalSeparator: "." + decimalSeparator: . suffix: thousands: k millions: M @@ -78,8 +72,7 @@ global: space: SPACE demoBanners: title: デモ版 - intro: >- - スタンドアローン版を手に入れ、すべての機能をアンロックしましょう! + intro: スタンドアローン版を手に入れ、すべての機能をアンロックしましょう! mainMenu: play: プレイ continue: 続きから @@ -91,9 +84,7 @@ mainMenu: discordLink: 公式Discordサーバー helpTranslate: 翻訳を助けてください! madeBy: によって作られました - browserWarning: >- - このゲームはお使いのブラウザでは速度が落ちることがあります。スタンドアローン版を入手するか、Chromeでプレイすることでこの問題は避けられます。 - + browserWarning: このゲームはお使いのブラウザでは速度が落ちることがあります。スタンドアローン版を入手するか、Chromeでプレイすることでこの問題は避けられます。 savegameLevel: レベル savegameLevelUnknown: 不明なレベル savegameUnnamed: 無名のデータ @@ -110,116 +101,81 @@ dialogs: viewUpdate: アップデートを見る showUpgrades: アップグレード表示 showKeybindings: キー設定表示 - importSavegameError: title: インポートエラー - text: >- - セーブデータのインポートに失敗しました: - + text: "セーブデータのインポートに失敗しました:" importSavegameSuccess: title: セーブデータのインポートに成功 text: セーブデータをインポートしました。 - gameLoadFailure: title: ゲームが壊れています - text: >- - セーブデータのロードに失敗しました: - + text: "セーブデータのロードに失敗しました:" confirmSavegameDelete: title: 削除確認 - text: >- - 本当に削除しますか?

- レベル: ''

- この操作は取り消しできません! - + text: "本当に削除しますか?

レベル: ''

+ この操作は取り消しできません!" savegameDeletionError: title: 削除に失敗 - text: >- - セーブデータの削除に失敗しました: - + text: "セーブデータの削除に失敗しました:" restartRequired: title: 再起動が必要 text: 設定を反映するには再起動が必要です - editKeybinding: title: キー設定の変更 desc: 割り当てるキーかマウスボタンを押してください。ESCでキャンセルします。 - resetKeybindingsConfirmation: title: キー設定のリセット desc: すべてのキー設定を初期値に戻します。実行する前によく確認してください。 - keybindingsResetOk: title: キー設定のリセット desc: キー設定を初期値に設定しました! - featureRestriction: title: デモ版 desc: アクセスした要素 () はデモ版では利用できません。スタンドアローン版の入手をご検討ください! - oneSavegameLimit: title: セーブデータ制限 desc: デモ版ではひとつのセーブデータのみ保持できます。既存のデータを削除するか、スタンドアローン版の入手をご検討ください! - updateSummary: title: 新アップデート! - desc: >- - 前回からの変更点: - + desc: "前回からの変更点:" upgradesIntroduction: title: アップグレード解除 desc: すべての納品された形はアップグレードの解除のためにカウントされています。作った生産ラインを削除しないようにしてください! アップグレードタブは画面の右上から確認できます。 - massDeleteConfirm: title: 削除確認 desc: 多数の建造物を削除しようとしています! ( 個の選択) 続行しますか? - massCutConfirm: title: カット確認 desc: 多数の建造物をカットしようとしています! ( 個の選択) 続行しますか? - massCutInsufficientConfirm: title: カット確認 desc: 設置コストが不足しています! 続行しますか? - blueprintsNotUnlocked: title: 未解除 desc: レベル12をクリアしてブループリント機能を解除してください! - keybindingsIntroduction: title: 便利なキー設定 - desc: >- - このゲームには大規模な工場の構築をスムーズにするため、沢山のキー設定があります。 - 以下に数例を示します。詳細はキー設定を確認してください

- CTRL + ドラッグ: 削除範囲を指定
- SHIFT: 押し続けると1種の建造物を連続配置
- ALT: 設置されたベルトの方向を逆転させる
- + desc: "このゲームには大規模な工場の構築をスムーズにするため、沢山のキー設定があります。 + 以下に数例を示します。詳細はキー設定を確認してください

CTRL + ドラッグ: 削除範囲を指定
SHIFT: 押し続けると1種の建造物を連続配置
ALT: 設置されたベルトの方向を逆転させる
" createMarker: title: マーカーを設置 titleEdit: マーカーを編集 desc: わかりやすい名前をつけてください。形を表す短いキーを含めることもできます。(ここから生成できます) editSignal: title: 信号を設定 - descItems: >- - プリセットを選択: + descItems: "プリセットを選択:" descShortKey: もしくは形を表す短いキーを入力してください。 (ここから生成できます) - markerDemoLimit: desc: デモ版ではマーカー設置は2つまでに制限されています。スタンドアローン版は無制限です! - exportScreenshotWarning: title: スクリーンショット出力 desc: スクリーンショット出力を実行します。この処理は工場の全体像があまりに大きいと、 ゲームが遅くなったりクラッシュしてしまう可能性があります! - renameSavegame: title: セーブデータの名前を変更 desc: セーブデータの名前を変更することができます - - entityWarning: - title: パフォーマンスの警告 - desc: あなたは沢山の工場を配置しましたが、このゲームは無限の建物を処理できるわけではありません。これは友好的なリマインダですが、より工場をコンパクトにすることに挑戦してみてください。 - ingame: keybindingsOverlay: moveMap: マップ移動 @@ -241,7 +197,6 @@ ingame: clearSelection: 選択範囲をクリア pipette: ピペット switchLayers: レイヤーを変更 - colors: red: 赤 green: 緑 @@ -252,11 +207,9 @@ ingame: white: 白 black: 黒 uncolored: 無色 - buildingPlacement: cycleBuildingVariants: キーを押して変更 hotkeyLabel: "ホットキー: " - infoTexts: speed: スピード range: レンジ @@ -265,27 +218,20 @@ ingame: itemsPerSecond: アイテム / 秒 itemsPerSecondDouble: (x2) tiles: タイル - levelCompleteNotification: levelTitle: レベル completed: 完了 unlockText: を解除! buttonNextLevel: 次のレベル - notifications: newUpgrade: 新しいアップグレードが利用可能です! gameSaved: ゲームをセーブしました。 freeplayLevelComplete: レベル をクリアしました! - shop: title: アップグレード buttonUnlock: アップグレード tier: 第 段階 - - tierLabels: - [I, II, III, IV, V, VI, VII, VIII, IX, X, XI, XII, XIII, XIV, XV, XVI, XVII, XVIII, XIX, XX] maximumLevel: 最大レベル (スピード x) - statistics: title: 統計情報 dataSources: @@ -299,60 +245,44 @@ ingame: title: 納品済 description: 中央の建造物に納品された形の総数です。 noShapesProduced: まだ形が生産されていません。 - shapesDisplayUnits: second: / 秒 minute: / 分 hour: / 時間 - settingsMenu: playtime: プレイ時間 - buildingsPlaced: 建造物 beltsPlaced: ベルト - - buttons: - continue: 続ける - settings: 設定 - menu: メニューに戻る - tutorialHints: title: ヒントが必要ですか? showHint: ヒントを見る hideHint: 閉じる - blueprintPlacer: cost: コスト - waypoints: waypoints: マーカー hub: HUB - description: >- - マーカーを左クリックでその場所にジャンプ、右クリックで削除します。

+ description: マーカーを左クリックでその場所にジャンプ、右クリックで削除します。

キーを押すことで現在地にマーカーを設置します。選択した位置で右クリックすることでもマーカー設置できます。 creationSuccessNotification: マーカーを設置しました shapeViewer: title: レイヤー empty: 空 copyKey: キーをコピー - interactiveTutorial: title: チュートリアル hints: 1_1_extractor: 抽出機円の形 の上において抽出しましょう! - 1_2_conveyor: >- - 抽出機を コンベアベルト でHUBまで繋げましょう!

Tip: マウスのドラッグ でベルトを引けます。 - + 1_2_conveyor: "抽出機を コンベアベルト でHUBまで繋げましょう!

Tip: + マウスのドラッグ でベルトを引けます。" 1_3_expand: "このゲームは放置系のゲームではありません! もっと早く要件を満たせるように、追加の抽出機とベルトを設置しましょう。

Tip: SHIFT キーを押し続けると抽出機を連続配置できます。Rキーで設置方向を回転できます。" - connectedMiners: one_miner: 1個の抽出機 n_miners: 個の抽出機 limited_items: に制限されます - watermark: title: デモバージョン desc: Steamバージョンの特典を確認するには、ここをクリックしてください! @@ -406,25 +336,25 @@ buildings: endOfDemo: お試し終了 belt: default: - name: &belt コンベアベルト + name: コンベアベルト description: アイテムを輸送します。マウスドラッグで連続配置できます。 miner: default: - name: &miner 抽出機 + name: 抽出機 description: 形や色の上に設置することで抽出できます。 chainable: name: 連鎖抽出機 description: 形や色の上に設置することで抽出できます。連鎖設置可能です。 underground_belt: default: - name: &underground_belt トンネル + name: トンネル description: 建造物や他のベルトの地下を通してベルトを配置できます。 tier2: name: トンネル レベルII description: 建造物や他のベルトの地下を通してベルトを配置できます。 balancer: default: - name: &balancer 分配機/合流機 + name: 分配機/合流機 description: 多機能 - すべての入力をすべての出力に均等に分配します。 merger: name: 合流機(コンパクト) @@ -440,14 +370,14 @@ buildings: description: 1つの入力を2つの出力に分配します。 cutter: default: - name: &cutter 切断機 + name: 切断機 description: 形を上下の直線で切断し、双方を出力します。もしひとつの出力しか使わない場合、他の出力を破棄しないと出力が詰まって停止することに注意してください! quad: name: 切断機 (四分割) description: 形を四分割します。もしひとつの出力しか使わない場合、他の出力を破棄しないと出力が詰まって停止することに注意してください! rotater: default: - name: &rotater 回転機 + name: 回転機 description: 形を時計回り方向に90度回転します。 ccw: name: 回転機 (逆) @@ -457,56 +387,53 @@ buildings: description: 形を180度回転します。 stacker: default: - name: &stacker 積層機 + name: 積層機 description: 入力アイテムを積み重ねます。もしうまく統合できなかった場合は、右の入力アイテムを左の入力アイテムの上に重ねます。 mixer: default: - name: &mixer 混合機 + name: 混合機 description: 2つの色を加算混合で混ぜ合わせます。 painter: default: - name: &painter 着色機 - description: &painter_desc 左から入力された形の全体を、上から入力された色で着色します。 + name: 着色機 + description: 左から入力された形の全体を、上から入力された色で着色します。 mirrored: - name: *painter + name: 着色機 description: 左から入力された形の全体を、下から入力された色で着色します。 double: name: 着色機 (ダブル) description: 左から入力された形を、上から入力された色で着色します。 quad: name: 着色機 (四分割) - description: 入力された形を四分割づつ別の色で塗り分けられます。 真らしい信号が流れているスロットのみがペイントされます! - + description: 入力された形を四分割づつ別の色で塗り分けられます。 + 真らしい信号が流れているスロットのみがペイントされます! trash: default: - name: &trash ゴミ箱 + name: ゴミ箱 description: すべての辺からの入力を破棄します。永遠に。 storage: default: - name: &storage ストレージ - description: >- - 所定の容量まで、アイテムを蓄えることができます。左側の出力が優先され、オーバーフローゲートとして利用できます。 + name: ストレージ + description: 所定の容量まで、アイテムを蓄えることができます。左側の出力が優先され、オーバーフローゲートとして利用できます。 wire: default: - name: &wire ワイヤ - description: &wire_desc 形状、色、真偽値(1/0)の信号を運ぶことができます。異なる色のワイヤは互いに接続しません。 + name: ワイヤ + description: 形状、色、真偽値(1/0)の信号を運ぶことができます。異なる色のワイヤは互いに接続しません。 second: - name: *wire - description: *wire_desc - + name: ワイヤ + description: 形状、色、真偽値(1/0)の信号を運ぶことができます。異なる色のワイヤは互いに接続しません。 wire_tunnel: default: - name: &wire_tunnel 交差ワイヤ + name: 交差ワイヤ description: 2本のワイヤを接続させることなく交差させることができます。 constant_signal: default: - name: &constant_signal 定値信号 + name: 定値信号 description: 常に同じ値を出力します。形状、色、真偽値(1/0)が使用できます。 lever: default: - name: &lever スイッチ - description: >- - ワイヤ上に真偽値(1/0)を出力できます。スイッチを押すことで1と0を切り替えることができ、 + name: スイッチ + description: ワイヤ上に真偽値(1/0)を出力できます。スイッチを押すことで1と0を切り替えることができ、 それを利用してアイテムフィルタ等を制御できます。 logic_gate: default: @@ -523,42 +450,34 @@ buildings: description: 両方の入力のうち少なくとも片方が真らしいなら、真偽値"1"を出力します。(真らしいとは、形状、色、または真偽値"1"のことです) transistor: default: - name: &transistor トランジスタ - description: &transistor_desc 横からの入力が真らしいなら、下からの入力を通過させます。(真らしいとは、形状、色、または真偽値"1"のことです) + name: トランジスタ + description: 横からの入力が真らしいなら、下からの入力を通過させます。(真らしいとは、形状、色、または真偽値"1"のことです) mirrored: - name: *transistor - description: *transistor_desc + name: トランジスタ + description: 横からの入力が真らしいなら、下からの入力を通過させます。(真らしいとは、形状、色、または真偽値"1"のことです) filter: default: - name: &filter アイテムフィルタ - description: >- - 入力された信号と一致するアイテムを上部に通過させ、残りを右側に通過させます。 - 真偽値(1/0)でも制御できます。 + name: アイテムフィルタ + description: 入力された信号と一致するアイテムを上部に通過させ、残りを右側に通過させます。 真偽値(1/0)でも制御できます。 display: default: - name: &display ディスプレイ - description: >- - 入力された信号をディスプレイに表示します。 - 形状、色、真偽値のいずれでも可能です。 + name: ディスプレイ + description: 入力された信号をディスプレイに表示します。 形状、色、真偽値のいずれでも可能です。 reader: default: - name: &reader ベルトリーダ - description: >- - 平均スループットを計測できます。 アンロック後は、 - 最後に通過したアイテムの情報を出力します。 + name: ベルトリーダ + description: 平均スループットを計測できます。 アンロック後は、 最後に通過したアイテムの情報を出力します。 analyzer: default: - name: &analyzer 形状解析機 + name: 形状解析機 description: 形状の最下層の右上の形状を分析し、形状と色に分解します。 comparator: default: - name: &comparator 比較機 - description: >- - 両方の信号が完全に一致している場合、真偽値"1"を出力します。 - 形状、色、真偽値を比較できます。 + name: 比較機 + description: 両方の信号が完全に一致している場合、真偽値"1"を出力します。 形状、色、真偽値を比較できます。 virtual_processor: default: - name: &virtual_processor 仮想切断機 + name: 仮想切断機 description: 形状の信号を2つに切断できます。 rotater: name: 仮想回転機 @@ -576,140 +495,130 @@ buildings: default: name: なんでも抽出機 description: サンドボックスモードでのみ使用可能で、ワイヤレイヤーで与えられた信号の形状を通常レイヤーに出力します。 - storyRewards: reward_cutter_and_trash: title: 形の切断 - desc: 切断機が利用可能になりました。これは入力された形を、向きを考慮せず上下の直線で半分に切断します。

利用しない側の出力に注意しましょう。破棄するなどをしない限り詰まって停止してしまいます - このためにゴミ箱も用意しました。入力アイテムをすべて破棄できます! + desc: 切断機が利用可能になりました。これは入力された形を、向きを考慮せず上下の直線で半分に切断します。

利用しない側の出力に注意しましょう。破棄するなどをしない限り詰まって停止してしまいます + - このためにゴミ箱も用意しました。入力アイテムをすべて破棄できます! reward_rotater: title: 回転 desc: 回転機が利用可能になりました。形を時計回り方向に90度回転させます。 reward_painter: title: 着色 - desc: >- - 着色機が利用可能になりました。(今まで形状でやってきた方法で)色を抽出し、 + desc: "着色機が利用可能になりました。(今まで形状でやってきた方法で)色を抽出し、 形状と合成することで着色します!

追伸: もし色覚特性をお持ちでしたら、 - 設定に色覚特性モードがあります! + 設定に色覚特性モードがあります!" reward_mixer: title: 色の混合 - desc: 混合機が利用可能になりました。 - この建造物は2つの色を加算混合で混ぜ合わせます。 + desc: 混合機が利用可能になりました。 - + この建造物は2つの色を加算混合で混ぜ合わせます。 reward_stacker: title: 積層機 desc: 積層機で形を組み合わせ可能になりました。双方の入力を組み合わせ、もし連続した形になっていればそれらは融合してひとつになります! もしできなかった場合は、左の入力の上に右の入力が重なります。 reward_balancer: title: 分配機/合流機 - desc: >- - 多機能な分配機/合流機が利用可能になりました。 - より大規模な工場を構築するため、複数のベルト間でアイテムを合流、分配できます!

+ desc: The multifunctional balancer has been unlocked - It can + be used to build bigger factories by splitting and merging + items onto multiple belts! reward_tunnel: title: トンネル desc: トンネルが利用可能になりました。 - 他のベルトや建造物の地下を通してベルトが配置可能です! reward_rotater_ccw: title: 反時計回りの回転 - desc: 回転機のバリエーションが利用可能になりました。 - 反時計回りの回転ができるようになります! 回転機を選択し、'T'キーを押すことで方向の切り替えができます + desc: 回転機のバリエーションが利用可能になりました。 - + 反時計回りの回転ができるようになります! 回転機を選択し、'T'キーを押すことで方向の切り替えができます reward_miner_chainable: title: 連鎖抽出機 - desc: >- - 連鎖抽出機が利用可能になりました。他の抽出機に出力を渡すことができるので、資源の抽出がより効率的になります! - 補足: ツールバーの旧い抽出機が置き換えられました! + desc: "You have unlocked the chained extractor! It can + forward its resources to other extractors so you + can more efficiently extract resources!

PS: The old + extractor has been replaced in your toolbar now!" reward_underground_belt_tier_2: title: トンネル レベルII - desc: トンネルのバリエーションが利用可能になりました。 - 距離拡張版が追加され、以前のものと組み合わせて目的に応じて利用することができます! + desc: トンネルのバリエーションが利用可能になりました。 - + 距離拡張版が追加され、以前のものと組み合わせて目的に応じて利用することができます! reward_merger: title: コンパクトな合流機 - desc: >- - 合流機コンパクトバージョンが利用可能になりました! - 2つの入力を1つの出力に合流させます! + desc: 合流機コンパクトバージョンが利用可能になりました! - + 2つの入力を1つの出力に合流させます! reward_splitter: title: コンパクトな分配機 - desc: >- - 分配機コンパクトバージョンが利用可能になりました! - 1つの入力を2つの出力に分配します! + desc: 分配機コンパクトバージョンが利用可能になりました! - + 1つの入力を2つの出力に分配します! reward_belt_reader: title: ベルトリーダ - desc: >- - ベルトリーダが利用可能になりました!ベルトのスループットを計測できます。

ワイヤーのロックが解除されれば、より便利になります! + desc: ベルトリーダが利用可能になりました!ベルトのスループットを計測できます。

ワイヤーのロックが解除されれば、より便利になります! reward_cutter_quad: title: 四分割 - desc: >- - 切断機のバリエーションが利用可能になりました。 - + desc: 切断機のバリエーションが利用可能になりました。 - 上下の二分割ではなく、四分割に切断できます! reward_painter_double: title: 着色機 (ダブル) - desc: >- - 着色機のバリエーションが利用可能になりました。 - + desc: 着色機のバリエーションが利用可能になりました。 - 通常の着色機と同様に機能しますが、ひとつの色の消費で一度に2つの形を着色処理できます! reward_storage: title: 余剰の貯蓄 - desc: >- - ゴミ箱のバリエーションが利用可能になりました。 - 容量上限までアイテムを格納することができます!

+ desc: ゴミ箱のバリエーションが利用可能になりました。 - 容量上限までアイテムを格納することができます!

左側の出力を優先するため、オーバーフローゲートとしても使用できます! - reward_blueprints: title: ブループリント desc: 工場の建造物のコピー&ペーストが利用可能になりました! 範囲選択(CTRLキーを押したままマウスドラッグ)した状態で、'C'キーを押すことでコピーができます。

ペーストはタダではありません。ブループリントの形を生産することで可能になります!(たった今納品したものです) reward_rotater_180: title: 180度の回転 - desc: >- - 回転機のバリエーションが利用可能になりました! - 180度の回転ができるようになります!(サプライズ! :D) + desc: 回転機のバリエーションが利用可能になりました! 180度の回転ができるようになります!(サプライズ! :D) reward_wires_painter_and_levers: title: ワイヤ&着色機(四分割) - desc: >- - ワイヤレイヤが利用可能になりました!: 通常レイヤとは別のレイヤーであり、異なる機能が使用できます!

- 最初に、着色機(四分割)が利用可能です。着色したいスロットを、ワイヤレイヤで接続します。

- ワイヤレイヤに切り替えるには、Eを押します。 + desc: "ワイヤレイヤが利用可能になりました!: + 通常レイヤとは別のレイヤーであり、異なる機能が使用できます!

+ 最初に、着色機(四分割)が利用可能です。着色したいスロットを、ワイヤレイヤで接続します。
ワイヤレイヤに切り替えるには、Eを押します。" reward_filter: title: アイテムフィルタ - desc: >- - アイテムフィルタが利用可能になりました! ワイヤレイヤの信号と一致するかどうかに応じて、 + desc: アイテムフィルタが利用可能になりました! ワイヤレイヤの信号と一致するかどうかに応じて、 アイテムを上部または右側の出力に分離します。

真偽値(0/1)信号を利用することで どんなアイテムでも通過させるか、または通過させないかを選ぶこともできます。 reward_display: title: ディスプレイ - desc: >- - ディスプレイが利用可能になりました! ワイヤレイヤで信号を接続することで、その内容を視認することができます! - 補足: ベルトリーダーとストレージが最後に通過したアイテムを出力していることに気づきましたか?ディスプレイに表示するのを試してみてください! + desc: "You have unlocked the Display - Connect a signal on the + wires layer to visualize it!

PS: Did you notice the belt + reader and storage output their last read item? Try showing it on a + display!" reward_constant_signal: title: 定数信号 - desc: >- - 定数信号が利用可能になりました! - これは、例えばアイテムフィルタに接続する場合に便利です。 - 定数信号は、形状、または真偽値(1/0)を出力できます。 + desc: You unlocked the constant signal building on the wires + layer! This is useful to connect it to item filters + for example.

The constant signal can emit a + shape, color or + boolean (1 or 0). reward_logic_gates: title: 論理ゲート - desc: >- - 論理ゲートが利用可能になりました! 興奮するほどでは - ありませんが、これらは非常に優秀です!

- AND, OR, XOR and NOTを計算できます!

ボーナスとしてトランジスタも追加しました! + desc: 論理ゲートが利用可能になりました! 興奮するほどでは ありませんが、これらは非常に優秀です!

+ AND, OR, XOR and + NOTを計算できます!

ボーナスとしてトランジスタも追加しました! reward_virtual_processing: title: 仮想処理 - desc: >- - 形状処理をシミュレートできる新しい部品を沢山追加しました!

+ desc: 形状処理をシミュレートできる新しい部品を沢山追加しました!

ワイヤレイヤで切断、回転、積層をシミュレートできるようになりました。 - これからゲームを続けるにあたり、3つの方法があります:

- - 完全自動化された機械を構築し、HUBが要求する形状を作成する(試してみることをオススメします!)。

- - ワイヤでイカしたものを作る。

- - 今までのように工場を建設する。

- いずれにしても、楽しんでください! - + これからゲームを続けるにあたり、3つの方法があります:

- + 完全自動化された機械を構築し、HUBが要求する形状を作成する(試してみることをオススメします!)。

+ - ワイヤでイカしたものを作る。

- 今までのように工場を建設する。

いずれにしても、楽しんでください! no_reward: title: 次のレベル - desc: >- - このレベルには報酬はありません。次にはあるでしょう!

補足: すでに作った生産ラインは削除しないようにしましょう。 - - 生産された形はすべて、後にアップグレードの解除のために必要になります! + desc: "このレベルには報酬はありません。次にはあるでしょう!

補足: すでに作った生産ラインは削除しないようにしましょう。 - + 生産された形はすべて、後にアップグレードの解除のために必要になりま\ + す!" no_reward_freeplay: title: 次のレベル - desc: >- - おめでとうございます! + desc: おめでとうございます! reward_freeplay: title: フリープレイ - desc: >- - やりましたね! フリープレイモードが利用可能になりました。 - これからは納品すべき形はランダムに生成されます!

+ desc: やりましたね! フリープレイモードが利用可能になりました。 - + これからは納品すべき形はランダムに生成されます!

今後、ハブにはスループットが必要になるため、要求する形状を自動的に納品するマシンを構築することを強くお勧めします!

ハブは要求する形状をワイヤー層に出力するので、それを分析し自動的に調整する工場を作成するだけです。 - reward_demo_end: title: お試し終了 - desc: >- - デモ版の最後に到達しました! + desc: デモ版の最後に到達しました! settings: title: 設定 categories: @@ -726,8 +635,7 @@ settings: labels: uiScale: title: 画面表示サイズ - description: >- - ユーザーインターフェイスのサイズを変更します。解像度をベースに調整されますが、この設定でそれを変更できます。 + description: ユーザーインターフェイスのサイズを変更します。解像度をベースに調整されますが、この設定でそれを変更できます。 scales: super_small: 極小 small: 小 @@ -736,9 +644,7 @@ settings: huge: 極大 autosaveInterval: title: オートセーブ間隔 - description: >- - ゲームが自動的にセーブされる頻度を設定します。無効化することも可能です。 - + description: ゲームが自動的にセーブされる頻度を設定します。無効化することも可能です。 intervals: one_minute: 1分 two_minutes: 2分 @@ -783,7 +689,6 @@ settings: soundVolume: title: 音量(SE) description: 効果音の音量を設定してください。 - musicVolume: title: 音量(BGM) description: 音楽の音量を設定してください。 @@ -802,7 +707,6 @@ settings: offerHints: title: ヒントとチュートリアル description: ゲーム中、ヒントとチュートリアルを表示します。レベルごとに不要なUI要素も非表示になり、ゲームに集中しやすくなります。 - enableTunnelSmartplace: title: スマートトンネル description: 有効にすると、トンネルを設置した際に不要なベルトを自動的に除去します。 @@ -818,30 +722,23 @@ settings: description: レートのみを表示することで、建造物の情報ボックスを短くします。選択しない場合は、説明文と画像も表示されます。 disableCutDeleteWarnings: title: カット/削除の警告を無効化 - description: >- - 100個以上のエンティティをカット/削除する際に表示される警告ダイアログを無効にします。 - + description: 100個以上のエンティティをカット/削除する際に表示される警告ダイアログを無効にします。 lowQualityMapResources: title: リソース表示の簡易化 - description: ズームインしたときのリソース表示を簡素化して、パフォーマンスを向上させます。 - 外見もすっきりしますので、ぜひお試しください! + description: ズームインしたときのリソース表示を簡素化して、パフォーマンスを向上させます。 外見もすっきりしますので、ぜひお試しください! disableTileGrid: title: グリッドの無効化 - description: 配置用のグリッドを無効にして、パフォーマンスを向上させます。 - これにより、ゲームの見た目もすっきりします。 + description: 配置用のグリッドを無効にして、パフォーマンスを向上させます。 これにより、ゲームの見た目もすっきりします。 clearCursorOnDeleteWhilePlacing: title: 右クリックで配置をキャンセル - description: - デフォルトで有効です。建物を設置しているときに右クリックすると、選択中の建物がキャンセルされます。 + description: デフォルトで有効です。建物を設置しているときに右クリックすると、選択中の建物がキャンセルされます。 無効にすると、建物の設置中に右クリックで建物を削除できます。 lowQualityTextures: title: 低品質のテクスチャ(視認性低下) - description: 低品質のテクスチャを使用してパフォーマンスを向上させます。 - ゲームの視認性が非常に低下します! + description: 低品質のテクスチャを使用してパフォーマンスを向上させます。 ゲームの視認性が非常に低下します! displayChunkBorders: title: チャンクの境界線を表示する - description: このゲームでは16x16タイルのチャンクで構成されています。 - 有効にすると、チャンクの境界線が表示されます。 + description: このゲームでは16x16タイルのチャンクで構成されています。 有効にすると、チャンクの境界線が表示されます。 pickMinerOnPatch: title: 資源で抽出機を選択 description: デフォルトで有効です。資源の上でスポイトを使用すると、抽出機を選択します。 @@ -852,14 +749,18 @@ settings: enableMousePan: title: マウスで画面を移動 description: 画面の端にカーソルを合わせることで移動できます。移動速度を設定することで、速度を変更できます。 - + zoomToCursor: + title: Zoom towards Cursor + description: If activated the zoom will happen in the direction of your mouse + position, otherwise in the middle of the screen. + mapResourcesScale: + title: Map Resources Size + description: Controls the size of the shapes on the map overview (when zooming + out). keybindings: title: キー設定 - hint: >- - Tip: CTRL, SHIFT, ALTを利用するようにしてください。これらはそれぞれ建造物配置の際の機能があります。 - + hint: "Tip: CTRL, SHIFT, ALTを利用するようにしてください。これらはそれぞれ建造物配置の際の機能があります。" resetKeybindings: キー設定をリセット - categoryLabels: general: アプリケーション ingame: ゲーム @@ -877,73 +778,62 @@ keybindings: mapMoveLeft: 左移動 mapMoveFaster: より速く移動 centerMap: マップ中央移動 - mapZoomIn: ズームイン mapZoomOut: ズームアウト createMarker: マーカー設置 - menuOpenShop: アップグレード menuOpenStats: 統計情報 menuClose: メニューを閉じる - toggleHud: HUD切り替え toggleFPSInfo: FPS、デバッグ情報表示切り替え switchLayers: レイヤを変更 exportScreenshot: 工場の全体像を画像出力 - - # --- Do not translate the values in this section - belt: *belt - balancer: *balancer - underground_belt: *underground_belt - miner: *miner - cutter: *cutter - rotater: *rotater - stacker: *stacker - mixer: *mixer - painter: *painter - trash: *trash - storage: *storage - wire: *wire - constant_signal: *constant_signal + belt: コンベアベルト + balancer: 分配機/合流機 + underground_belt: トンネル + miner: 抽出機 + cutter: 切断機 + rotater: 回転機 + stacker: 積層機 + mixer: 混合機 + painter: 着色機 + trash: ゴミ箱 + storage: ストレージ + wire: ワイヤ + constant_signal: 定値信号 logic_gate: Logic Gate - lever: *lever - filter: *filter - wire_tunnel: *wire_tunnel - display: *display - reader: *reader - virtual_processor: *virtual_processor - transistor: *transistor - analyzer: *analyzer - comparator: *comparator + lever: スイッチ + filter: アイテムフィルタ + wire_tunnel: 交差ワイヤ + display: ディスプレイ + reader: ベルトリーダ + virtual_processor: 仮想切断機 + transistor: トランジスタ + analyzer: 形状解析機 + comparator: 比較機 item_producer: なんでも抽出機(サンドボックス) - # --- - pipette: スポイト rotateWhilePlacing: 回転 - rotateInverseModifier: >- - Modifier: 逆時計回りにする + rotateInverseModifier: "Modifier: 逆時計回りにする" cycleBuildingVariants: バリエーション変更 confirmMassDelete: 複数選択削除の確認 pasteLastBlueprint: 直前のブループリントをペーストする cycleBuildings: 建造物の選択 lockBeltDirection: ベルトプランナーを有効化 - switchDirectionLockSide: >- - プランナー: 通る側を切り替え - copyWireValue: >- - ワイヤ: カーソルに合っている形状信号をキーとしてコピー + switchDirectionLockSide: "プランナー: 通る側を切り替え" + copyWireValue: "ワイヤ: カーソルに合っている形状信号をキーとしてコピー" massSelectStart: マウスドラッグで開始 massSelectSelectMultiple: 複数範囲選択 massSelectCopy: 範囲コピー massSelectCut: 範囲カット - placementDisableAutoOrientation: 自動向き合わせ無効 placeMultiple: 配置モードの維持 placeInverse: ベルトの自動向き合わせを逆転 - about: title: このゲームについて body: >- - このゲームはオープンソースであり、Tobias Springer (私)によって開発されています。

+ このゲームはオープンソースであり、Tobias Springer (私)によって開発されています。

開発に参加したい場合は以下をチェックしてみてください。shapez.io on github.

@@ -961,9 +851,7 @@ demo: oneGameLimit: セーブデータの1個制限 customizeKeybindings: キー設定のカスタマイズ exportingBase: 工場の全体像の画像出力 - settingNotAvailable: デモ版では利用できません。 - tips: - ハブは現在指定されている形状だけではなく、あらゆる種類の入力を受け付けることができます。 - あなたの工場が拡張可能か確認してください - あとで報われるでしょう! diff --git a/translations/base-kor.yaml b/translations/base-kor.yaml index 4c4a48bc..544d7615 100644 --- a/translations/base-kor.yaml +++ b/translations/base-kor.yaml @@ -1,53 +1,48 @@ steamPage: shortText: shapez.io는 무한한 공간에서 점점 더 복잡한 도형의 생산과 조합을 자동화하는 공장을 짓는 게임입니다. - discordLinkShort: Official Discord + discordLinkShort: 공식 Discord intro: >- - Shapez.io is a relaxed game in which you have to build factories for the - automated production of geometric shapes. + Shapez.io는 다양한 기하학적 도형을 만드는 공장을 건설하는 편안한 게임입니다. 레벨이 올라갈수록 더욱 복잡한 도형을 + 만들어야 하고 그만큼 무한히 커지는 지도 안에 당신의 공장을 세워야 합니다. - As the level increases, the shapes become more and more complex, and you have to spread out on the infinite map. + 심지어 그것만으로는 충분하지 않을 겁니다. 수요는 기하급수적으로 늘어나게 될 것이고, 더욱 복잡한 도형을 더욱 많이 생산하여야 하므로, 유일하게 도움이 되는 것은 끊임없이 확장을 하는 것입니다! 처음에는 단순한 도형만을 만들지만, 나중에는 색소를 추출하고 혼합하여 도형에 색칠을 해야 합니다! - And as if that wasn't enough, you also have to produce exponentially more to satisfy the demands - the only thing that helps is scaling! - - While you only process shapes at the beginning, you have to color them later - for this you have to extract and mix colors! - - Buying the game on Steam gives you access to the full version, but you can also play a demo on shapez.io first and decide later! - title_advantages: Standalone Advantages + Steam에서 게임을 구매하여 정식 버전의 콘텐츠를 사용하실 수 있지만, 먼저 Shapez.io의 체험판 버전을 플레이해보시고 구매를 고려하셔도 됩니다! + title_advantages: 정식 버전의 장점 advantages: - - 12 New Level for a total of 26 levels - - 18 New Buildings for a fully automated factory! - - 20 Upgrade Tiers for many hours of fun! - - Wires Update for an entirely new dimension! - - Dark Mode! - - Unlimited Savegames - - Unlimited Markers - - Support me! ❤️ - title_future: Planned Content + - 새로운 12 레벨의 추가로 총 26레벨까지 + - 완벽한 자동화를 위한 새로운 18개의 건물! + - 20 티어 업그레이드로 오랫동안 즐겨보세요! + - 전선 업데이트로 완전히 새로운 차원을 접해보세요! + - 다크 모드! + - 무한한 세이브 파일 + - 무한한 마커 + - 저를 지원해주세요! ❤️ + title_future: 계획된 콘텐츠 planned: - - Blueprint Library (Standalone Exclusive) - - Steam Achievements - - Puzzle Mode - - Minimap - - Mods - - Sandbox mode - - ... and a lot more! - title_open_source: This game is open source! - title_links: Links + - 청사진 라이브러리 + - Steam 도전과제 + - 퍼즐 모드 + - 미니맵 + - 모드 + - 샌드박스 모드 + - ... 그리고 더 다양한 것까지! + title_open_source: 이 게임은 오픈 소스입니다! + title_links: 링크 links: - discord: Official Discord - roadmap: Roadmap + discord: 공식 Discord + roadmap: 로드맵 subreddit: Subreddit - source_code: Source code (GitHub) - translate: Help translate + source_code: 소스 코드 (GitHub) + translate: 번역에 도움주세요 text_open_source: >- - Anybody can contribute, I'm actively involved in the community and - attempt to review all suggestions and take feedback into consideration - where possible. + 누구나 번역에 기여하실 수 있으며, 저는 커뮤니티에서 적극적으로 참여하여 모든 제안을 검토하고 가능한 모든 피드백도 고려하고자 + 합니다. - Be sure to check out my trello board for the full roadmap! + 모든 로드맵을 보시려면 저의 trello 보드를 참고해주세요. global: - loading: 로딩중 - error: 에러 + loading: 불러오는 중 + error: 오류 thousandsDivider: "," decimalSeparator: . suffix: @@ -77,25 +72,24 @@ global: shift: SHIFT space: SPACE demoBanners: - title: 무료 버전 - intro: 유료 버전을 구매해서 모든 콘텐츠를 사용해 보세요! + title: 체험판 버전 + intro: 정식 버전을 구매해서 모든 콘텐츠를 사용해 보세요! mainMenu: play: 시작 changelog: 버전 기록 - importSavegame: 불러오기 + importSavegame: 가져오기 openSourceHint: 이 게임은 오픈 소스입니다! discordLink: 공식 디스코드 서버 helpTranslate: 번역을 도와주세요! - browserWarning: - 이 게임은 당신의 브라우저에서 느리게 작동하는 것으로 알려져 있습니다. 더 좋은 성능을 위해 유료 버전을 구매하거나 - 크롬을 다운받으세요. + browserWarning: 이 게임은 현재 브라우저에서 느리게 작동하는 것으로 알려져 있습니다! 더 좋은 성능을 위해 정식 버전을 구매하거나 + Google Chrome 브라우저를 다운로드하세요. savegameLevel: 레벨 - savegameLevelUnknown: 레벨 모름 + savegameLevelUnknown: 미확인 레벨 continue: 계속하기 - newGame: 새 게임 + newGame: 새로하기 madeBy: 제작 - subreddit: 레딧 - savegameUnnamed: Unnamed + subreddit: Reddit + savegameUnnamed: 이름 없음 dialogs: buttons: ok: 확인 @@ -110,184 +104,150 @@ dialogs: showUpgrades: 업그레이드 보기 showKeybindings: 조작법 보기 importSavegameError: - title: 불러오기 오류 - text: "저장 파일을 불러오지 못했습니다:" + title: 가져오기 오류 + text: "세이브 파일을 가져오지 못했습니다:" importSavegameSuccess: - title: 저장 파일 불러오기 성공 - text: 저장 파일을 성공적으로 불러왔습니다. + title: 세이브 파일 가져오기 성공 + text: 세이브 파일을 성공적으로 불러왔습니다. gameLoadFailure: - title: 저장 파일 에러 - text: "저장 파일을 불러오지 못했습니다:" + title: 세이브 파일 에러 + text: "세이브 파일을 불러오지 못했습니다:" confirmSavegameDelete: title: 삭제 확인 - text: Are you sure you want to delete the following game?

- '' at level

This can not be - undone! + text: 이 세이브 파일을 정말로 삭제하시겠습니까?

'' + 레벨

이 작업은 되돌릴 수 없습니다! savegameDeletionError: title: 삭제 실패 - text: 저장 파일을 삭제하지 못했습니다. + text: 세이브 파일을 삭제하지 못했습니다. restartRequired: - title: 다시 시작 필요 - text: 설정을 적용하려면 게임을 다시 시작해야 됩니다. + title: 게임 재시작 필요 + text: 설정을 적용하려면 게임을 재시작해야 합니다. editKeybinding: - title: 키바인딩 바꾸기 + title: 조작법 바꾸기 desc: 당신이 원하는 키나 마우스 버튼을 눌러서 바꾸거나 ESC를 눌러 취소하세요. resetKeybindingsConfirmation: - title: 키바인딩 재설정 - desc: 이것은 모든 키바인딩을 기본값으로 초기화합니다. + title: 조작법 초기화 + desc: 설정된 모든 조작법이 기본값으로 초기화됩니다. 초기화 전에 확인해 주세요. keybindingsResetOk: - title: 키바인딩 재설정 완료 - desc: 모든 키바인딩이 기본값으로 재설정 되었습니다! + title: 조작법 초기화 완료 + desc: 모든 조작법이 기본값으로 초기화 되었습니다! featureRestriction: title: 체험판 버전 - desc: 체험판 버전에는 없는 콘텐츠()로 시도했습니다. 유료 버전을 구입해서 모든 콘텐츠를 사용해보세요! + desc: 체험판 버전에는 없는 콘텐츠 ()로 시도했습니다. 정식 버전을 구입해서 모든 콘텐츠를 사용해보세요! oneSavegameLimit: - title: 저장파일 개수 제한 - desc: 체험판 버전에서는 저장 파일을 한 번에 한 개만 사용할 수 있습니다. 이미 있는 저장 파일을 지우거나 유료 버전을 구입 해주새요. + title: 세이브 파일 개수 제한 + desc: 체험판 버전에서는 세이브 파일을 한 번에 한 개만 사용할 수 있습니다. 이미 있는 세이브 파일을 지우거나 정식 버전을 구입 해주새요. updateSummary: - title: 신규 버전! - desc: 지난번 플레이 이후 변경사항은 다음과 같습니다. + title: 새로운 업데이트! + desc: 지난번 플레이 이후 변경 사항은 다음과 같습니다. upgradesIntroduction: title: 업그레이드 하기 - desc: - 여러분이 만든 모든 도형은 업그레이드에 사용 될 수 있습니다! - 만들어 놓은 공장을 허물지 마세요! + desc: 기존의 공장을 허물지 마세요! 여러분이 그동안 만들어 수집한 모든 도형은 업그레이드에 사용됩니다. 업그레이드 버튼은 화면의 오른쪽 위에 있습니다. massDeleteConfirm: title: 삭제 확인 - desc: 당신은 많은 건물을 삭제하려고 하고있습니다! (정확히는 개) 삭제하시겠습니까? + desc: 굉장히 많은 수 (정확히는 개)의 건물을 삭제하려 합니다! 이 작업을 수행하시겠습니까? massCutConfirm: title: 자르기 확인 - desc: 당신은 많은 건물을 자르려고 하고있습니다! (정확히는 개) 자르시겠습니까? + desc: 굉장히 많은 수 (정확히는 개)의 건물을 자르려고 합니다! 이 작업을 수행하시겠습니까? blueprintsNotUnlocked: title: 아직 사용 불가 - desc: 복사 기능은 아직 열리지 않았습니다! 레벨을 올려서 잠금을 해제하세요. + desc: 12 레벨부터 청사진 기능이 해금됩니다! keybindingsIntroduction: - title: 유용한 키바인딩 - desc: "큰 공장을 지을 때 유용한 키바인딩이 많습니다! 아래를 확인하세요.나머지 키바인딩도 - 확인해보세요!!

CTRL + - Drag: 영역을 선택해서 복사/삭제하세요.
SHIFT: - 한꺼번에 여러 개의 건물을 배치하세요.
ALT: 설치된 - 컨베이어 벨트의 방향을 바꾸세요.
" + title: 유용한 조작법 + desc: "이 게임에는 거대한 공장을 수월하게 세우기 위한 많은 조작법이 있습니다. 아래는 그 대표적인 것이며, 자세한 조작법은 + 조작법 설정을 참고해주세요!

CTRL + 드래그: 영역을 선택합니다.
SHIFT: 누르는 동안 같은 건물을 여러개 배치할 수 있습니다.
+ ALT: 배치된 벨트의 방향을 반전시킵니다.
" createMarker: title: 새로운 마커 - desc: Give it a meaningful name, you can also include a short - key of a shape (Which you can generate here) titleEdit: 마커 변경 + desc: 의미있는 이름을 정해주거나 단축키를 통해 도형을 직접 삽입할 수도 있습니다. + (여기에서 만드실 수 있습니다). markerDemoLimit: - desc: 체험판 버전에서는 마커를 2개 까지만 놓을 수 있습니다. 유료 버전을 구입하면 마커를 무제한으로 놓을 수 있습니다! + desc: 체험판 버전에서는 마커를 2개 까지만 배치할 수 있습니다. 정식 버전을 구입하면 마커를 무제한으로 배치할 수 있습니다! exportScreenshotWarning: title: 스크린샷 내보내기 - desc: - 당신은 공장을 스크린샷으로 내보내려 하고있습니다. 공장이 너무 큰 경우에는 시간이 오래 걸리거나 게임이 꺼질 수도 있음을 - 알려드립니다! + desc: 당신의 공장을 스크린샷으로 내보내려 합니다. 매우 거대한 크기의 공장이라면 그 처리 시간이 상당히 오래 걸릴 것이며, 최악의 경우 + 게임이 중단될 수 있습니다! massCutInsufficientConfirm: title: 자르기 확인 - desc: 이 곳에는 붙여넣기를 할 수 없습니다! 정말 자르시겠습니까? + desc: 이 영역에는 붙여넣기를 할 수 없습니다! 정말 자르시겠습니까? editSignal: - title: Set Signal - descItems: "Choose a pre-defined item:" - descShortKey: ... or enter the short key of a shape (Which you - can generate here) + title: 신호 설정 + descItems: "미리 정의된 아이템을 선택합니다:" + descShortKey: ... 또는 도형 단축키를 사용합니다 (여기에서 만드실 수 있습니다). renameSavegame: - title: Rename Savegame - desc: You can rename your savegame here. - entityWarning: - title: Performance Warning - desc: You have placed a lot of buildings, this is just a friendly reminder that - the game can not handle an endless count of buildings - So try to - keep your factories compact! + title: 세이브 파일 이름 설정 + desc: 여기에서 세이브 파일의 이름을 바꿀 수 있습니다. ingame: keybindingsOverlay: - moveMap: 움직이기 + moveMap: 이동 selectBuildings: 영역 선택 - stopPlacement: 건물 놓기 중지 + stopPlacement: 배치 중단 rotateBuilding: 건물 회전 - placeMultiple: 여러 개 놓기 + placeMultiple: 건물 여러번 배치 reverseOrientation: 방향 뒤집기 disableAutoOrientation: 자동 회전 끄기 - toggleHud: UI 끄기/키기 - placeBuilding: 건물 놓기 - createMarker: 마커 놓기 + toggleHud: HUD 토글 + placeBuilding: 건물 배치 + createMarker: 마커 배치 delete: 삭제 pasteLastBlueprint: 마지막 청사진 붙여넣기 - lockBeltDirection: 벨트 플래너 활성화하기 - plannerSwitchSide: 벨트 플래너 방향바꾸기 - cutSelection: 선택된 부분 자르기 - copySelection: 선택된 부분 복사하기 - clearSelection: 선택된 부분 지우기 - pipette: 스포이드 + lockBeltDirection: 벨트 계획기 활성화 + plannerSwitchSide: 벨트 계획기 방향 전환 + cutSelection: 자르기 + copySelection: 복사하기 + clearSelection: 지우기 + pipette: 피펫 switchLayers: 레이어 전환 buildingPlacement: - cycleBuildingVariants: 를 눌러 변형된 버전 선택 - hotkeyLabel: "Hotkey: " + cycleBuildingVariants: 키를 눌러 변형 전환 + hotkeyLabel: "단축키: " infoTexts: speed: 속도 range: 최대 거리 - storage: 저장공간 + storage: 용량 oneItemPerSecond: 초당 1개 itemsPerSecond: 초당 개 itemsPerSecondDouble: (x2) tiles: 타일 levelCompleteNotification: - levelTitle: 레벨 + levelTitle: 레벨 completed: 완료 unlockText: 잠금 해제! buttonNextLevel: 다음 레벨 notifications: - newUpgrade: 새로운 업그레이드를 할 수 있습니다! + newUpgrade: 새로운 업그레이드가 있습니다! gameSaved: 게임이 저장되었습니다. - freeplayLevelComplete: Level has been completed! + freeplayLevelComplete: 레벨 성공! shop: title: 업그레이드 - buttonUnlock: 업그레이드하기 - tier: 티어 - tierLabels: - - I - - II - - III - - IV - - V - - VI - - VII - - VIII - - IX - - X - - XI - - XII - - XIII - - XIV - - XV - - XVI - - XVII - - XVIII - - XIX - - XX - maximumLevel: 최대 레벨 (속도 x) + buttonUnlock: 업그레이드 + tier: 티어 + maximumLevel: 최종 레벨 (속도 x) statistics: title: 통계 dataSources: stored: title: 저장된 도형 - description: 당신의 중앙 건물에 저장되어 있는 도형들의 수 + description: 허브에 저장되어 있는 도형의 개수 produced: title: 제작된 도형 - description: 당신의 공장에서 만들어지고 있는 모든 도형의 개수 + description: 공장에서 만들어지고 있는 모든 도형의 개수 delivered: - title: 도착한 도형 - description: 당신의 중앙 건물에 도착하고 있는 도형의 개수 + title: 운송된 도형 + description: 허브에 도착하고 있는 도형의 개수 noShapesProduced: 지금까지 제작된 도형이 없습니다. shapesDisplayUnits: - second: / s - minute: / m - hour: / h + second: 초당 + minute: 분당 + hour: 시간당 settingsMenu: playtime: 플레이 시간 - buildingsPlaced: 배치한 건물 - beltsPlaced: 배치한 컨베이어 벨트 - buttons: - continue: 계속하기 - settings: 설정 - menu: 메뉴로 돌아가기 + buildingsPlaced: 건물 수 + beltsPlaced: 벨트 수 tutorialHints: title: 도움이 필요하세요? showHint: 힌트 보기 @@ -296,74 +256,75 @@ ingame: cost: 가격 waypoints: waypoints: 마커 - hub: 중앙 건물 - description: - 마커를 좌클릭해서 그곳으로 가고, 우클릭해서 삭제합니다.

을 눌러 지금 있는 곳에 - 마커를 놓거나 우클릭해서 원하는 곳에 놓으세요. + hub: 허브 + description: 마커를 좌클릭하여 그곳으로 이동하고, 우클릭으로 삭제할 수 있습니다.

마커를 배치하기 위해 + 키로 지금 있는 위치에 혹은 우클릭하여 원하는 위치에 배치할 수 + 있습니다. creationSuccessNotification: 마커가 성공적으로 생성되었습니다. interactiveTutorial: title: 튜토리얼 hints: - 1_1_extractor: 추출기원 모양의 도형에 놓아서 추출하세요! - 1_2_conveyor: - "추출기를 컨베이어 벨트로 당신의 중앙 건물에 연결하세요!

팁: 마우스로 - 벨트를 클릭하고 드래그하세요!" - 1_3_expand: "이것은 방치형 게임이 아닙니다! 추출기를 더 놓아 목표를 빨리 - 달성하세요.

팁: SHIFT를 눌러 여러 개의 추출기를 놓고 - R로 방향을 바꾸세요." + 1_1_extractor: 원형 도형을 추출하기 위해 그 위에 추출기를 선택한 뒤 + 배치하여 추출하세요! + 1_2_conveyor: "이제 컨베이어 벨트를 추출기와 허브를 서로 연결하세요!

+ 팁: 벨트를 마우스로 클릭한 뒤 드래그하세요!" + 1_3_expand: "이 게임은 방치형 게임이 아닙니다! 더 많은 추출기와 + 벨트를 만들어 지정된 목표를 빨리 달성하세요.

+ 팁: SHIFT 키를 누른 상태에서는 빠르게 + 배치할 수 있고, R 키를 눌러 회전할 수 있습니다." colors: - red: 빨강 - green: 초록 - blue: 파랑 - yellow: 노랑 - purple: 보라 - cyan: 청록 - white: 하양 + red: 빨간색 + green: 초록색 + blue: 파란색 + yellow: 노란색 + purple: 보라색 + cyan: 시안색 + white: 흰색 + black: 검은색 uncolored: 회색 - black: 검정 shapeViewer: title: 층 empty: 비었음 copyKey: 키 복사하기 connectedMiners: - one_miner: 1 Miner - n_miners: Miners - limited_items: Limited to + one_miner: 추출기 1 개 + n_miners: 추출기 개 + limited_items: 개로 제한됨 watermark: - title: Demo version - desc: Click here to see the Steam version advantages! - get_on_steam: Get on steam + title: 체험판 버전 + desc: 정식 버전의 장점을 보려면 여기를 클릭하세요! + get_on_steam: Steam으로 가기 standaloneAdvantages: - title: Get the full version! - no_thanks: No, thanks! + title: 정식 버전을 구매하세요! + no_thanks: 괜찮아요! points: levels: - title: 12 New Levels - desc: For a total of 26 levels! + title: 새로운 12 레벨 + desc: 최대 26 레벨로 확장됩니다! buildings: - title: 18 New Buildings - desc: Fully automate your factory! + title: 새로운 18개의 건축물 + desc: 완벽한 자동화된 공장을 위한 건물들입니다! savegames: - title: ∞ Savegames - desc: As many as your heart desires! + title: 무한한 세이브 파일 + desc: 당신이 내키는대로 마음껏 할 수 있습니다! upgrades: - title: 20 Upgrade Tiers - desc: This demo version has only 5! + title: 20 티어까지 확장된 업그레이드 + desc: 체험판에서는 5 티어까지만 사용할 수 있습니다! markers: - title: ∞ Markers - desc: Never get lost in your factory! + title: 무한한 마커 배치 + desc: 넓은 공장에서 길을 잃지 마세요! wires: - title: Wires - desc: An entirely new dimension! + title: 전선 + desc: 완전히 새로운 차원의 도입! darkmode: - title: Dark Mode - desc: Stop hurting your eyes! + title: 다크 모드 + desc: 당신의 눈을 피곤하게 만들지 마세요! support: - title: Support me - desc: I develop it in my spare time! + title: 저를 지원해주세요 + desc: 저는 여가 시간에 게임을 개발합니다! shopUpgrades: belt: - name: 컨베이어 벨트, 배분기, 터널 + name: 벨트, 밸런서, 터널 description: 속도 x → x miner: name: 추출기 @@ -372,365 +333,327 @@ shopUpgrades: name: 절단기, 회전기, 결합기 description: 속도 x → x painting: - name: 색 혼합기, 도형 색칠기 + name: 혼합기, 색칠기 description: 속도 x → x buildings: hub: deliver: 목표 toUnlock: 보상 levelShortcut: 레벨 - endOfDemo: End of Demo + endOfDemo: 체험판 끝 belt: default: name: 컨베이어 벨트 - description: 도형을 운반. 클릭 및 드래그해서 여러 개 배치. + description: 아이템을 운송합니다. 클릭 드래그하여 긴 줄을 한번에 배치할 수 있습니다. miner: default: name: 추출기 - description: 도형 또는 색소 위에 놓아서 추출하기 + description: 도형이나 색소 위에 배치하여 추출합니다. chainable: - name: 체인 추출기 - description: 도형 또는 색소 위에 놓아서 추출하기. 여러 개를 연결할 수 있음. + name: 연쇄 추출기 + description: 도형이나 색소 위에 배치하여 추출합니다. 서로 연결될 수 있습니다. underground_belt: default: name: 터널 - description: 도형을 건물과 벨트 밑으로 통과시킴. + description: 건물과 벨트 아래로 공간을 만들어 아이템을 통과시킵니다. tier2: name: 터널 티어 II - description: 도형을 건물과 벨트 밑으로 터널 보다 빨리 통과시킴. + description: 건물과 벨트 아래로 공간을 만들어 아이템을 통과시킵니다. cutter: default: name: 절단기 - description: - 도형을 위에서 아래로 2개로 나눈다. 만약, 출력한 2개중 1개만 사용하면 기계가 멈추니 사용하지 않는 - 나머지 한 개는 버릴 것 + description: 도형을 수직으로 잘라 두 가지 도형으로 나눕니다. 한쪽만 사용할 경우라면 다른 부분을 파괴하지 않을 경우 + 절단기가 막혀 멈추게 됩니다! quad: - name: 절단기 (4단) - description: 도형을 4개로 나눈다. 만약, 한 개만 사용하면 기계가 멈추니 나머지는 버릴 것 + name: 4단 절단기 + description: 도형을 즉시 네 개로 자릅니다. 한쪽만 사용할 경우라면 다른 부분을 파괴하지 않을 경우 절단기가 막혀 + 멈추게 됩니다! rotater: default: name: 회전기 - description: 도형을 시계방향으로 90도 회전시킨다. + description: 도형을 시계 방향으로 90도 회전시킵니다. ccw: - name: 회전기 (반시계방향) - description: 도형을 반시계방향으로 90도 회전시킨다. + name: 반시계 방향 회전기 + description: 도형을 반시계 방향으로 90도 회전시킵니다. rotate180: - name: Rotate (180) - description: Rotates shapes by 180 degrees. + name: 180도 회전기 + description: 도형을 180도 회전시킵니다. stacker: default: name: 결합기 - description: 도형 2개를 쌓는다. 합칠 수가 없다면 오른쪽 도형이 왼쪽 도형 위에 놓아진다. + description: 도형을 서로 결합하고 쌓습니다. 서로 결합할 수 있다면 두 도형을 붙여 하나로 만들고, 그렇지 않으면 오른쪽 도형이 왼쪽 + 도형 위에 쌓이게 됩니다. mixer: default: - name: 색 혼합기 - description: 두가지 색을 섞어서 다른 색을 만든다. + name: 색소 혼합기 + description: 두 가지의 색소를 혼합해 다른 혼합 색소를 만듭니다. painter: default: - name: 도형 색칠기 - description: 도형을 색소로 색칠한다. + name: 색칠기 + description: 색소를 이용해 도형을 색칠합니다. 위쪽에서 받는 색소로 왼쪽에서 받는 도형 전체를 색칠합니다. double: - name: 2단 도형 색칠기 - description: 왼쪽에 입력되는 도형을 위에서 입력되는 색소로 색칠한다. + name: 2단 색칠기 + description: 색소를 이용해 도형을 색칠합니다. 위쪽에서 받는 색소로 왼쪽에서 받는 도형 전체를 색칠합니다. quad: - name: 4단 도형 색칠기 - description: Allows you to color each quadrant of the shape individually. Only - slots with a truthy signal on the wires layer - will be painted! + name: 4단 색칠기 + description: 도형의 각 사분면에 개별적으로 색상을 칠할 수 있습니다. 전선 레이어를 통해 참 신호가 + 있는 슬롯만 칠해집니다! mirrored: - name: 도형 색칠기 - description: 도형을 색소로 색칠한다. + name: 색칠기 + description: 색소를 이용해 도형을 색칠합니다. 아래쪽에서 받는 색소로 왼쪽에서 받는 도형 전체를 색칠합니다. trash: default: name: 휴지통 - description: 양쪽에서 오는 모든 입력물을 버린다. + description: 어느 곳에서든 들어오는 아이템을 영원히 파괴합니다. wire: default: name: 전선 - description: 에너지를 전송한다. + description: 아이템, 색상, 또는 불 값 (0 혹은 1)이 될 수 있는 신호를 전송합니다. 색이 다른 전선은 서로 연결되지 않습니다. second: - name: Wire - description: Transfers signals, which can be items, colors or booleans (1 / 0). - Different colored wires do not connect. + name: 전선 + description: 아이템, 색상, 또는 불 값 (0 혹은 1)이 될 수 있는 신호를 전송합니다. 색이 다른 전선은 서로 연결되지 않습니다. balancer: default: - name: Balancer - description: Multifunctional - Evenly distributes all inputs onto all outputs. + name: 밸런서 + description: 들어오는 아이템을 균등하게 분배합니다. merger: - name: Merger (compact) - description: Merges two conveyor belts into one. + name: 압축형 병합기 + description: 컨베이어 벨트 두 줄을 한 줄로 합칩니다. merger-inverse: - name: Merger (compact) - description: Merges two conveyor belts into one. + name: 압축형 병합기 + description: 컨베이어 벨트 두 줄을 한 줄로 합칩니다. splitter: - name: Splitter (compact) - description: Splits one conveyor belt into two. + name: 압축형 분배기 + description: 컨베이어 벨트 한 줄을 두 줄로 나눕니다. splitter-inverse: - name: Splitter (compact) - description: Splits one conveyor belt into two. + name: 압축형 분배기 + description: 컨베이어 벨트 한 줄을 두 줄로 나눕니다. storage: default: - name: Storage - description: Stores excess items, up to a given capacity. Prioritizes the left - output and can be used as an overflow gate. + name: 저장고 + description: 아이템을 지정된 용량까지 저장합니다. 왼쪽 출력이 가장 우선되므로 오버플로 회로로 사용할 수 있습니다. wire_tunnel: default: - name: Wire Crossing - description: Allows to cross two wires without connecting them. + name: 전선 교차 장치 + description: 두 전선이 서로 연결되지 않고 교차할 수 있도록 합니다. constant_signal: default: - name: Constant Signal - description: Emits a constant signal, which can be either a shape, color or - boolean (1 / 0). + name: 일정 신호 + description: 모양, 색상, 또는 불 값 (1 혹은 0)이 될 수 있는 상수 신호를 방출합니다. lever: default: - name: Switch - description: Can be toggled to emit a boolean signal (1 / 0) on the wires layer, - which can then be used to control for example an item filter. + name: 스위치 + description: 전선 레이어에서 불 값 (1 또는 0)를 방출하도록 전환할 수 있으며, 그 후 아이템 선별같은 구성 요소를 제어하는 데 + 사용될 수 있습니다. logic_gate: default: - name: AND Gate - description: Emits a boolean "1" if both inputs are truthy. (Truthy means shape, - color or boolean "1") + name: AND 회로 + description: 두 입력이 모두 참일 경우 불 값 "1"을 내보냅니다 (참은 도형, 색상, 불 값 "1"을 의미합니다). not: - name: NOT Gate - description: Emits a boolean "1" if the input is not truthy. (Truthy means - shape, color or boolean "1") + name: NOT 회로 + description: 입력이 거짓일 경우 불 값 "1"을 내보냅니다 (참은 도형, 색상, 불 값 "1"을 의미합니다). xor: - name: XOR Gate - description: Emits a boolean "1" if one of the inputs is truthy, but not both. - (Truthy means shape, color or boolean "1") + name: XOR 회로 + description: 입력 중 하나만 참이고 둘 다 같지 않을 경우 불 값 "1"을 내보냅니다 (참은 도형, 색상, 불 값 "1"을 + 의미합니다). or: - name: OR Gate - description: Emits a boolean "1" if one of the inputs is truthy. (Truthy means - shape, color or boolean "1") + name: OR 회로 + description: 입력 중 하나 이상이 참일 경우 불 값 "1"을 내보냅니다 (참은 도형, 색상, 불 값 "1"을 의미합니다). transistor: default: - name: Transistor - description: Forwards the bottom input if the side input is truthy (a shape, - color or "1"). + name: 트랜지스터 + description: 양 옆의 입력이 참일 경우 아래쪽 입력을 전달합니다 (모양, 색상, 또는 값 "1"일 경우). mirrored: - name: Transistor - description: Forwards the bottom input if the side input is truthy (a shape, - color or "1"). + name: 트랜지스터 + description: 양 옆의 입력이 참일 경우 아래쪽 입력을 전달합니다 (모양, 색상, 또는 값 "1"일 경우) filter: default: - name: Filter - description: Connect a signal to route all matching items to the top and the - remaining to the right. Can be controlled with boolean signals - too. + name: 선별기 + description: 신호를 연결하여 일치하는 모든 아이템은 위로, 나머지 아이템은 오른쪽으로 내보냅니다. 불 값으로도 제어할 수 있습니다. display: default: - name: Display - description: Connect a signal to show it on the display - It can be a shape, - color or boolean. + name: 디스플레이 + description: 신호를 연결하여 디스플레이에 도형이나 색상 또는 불 값을 표시합니다. reader: default: - name: Belt Reader - description: Allows to measure the average belt throughput. Outputs the last - read item on the wires layer (once unlocked). + name: 벨트 판독기 + description: 벨트의 평균 처리량을 측정합니다. (잠금 해제된) 전선 레이어에서 마지막으로 읽은 아이템을 출력합니다. analyzer: default: - name: Shape Analyzer - description: Analyzes the top right quadrant of the lowest layer of the shape - and returns its shape and color. + name: 도형 분석기 + description: 도형의 가장 낮은 레이어의 오른쪽 상단 사분면을 분석하고 모양과 색상을 반환합니다. comparator: default: - name: Compare - description: Returns boolean "1" if both signals are exactly equal. Can compare - shapes, items and booleans. + name: 비교기 + description: 두 신호가 정확히 동일할 경우 불 값 "1"을 반환합니다. 도형, 색상, 불 값을 비교할 수 있습니다. virtual_processor: default: - name: Virtual Cutter - description: Virtually cuts the shape into two halves. + name: 가상 절단기 + description: 가상으로 도형을 잘라 반으로 나눕니다. rotater: - name: Virtual Rotater - description: Virtually rotates the shape, both clockwise and counter-clockwise. + name: 가상 회전기 + description: 가상으로 도형을 시계 방향이나 반시계 방향으로 회전합니다. unstacker: - name: Virtual Unstacker - description: Virtually extracts the topmost layer to the right output and the - remaining ones to the left. + name: 가상 결합해제기 + description: 가상으로 최상단에 있는 레이어를 오른쪽으로 출력하고, 나머지는 왼쪽으로 출력합니다. stacker: - name: Virtual Stacker - description: Virtually stacks the right shape onto the left. + name: 가상 결합기 + description: 가상으로 오른쪽 도형을 왼쪽 도형에 쌓습니다. painter: - name: Virtual Painter - description: Virtually paints the shape from the bottom input with the shape on - the right input. + name: 가상 색칠기 + description: 가상으로 하단 입력의 모양을 오른쪽 입력의 색상으로 칠합니다. item_producer: default: - name: Item Producer - description: Available in sandbox mode only, outputs the given signal from the - wires layer on the regular layer. + name: 아이템 생성기 + description: 샌드박스 모드에서만 사용할 수 있는 아이템으로, 일반 레이어 위에 있는 전선 레이어에서 주어진 신호를 출력합니다. storyRewards: reward_cutter_and_trash: title: 절단기 - desc: You just unlocked the cutter, which cuts shapes in half - from top to bottom regardless of its - orientation!

Be sure to get rid of the waste, or - otherwise it will clog and stall - For this purpose - I have given you the trash, which destroys - everything you put into it! + desc: 절단기가 잠금 해제되었습니다! 절단기는 들어오는 도형이 + 어떤 모양을 하고 있던 수직으로 잘라 + 반으로 나눕니다!

쓰지 않는 도형은 쓰레기로 처리하세요, + 그렇지 않으면 작동을 멈출 것입니다! 이러한 목적을 위해 + 휴지통도 함께 지급되었습니다. + 휴지통에 들어간 것은 모두 파괴됩니다! reward_rotater: title: 회전기 - desc: 회전기가 잠금 해제되었습니다! 이것은 도형을 시계방향으로 90도 회전 시킵니다. + desc: 회전기가 잠금 해제되었습니다! 회전기는 들어오는 도형을 시계 방향으로 90도 회전시켜줍니다. reward_painter: title: 색칠기 - desc: - "색칠기가 잠금 해제되었습니다. - 추출한 색소(도형을 추출하는 것처럼)를 색칠기에서 도형과 합쳐 - 색칠된 도형을 얻으세요!

추신: 색맹이라면, 설정에서 색맹 모드를 활성화 - 시키세요!" + desc: "색칠기가 잠금 해제되었습니다! 도형과 마찬가지로 색소를 추출하고 색칠기에 넣거 도형과 결합하여 색칠된 + 도형을 만들도록 하세요!

추신: 만약 당신이 색맹이라면, 설정에서 색맹 모드를 + 활성화하세요!" reward_mixer: title: 혼합기 - desc: 혼합기가 잠금 해제되었습니다! 이 건물로 두 색소를 혼합하세요! + desc: 혼합기가 잠금 해제되었습니다! 서로 다른 두 색소를 혼합하여 새로운 혼합 + 색소를 얻을 수 있습니다! reward_stacker: title: 결합기 - desc: 결합기가 잠금 해제되었습니다! 두 도형이 서로 옆에 놓을 수 있는 경우, 두 도형이 - 결합됩니다. 그렇지 않은 경우, 오른쪽 도형이 왼쪽 도형 위에 - 쌓이게됩니다. + desc: 결합기가 잠금 해제되었습니다! 이제 결합기를 통해 여러 도형을 붙이고 결합할 수 있습니다! 들어오는 두 + 도형의 모양이 서로 나란히 붙일 수 있다면, 하나의 도형으로 결합됩니다. 만약 서로 + 겹쳐진다면, 오른쪽 도형이 왼쪽 도형의 위에 쌓이게 됩니다! reward_splitter: - title: 배분기 - desc: You have unlocked a splitter variant of the - balancer - It accepts one input and splits them - into two! + title: 압축형 분배기 + desc: 밸런서의 새로운 형태인 분배기가 잠금 해제되었습니다! 이제 벨트 한 줄을 + 즉시 두 줄로 분배합니다! reward_tunnel: title: 터널 - desc: 터널이 잠금 해제되었습니다! 자원을 건물과 벨트 밑으로 운송 할 수 있습니다. + desc: 터널이 잠금 해제되었습니다! 이제 벨트와 건물 아래로 공간을 만들어내 옮길 수 있습니다! reward_rotater_ccw: - title: 회전기 (반시계방향) - desc: 반시계방향 회전기가 잠금 해제되었습니다! 이것을 배치하려면 회전기를 선택하고 - T를 눌러서 변형된 버전을 사용하세요! + title: 반시계 방향 회전기 + desc: 반시계 방향 회전기가 잠금 해제되었습니다! 반시계 방향 회전기는 회전기의 다른 형태로, 이름처럼 + 들어오는 도형을 반시계 방향으로 90도만큼 회전시킵니다! 제작하려면 회전기를 선택한 후 'T' 키를 눌러 + 다른 형태로 전환하세요! reward_miner_chainable: - title: 체인 추출기 - desc: "You have unlocked the chained extractor! It can - forward its resources to other extractors so you - can more efficiently extract resources!

PS: The old - extractor has been replaced in your toolbar now!" + title: 연쇄 추출기 + desc: "연쇄 추출기가 잠금 해제되었습니다! 자원을 보다 더욱 효율적으로 추출할 수 있도록 앞에 + 있는 추출기로 자원을 보낼 수 있습니다!

추신: 이제 툴바에 있는 기존 추출기는 연쇄 + 추출기로 대체되었습니다!" reward_underground_belt_tier_2: title: 터널 티어 II - desc: - 새로운 종류의 터널이 잠금 해제되었습니다! 새 터널은 보다 넓은 범위를 - 가졌으며, 터널들은 같은 종류끼리만 연결됩니다. + desc: 터널의 새로운 형태가 잠금 해제되었습니다! 터널 티어 II는 더욱 긴 + 거리를 운송할 수 있고 기존 터널과 겹쳐지지 않고도 자원을 보낼 수 있습니다! reward_cutter_quad: - title: 절단기 (4단) - desc: 새로운 종류의 절단기가 잠금 해제되었습니다! 새 절단기는 도형을 2조각이 아니라 - 4조각으로 자릅니다. + title: 4단 절단기 + desc: 새로운 종류의 절단기가 잠금 해제되었습니다! 4단 절단기는 도형을 두 조각이 아닌 네 + 조각으로 자를 수 있습니다! reward_painter_double: - title: 색칠기 (2단) - desc: - 새로운 종류의 색칠기가 잠금 해제되었습니다! 새 색칠기는 색소 하나로 2개의 - 도형을 색칠할 수 있습니다. + title: 2단 색칠기 + desc: 새로운 종류의 절단기가 잠금 해제되었습니다! 일반적인 색칠기와 거의 동일하지만, 하나의 색소를 사용하여 + 동시에 두 개의 도형을 색칠할 수 있습니다! reward_storage: - title: 저장소 - desc: You have unlocked the storage building - It allows you to - store items up to a given capacity!

It priorities the left - output, so you can also use it as an overflow gate! + title: 저장고 + desc: 저장고가 잠금 해제되었습니다! 저장고는 최대 용량까지 도형을 저장할 수 있습니다!

왼쪽 + 출력이 우선되므로 오버플로 회로로도 활용될 수 있습니다! reward_freeplay: - title: 자유 모드 - desc: You did it! You unlocked the free-play mode! This means - that shapes are now randomly generated!

- Since the hub will require a throughput from now - on, I highly recommend to build a machine which automatically - delivers the requested shape!

The HUB outputs the requested - shape on the wires layer, so all you have to do is to analyze it and - automatically configure your factory based on that. + title: 자유플레이 + 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' 키를 눌러 복사할 수 있습니다.

하지만 공짜는 + 아닙니다, 청사진 모양 도형을 허브에 저장하고 그것을 일부 사용해 붙여넣기 + 기능을 사용할 수 있습니다! (방금 당신이 만든 것입니다.) no_reward: title: 다음 레벨 - desc: - "이 단계는 아무런 보상이 없습니다. 하지만 다음 단계에는 있죠!

추신: 현존하는 공장을 부수지 않는 것이 좋습니다. - - 추후 업그레이드를 해제하기 위해 모든 도형들이 - 필요합니다!" + desc: "이번 레벨의 보상은 없네요. 대신 다음 레벨에서 줄겁니다!

추신: + 기존 공장을 파괴하지는 마세요. 후에 업그레이드 잠금 해제되면 + 기존의 모든 도형이 필요합니다!" no_reward_freeplay: title: 다음 레벨 - desc: 축하드립니다! 유료 버전을 위한 더 많은 컨텐츠를 만들고 있습니다. + desc: 축하드립니다! reward_balancer: - title: Balancer - 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_merger: - title: Compact Merger - desc: You have unlocked a merger variant of the - balancer - It accepts two inputs and merges them - into one belt! + title: 압축형 병합기 + desc: 밸런서의 새로운 형태인 병합기가 잠금 해제되었습니다! 이제 벨트 두 줄을 + 즉시 한 줄로 병합합니다! reward_belt_reader: - title: Belt reader - desc: You have now unlocked the belt reader! It allows you to - measure the throughput of a belt.

And wait until you unlock - wires - then it gets really useful! + title: 벨트 판독기 + desc: 벨트 판독기가 잠금 해제되었습니다! 이제 벨트의 처리량을 확인할 수 있습니다.

그리고, + 전선이 잠금 해제될 때 까지 기다리신다면 정말 유용하게 사용할 수 있을 겁니다! reward_rotater_180: - title: Rotater (180 degrees) - desc: You just unlocked the 180 degress rotater! - It allows - you to rotate a shape by 180 degress (Surprise! :D) + title: 180도 회전기 + desc: 180도 회전기가 잠금 해제되었습니다! 이제 도형을 바로 180도로 회전시킬 수 있습니다. (짜잔! + :D) reward_display: - title: Display - desc: "You have unlocked the Display - Connect a signal on the - wires layer to visualize it!

PS: Did you notice the belt - reader and storage output their last read item? Try showing it on a - display!" + title: 디스플레이 + desc: "디스플레이가 잠금 해제되었습니다! 이제 신호를 전선 레이어에 연결해 시각화할 수 + 있습니다.

추신: 벨트 판독기와 저장고가 마지막으로 읽은 아이템을 출력했나요? 디스플레이로 한번 봐보세요!" reward_constant_signal: - title: Constant Signal - desc: You unlocked the constant signal building on the wires - layer! This is useful to connect it to item filters - for example.

The constant signal can emit a - shape, color or - boolean (1 / 0). + title: 일정 신호 + desc: 전선 레이어에서 구축할 수 있는 일정 신호가 잠금 해제되었습니다! 간단한 예시로, 아이템 + 선별에 연결하여 사용하는 데 유용합니다.

일정 신호는 도형, + 색상, 혹은 불 값 (1 혹은 0)을 출력할 수 있습니다. reward_logic_gates: - title: Logic Gates - desc: You unlocked logic gates! You don't have to be excited - about this, but it's actually super cool!

With those gates - you can now compute AND, OR, XOR and NOT operations.

As a - bonus on top I also just gave you a transistor! + title: 논리 회로 + desc: 논리 회로가 잠금 해제되었습니다! 굳이 흥분할 필요는 없지만, 진짜 멋진 기술입니다!

+ 논리 회로를 통해 이제 AND, OR, XOR, NOT 논리 연산을 할 수 있습니다.

보너스로, + 트랜지스터도 지급되었습니다! reward_virtual_processing: - title: Virtual Processing - desc: I just gave a whole bunch of new buildings which allow you to - simulate the processing of shapes!

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

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

- Build - something cool with wires.

- Continue to play - regulary.

Whatever you choose, remember to have fun! + title: 가상 처리 + desc: 도형의 처리를 시뮬레이션할 수 있는 다양한 새로운 건물이 잠금 해제되었습니다!

이제 전선 + 레이어에서 도형에 대한 절단기, 회전기, 결합기 등을 가상으로 시뮬레이션할 수 있습니다! 이제 게임 진행에 있어 다음 세 + 가지의 방법이 존재합니다:

- 완전 자동화된 기계를 구축하고 허브에서 요구되는 + 도형을 제작합니다. (먼저 시도해볼 것을 권합니다!).

- 전선을 통해 멋진 것들 만듭니다.

+ - 평소처럼 게임을 진행합니다.

어떤 방식으로든, 재미있게 게임을 플레이해주시길 바랍니다! reward_wires_painter_and_levers: - title: Wires & Quad Painter - desc: "You just unlocked the Wires Layer: It is a separate - layer on top of the regular layer and introduces a lot of new - mechanics!

For the beginning I unlocked you the Quad - Painter - Connect the slots you would like to paint with on - the wires layer!

To switch to the wires layer, press - E." + title: 전선과 4단 색칠기 + desc: "전선 레이어가 잠금 해제되었습니다! 전선 레이어는 + 일반 레이어 위에 존재하는 별도의 레이어로, 이를 통한 다양하고 새로운 + 메커니즘을 소개하겠습니다!

우선 4단 색칠기가 + 잠금 해제되었습니다. 전선 레이어에서 색칠하고 싶은 슬롯에 전선을 연결하세요! + 전선 레이어로 전환하려면 E 키를 누르세요." reward_filter: - title: Item Filter - desc: You unlocked the Item Filter! It will route items either - to the top or the right output depending on whether they match the - signal from the wires layer or not.

You can also pass in a - boolean signal (1 / 0) to entirely activate or disable it. + title: 아이템 선별기 + desc: 아이템 선별기가 잠금 해제되었습니다! 전선 레이어의 신호와 일치하는지에 대한 여부로 아이템을 위쪽 + 출력이나 오른쪽 출력으로 보냅니다.

불 값 (1 또는 0)을 전달하여 완전히 활성화과 비활성화를 전환할 수 + 있습니다. reward_demo_end: - title: End of Demo - desc: You have reached the end of the demo version! + title: 체험판 끝 + desc: 체험판 콘텐츠가 모두 끝이 났습니다! settings: title: 설정 categories: general: 일반 - userInterface: 유저 인터페이스 + userInterface: 사용자 인터페이스 advanced: 고급 - performance: Performance + performance: 성능 versionBadges: dev: 개발 staging: 검증 prod: 배포 - buildDate: 날짜 + buildDate: 제작 labels: uiScale: title: UI 크기 - description: "UI의 크기를 변경시키기:" + description: 사용자 인터페이스의 크기를 변경합니다. 인터페이스는 당신의 해상도에 따라 확장되는데 이 설정은 그 확장의 정도를 + 제어합니다. scales: super_small: 매우 작게 small: 작게 @@ -738,8 +661,8 @@ settings: large: 크게 huge: 매우 크게 scrollWheelSensitivity: - title: 확대 민감도 - description: 마우스 휠이나 트랙패드로 확대하는 데의 민감도 + title: 화면 줌 감도 + description: (마우스 휠이나 트랙 패드를 통한) 화면 줌 인/아웃 감도를 설정합니다. sensitivity: super_slow: 매우 느리게 slow: 느리게 @@ -748,20 +671,20 @@ settings: super_fast: 매우 빠르게 movementSpeed: title: 이동 속도 - description: 키보드를 사용할 때, 화면 이동 속도를 설정합니다. + description: 키보드나 마우스를 화면 테두리로 옮겨 화면을 이동할 때 이동 속도를 설정합니다. speeds: super_slow: 매우 느리게 slow: 느리게 regular: 보통 fast: 빠르게 super_fast: 매우 빠르게 - extremely_fast: 극한의 속도 + extremely_fast: 극도로 빠르게 language: title: 언어 - description: 언어 바꾸기 - 모든 언어팩은 사용자들이 만든 것이므로 정확하지 않을 수 있습니다! + description: 언어를 바꿉니다. 모든 언어팩은 사용자들이 모여 만드므로 정확하지 않을 수 있습니다! fullscreen: - title: 전체화면 - description: 이 게임은 전체화면으로 하는 것이 가장 좋습니다. 전체화면은 유료 버전에서만 가능합니다. + title: 전체 화면 + description: 이 게임은 전체 화면으로 하는 것이 가장 좋습니다. 전체 화면 기능은 정식 버전에서만 가능합니다. soundsMuted: title: 효과음 끄기 description: 모든 효과음을 끕니다. @@ -770,33 +693,30 @@ settings: description: 모든 배경 음악을 끕니다. theme: title: 게임 테마 - description: 게임 테마를 고르세요. (밝음/어두움). + description: 게임 테마를 고르세요. (라이트/다크). themes: - dark: 어두운 테마 - light: 밝은 테마 + dark: 다크 + light: 라이트 refreshRate: - title: 시뮬레이션 빈도 - description: - 144hz 모니터가 있다면 이 설정을 바꿔 게임이 높은 빈도로 적절히 시뮬레이션되게 하세요. 만약에 컴퓨터가 느리다면 - FPS에 영양을 미칠 수 있습니다. + title: 틱 빈도 + description: 이것은 초당 발생하는 게임 틱 수를 결정합니다. 일반적으로 틱 속도가 높을수록 정밀도는 향상되나 성능은 낮아집니다. 낮은 + 틱 빈도에서는 처리량이 정확하지 않을 수 있습니다. alwaysMultiplace: - title: 항상 여러 개 배치 - description: - 활성화된 경우 모든 건물은 따로 취소하기 전까지 배치 후 선택된 상태로 유지됩니다. SHIFT를 계속 누르고 있는 것과 - 같은 효과입니다. + title: 다수 배치 항시 켜기 + description: 활성화할 경우 모든 건물은 배치한 후 취소할 때 까지 커서에 선택된 상태를 유지합니다. 이 기능은 SHIFT 키를 계속 + 누르는 것과 같습니다. offerHints: title: 힌트와 튜토리얼 - description: - 이것을 끄면 힌트와 튜토리얼이 나오지 않습니다. 또한 특정 UI 요소를 지정된 레벨까지 숨겨 게임에 쉽게 들어갈 수 - 있습니다. + description: 게임 플레이하는 동안 힌트와 튜토리얼을 보여줄 지를 결정합니다. 또한 게임에 더 쉽게 빠져들 수 있도록 특정 레벨까지 + 특정한 UI 요소를 숨깁니다. enableTunnelSmartplace: - title: 스마트 터널 - description: 활성화 시키면, 터널을 설치하는 것이 자동적으로 불필요한 벨트를 없앱니다. 또한, 터널을 당겨서 남는 터널을 없앱니다. + title: 지능적인 터널 배치 + description: 활성화할 경우 터널을 설치할 때 터널 사이에 있는 불필요한 벨트를 제거합니다. 또한 터널을 당겨서 남는 터널을 없앱니다. vignette: title: 비네트 효과 description: 화면의 모서리를 어둡게 만들어 텍스트를 읽기 쉽게 해주는 비네트 효과를 활성화 시킵니다. autosaveInterval: - title: 자동저장 주기 + title: 자동 저장 주기 description: 자동저장을 얼마나 자주 할 것인지 정합니다. 자동저장 기능을 끌 수도 있습니다. intervals: one_minute: 1분 @@ -804,106 +724,103 @@ settings: five_minutes: 5분 ten_minutes: 10분 twenty_minutes: 20분 - disabled: 기능 끄기 + disabled: 끄기 compactBuildingInfo: - title: 간단한 건물 정보 - description: 건물의 정보창을 해당 건물의 능률만 보이도록 줄입니다. 아니면, 설명과 이미지가 보입니다. + title: 건물 정보 간소화 + description: 건물에 대한 정보 상자를 줄여 처리 비율만 나타냅니다. 아니라면 설명과 이미지가 함께 표시됩니다. disableCutDeleteWarnings: - title: 자르기/삭제 경고기능 끄기 - description: 100개 이상의 건물을 자르기/삭제할 때 경고창이 나오지 않게 합니다. + title: 자르기 및 삭제 경고 끄기 + description: 100개가 넘는 개체를 자르거나 삭제할 때 표시되는 경고창을 띄우지 않도록 합니다. enableColorBlindHelper: title: 색맹 모드 - description: 색맹이 게임을 플레이하는데 도움을 주는 다양한 도구를 활성화 시킵니다. + description: 색맹 사용자를 위해 게임을 플레이하는 데 도움을 주는 다양한 도구를 활성화합니다. rotationByBuilding: title: 건물 유형에 따른 방향 - description: - 각 건물 유형은 최근에 설정한 방향을 개별적으로 기억합니다. 다른 유형의 건물 배치 간에 자주 방향을 전환할 경우, 이 - 방법이 더 편할 수 있습니다. + description: 각 건물 유형마다 개별적으로 마지막으로 설정했던 방향을 기억하도록 합니다. 다른 건물 변형을 자주 전환하는 경우 이 + 방법이 더욱 편할 수 있습니다. soundVolume: - title: Sound Volume - description: Set the volume for sound effects + title: 효과음 볼륨 + description: 효과음의 볼륨을 조절합니다. musicVolume: - title: Music Volume - description: Set the volume for music + title: 음악 볼륨 + description: 음악 볼륨을 조절합니다. lowQualityMapResources: - title: Low Quality Map Resources - description: Simplifies the rendering of resources on the map when zoomed in to - improve performance. It even looks cleaner, so be sure to try it - out! + title: 저품질 지도 자원 + description: 성능 향상을 위해 지도 확대 시 자원의 렌더링을 단순화합니다. 더욱 깨끗해 보이는 편이니 꼭 해보세요! disableTileGrid: - title: Disable Grid - description: Disabling the tile grid can help with the performance. This also - makes the game look cleaner! + title: 그리드 비활성화 + description: 성능 향상을 위해 타일 그리드를 비활성화할 수 있습니다. 이 역시 게임을 더욱 깨끗하게 보여줍니다! clearCursorOnDeleteWhilePlacing: - title: Clear Cursor on Right Click - description: Enabled by default, clears the cursor whenever you right click - while you have a building selected for placement. If disabled, - you can delete buildings by right-clicking while placing a - building. + title: 우클릭 시 커서 지우기 + description: 기본적으로 활성화되어 있으며, 배치할 건물을 선택한 상태에서 마우스 우클릭 시 커서를 지웁니다. 비활성화할 경우, 건물을 + 커서에 선택한 채로 우클릭하면 바로 건물을 삭제할 수 있습니다. lowQualityTextures: - title: Low quality textures (Ugly) - description: Uses low quality textures to save performance. This will make the - game look very ugly! + title: 저품질 텍스처 (못생김) + description: 성능 향상을 위해 낮은 품질의 텍스처를 사용합니다. 이것은 게임을 아주 못생기게 만들 겁니다! displayChunkBorders: - title: Display Chunk Borders - description: The game is divided into chunks of 16x16 tiles, if this setting is - enabled the borders of each chunk are displayed. + title: 청크 경계 표시 + description: 게임의 청크는 16x16의 타일로 나누어지며, 이 설정을 활성화하면 각 청크의 테두리가 표시됩니다. pickMinerOnPatch: - title: Pick miner on resource patch - description: Enabled by default, selects the miner if you use the pipette when - hovering a resource patch. + title: 자원 패치에서 추출기 즉시 선택 + description: 기본적으로 활성화되어 있으며, 자원 패치에서 피펫 기능을 사용 시 즉시 추출기를 선택합니다. simplifiedBelts: - title: Simplified Belts (Ugly) - description: Does not render belt items except when hovering the belt to save - performance. I do not recommend to play with this setting if you - do not absolutely need the performance. + title: 벨트 단순화 (못생김) + description: 성능 향상을 위해 벨트를 가리킬 때를 제외한 모든 상황에서 벨트 아이템을 렌더링하지 않습니다. 이 기능을 사용할 할 + 정도로 심각한 성능 문제가 일어나지 않는 한, 이 설정을 사용할 필요는 없습니다. enableMousePan: - title: Enable Mouse Pan - description: Allows to move the map by moving the cursor to the edges of the - screen. The speed depends on the Movement Speed setting. + title: 화면 가장자리 패닝 + description: 커서를 화면 가장자리로 옮기면 스크롤되어 지도를 이동할 수 있습니다. 스크롤 속도는 이동 속도 설정에 따릅니다. + zoomToCursor: + title: 커서를 기준으로 줌 + description: 활성화할 경우 화면 줌 인 아웃이 마우스 커서가 있는 지점을 기준이 되며, + 아닐 경우 화면 중앙이 기준이 됩니다. + mapResourcesScale: + title: 지도 자원 크기 + description: 지도를 축소할 때 나타나는 도형의 크기를 제어합니다. rangeSliderPercentage: % keybindings: - title: 키바인딩 - hint: "팁: CTRL, SHIFT, ALT를 활용하세요. 건물을 배치할 때 유용합니다." - resetKeybindings: 키바인딩 리셋 + title: 조작법 + hint: "팁: CTRL, SHIFT, ALT를 적절히 사용하세요! + 건물을 배치할 때 유용합니다." + resetKeybindings: 초기화 categoryLabels: - general: 앱 + general: 애플리케이션 ingame: 게임 navigation: 둘러보기 - placement: 놓기 - massSelect: 다중 선택 + placement: 배치 + massSelect: 대량 선택 buildings: 건물 단축키 - placementModifiers: 배치 옵션 + placementModifiers: 배치 설정 mappings: confirm: 확인 back: 취소 - mapMoveUp: 위로 가기 - mapMoveRight: 오른쪽으로 가기 - mapMoveDown: 밑으로 가기 - mapMoveLeft: 왼쪽으로 가기 - centerMap: 맵 중앙으로 가기 + mapMoveUp: 위쪽 이동 + mapMoveRight: 오른쪽 이동 + mapMoveDown: 아래쪽 이동 + mapMoveLeft: 왼쪽 이동 + centerMap: 맵 중앙으로 이동 mapZoomIn: 확대 mapZoomOut: 축소 - createMarker: 마커 놓기 + createMarker: 마커 배치 menuOpenShop: 업그레이드 menuOpenStats: 통계 - toggleHud: UI보기/숨기기 - toggleFPSInfo: FPS 와 디버그 보기/숨기기 + toggleHud: HUD 토글 + toggleFPSInfo: FPS와 디버그 정보 토글 belt: 컨베이어 벨트 underground_belt: 터널 miner: 추출기 cutter: 절단기 rotater: 회전기 stacker: 결합기 - mixer: 색 혼합기 - painter: 도형 색칠기 + mixer: 혼합기 + painter: 색칠기 trash: 휴지통 rotateWhilePlacing: 회전 - rotateInverseModifier: "Modifier: 대신 반시계방향으로 회전" - cycleBuildingVariants: 변형종 사용 - confirmMassDelete: 대량 삭제 확인 - pasteLastBlueprint: 최근 청사진 붙여넣기 - cycleBuildings: 건물 사이클 + rotateInverseModifier: "변경: 대신 반시계 방향" + cycleBuildingVariants: 형태 전환 + confirmMassDelete: 대량 삭제 + pasteLastBlueprint: 마지막 청사진 붙여넣기 + cycleBuildings: 건물 전환 massSelectStart: 누르고 드래그해서 시작 massSelectSelectMultiple: 여러 곳 선택 massSelectCopy: 영역 복사 @@ -913,108 +830,105 @@ keybindings: placeInverse: 반대 방향으로 벨트 배치 exportScreenshot: 공장 전체를 이미지로 내보내기 mapMoveFaster: 더 빠르게 움직이기 - lockBeltDirection: 벨트 플래너 활성화 - switchDirectionLockSide: "플래너: 방향 바꾸기" - pipette: 스포이드 + lockBeltDirection: 벨트 계획기 활성화 + switchDirectionLockSide: "계획기: 방향 바꾸기" + pipette: 피펫 menuClose: 메뉴 닫기 switchLayers: 레이어 전환 wire: 전선 - balancer: Balancer - storage: Storage - constant_signal: Constant Signal - logic_gate: Logic Gate - lever: Switch (regular) - filter: Filter - wire_tunnel: Wire Crossing - display: Display - reader: Belt Reader - virtual_processor: Virtual Cutter - transistor: Transistor - analyzer: Shape Analyzer - comparator: Compare - item_producer: Item Producer (Sandbox) - copyWireValue: "Wires: Copy value below cursor" + balancer: 밸런서 + storage: 저장고 + constant_signal: 일정 신호 + logic_gate: 논리 회로 + lever: 레버 + filter: 선별기 + wire_tunnel: 전선 교차 장치 + display: 디스플레이 + reader: 벨트 판독기 + virtual_processor: 가상 절단기 + transistor: 트랜지스터 + analyzer: 도형 분석기 + comparator: 비교기 + item_producer: 아이템 생성기 (샌드박스) + copyWireValue: "전선: 커서 아래 값 복사" about: - title: 이 게임의 정보 + title: 게임 정보 body: >- 이 게임은 오픈소스이고, Tobias Springer가 개발했습니다.

- 이 게임을 위해 이바지하고 싶다면, shapez.io on github를 확인하세요.

+ 이 게임을 위해 기여하고 싶다면, shapez.io on github를 확인하세요.

- 이 게임은 디스코드 커뮤니티의 공헌이 없었다면 불가능했을 것입니다. 우리 디스코드 커뮤니티에 꼭 참여해주세요! 디스코드 서버!

+ 이 게임은 Discord 커뮤니티의 공헌이 없었다면 불가능했을 것입니다. 우리 Discord 커뮤니티에 꼭 참여해주세요! Discord 서버!

사운드 트랙 제작자는 Peppsen입니다. - 킹갓제너럴 Peppsen!

마지막으로, 나의 절친 Niklas에게 크나큰 감사를 표합니다 - 우리가 함께 해낸 Factorio가 아니었다면, 이 게임은 존재하지 않았을 겁니다! changelog: - title: 업데이트 기록 + title: 업데이트 목록 demo: features: - restoringGames: 게임 자장 파일 리스토어 하기 - importingGames: 게임 저장 파일 불러오기 - oneGameLimit: 게임 저장 파일 최대 1개 - customizeKeybindings: 키 설정하기 + restoringGames: 게임 세이브 파일 복원 하기 + importingGames: 게임 세이브 파일 가져오기 + oneGameLimit: 게임 세이브 파일 최대 1개 + customizeKeybindings: 조작법 설정하기 exportingBase: 공장 전체를 이미지로 내보내기 settingNotAvailable: 체험판 버전에서 사용 불가 tips: - - The hub accepts input of any kind, not just the current shape! - - Make sure your factories are modular - it will pay out! - - Don't build too close to the hub, or it will be a huge chaos! - - If stacking does not work, try switching the inputs. - - You can toggle the belt planner direction by pressing R. - - Holding CTRL allows dragging of belts without auto-orientation. - - Ratios stay the same, as long as all upgrades are on the same Tier. - - Serial execution is more efficient than parallel. - - You will unlock more variants of buildings later in the game! - - You can use T to switch between different variants. - - Symmetry is key! - - You can weave different tiers of tunnels. - - Try to build compact factories - it will pay out! - - The painter has a mirrored variant which you can select with T - - Having the right building ratios will maximize efficiency. - - At maximum level, 5 extractors will fill a single belt. - - Don't forget about tunnels! - - You don't need to divide up items evenly for full efficiency. - - Holding SHIFT will activate the belt planner, letting you place - long lines of belts easily. - - Cutters always cut vertically, regardless of their orientation. - - To get white mix all three colors. - - The storage buffer priorities the first output. - - Invest time to build repeatable designs - it's worth it! - - Holding CTRL allows to place multiple buildings. - - You can hold ALT to invert the direction of placed belts. - - Efficiency is key! - - Shape patches that are further away from the hub are more complex. - - Machines have a limited speed, divide them up for maximum efficiency. - - Use balancers to maximize your efficiency. - - Organization is important. Try not to cross conveyors too much. - - Plan in advance, or it will be a huge chaos! - - Don't remove your old factories! You'll need them to unlock upgrades. - - Try beating level 20 on your own before seeking for help! - - Don't complicate things, try to stay simple and you'll go far. - - You may need to re-use factories later in the game. Plan your factories to - be re-usable. - - Sometimes, you can find a needed shape in the map without creating it with - stackers. - - Full windmills / pinwheels can never spawn naturally. - - Color your shapes before cutting for maximum efficiency. - - With modules, space is merely a perception; a concern for mortal men. - - Make a separate blueprint factory. They're important for modules. - - Have a closer look on the color mixer, and your questions will be answered. - - Use CTRL + Click to select an area. - - Building too close to the hub can get in the way of later projects. - - The pin icon next to each shape in the upgrade list pins it to the screen. - - Mix all primary colors together to make white! - - You have an infinite map, don't cramp your factory, expand! - - Also try Factorio! It's my favorite game. - - The quad cutter cuts clockwise starting from the top right! - - You can download your savegames in the main menu! - - This game has a lot of useful keybindings! Be sure to check out the - settings page. - - This game has a lot of settings, be sure to check them out! - - The marker to your hub has a small compass to indicate its direction! - - To clear belts, cut the area and then paste it at the same location. - - Press F4 to show your FPS and Tick Rate. - - Press F4 twice to show the tile of your mouse and camera. - - You can click a pinned shape on the left side to unpin it. + - 허브는 단지 필요로 하는 도형은 물론, 모든 형태의 도형 입력을 허용합니다! + - 공장을 짓기 전에 확장 가능성을 고려하세요. 분명 좋은 대가를 받게 될겁니다! + - 공장을 허브에 너무 가까이 짓지 마세요. 그렇지 않으면 크나큰 혼란을 겪을 수 있습니다! + - 결합기가 작동되지 않는다면, 두 도형의 입력을 서로 바꿔보세요. + - 벨트 계획 중 R 키를 눌러 방향을 전환할 수 있습니다. + - CTRL 키를 누른 채 벨트를 배치하면 자동으로 벨트가 연결되지 않게 됩니다. + - 모든 업그레이드의 티어가 동일하다면 모든 공장의 처리 비율도 동일합니다. + - 높은 효율에는 병렬보다 직렬입니다. + - 나중에 더 많은 변형 건물을 잠금 해제할 수 있습니다! + - T 키를 눌러 건물의 다른 형태로 전환할 수 있습니다. + - 대칭이 핵심입니다! + - 서로 다른 티어의 터널은 같은 줄에서 서로 겹쳐지지 않습니다. + - 공장은 가능한 작게, 오밀조밀하게 지으세요. 분명 좋은 대가를 받게 될겁니다! + - 색칠기에는 T키로 선택할 수 있는 반전 형태가 있습니다. + - 각 건물 간의 처리 비율이 동일하다면 그 효율은 극대화됩니다. + - 최대 레벨에서, 한 줄의 벨트를 채우기 위해 다섯 개의 추출기가 필요합니다. + - 터널을 잊지 마세요! + - 완벽한 효율성을 위해 굳이 아이템을 균등하게 배분할 필요는 없습니다. + - SHIFT키를 누르면 벨트 계획기가 활성화되어 + 긴 길이의 벨트 한 줄을 쉽게 배치할 수 있습니다. + - 절단기는 들어오는 도형과 배치된 절단기의 방향에 관계 없이, 언제나 수직으로 자릅니다. + - 흰색은 세 가지의 색소를 혼합해야 합니다. + - 저장고의 양쪽 출력 중 왼쪽 출력이 가장 우선됩니다. + - 가능한 반복적으로 배치할 수 있는 일종의 연쇄 구조를 만들도록 하세요! 그만한 가치가 충분합니다! + - CTRL키를 누른 상태로 건물을 배치하여 여러 건물을 빠르게 배치할 수 있습니다. + - ALT키를 누른 상태로 벨트를 배치하면 벨트의 방향이 반전됩니다. + - 효율성이 핵심입니다! + - 허브에서 멀리 떨어질수록, 도형 패치의 형태가 더욱 복잡해집니다. + - 기계는 속도가 한정되어 있습니다. 최대 효율을 위해 자원을 분할하세요. + - 밸런서를 통해 효율성을 극대화시키세요. + - 구성이 중요합니다. 컨베이어 벨트가 서로 너무 교차되지 않도록 하세요. + - 미래를 계획하세요. 그렇지 않으면 크나큰 혼란을 겪을 수 있습니다! + - 굳이 오래된 공장을 철거하지는 마세요! 업그레이드를 하려면 이전의 도형도 필요합니다! + - 도움을 받기 전에, 혼자서 직접 레벨 20까지 클리어를 시도해보세요! + - 복잡하게 대신 단순하게, 그러면 멀리 나아갈 수 있을 겁니다. + - 나중에 지었던 공장을 재사용해야 할 때가 있을 겁니다. 언제나 공장을 재사용할 수 있도록 계획하세요. + - 간혹 굳이 절단하고 결합하지 않고도, 지도 곳곳에서 필요한 형태의 도형을 찾을 수도 있습니다. + - 온전한 모양의 풍차 모양 도형은 절대 자연적으로 생성되지 않습니다. + - 색소를 최대한 덜 사용하기 위해, 절단하기 전에 먼저 색칠기로 색칠하세요. + - 극한의 확장성을 가진 모듈 공장을 사용한다면 이 넓은 땅은 인간의 관심사에 있는 공간에 불과합니다. + - 별도의 청사진 공장을 만드세요. 언제나 확장성이 중요합니다. + - 혼합기를 잘 살펴보면, 그 질문에 대한 답을 얻을 수 있습니다. + - CTRL + 클릭을 사용하여 영역을 선택합니다. + - 공장을 허브에 가까이 지으면 나중에 거대한 프로젝트에 방해가 될 수 있습니다. + - 업그레이드 목록에 나타나는 도형 옆의 핀 아이콘을 누르면 화면에 고정됩니다. + - 세 가지의 기본 색상을 모두 섞어서 흰색을 만드세요! + - 당신에겐 무한한 땅이 있습니다. 굳이 공간을 적게 쓸 필요는 없으니, 맘껏 확장하세요! + - Factorio도 플레이 해보세요! 제가 가장 좋아하는 게임입니다. + - 4단 절단기는 오른쪽 상단부터 시계 방향으로 차례로 절단합니다. + - 메인 메뉴에서 세이브 파일을 다운로드 할 수 있습니다. + - 이 게임에는 유용한 조작법이 많습니다! 설정 페이지에서 확인해보세요. + - 이 게임에는 다양한 설정이 있습니다. 꼭 확인해 보세요! + - 허브의 마커에는 방향을 나타내는 작은 나침반이 있습니다! + - 벨트를 제거하고 싶다면 해당 영역을 잘라낸 다음, 같은 위치에 붙여 넣으세요. + - F4 키를 누르면 FPS와 틱 비율을 표시합니다. + - F4 키를 두번 누르면 마우스와 카메라의 타일을 표시합니다. + - 왼쪽에 고정된 도형을 클릭하여 고정을 해제할 수 있습니다. diff --git a/translations/base-lt.yaml b/translations/base-lt.yaml index 142d1fc8..d72e40a3 100644 --- a/translations/base-lt.yaml +++ b/translations/base-lt.yaml @@ -197,11 +197,6 @@ dialogs: renameSavegame: title: Rename Savegame desc: You can rename your savegame here. - entityWarning: - title: Performance Warning - desc: You have placed a lot of buildings, this is just a friendly reminder that - the game can not handle an endless count of buildings - So try to - keep your factories compact! ingame: keybindingsOverlay: moveMap: Move @@ -247,27 +242,6 @@ ingame: title: Upgrades buttonUnlock: Upgrade tier: Tier - tierLabels: - - I - - II - - III - - IV - - V - - VI - - VII - - VIII - - IX - - X - - XI - - XII - - XIII - - XIV - - XV - - XVI - - XVII - - XVIII - - XIX - - XX maximumLevel: MAXIMUM LEVEL (Speed x) statistics: title: Statistics @@ -291,10 +265,6 @@ ingame: playtime: Playtime buildingsPlaced: Buildings beltsPlaced: Belts - buttons: - continue: Continue - settings: Settings - menu: Return to menu tutorialHints: title: Need help? showHint: Show hint @@ -673,7 +643,7 @@ storyRewards: title: Balancer desc: The multifunctional balancer has been unlocked - It can be used to build bigger factories by splitting and merging - items onto multiple belts!

+ items
onto multiple belts! reward_merger: title: Compact Merger desc: You have unlocked a merger variant of the @@ -890,6 +860,14 @@ settings: title: Enable Mouse Pan description: Allows to move the map by moving the cursor to the edges of the screen. The speed depends on the Movement Speed setting. + zoomToCursor: + title: Zoom towards Cursor + description: If activated the zoom will happen in the direction of your mouse + position, otherwise in the middle of the screen. + mapResourcesScale: + title: Map Resources Size + description: Controls the size of the shapes on the map overview (when zooming + out). rangeSliderPercentage: % keybindings: title: Keybindings diff --git a/translations/base-nl.yaml b/translations/base-nl.yaml index bcc66885..7de02254 100644 --- a/translations/base-nl.yaml +++ b/translations/base-nl.yaml @@ -204,11 +204,6 @@ dialogs: renameSavegame: title: Hernoem opgeslagen spel desc: Geef je opgeslagen spel een nieuwe naam. - entityWarning: - title: Performance Waarschuwing - desc: Je hebt veel gebouwen geplaatst. Dit is een vriendelijke herinnering dat - het spel niet oneindig veel gebouwen kan bewaren. Probeer je - fabrieken compact te houden. ingame: keybindingsOverlay: moveMap: Beweeg speelveld @@ -254,27 +249,6 @@ ingame: title: Upgrades buttonUnlock: Upgrade tier: Niveau - tierLabels: - - I - - II - - III - - IV - - V - - VI - - VII - - VIII - - IX - - X - - XI - - XII - - XIII - - XIV - - XV - - XVI - - XVII - - XVIII - - XIX - - XX maximumLevel: MAXIMAAL NIVEAU (Snelheid x) statistics: title: Statistieken @@ -298,10 +272,6 @@ ingame: playtime: Speeltijd buildingsPlaced: Gebouwen beltsPlaced: Lopende banden - buttons: - continue: Verder spelen - settings: Opties - menu: Terug naar het Menu tutorialHints: title: Hulp nodig? showHint: Zie hint @@ -683,9 +653,9 @@ storyRewards: uitgebereid in de standalone! reward_balancer: title: Verdeler - desc: De multifunctionele verdeler is vrijgespeeld - Dit gebouw - can worden gebruikt om je fabrieken nog groter te maken door lopende - banden te splitsen en samen te voegen!

+ desc: The multifunctional balancer has been unlocked - It can + be used to build bigger factories by splitting and merging + items onto multiple belts! reward_merger: title: Compacte samenvoeger desc: Je hebt een variant op de samenvoeger van de @@ -907,6 +877,14 @@ settings: description: Schakel deze functie in om met je muis het veld te kunnen bewegen. Plaats de cursor boven, rechts, links of onder om daar naartoe te bewegen. + zoomToCursor: + title: Zoom towards Cursor + description: If activated the zoom will happen in the direction of your mouse + position, otherwise in the middle of the screen. + mapResourcesScale: + title: Map Resources Size + description: Controls the size of the shapes on the map overview (when zooming + out). rangeSliderPercentage: % keybindings: title: Sneltoetsen diff --git a/translations/base-no.yaml b/translations/base-no.yaml index 8de1a8ce..19eb0ae2 100644 --- a/translations/base-no.yaml +++ b/translations/base-no.yaml @@ -203,11 +203,6 @@ dialogs: renameSavegame: title: Rename Savegame desc: You can rename your savegame here. - entityWarning: - title: Performance Warning - desc: You have placed a lot of buildings, this is just a friendly reminder that - the game can not handle an endless count of buildings - So try to - keep your factories compact! ingame: keybindingsOverlay: moveMap: Beveg @@ -253,27 +248,6 @@ ingame: title: Oppgraderinger buttonUnlock: Oppgrader tier: Nivå - tierLabels: - - I - - II - - III - - IV - - V - - VI - - VII - - VIII - - IX - - X - - XI - - XII - - XIII - - XIV - - XV - - XVI - - XVII - - XVIII - - XIX - - XX maximumLevel: MAKS NIVÅ (Hastighet x) statistics: title: Statistikk @@ -297,10 +271,6 @@ ingame: playtime: Spilletid buildingsPlaced: Bygninger beltsPlaced: Samlebånd - buttons: - continue: Fortsett - settings: Innstillinger - menu: Tilbake til hovedmeny tutorialHints: title: Trenger du hjelp? showHint: Vis hint @@ -687,7 +657,7 @@ storyRewards: title: Balancer desc: The multifunctional balancer has been unlocked - It can be used to build bigger factories by splitting and merging - items onto multiple belts!

+ items onto multiple belts! reward_merger: title: Compact Merger desc: You have unlocked a merger variant of the @@ -906,6 +876,14 @@ settings: title: Enable Mouse Pan description: Allows to move the map by moving the cursor to the edges of the screen. The speed depends on the Movement Speed setting. + zoomToCursor: + title: Zoom towards Cursor + description: If activated the zoom will happen in the direction of your mouse + position, otherwise in the middle of the screen. + mapResourcesScale: + title: Map Resources Size + description: Controls the size of the shapes on the map overview (when zooming + out). rangeSliderPercentage: % keybindings: title: Hurtigtaster diff --git a/translations/base-pl.yaml b/translations/base-pl.yaml index 71a5f867..6eb20c81 100644 --- a/translations/base-pl.yaml +++ b/translations/base-pl.yaml @@ -4,8 +4,8 @@ steamPage: mapie, która nie ma końca. discordLinkShort: Oficjalny serwer Discord intro: >- - Shapez.io jest spokojną grą, której celem jest budowanie automatycznych fabryk - produkujących różne kształty geometryczne. + Shapez.io jest spokojną grą, której celem jest budowanie automatycznych + fabryk produkujących różne kształty geometryczne. W miarę zwiększania się poziomów, kształty będą stawać się coraz bardziej skomplikowane, a Twoja fabryka będzie musiała się rozpszetrzenić na mapie o nieskończonej wielkości. @@ -17,7 +17,8 @@ steamPage: title_advantages: Korzyści wersji pełnej advantages: - 12 Nowych poziomów (razem 26 poziomów)s - - 18 Nowych budynków umożliwiających zbudowanie całkowicie automatycznej fabryki! + - 18 Nowych budynków umożliwiających zbudowanie całkowicie + automatycznej fabryki! - 20 Poziomów ulepszeń zapewniających wiele godzin zabawy! - Aktualizacja z przewodami dodająca całkowicie nowy wymiar! - Tryb Ciemny! @@ -42,8 +43,8 @@ steamPage: source_code: Kod źródłowy (GitHub) translate: Pomóż w tłumaczeniu text_open_source: >- - Każdy może pomóc w tworzeniu gry, jestem aktywny wśród społeczności - i próbuję odbierać wszystkie sugestie i brać je pod uwagę, gdzie tylko + Każdy może pomóc w tworzeniu gry, jestem aktywny wśród społeczności i + próbuję odbierać wszystkie sugestie i brać je pod uwagę, gdzie tylko jest to możliwe. Sprawdź moją tablicę Trello, by zobaczyć moje dalsze plany! @@ -122,8 +123,8 @@ dialogs: confirmSavegameDelete: title: Potwierdź usuwanie text: Czy jesteś pewny, że chcesz usunąć poniższy zapis gry?

- '' (poziom )

- Ta akcja nie może być cofnięta! + '' (poziom )

Ta akcja nie może + być cofnięta! savegameDeletionError: title: Błąd usuwania text: "Nie udało się usunąć zapisu:" @@ -177,8 +178,8 @@ dialogs: taśmociągów.
" createMarker: title: Nowy Znacznik - desc: Nadaj mu nazwę. Możesz w niej zawrzeć kod - kształtu (Który możesz wygenerować tutaj) + desc: Nadaj mu nazwę. Możesz w niej zawrzeć kod kształtu (Który + możesz wygenerować tutaj) titleEdit: Edytuj Znacznik markerDemoLimit: desc: Możesz stworzyć tylko dwa własne znaczniki w wersji demo. Zakup pełną @@ -205,11 +206,6 @@ dialogs: renameSavegame: title: Zmień nazwę zapisu gry desc: Tutaj możesz zmienić nazwę zapisu gry. - entityWarning: - title: Uwaga o Wydajności gry - desc: Postawiłeś dużo budynków, to jest tylko przyjazne przypomnienie, że - gra nie może utrzymać nieskończonej ilości budynków - Więc spróbuj - zrobić swoje budowle kompaktowe! ingame: keybindingsOverlay: moveMap: Ruch @@ -265,27 +261,6 @@ ingame: title: Ulepszenia buttonUnlock: Ulepsz tier: Poziom - tierLabels: - - I - - II - - III - - IV - - V - - VI - - VII - - VIII - - IX - - X - - XI - - XII - - XIII - - XIV - - XV - - XVI - - XVII - - XVIII - - XIX - - XX maximumLevel: POZIOM MAKSYMALNY (Szybkość x) statistics: title: Statystyki @@ -309,10 +284,6 @@ ingame: playtime: Czas Gry buildingsPlaced: Budynki beltsPlaced: Taśmociągi - buttons: - continue: Kontynuuj - settings: Ustawienia - menu: Powrót do menu tutorialHints: title: Potrzebujesz pomocy? showHint: Pokaż Wskazówkę @@ -475,13 +446,14 @@ buildings: description: Pozwala na transportowanie energii. second: name: Przewód logiczny - description: - Przekazuje sygnały, które mogą być w postaci przedmiotów, kolorów lub wartości typu Prawda/Fałsz. - Przewody o różnych kolorach nie łączą sie ze sobą. + description: Przekazuje sygnały, które mogą być w postaci przedmiotów, kolorów + lub wartości typu Prawda/Fałsz. Przewody o różnych kolorach nie + łączą sie ze sobą. balancer: default: name: Dystrybutor - description: Wielofunkcyjny - Równo rozdziela wszystkie kształty wejściowe do wyjść. + description: Wielofunkcyjny - Równo rozdziela wszystkie kształty wejściowe do + wyjść. merger: name: Łącznik (kompaktowy) description: Łączy dwa taśmociągi w jeden. @@ -497,8 +469,9 @@ buildings: storage: default: name: Magazyn - description: Przechowuje dodatkowe przedmioty, do pewnej ilości. Może zostać użyty jako - brama przepełnieniowa. Prawe wyjście posiada większy piorytet. + description: Przechowuje dodatkowe przedmioty, do pewnej ilości. Może zostać + użyty jako brama przepełnieniowa. Prawe wyjście posiada większy + piorytet. wire_tunnel: default: name: Skrzyżowanie przewodów @@ -506,63 +479,72 @@ buildings: constant_signal: default: name: Stały sygnał - description: Emituje stały sygnał, który może być w postaci przedmiotu, koloru lub wartości typu Prawda/Fałsz. + description: Emituje stały sygnał, który może być w postaci przedmiotu, koloru + lub wartości typu Prawda/Fałsz. lever: default: name: Przełącznik - description: >- - Może zostać przełączony, by emitować sygnał typu prawda/fałsz, - co pozwala na przykład: na przełączanie filtra przedmiotów. + description: "Może zostać przełączony, by emitować sygnał typu prawda/fałsz, co + pozwala na przykład: na przełączanie filtra przedmiotów." logic_gate: default: name: Bramka AND - description: Emituje sygnał "Prawda", jeżeli oba wejścia są wartością typu Prawda. - (Prawda oznacza dowolny kształt lub kolor, a także sygnał "Prawda") + description: Emituje sygnał "Prawda", jeżeli oba wejścia są wartością typu + Prawda. (Prawda oznacza dowolny kształt lub kolor, a także + sygnał "Prawda") not: name: Bramka NOT - description: Emituje sygnał "Prawda", jeżeli wejście NIE jest wartością typu Prawda. - (Prawda oznacza dowolny kształt lub kolor, a także sygnał "Prawda") + description: Emituje sygnał "Prawda", jeżeli wejście NIE jest wartością typu + Prawda. (Prawda oznacza dowolny kształt lub kolor, a także + sygnał "Prawda") xor: name: Bramka XOR - description: Emituje sygnał "Prawda", jeżeli tylko jedno wejście jest wartością typu Prawda. - (Prawda oznacza dowolny kształt lub kolor, a także sygnał "Prawda") + description: Emituje sygnał "Prawda", jeżeli tylko jedno wejście jest wartością + typu Prawda. (Prawda oznacza dowolny kształt lub kolor, a także + sygnał "Prawda") or: name: Bramka OR - description: Emituje sygnał "Prawda", jeżeli dowolne wejście jest wartością typu Prawda. - (Prawda oznacza dowolny kształt lub kolor, a także sygnał "Prawda") + description: Emituje sygnał "Prawda", jeżeli dowolne wejście jest wartością typu + Prawda. (Prawda oznacza dowolny kształt lub kolor, a także + sygnał "Prawda") transistor: default: name: Tranzystor - description: Przekazuje dolne wejście, jeżeli wejście boczne jest wartością typu Prawda. - (Prawda oznacza dowolny kształt lub kolor, a także sygnał "Prawda") + description: Przekazuje dolne wejście, jeżeli wejście boczne jest wartością typu + Prawda. (Prawda oznacza dowolny kształt lub kolor, a także + sygnał "Prawda") mirrored: name: Tranzystor - description: Przekazuje dolne wejście, jeżeli wejście boczne jest wartością typu Prawda. - (Prawda oznacza dowolny kształt lub kolor, a także sygnał "Prawda") + description: Przekazuje dolne wejście, jeżeli wejście boczne jest wartością typu + Prawda. (Prawda oznacza dowolny kształt lub kolor, a także + sygnał "Prawda") filter: default: name: Filtr - description: Podłącz sygnał, by przekierować wszystkie pasujące przedmioty na górę, a - resztę na prawo. Może być również sterowany za pomocą sygnałów Prawda/Fałsz. + description: Podłącz sygnał, by przekierować wszystkie pasujące przedmioty na + górę, a resztę na prawo. Może być również sterowany za pomocą + sygnałów Prawda/Fałsz. display: default: name: Wyświetlacz - description: Podłącz sygnał, by pokazać go na wyświetlaczu - Może on być kształtem, kolorem - lub wartością Prawda/Fałsz. + description: Podłącz sygnał, by pokazać go na wyświetlaczu - Może on być + kształtem, kolorem lub wartością Prawda/Fałsz. reader: default: name: Czytnik taśmociągów - description: Pozwala na odczytywanie średniej przepustowości taśmociągu. Emituje ostatnio - odczytany przedmiot na warstwie przewodów (gdy ją odblokujesz). + description: Pozwala na odczytywanie średniej przepustowości taśmociągu. Emituje + ostatnio odczytany przedmiot na warstwie przewodów (gdy ją + odblokujesz). analyzer: default: name: Analizator kształtów - description: Analizuje prawą górną ćwiartkę najniższej warstwy i zwraca jej kształt i kolor. + description: Analizuje prawą górną ćwiartkę najniższej warstwy i zwraca jej + kształt i kolor. comparator: default: name: Porównywacz - description: Zwraca sygnał "Prawda", jeżeli oba sygnały są dokładnie takie same. Działa na - kształtach, kolorach i wartościach Prawda/Fałsz. + description: Zwraca sygnał "Prawda", jeżeli oba sygnały są dokładnie takie same. + Działa na kształtach, kolorach i wartościach Prawda/Fałsz. virtual_processor: default: name: Wirtualny Przecinak @@ -572,15 +554,15 @@ buildings: description: Wirtualnie obraca kształt, potrafi to robić w oba kierunki. unstacker: name: Wirtualny Odklejacz - description: Wirtualnie oddziela najwyższą warstwę na prawe wyjście i - resztę na lewe. + description: Wirtualnie oddziela najwyższą warstwę na prawe wyjście i resztę na + lewe. stacker: name: Wirtualny Sklejacz description: Wirtualnie skleja prawy kształt na lewy. painter: name: Wirtualny Malarz - description: Wirtualnie maluje kształt z dolnego wejścia barwnikiem z - prawego wejścia. + description: Wirtualnie maluje kształt z dolnego wejścia barwnikiem z prawego + wejścia. item_producer: default: name: Producent kształtów @@ -589,10 +571,11 @@ buildings: storyRewards: reward_cutter_and_trash: title: Przecinanie Kształtów - desc: Właśnie odblokowałeś przecinaka, który przecina kstałty na pół - od góry na dół bez znaczenia na ich orientację!

- Upewnij się, że usuwasz śmieci - w przeciwnym przypadku maszyna zapcha - się i przestanie działać! Do tego celu dałem ci śmietnik, + desc: Właśnie odblokowałeś przecinaka, który przecina kstałty + na pół od góry na dół bez znaczenia na ich + orientację!

Upewnij się, że usuwasz śmieci - w + przeciwnym przypadku maszyna zapcha się i przestanie + działać! Do tego celu dałem ci śmietnik, który usuwa wszystko, co do niego włożysz! reward_rotater: title: Obracanie @@ -617,8 +600,9 @@ storyRewards: kształt po prawej jest kładziony na ten z lewej!" reward_splitter: title: Rozdzielacz/Łącznik - desc: Właśnie odblokowałeś rozdzielacz - typ dystrybutor, - który akceptuje jedno wejście i rozdziela je na dwa! + desc: Właśnie odblokowałeś rozdzielacz - typ + dystrybutor, który akceptuje jedno wejście i + rozdziela je na dwa! reward_tunnel: title: Tunel desc: Tunel został odblokowany - Możesz teraz prowadzić @@ -632,8 +616,8 @@ storyRewards: title: Wydobycie Łańcuchowe desc: "Właśnie odblokowałeś łańcuchowy ekstraktor! Może on przekazywać swoje surowce do innych ekstraktorów, - byś mógł bardziej efektywnie wydobywać surowce!

PS: Stary ekstraktor - na pasku narzędzi został teraz zastąpiony nowym!" + byś mógł bardziej efektywnie wydobywać surowce!

PS: Stary + ekstraktor na pasku narzędzi został teraz zastąpiony nowym!" reward_underground_belt_tier_2: title: Tunel Poziomu II desc: Odblokowano nowy wariant tunelu - Posiada większy @@ -650,17 +634,20 @@ storyRewards: raz, pobierając wyłącznie jeden barwnik! reward_storage: title: Magazyn - desc: Właśnie odblokowałeś magazyn - Pozwala na przecowywanie przedmiotów, - do pewnej ilości!

Prawe wyjście posiada większy piorytet, więc może być on - użyty jako brama przepełnieniowa! + desc: Właśnie odblokowałeś magazyn - Pozwala na przecowywanie + przedmiotów, do pewnej ilości!

Prawe wyjście posiada większy + piorytet, więc może być on użyty jako brama + przepełnieniowa! reward_freeplay: title: Tryb swobodny desc: Udało ci się! Odblokowałeś tryb swobodny! To oznacza, że - kształty są teraz losowo generowane!

- Od teraz budynek główny będzie wymagał odpowiedniej przepustowości - kształtów, zatem sugeruję budowę maszyny, która będzie atuomatycznie dostarczała - wymagany kształt!

Budynek główny emituje wymagany kształt na warstwie - przewodów, więc wystarczy analizować ten sygnał i konfigurować fabrykę bazując na nim. + kształty są teraz losowo generowane!

Od + teraz budynek główny będzie wymagał odpowiedniej + przepustowości kształtów, zatem sugeruję budowę + maszyny, która będzie atuomatycznie dostarczała wymagany + kształt!

Budynek główny emituje wymagany kształt na warstwie + przewodów, więc wystarczy analizować ten sygnał i konfigurować + fabrykę bazując na nim. reward_blueprints: title: Schematy desc: Możesz teraz kopiować i wklejać części swojej fabryki! @@ -680,62 +667,69 @@ storyRewards: pełnej! reward_balancer: title: Dystrybutor - desc: Właśnie odblokowałeś wielofunkcyjny dystrybutor - Pozwala - na budowę większych fabryk poprzez rozdzielanie i łączenie - taśmociągów!

+ desc: The multifunctional balancer has been unlocked - It can + be used to build bigger factories by splitting and merging + items onto multiple belts! reward_merger: title: Kompaktowy łącznik - desc: Właśnie odblokowałeś łącznik - typ dystrybutora, - który akceptuje dwa wejścia i łączy je na jeden taśmociąg! + desc: Właśnie odblokowałeś łącznik - typ + dystrybutora, który akceptuje dwa wejścia i łączy + je na jeden taśmociąg! reward_belt_reader: title: Czytnik taśmociągów desc: Właśnie odblokowałeś czytnik taśmociągów! Pozwala ci na - mierzenie przepustowości taśmociągu.

Czekaj tylko, aż odblokujesz przewody - logiczne - dopiero wtedy staje się bardzo użyteczny! + mierzenie przepustowości taśmociągu.

Czekaj tylko, aż + odblokujesz przewody logiczne - dopiero wtedy staje się bardzo + użyteczny! reward_rotater_180: title: Obracacz (180°) - desc: Właśnie odblokowałeś kolejny wariant obraczacza! - Pozwala ci na - obrócenie kształtu o 180 stopni! + desc: Właśnie odblokowałeś kolejny wariant obraczacza! - + Pozwala ci na obrócenie kształtu o 180 stopni! reward_display: title: Wyświetlacz - desc: "Właśnie odblokowałeś Wyświetlacz - Podłącz sygnał na warstwie - przewodów, by go zwizualizować!

PS: Czy zauważyłeś, że czytnik taśmociągów - i magazyn emitują ostatni przedmiot jako sygnał? Spróbuj wyświetlić go na wyświetlaczu!" + desc: "Właśnie odblokowałeś Wyświetlacz - Podłącz sygnał na + warstwie przewodów, by go zwizualizować!

PS: Czy zauważyłeś, + że czytnik taśmociągów i magazyn emitują ostatni przedmiot jako + sygnał? Spróbuj wyświetlić go na wyświetlaczu!" reward_constant_signal: title: Stały sygnał - desc: >- - Właśnie odblokowałeś budynek emitujący stały sygnał na warstwie przewodów! - Jest on przydatny na przykład: do ustawiania filtrów

- Sygnał może być kształtem, kolorem lub wartością - Prawda/Fałsz. + desc: "Właśnie odblokowałeś budynek emitujący stały sygnał na + warstwie przewodów! Jest on przydatny na przykład: do ustawiania + filtrów

Sygnał może być + kształtem, kolorem lub wartością + Prawda/Fałsz." reward_logic_gates: title: Bramki logiczne - desc: Właśnie odblokowałeś bramki logiczne! Nie musisz być z tego powodu - podekscytowany, ale one są bardzo fajne!

Z tymi bramkami możesz teraz wykonywać - operacje AND, OR, XOR i NOT.

Dodatkowo dałem ci tranzystor! + desc: Właśnie odblokowałeś bramki logiczne! Nie musisz być z + tego powodu podekscytowany, ale one są bardzo fajne!

Z tymi + bramkami możesz teraz wykonywać operacje AND, OR, XOR i NOT.

+ Dodatkowo dałem ci tranzystor! reward_virtual_processing: title: Wirtualne przetwarzanie - desc: Właśnie dałem ci mnóstwo budynków, które pozwolą ci - symulować przetwarzanie kształtów!

Możesz teraz symulować - przecinaka, obracacza, sklejacza i wiele więcej na warstwie przewodów! - Teraz masz trzy opcje na kontynuację gry:

- - Zbuduj zautomatyzowaną maszynę, która stworzy każdy kstałt - ządany przez budynek główny (Polecam tą opcję!).

- Zbuduj - coś ciekawego za pomocą przewodów.

- Kontynuuj zwykłą - rozgrywkę.

Cokolwiek wybierzesz, pamiętaj by się dobrze bawić! + desc: Właśnie dałem ci mnóstwo budynków, które pozwolą ci symulować + przetwarzanie kształtów!

Możesz teraz symulować + przecinaka, obracacza, sklejacza i wiele więcej na warstwie + przewodów! Teraz masz trzy opcje na kontynuację gry:

- + Zbuduj zautomatyzowaną maszynę, która stworzy każdy + kstałt ządany przez budynek główny (Polecam tą opcję!).

- + Zbuduj coś ciekawego za pomocą przewodów.

- Kontynuuj zwykłą + rozgrywkę.

Cokolwiek wybierzesz, pamiętaj by się dobrze + bawić! reward_wires_painter_and_levers: title: Przewody i poczwórny malarz desc: "Właśnie odblokowałeś Warstwę przewodów: Jest to osobna - warstwa położnoa na istniejącej, która wprowadza wiele nowych mechanik!

- Na początek dałem ci Poczwórnego Malarza - Podłącz ćwiartki, które - chcesz pomalować na warstwie przewodów!

By przełączyć się na warstwę przewodów, + warstwa położnoa na istniejącej, która wprowadza wiele nowych + mechanik!

Na początek dałem ci Poczwórnego + Malarza - Podłącz ćwiartki, które chcesz pomalować na + warstwie przewodów!

By przełączyć się na warstwę przewodów, wciśnij E." reward_filter: title: Filtr przedmiotów - desc: Właśnie odblokowałeś Filtr Przedmiotów! Będzie on przekirowywał - przedmioty do górnego lub prawego wyjścia, zależnie od tego, czy pasują one do - sygnału z warstwy przewodów.

Możesz również przekazać sygnał typu Prawda/Fałsz, - by całkowicie go włączyć lub wyłączyć. + desc: Właśnie odblokowałeś Filtr Przedmiotów! Będzie on + przekirowywał przedmioty do górnego lub prawego wyjścia, zależnie od + tego, czy pasują one do sygnału z warstwy przewodów.

Możesz + również przekazać sygnał typu Prawda/Fałsz, by całkowicie go włączyć + lub wyłączyć. reward_demo_end: title: Koniec wersji demo desc: Dotarłeś do końca wersji demo! @@ -860,38 +854,48 @@ settings: description: Ustaw głośność muzyki lowQualityMapResources: title: Zasoby mapy o niskiej jakości - description: Upraszcza renderowanie zasobów na mapie, gdy kamera jest przybliżona, - by zwiększyć wydajność. Wygląda to nawet ładnie, więc wypróbuj tą funkcję! + description: Upraszcza renderowanie zasobów na mapie, gdy kamera jest + przybliżona, by zwiększyć wydajność. Wygląda to nawet ładnie, + więc wypróbuj tą funkcję! disableTileGrid: title: Wyłącz siatkę description: Wyłączenie siatki może pomóc z wydajnością. Oprócz tego, poprawia wygląd gry! clearCursorOnDeleteWhilePlacing: title: Wyczyść kursor przy kliknięciu PPM - description: Domyślnie włączone, resetuje wybrany budynek do budowy, - gdy klikasz prawym przyciskiem myszy. Jeżeli to wyłączysz, możesz - usuwać budynki podczas budowania używając tego samego przycisku. + description: Domyślnie włączone, resetuje wybrany budynek do budowy, gdy klikasz + prawym przyciskiem myszy. Jeżeli to wyłączysz, możesz usuwać + budynki podczas budowania używając tego samego przycisku. lowQualityTextures: title: Tekstury niskiej jakości (Brzydkie) description: Używa niskej jakości tekstur, by zwiększyć wydajność. Spowoduje to, że gra będzie wyglądać bardzo brzydko! displayChunkBorders: title: Wyświetl granice chunków - description: Gra jest podzielona na chunki o wielkości 16x15 kratek. - Włączenie tego ustawienia powoduje wyświetlenie granicy każdego chunku. + description: Gra jest podzielona na chunki o wielkości 16x15 kratek. Włączenie + tego ustawienia powoduje wyświetlenie granicy każdego chunku. pickMinerOnPatch: title: Wybierz ekstraktor zamiast źródła - description: Domyślnie włączone, wybiera ekstraktor, jeżeli spróbujesz - wybrać źródło surowców za pomocą pipety + description: Domyślnie włączone, wybiera ekstraktor, jeżeli spróbujesz wybrać + źródło surowców za pomocą pipety simplifiedBelts: title: Uproszczone taśmociągi (Brzydkie) - description: Nie renderuje przedmiotów na taśmociągach, jeżeli nie - są zaznaczone kursorem, by zwiększyć wydajność. Nie zalecam - używać tego ustawienia, chyba że absolutnie potrzebujesz wydajności. + description: Nie renderuje przedmiotów na taśmociągach, jeżeli nie są zaznaczone + kursorem, by zwiększyć wydajność. Nie zalecam używać tego + ustawienia, chyba że absolutnie potrzebujesz wydajności. enableMousePan: title: Włącz przesuwanie myszą - description: Pozwala na poruszanie kamerą poprzez przez przesuwanie kursora - do granicy ekranu. Szybkość jest zależna od ustawienia Prędkość poruszania. + description: Pozwala na poruszanie kamerą poprzez przez przesuwanie kursora do + granicy ekranu. Szybkość jest zależna od ustawienia Prędkość + poruszania. + zoomToCursor: + title: Zoom towards Cursor + description: If activated the zoom will happen in the direction of your mouse + position, otherwise in the middle of the screen. + mapResourcesScale: + title: Map Resources Size + description: Controls the size of the shapes on the map overview (when zooming + out). rangeSliderPercentage: % keybindings: title: Klawiszologia @@ -992,13 +996,16 @@ demo: exportingBase: Eksportowanie całej fabryki jako zrzut ekranu settingNotAvailable: Niedostępne w wersji demo. tips: - - Budynek główny akceptuje wejście każdego rodzaju - nie tylko aktualny kształt! + - Budynek główny akceptuje wejście każdego rodzaju - nie tylko aktualny + kształt! - Upewnij się, że twoje fabryki są modularne - opłaci się to! - Nie buduj zbyt blisko budynku głównego, albo będziesz miał wielki chaos! - Jeżeli łączenie kształtów nie działa, spróbuj zamienić wejścia. - Możesz zmienić kierunek planera taśmociągów poprzez naciśnięcie R. - - Przytrymanie CTRL pozwala na przeciąganie taśmociągów bez automatycznego zmieniania kierunków. - - Stosunku pozostają takie same, dopóki wszystkie ulepszenia są na tym samym poziomie. + - Przytrymanie CTRL pozwala na przeciąganie taśmociągów bez + automatycznego zmieniania kierunków. + - Stosunku pozostają takie same, dopóki wszystkie ulepszenia są na tym samym + poziomie. - Seryjne wykonanie jest badziej wydajne niż równoległe. - Odblokujesz więcej wariantów budynków później w rozgrywce! - Możesz użyć T, by zmienić warianty budynków. @@ -1010,40 +1017,53 @@ tips: - Na najwyższym poziomie, 5 ekstraktorów zapełni pojedynczy taśmociąg. - Nie zapomnij o tunelach! - Nie musisz dzielić równo przedmiotów, by osiągnąć pełną wydajność. - - Przytrymanie SHIFT aktywuje planera taśmociągów, pozwalającego ci na łatwe budowanie długich taśmociągów. + - Przytrymanie SHIFT aktywuje planera taśmociągów, pozwalającego ci + na łatwe budowanie długich taśmociągów. - Przecinaki zawsze tną pionowo, nie zważając na ich orientację. - Zmieszanie wszystich 3 barwników daje biały barwnik. - Pierwsze wyjście z magazynu ma najwyższy piorytet. - Zainwestuj czas w budowanie powtarzalnych układów fabryk - warto! - - Przytrymanie CTRL pozwala na układanie wielu budynków tego samego typu. + - Przytrymanie CTRL pozwala na układanie wielu budynków tego samego + typu. - Możesz przytrzymać ALT, by odwrócić kierunek układanych taśmociągów. - Wydajność to klucz do sukcesu! - Kształty położone dalej od budynku głównego są bardziej skomplikowane. - - Maszyny mają limitowaną prędkość, podziel wejścia między wiele ich, by zmaksymalizować wydajność. + - Maszyny mają limitowaną prędkość, podziel wejścia między wiele ich, by + zmaksymalizować wydajność. - Użyj dystrybutorów, by zmaksymalizować wydajność. - Organizacja jest ważna. Próbuj nie krzyżować zbyt wielu taśmociągów. - Planuj na przyszłość, albo wszystko będzie wielkim chaosem! - - Nie usuwaj swoich starych fabryk! Będziesz ich potrzebował, by odblokować ulepszenia. + - Nie usuwaj swoich starych fabryk! Będziesz ich potrzebował, by odblokować + ulepszenia. - Spróbuj przejść poziom 20 samemu, zanim zaczniesz szukać pomocy! - Nie komplikuj rzeczy, próbuj budować proste rzeczy, a zajdziesz daleko. - - Możesz potrzebować ponownie używać swoich fabryk w późniejszej fazie rozgrywki. Planuj swoje fabryki, by były zdatne do ponownego użycia. - - Czasami znajdziesz wymagany kształt na mapie, bez potrzeby tworzenia go za pomoca sklejaczy. + - Możesz potrzebować ponownie używać swoich fabryk w późniejszej fazie + rozgrywki. Planuj swoje fabryki, by były zdatne do ponownego użycia. + - Czasami znajdziesz wymagany kształt na mapie, bez potrzeby tworzenia go za + pomoca sklejaczy. - Pełne "wiatraczki" nigdy nie pojawią się naturalnie na mapie. - Maluj swoje kształty przed przecianiem dla maksymalnej wydajności. - - Z modułami, miejsce jest tylko tym, co postrzegamy; troska dla śmiertelników + - Z modułami, miejsce jest tylko tym, co postrzegamy; troska dla + śmiertelników - Zbuduj osobną fabrykę schematów. Są one bardzo potrzebne do modułów. - - Obejrz dokładnie mikser kolorów, a wszystkie twoje pytania zostaną rozwiązane. + - Obejrz dokładnie mikser kolorów, a wszystkie twoje pytania zostaną + rozwiązane. - Przytrzymaj CTRL i przeciągnij, by zaznaczyć obszar - - Budowanie zbyt blisko budynku głównego może przeszkodzić ci w późniejszych projektach. - - Ikona pinezki przy każdym kształcie na liście ulepszeń przypina je na ekranie. + - Budowanie zbyt blisko budynku głównego może przeszkodzić ci w późniejszych + projektach. + - Ikona pinezki przy każdym kształcie na liście ulepszeń przypina je na + ekranie. - Połącz wszystkie głowne kolory, by stworzyć biały! - Masz nieskończoną mapę, nie ściskaj swojej fabryki, rozszerzaj ją! - Spróbuj też Factorio! To moja ulubiona gra. - - Poczwórny przecinak tnie zgodnie z ruchem wskazówek zegara, zaczynając do prawej górnej ćwiartki! + - Poczwórny przecinak tnie zgodnie z ruchem wskazówek zegara, zaczynając do + prawej górnej ćwiartki! - Możesz pobrać swoje zapisy gry w głownym menu gry! - - Ta gra posiada dużo użytecznych skrótów klawiszowych! Sprawdź stronę ustawień! + - Ta gra posiada dużo użytecznych skrótów klawiszowych! Sprawdź stronę + ustawień! - Ta gra posiada mnóstwo ustawień, sprawdź je! - - Znacznik do budynku główneko posiada mały kompas, wskazujący do niego kierunek! + - Znacznik do budynku główneko posiada mały kompas, wskazujący do niego + kierunek! - By wyczyścić taśmociągi, wytnij obszar i wklej go w tym samym miejscu. - Naciśnij F4, by zobaczyć ilość FPS i tempo ticków. - Naciśnij F4 dwa razy, by zobaczyć kratkę twojej myszy i kamery. diff --git a/translations/base-pt-BR.yaml b/translations/base-pt-BR.yaml index 95a60cf4..091adb32 100644 --- a/translations/base-pt-BR.yaml +++ b/translations/base-pt-BR.yaml @@ -119,9 +119,8 @@ dialogs: text: "Houve uma falha ao carregar seu jogo salvo:" confirmSavegameDelete: title: Confirmar exclusão - text: Tem certeza que deseja deletar o jogo a seguir?

- '' no nível

Isso não - pode ser revertido! + text: Tem certeza que deseja deletar o jogo a seguir?

'' + no nível

Isso não pode ser revertido! savegameDeletionError: title: Falha ao deletar text: "Houve uma falha ao deletar seu jogo salvo:" @@ -199,11 +198,6 @@ dialogs: renameSavegame: title: Renomear Save desc: Você pode renomear seu save aqui. - entityWarning: - title: Aviso de Performance - desc: Você colocou muitas construções, isso é apenas um aviso amigável de que o - jogo não suporta construções infinitas - Então tente manter suas - fábricas compactas! ingame: keybindingsOverlay: moveMap: Mover @@ -259,27 +253,6 @@ ingame: title: Melhorias buttonUnlock: Melhorar tier: Nível - tierLabels: - - I - - II - - III - - IV - - V - - VI - - VII - - VIII - - IX - - X - - XI - - XII - - XIII - - XIV - - XV - - XVI - - XVII - - XVIII - - XIX - - XX maximumLevel: NÍVEL MÁXIMO (Velocidade x) statistics: title: Estatísticas @@ -303,10 +276,6 @@ ingame: playtime: Tempo de Jogo buildingsPlaced: Construções beltsPlaced: Esteiras - buttons: - continue: Continuar - settings: Configurações - menu: Voltar ao menu tutorialHints: title: Quer ajuda? showHint: Mostrar dica @@ -466,9 +435,9 @@ buildings: description: Colore as formas na entrada esquerda com a cor da entrada superior. quad: name: Pintor (Quádruplo) - description: Permite que você pinte cada quadrante da forma individualmente. Apenas - entrada com um sinal verdadeiro no plano de fios - serão pintadas! + description: Permite que você pinte cada quadrante da forma individualmente. + Apenas entradas com um sinal verdadeiro no + plano de fios serão pintadas! trash: default: name: Lixo @@ -593,12 +562,11 @@ buildings: storyRewards: reward_cutter_and_trash: title: Cortando formas - desc: Você acabou de desbloquear o cortador, que corta formas pela metade - de cima para baixo independente de sua + desc: Você acabou de desbloquear o cortador, que corta formas + pela metade de cima para baixo independente de sua orientação!

Lembre-se de se livrar do lixo, caso - contrário, a máquina irá entupir - Por isso - eu te dei o lixo, que destrói - tudo que você coloca nele! + contrário, a máquina irá entupir - Por isso eu te + dei o lixo, que destrói tudo que você coloca nele! reward_rotater: title: Rotação desc: O rotacionador foi desbloqueado! Gira as formas no @@ -620,10 +588,10 @@ storyRewards: da outra, serão fundidas. Caso contrário, a entrada direita é empilhada em cima da entrada esquerda! reward_splitter: - title: Distribuidor - desc: You have unlocked a splitter variant of the - balancer - It accepts one input and splits them - into two! + title: Divisor + desc: Você desbloqueou uma variante divisora do + balanceador - Ela aceita uma entrada e a divide em + duas saídas! reward_tunnel: title: Túnel desc: O túnel foi desbloqueado - Agora você pode transportar @@ -662,13 +630,15 @@ storyRewards: output, so you can also use it as an overflow gate! reward_freeplay: title: Modo Livre - desc: Você conseguiu! Você desbloqueou o modo livre! Isso significa - que formas agora são geradas aleatóriamente!

- Já que o HUB vai precisar de uma entrada constante a partir de - agora, eu altamente recomendo que você construa uma máquina que entregue - automaticamente as formas pedidas!

O HUB emite a forma pedida - no plano dos fios, então tudo que você precisa fazer é analizá-la e - automaticamente configurar sua fábrica baseado nessa análise. + desc: Você conseguiu! Você desbloqueou o modo livre! Isso + significa que formas agora são geradas + aleatóriamente!

Já que o HUB vai precisar + de uma entrada constante a partir de agora, eu + altamente recomendo que você construa uma máquina que entregue + automaticamente as formas pedidas!

O HUB emite a forma + pedida no plano dos fios, então tudo que você precisa fazer é + analizá-la e automaticamente configurar sua fábrica baseado nessa + análise. reward_blueprints: title: Projetos desc: Agora você pode copiar e colar partes de sua fábrica! @@ -687,9 +657,9 @@ storyRewards: desc: Parabéns! Aliás, mais conteúdo vindo na versão completa! reward_balancer: title: Balanceador - desc: O balanceador multifuncional foi desbloqueado - Ele pode - ser usado para construir fábricas maiores dividindo e unindo - itens em múltiplas esteiras!

+ desc: The multifunctional balancer has been unlocked - It can + be used to build bigger factories by splitting and merging + items onto multiple belts! reward_merger: title: Unificador Compacto desc: Você desbloqueou uma variante unificadora do @@ -706,10 +676,10 @@ storyRewards: Ele permite que você rotacione uma forma em 180 graus (Surpresa! :D) reward_display: title: Display - desc: "Você desbloqueou o Display - Conecte um sinal no - plano de fios para poder vê-lo!

PS: Você percebeu que ambos o leitor - de esteiras e o armazenamento emitem o último item lido? Tente mostrar - isso em um display!" + desc: "Você desbloqueou o Display - Conecte um sinal no plano + de fios para poder vê-lo!

PS: Você percebeu que ambos o + leitor de esteiras e o armazenamento emitem o último item lido? + Tente mostrar isso em um display!" reward_constant_signal: title: Sinal Constante desc: Você desbloqueou a construção que emite um sinal @@ -915,6 +885,14 @@ settings: title: Habilitar Movimento com o Mouse description: Permite mover o mapa ao mover o cursor para as bordas da tela. A velocidade depende da configuração Velocidade de Movimento. + zoomToCursor: + title: Zoom towards Cursor + description: If activated the zoom will happen in the direction of your mouse + position, otherwise in the middle of the screen. + mapResourcesScale: + title: Map Resources Size + description: Controls the size of the shapes on the map overview (when zooming + out). rangeSliderPercentage: % keybindings: title: Controles diff --git a/translations/base-pt-PT.yaml b/translations/base-pt-PT.yaml index af3ea93c..234d1883 100644 --- a/translations/base-pt-PT.yaml +++ b/translations/base-pt-PT.yaml @@ -4,48 +4,49 @@ steamPage: complexas num mapa infinito. discordLinkShort: Discord Oficial intro: >- - Shapez.io é um jogo relaxante onde tens que construir fábricas para a produção automatizada de formas geométricas. + Shapez.io é um jogo relaxante onde tens de construir fábricas para + autumatizar a produção de formas geométricas. - Enquanto o nível aumenta, as formas ficam cada vez mais e mais complexas, e tens de te expandir por um mapa infinito. + Com o aumento do nível, as formas começam a ser cada vez mais e mais complexas, e tu terás de te expandir num mapa infinito. - E como se isso não fosse suficiente, também tens de produzir cada vez mais para satisfazer a demanda - a única coisa que ajuda é aumentar! + E como se isso não fosse suficiente, tu também terás de produzir de forma exponencial para satisfazeres as tuas necessidades - a única coisa que ajuda é aumentar! - Enquanto só podes processar formas no inicio, vais ter de as colorir mais tarde - para isto vais ter de extrair e juntar cores! + Embora no inicio apenas tenhas de processar formas, mais tarde, vais ter de as colorir - para isto terás de extrair e misturar cores! - Comprar o jogo na Steam dá-te acesso à versão completa, mas também podes jogar a demo em shapez.io primeiro e decidir depois! - title_advantages: Vantagens Standalone + Comprar o jogo na Steam dar-te-á acesso à versão completa, mas também podes jogar a versão demo em shapez.io primeiro e decidir mais tarde! + title_advantages: Vantagens da versão completa advantages: - - 12 Novos Níveis para um total de 26 níveis - - 18 Novos Edifícios para uma fábrica totalmente automatizada! - - 20 Níveis de Upgrade para muitas horas de diversão! - - Atualização de Fios para uma completamente nova dimensão! - - Modo Escuro! - - Savegames Ilimitados - - Marcos Ilimitados - - Suporta-me! ❤️ + - 12 Novos Níveis para um total de 26 Níves + - 18 Novas Construções para uma fábrica totalmente automatizada! + - 20 Níveis de Melhoria para muitas horas de diversão! + - Atualização de Fios para uma dimensão totalmente nova! + - Modo escuro! + - Savegames ilimitados + - Marcos ilimitados + - Ajuda-me! ❤️ title_future: Conteúdo Planeado planned: - - Blueprint Library (Exclusivo Standalone) - - Steam Achievements + - Biblioteca Blueprint (Exclusivo na versão Completa) + - Conquistas na Steam - Modo Puzzle - - Minimap - - Mods + - Minimapa + - Modos - Modo Sandbox - - ... e muito mais! - title_open_source: Este jogo é código aberto! + - ... e Muito Mais! + title_open_source: Este jogo é open source (código aberto)! title_links: Links links: discord: Discord Oficial - roadmap: Roadmap + roadmap: Roteiro de desenvolvimento subreddit: Subreddit - source_code: Source code (GitHub) - translate: Ajuda a traduzir + source_code: Código Fonte (GitHub) + translate: Ajuda a Traduzir text_open_source: >- - Qualquer pessoa pode contribuir, estou ativamente envolvido na comunidade e - tento rever todas as sugestões e levo o feedback em consideração sempre que possível. - - Verifique o meu trello board para o roadmap completo! + Qualquer um pode contribuir, estou ativamente envolvido na comunidade e + tento ver todas as sugestões e ter em consideração o feedback recebido + assim que possível. + Segue o meu trello board para veres todo o roteiro de desenvolvimento! global: loading: A Carregar error: Erro @@ -97,7 +98,6 @@ mainMenu: madeBy: Criado por subreddit: Reddit savegameUnnamed: Sem Nome - dialogs: buttons: ok: OK @@ -122,9 +122,8 @@ dialogs: text: "Erro ao carregar o teu savegame:" confirmSavegameDelete: title: Confirmar eliminação - text: Tens que queres apagar o seguinte jogo?

- '' no Nível

Isto não pode - desfeito! + text: Tens a certeza que queres apagar o seguinte jogo?

'' + no nível

Isto não pode ser desfeito! savegameDeletionError: title: Erro de eliminação text: "Erro ao eliminar o teu savegame:" @@ -179,8 +178,9 @@ dialogs: class='keybinding'>ALT: Inverte as posições.
" createMarker: title: Novo Marco - desc: Dá-lhe um nome com significado, também poderás adicionar um - pequeno código de uma forma (Que podes gerar aqui here) + desc: Dá-lhe um nome com significado, também poderás adicionar um + pequeno código de uma forma. (Pode ser gerado + aqui) titleEdit: Editar Marco markerDemoLimit: desc: Apenas podes criar dois marcos na versão Demo. Adquire o jogo completo @@ -199,17 +199,13 @@ dialogs: desc: Não consegues pagar para colar esta área! Tens a certeza que pretendes cortá-la? editSignal: - title: Definir Sinal + title: Define o Sinal descItems: "Escolhe um item pre-definido:" - descShortKey: ... ou entra o atalho duma forma (Que podes - gerar aqui) + descShortKey: ... ou insere o pequeno código de uma forma (Pode + ser gerado aqui) renameSavegame: title: Renomear Savegame desc: Podes renomear o teu savegame aqui. - entityWarning: - title: Aviso de Desempenho - desc: Tu colocaste muitos edifícios, isto é apenas um lembrete amigável que o jogo não consegue aguentar com um número infinito de edifícios - Tenta meter as tuas fábricas compactas! - ingame: keybindingsOverlay: moveMap: Mover @@ -255,27 +251,6 @@ ingame: title: Upgrades buttonUnlock: Upgrade tier: Nível - tierLabels: - - I - - II - - III - - IV - - V - - VI - - VII - - VIII - - IX - - X - - XI - - XII - - XIII - - XIV - - XV - - XVI - - XVII - - XVIII - - XIX - - XX maximumLevel: NÍVEL MÁXIMO (Velocidade x) statistics: title: Estatísticas @@ -295,15 +270,10 @@ ingame: second: / s minute: / m hour: / h - settingsMenu: playtime: Tempo de jogo buildingsPlaced: Construções beltsPlaced: Tapetes rolantes - buttons: - continue: Continuar - settings: Definições - menu: Voltar ao menu tutorialHints: title: Precisas de ajuda? showHint: Mostrar dica @@ -345,42 +315,41 @@ ingame: empty: Vazio copyKey: Chave de cópia connectedMiners: - one_miner: 1 Minerador - n_miners: Mineradores - limited_items: Limitado a + one_miner: 1 Extrator + n_miners: Extratores + limited_items: Limite de watermark: title: Versão Demo - desc: Clica aqui para ver as vantagens da versão Steam! - get_on_steam: Compra na steam + desc: Clica aqui para veres as vantagens da versão na Steam! + get_on_steam: Compra na Steam standaloneAdvantages: title: Obtém a versão completa! no_thanks: Não, obrigado! points: levels: title: 12 Novos Níveis - desc: Para um total de 26 níveis! + desc: Para um total de 26 Níveis! buildings: - title: 18 Novos Edifícios - desc: Automatiza completamente a tua fábrica! + title: 18 Novas contruções + desc: Para uma fábrica totalmente automatizada! savegames: title: Savegames ∞ - desc: Quantos o teu coração quiser! + desc: Tantos quanto o teu corção desejar! upgrades: - title: 20 Níveis de Upgrades - desc: Esta versão demo só tem 5! + title: 20 Níveis de melhoria + desc: Nesta versão demo apenas tens 5! markers: title: Marcos ∞ - desc: Nunca te percas na tua fábrica! + desc: Nunca te percas na tua Fábrica! wires: title: Fios - desc: Uma completamente nova dimensão! + desc: Uma dimensão totalmente nova!! darkmode: title: Modo Escuro - desc: Para de magoar os meus olhos! + desc: Não magoes os teus olhos! support: - title: Suporta-me - desc: Eu desenvolvo o jogo no meu tempo livre! - + title: Ajuda-me + desc: Eu desenvolvo este jogo no meu tempo livre! shopUpgrades: belt: name: Tapetes, Distribuidores e Túneis @@ -398,7 +367,7 @@ buildings: belt: default: name: Tapete Rolante - description: Transporta items. Mantém pressionado e arrasta para colocar vários. + description: Transporta itens. Mantém pressionado e arrasta para colocar vários. miner: default: name: Extrator @@ -431,10 +400,10 @@ buildings: description: Roda as formas 90º no sentido dos ponteiros do relógio. ccw: name: Rodar (CCW) - description: Roda as formas 90º no sentido contrário ao dos ponteiros do relógio. - + description: Roda as formas 90º no sentido contrário ao dos ponteiros do + relógio. rotate180: - name: Rodar (180) + name: Rodar (180º) description: Roda as formas 180º. stacker: default: @@ -456,7 +425,9 @@ buildings: entrada superior. quad: name: Pintor (Quádruplo) - description: Pinta cada quadrante da forma geométrica com uma cor diferente. Apenas slots com um sinal verdadeiro na camada de fios vão ser pintados! + description: Permite colorir cada quadrante da forma individualmente. Apenas + entradas com um sinal verdadeira na camada de + fios irá ser pintada! mirrored: name: Pintor description: Pinta a forma geométrica da entrada esquerda com a cor da entrada @@ -465,28 +436,31 @@ buildings: default: name: Lixo description: Aceita entradas de todos os lados e destrói-os. Para sempre. - hub: deliver: Entrega toUnlock: para desbloquear levelShortcut: NVL + endOfDemo: Fim do Demo wire: default: name: Fio Elétrico - description: Transfere sinais, que podem ser itens, cores ou boleanos (1 / 0). Fios com cores diferentes não conectam. + description: Tranfere sinais, que podem ser itens, cores ou um sinal binário (1 + ou 0). Fios de cores diferestes não se conectam. second: name: Fio Elétrico - description: Transfere sinais, que podem ser itens, cores ou boleanos (1 / 0). Fios com cores diferentes não conectam. + description: Tranfere sinais, que podem ser itens, cores ou um sinal binário (1 + ou 0). Fios de cores diferestes não se conectam. balancer: default: - name: Balanceador - description: Multifuncional - Distribui uniformemente todas as entradas para todas as saídas. + name: Distribuidor + description: Multifunções - Distribui igualmente todas as entradas por todas as + saídas. merger: - name: Junção (compacto) - description: Junta um tapete rolante em dois. + name: Misturador (comp.) + description: Junta dois tapetes rolantes num só. merger-inverse: - name: Junção (compacto) - description: Junta um tapete rolante em dois. + name: Misturador (comp.) + description: Junta dois tapetes rolantes num só. splitter: name: Divisor (compacto) description: Divide um tapete rolante em dois. @@ -496,97 +470,111 @@ buildings: storage: default: name: Armazém - description: >- - Guarda itens em excesso, até uma quantidade determinada. Prioritiza a entrada esquerda - e pode ser usada como um portão de transbordar. + description: Armazena itens em excesso, até uma determinada capacidade. Dá + prioridade à saída da esquerda e pode ser usado como uma porta + de transbordo. wire_tunnel: default: - name: Túnel de Fio - description: Permite cruzar dois fios sem os conectar. + name: Túnel de Fios + description: Permite que dois fios cruzem sem conectarem-se um ao outro. constant_signal: default: - name: Sinal constante - description: >- - Emite um sinal constante, que pode ser uma forma, cor ou um booleano (1 / 0). + name: Sinal Constante + description: Emite um sinal constante , que pode ser uma forma, cor ou um sinal + binário (1 ou 0). lever: default: name: Interruptor - description: >- - Pode ser alternado para emitir um sinal booleano (1 / 0) na camada de fios, que pode então ser usada - para controlar por exemplo um filtro de itens. + description: Pode emitir alternadamente um sinal binário (1 ou 0) na camada de + fios, que pode posteriormente ser usado, por exemplo, num filtro + de itens. logic_gate: default: name: Portão AND - description: Emite um booleano "1" se ambas as entradas são verdadeiras. (Verdadeiro significa forma, cor ou booleano "1") + description: 'Emite um sinal binário "1" se ambas as entradas forem verdadeiras. + (Verdadeiro significa: forma, cor ou sinal binário "1")' not: name: Portão NOT - description: Emite um booleano "1" se a entrada não é verdadeira. (Verdadeiro significa forma, cor ou booleano "1") + description: 'Emite um sinal binário "1" se a entrada não for verdadeira. + (Verdadeiro significa: forma, cor ou sinal binário "1")' xor: name: Portão XOR - description: Emite um booleano "1" se uma das entradas é verdadeira, mas não as duas. (Verdadeiro significa forma, cor ou booleano "1") + description: 'Emite um sinal binário "1" se uma das entradas for verdadeira, mas + não as duas. (Verdadeiro significa: forma, cor ou sinal binário + "1")' or: name: Portão OR - description: Emite um booleano "1" se uma entrada é verdadeira. (Verdadeiro significa forma, cor ou booleano "1") + description: 'Emite um sinal binário "1" se uma entrada é verdadeira. + (Verdadeiro significa: forma, cor ou sinal binário "1")' transistor: default: - name: Transistor - description: Encaminha a entrada inferior se a entrada lateral for verdade (uma forma, cor ou "1"). + name: Transístor + description: Encaminha a entrada inferior se a entrada lateral for verdade (uma + forma, cor ou "1"). mirrored: - name: Transistor - description: Encaminha a entrada inferior se a entrada lateral for verdade (uma forma, cor ou "1"). + name: Transístor + description: Encaminha a entrada inferior se a entrada lateral for verdade (uma + forma, cor ou "1"). filter: default: - name: Filtro - description: Conecta um sinal para encaminhar todos os itens correspondentes para o topo e o resto - para a direita. Pode ser controlado com sinais booleanos também. + name: Filtro de Itens + description: Conecta um sinal que irá encaminhar todos os itens correspondentes + para o topo e os restantes para a direita. Também pode ser + controlado com sinais binários. display: default: - name: Display - description: Conecta um sinal para mostrar no display - Pode ser uma forma, cor ou - booleano. + name: Visor + description: Conecta um sinal para mostrar no Visor - Pode ser uma forma, cor ou + um sinal binário. reader: default: name: Leitor de Tapete - description: Permite medir o rendimento do tapete. Produz o último item lido na camada de - fios (quando desbloqueada). + description: Permite medir a passagem média de itens no tapete. Fornece o último + item lido na camada de fios (quando desbloqueada). analyzer: default: name: Analizador de Forma - description: Analiza o quadrante do topo direito da camada mais baixa da forma e produz - a forma ou cor. + description: Analiza o quadrante superior direito da camada mais baixa da forma + e retorna a forma ou cor. comparator: default: - name: Comparar - description: Produz o booleano "1" se ambos os itens são exatamente iguais. Pode comparar formas, - itens e booleanos. + name: Comparador + description: Produz o sinal binário "1" se ambos os itens são exatamente iguais. + Pode comparar formas, itens e sinais binários. virtual_processor: default: name: Cortador Virtual - description: Computa + description: Virtualmente, corta as formas em duas metades. rotater: - name: Rodar Virtual - description: Roda virtualmente as formas 90º no sentido dos ponteiros do relógio. + name: Rodador Virtual + description: Virtualmente, roda a forma tanto no sentido horário quanto no + anti-horário. unstacker: name: Desempilhador Virtual - description: Produz a camada no topo para a direita, e o resto para esquerda. + description: Virtualmente, remove a camada do topo para a saída da direita e o + restante para a da esquerda. stacker: name: Empilhador Virtual - description: Empilha virtualmente o item da direita em cima do item da esquerda. + description: Virtualmente empilhada a forma da direita em cima do item da + esquerda. painter: name: Pintor Virtual - description: Pinta virtualmente a forma de baixo com a forma da direita. + description: Virtualmente, pinta a forma a forma da entrada de baixo com o item + da entrada da direita. item_producer: default: name: Produtor de Itens - description: Disponível apenas no modo sandbox, produz o sinal dado da camada de fios na camala normal. - + description: Disponível apenas no modo sandbox, produz o sinal dado na camada de + fios na camada normal. storyRewards: reward_cutter_and_trash: title: Corte de formas - desc: Acabaste de desbloquear o Cortador, que corta as formas - geométricas ao meio de cima para baixo independentemente da orientação!

Certifica-te de que te - livras do desperdício, caso contrário encravará - - Por isso, dou-te um lixo, que destruirá tudo o que lá colocares! + desc: Acabaste de desbloquear o cortador, que corta as formas + ao meio de cima para baixo independentemente da sua + orientação!

Certefica-te de que te livras do + desperdício, caso contrário irá encravar e parar - + Para este propósito eu dei-te um lixo, que destrói + tudo o que lá colocares! reward_rotater: title: Rotação desc: O Rodador foi desbloqueado! Ele roda as formas @@ -610,8 +598,9 @@ storyRewards: esquerda! reward_splitter: title: Divisor - desc: Desbloqueaste o divisor, variante do - balanceador - Aceita uma entrada e divide-a em duas! + desc: Desbloqueaste o dvisor uma variante do + distribuidor - Aceita uma entradae divide-a em + duas! reward_tunnel: title: Túnel desc: O Túnel foi desbloqueado - Com ele podes passar itens @@ -624,94 +613,125 @@ storyRewards: variantes
! reward_miner_chainable: title: Extração em série - desc: >- - Desbloqueaste o Extrator em série! Permite enviar - o recurso extraído para outros extratores, permitindo uma - extração mais eficiente!

PS: O velho extrator já foi trocado na tua toolbar! + desc: "Desbloqueaste o extrator em séire! Permite + enviar os recursos para outros extratores, sendo + assim permite uma extração de recursos mais eficiente!

PS: O + extrator antigo já foi trocado na tua lista de construções!" reward_underground_belt_tier_2: title: Túnel Nível II - desc: Desbloqueaste uma nova variante do Túnel - Tem um maior alcance, e podes interlaçar as duas variantes entre si! + desc: Desbloqueaste uma nova variante do Túnel - Tem um + maior alcance, e podes interlaçar as duas variantes + entre si! reward_cutter_quad: title: Corte quádruplo - desc: Desbloqueaste a variante do Cortador - Permite cortar formas geométricas em quatro partes em vez de apenas duas! + desc: Desbloqueaste a variante do Cortador - Permite cortar + formas geométricas em quatro partes em vez de + apenas duas! reward_painter_double: title: Pintura dupla - desc: Desbloqueaste uma variante do Pintor - Funciona como um pintor normal mas processa duas formas ao mesmo tempo consumindo apenas uma cor em vez de duas! + desc: Desbloqueaste uma variante do Pintor - Funciona como um + pintor normal mas processa duas formas ao mesmo + tempo consumindo apenas uma cor em vez de duas! reward_storage: title: Armazém - desc: Desbloqueaste uma variante do Lixo - Permite armazenar items até uma determinada capacidade!

Prioritiza a saída esquerda, por isso também o podes usar como um portão de transbordar. + desc: Desbloqueaste uma variante do lixo - Permite armazenar + itens, até uma determinada capacidade!

Dá prioridade à saída + da esquerda e pode ser usado como uma porta de + transbordo! reward_freeplay: title: Jogo livre - desc: Conseguiste! Desbloqueaste o modo jogo livre! Isto significa que agora as formas são geradas aleatoriamente!

- Como o edifício central vai precisar de uma taxa de transferência a partir de - agora, eu recomendo contruires uma máquina que automaticamente - entrega a forma pedida!

O edifício central produz a forma pedida na camada de fios, - então tudo o que tens de fazer é analizar-la e automaticamente configurar a tua fábrica à volta disso. + desc: Conseguiste! Desbloqueaste o modo jogo livre! Isto + significa que agora as formas são geradas + aleatoriamente!

Como o edifício central vai + precisar de uma taxa de rendimento a partir de + agora, recomendo vivamente a contruires uma máquina que, + automaticamente, entraga as formas pedidas!

O edifício + central emite a forma pedida na camada de fios,sendo assim tudo o + que tens a fazer é analiza-la e automaticamente configurares a tua + fábrica baseada nisso. reward_blueprints: title: Projetos - desc: Agora podes copiar e colar partes da tua fábrica! Seleciona uma área (Mantém pressionado CTRL e arrasta com o rato), e pressiona 'C' para copiar.

Colar não é gratuito, precisas de produzir formas projeto para o pagares! (Aquelas que acabaste de entregar). + desc: Agora podes copiar e colar partes da tua fábrica! + Seleciona uma área (Mantém pressionado CTRL e arrasta com o rato), e + pressiona 'C' para copiar.

Colar não é + gratuito, precisas de produzir formas + projeto para o pagares! (Aquelas que acabaste de entregar). no_reward: title: Próximo nível - desc: >- - Este nível não te deu nenhuma recompensa, mas o próximo dará!

PS: É melhor não destruires a tua fábrica atual - Precisarás de todas essas formas no futuro para desbloquear upgrades! + desc: "Este nível não te deu nenhuma recompensa, mas o próximo dará!

+ PS: É melhor não destruires a tua fábrica atual - Precisarás de + todas essas formas no futuro para + desbloquear upgrades!" no_reward_freeplay: title: Próximo nível desc: Parabéns! Já agora, está planeado mais conteúdo para o jogo completo! reward_balancer: - title: Balanceador - desc: O multifunctional balanceador foi desbloqueado - Pode ser usado - para construir fábricas maiores dividindo e juntando itens - por vários tapetes!

+ title: Distribuidor + desc: The multifunctional balancer has been unlocked - It can + be used to build bigger factories by splitting and merging + items onto multiple belts! reward_merger: - title: Junção (Compacto) - desc: Destravaste a junção variante do - balanceador - Aceita duas entradas e junta-as num só tapete! + title: Misturador (compacto) + desc: Desbloqueaste um misturador, uma variante do + distribuidor - Aceita duas entradas e junta-as num + só tapete rolante! reward_belt_reader: title: Leitor de Tapete - desc: Tu desbloqueaste o leitor de tapete! Permite-te medir - o rendimento dum tapete.

E espera até desbloqueares fios - aí é que é super útil! + desc: Desbloqueaste o leitor de tapete! Permite-te medires a + passagem média de itens no tapete.

E espera por desbloqueares + os fios - aí é que vão ser bastante úteis! reward_rotater_180: - title: Rodar (180 degrees) - desc: Acabaste de desbloquear a versão de 180º do Rotador! - Deixa-te rodar formas por 180º (Surpresa! :D) + title: Rodar (180º) + desc: Desbloqueaste o rodador de 180 graus! - Permite-te + rodares formas 180 graus (Surpresa! :D) reward_display: - title: Display - desc: >- - Destravaste o Display - Conecta um sinal elétrico na camada de fios para visualizar-lo!

PS: - Reparaste que o leitor de tapetes e o armazém produz o último item lido por eles na camada de fios? Tenta mostrar isso num display! + title: Visor + desc: "Desbloqueaste o Visor - Conecta um sinal na camada de + fios para o visualizares!

PS: Reparaste que o leitor de + tapete e o armazém emitem o último item lido por eles? tenta mostrar + isso num visor!" reward_constant_signal: title: Sinal Constante - desc: Acabaste de destravar o edifício sinal constante na camada de fios! - Isto é útil conectado a um filtro de itens por exemplo.

- O sinal constante pode emitir uma forma, - cor ou um booleano (1 / 0). + desc: Desbloqueaste o sinal constante contruido na camada de + fios! Isto é útil conectado com um filtro de itens + por exemplo.

O sinal constante pode emitir uma + forma, cor ou sinal + binário (1 ou 0). reward_logic_gates: title: Portões Lógicos - desc: "Tu desbloqueaste os portões lógicos! Não tens de estar excitado sobre isto, - mas é na verdade super fixe!

Com estes portões agora podes fazer operações booleanas - AND, OR, XOR e NOT!" + desc: Desbloqueaste os portões lógicos! N tens de te excitar + com isto, mas é realmente super fixe!

Com estes portões + agora podes realizar operações AND, OR, XOR and NOT.

Como um + bónus anteriormente já de dei um transístor! reward_virtual_processing: title: Processamento Virtual - desc: >- - Acabei de te dar um monte de novos edifícios que permitem-te - simular o processamento de formas!

Podes agora - simular um cortador, rodar, empilhador e mais na camada de fios!

- Com isto tens agora três opções para continuar o jogo:

- Construir - uma máquina automatizada para criar qualquer forma requerida - pelo edifício central (Isto é fixe, eu prometo!).

- Construir algo fixe com - fios.

- Continuar a jogar regularmente. Seja lá o que escolheres, lembra-te de te divertires! + desc: Acadei de te dar um monte de novas construções, que te vão permitir + simular o processamento de formas!

Agora + podes simular um cortador,um rodador, um empilhador e muito mais na + camada de fios! Com isto, agora tens três opções para continuares o + jogo:

- Construir uma máquina automática + para criar qualquer forma possível pedida pelo Edifício Central + (Reconmento-te a experimentares!).

- Contruir algo fixe com + os fios.

- Continuar a jogar regularmente.

+ Independentemente da tua escolha, lembra-te de te divertires! reward_wires_painter_and_levers: - title: Fios e Pintor Quádruplo - desc: "Acabaste de desbloquear a Camada de Fios: É uma camada separada - no topo da camada normal e introduz um monte de novas mecânicas!

- Para o início eu dei-te o Pintor Quádruplo - Conecta os slots que queres pintar na - camada de fios!

Para trocar para a camada de fios, pressiona E." + title: Fios & Pintor Quádruplo + desc: "Desbloquaste a Camada de Fios: É uma camada separada no + topo da camada normal e introduz um monte de novas + mecânicas!

Para o inicio eu dei-te o Pintor + Quádruplo - Conecta as entradasque queres pintar na camada + de fios!

Para trocares para a camada de fios, pressiona a + tecla E." reward_filter: title: Filtro de Itens - desc: Desbloqueaste o Filtro de Itens! Vai mandar items para a saída de topo ou para a saída da direita - dependendo se são iguais ao sinal da camada de fios.

Também podes passar um sinal booleano (1 / 0) para ativar-lo ou desativar-lo completamente. + desc: Desbloquaste o Filtro de Itens! Vai mandar itens ou para + o topo ou para a saída da esquerda dependendo depending se são + iguais ao sinal da camada de fios ou não.

Também podes + passar um sinal binário (1 ou 0) para ativa-lo ou desativa-lo + totalmente. reward_demo_end: title: Fim da Demo - desc: Chegaste ao fim da versão demo! + desc: Tu chegaste ao fim da versão demo! settings: title: Definições categories: @@ -727,8 +747,9 @@ settings: labels: uiScale: title: Escala da interface - description: >- - Altera o tamanho da interface do utilizador. A interface será redimensionada com base na resolução do teu dispositivo, mas esta definição controla a escala. + description: Altera o tamanho da interface do utilizador. A interface será + redimensionada com base na resolução do teu dispositivo, mas + esta definição controla a escala. scales: super_small: Super pequeno small: Pequeno @@ -737,8 +758,7 @@ settings: huge: Enorme scrollWheelSensitivity: title: Sensibilidade do zoom - description: >- - Define o quão sensível é o zoom (Roda do rato ou trackpad). + description: Define o quão sensível é o zoom (Roda do rato ou trackpado). sensitivity: super_slow: Muito lento slow: Lento @@ -747,39 +767,41 @@ settings: super_fast: Muito rápido language: title: Língua - description: >- - Muda a língua. Todas as traduções são contribuições dos utilizadores e podem estar incompletas! + description: Muda a língua. Todas as traduções são contribuições dos + utilizadores e podem estar incompletas! fullscreen: title: Ecrã inteiro - description: >- - É recomendado jogar o jogo em ecrã inteiro para a melhor experiência. Apenas disponível no jogo completo. + description: É recomendado jogar o jogo em ecrã inteiro para a melhor + experiência. Apenas disponível no jogo completo. soundsMuted: title: Desativar sons - description: >- - Se ativado, desativa todos os sons. + description: Se ativado, desativa todos os sons. musicMuted: title: Desativar música - description: >- - Se ativado, desativa todas as músicas. + description: Se ativado, desativa todas as músicas. theme: title: Tema do jogo - description: >- - Escolhe o tema do jogo (claro / escuro). + description: Escolhe o tema do jogo (claro / escuro). themes: dark: Escuro light: Claro refreshRate: title: Frequência - description: >- - Isto determina quantos game ticks ocorrem por segundo. No geral, uma frequência alta significa melhor precisão mas também pior desempenho. Em frequências baixas, o rendimento pode não ser exato. + description: Se tens um monitor 144hz, muda a frequência para que o jogo simule + corretamente frequências de autalização altas. Isto pode + resultar em perda de FPS se o teu computador for demasiado + lento. alwaysMultiplace: title: Colocação múltipla - description: >- - Se ativado, todas as construções permanecerão selecionadas após a colocação até cancelares. Isto é equivalente a pressionares SHIFT permanentemente. + description: Se ativado, todas as construções permanecerão selecionadas após a + colocação até cancelares. Isto é equivalente a pressionares + SHIFT permanentemente. offerHints: title: Dicas e tutoriais - description: >- - 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. + description: 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: Velocidade de movimentação description: Define quão rápida é a movimentação usando o teclado. @@ -792,19 +814,17 @@ settings: extremely_fast: Extremamente rápida enableTunnelSmartplace: title: Túneis inteligentes - description: >- - Quando ativado, a colocação de túneis removerá tapetes desnecessários automaticamente. - Isto também permite arrastar túneis e túneis em excesso serão removidos. + description: Quando ativado, a colocação de túneis removerá tapetes + desnecessários automaticamente. Isto também permite arrastar + túneis e túneis em excesso serão removidos. vignette: title: Vinheta - description: >- - Ativa a vinheta, que escurece os cantos do ecrã e torna a leitura do texto - mais fácil. + description: Ativa a vinheta, que escurece os cantos do ecrã e torna a leitura + do texto mais fácil. autosaveInterval: title: Intervalo de gravação automática - description: >- - Define o quão frequentemente o jogo grava automaticamente. Também podes desativar - aqui. + description: Define o quão frequentemente o jogo grava automaticamente. Também + podes desativar aqui. intervals: one_minute: 1 Minuto two_minutes: 2 Minutos @@ -814,23 +834,21 @@ settings: disabled: Desligado compactBuildingInfo: title: Informações de construções compactas - description: >- - Encurta caixas de informação e apenas mostra os respetivos rácios. Caso contrário - é mostrada a descrição e a imagem. + description: Encurta caixas de informação e apenas mostra os respetivos rácios. + Caso contrário é mostrada a descrição e a imagem. disableCutDeleteWarnings: title: Desativar Avisos de Corte/Eliminação - description: >- - Desativa os avisos mostrados quando é feito o corte ou a eliminação de mais de 100 - entidades. + description: Desativa os avisos mostrados quando é feito o corte ou a eliminação + de mais de 100 entidades. enableColorBlindHelper: title: Modo Daltónico - description: Ativa várias ferramentas que te permitirão jogar o jogo se fores daltónico. + description: Ativa várias ferramentas que te permitirão jogar o jogo se fores + daltónico. rotationByBuilding: title: Rotação por tipo de construção - description: >- - Cada tipo de construção lembra-se da última rotação que definiste. - Esta definição pode ser mais confortável se alterares frequentemente - a colocação de diferentes tipos de construções. + description: Cada tipo construção lembra-se da última rotação que definiste. + Esta definição pode ser mais confortável se alterares + frequentemente a colocação de diferentes tipos de construções. soundVolume: title: Volume do Som description: Define o volume para efeitos sonoros @@ -839,44 +857,55 @@ settings: description: Define o volume para música lowQualityMapResources: title: Recursos de Mapa de Baixa Qualidade - description: >- - Simplifica a renderização de recursos quanto o mapa está ampliado para melhorar o desempenho. Até parece mais limpo, então lembra-te de experimentar! + description: Simplifica a renderização de recursos quando o mapa está ampliado + para melhorar o desempenho. Até parece mais limpo, então + lembra-te de experimentares! disableTileGrid: title: Desativar Grelha - description: >- - Desativar a grelha pode ajudar com o desempenho. Isto também faz o jogo estar mais limpo! + description: Desativar a grelha pode ajudar com o desempenho. Isto também fazz o + jogo parecer mais limpo! clearCursorOnDeleteWhilePlacing: title: Limpar Cursor com Clique Direito - description: >- - Ativado por default, limpa o cursos sempre que clicas no botão direito do rato enquanto tens um edifício selecionado para colocamento. - Se desativado, podes apagar edifícios fazendo um clique direito enquanto colocas um edifício. + description: Ativado por padrão, limpa o cursor sempre que pressionas o botão + direito do rato enquanto tens um edifício para colocamento. Se + desativado, podes apagar construções pressionando o botão + direito do rato enquanto colocas um edifício. lowQualityTextures: title: Texturas de baixa qualidade (Feio) - description: >- - Usa texturas de baixa qualidade para melhorar o desempenho. Isto vai tornar o jogo muito feio! + description: sa texturas de baixa qualidade para melhorar o desempenho. sto vai + tornar o jogo parecer muito feio! displayChunkBorders: - title: Mostrar bordas de Chunks - description: >- - O jogo está dividido em pedaços de 16x16 quadrados, se esta definição estiver ativada - as bordas de cada pedaço são mostradas. + title: Mostrar bordas de limites (chunk borders) + description: O jogo está dividido em partes de 16x16 quadrados, se esta + dedinição estiver ativada as bordas de cada limitece são + mostradas. pickMinerOnPatch: - title: Selecionar extrator num remendo de recursos - description: >- - Ativado por default, seleciona o extrator se usares a pipeta enquanto estás num remendo de recursos. + title: Selecionar extrator num quadrado de recurso + description: Ativado por padrão, seleciona um extrator se usares a pipeta quando + estiveres com o rato em cima de um quadrado de recurso. simplifiedBelts: title: Tapetes rolantes simplificados (Feio) - description: >- - Não renderiza itens em tapetes excepto quando tens o rato em cima do tapete para salvar desempenho. - Não recomendo jogares com esta definição a menos que absolutamente precisas do desempenho. + description: Não renderiza os intens nos tapetes excepto quando tens o rato em + cima do tapete para melhorar o desempenho. Não recomendo a + jogares com esta definição ativada a não ser que precises mesmo + de melhorar o desempenho. enableMousePan: - title: Ativar Mouse Pan - description: >- - Permite-te mover o mapa apenas movendo o rato aos cantos do ecrã. A velocidade depende da definição Velocidade de movimentação. + title: Ativar rato panorâmico + description: Permite-te mover o mapa movento o rato nos cantos do ecrâ. A + velociade depende da definição de velocidade de movimentação. + zoomToCursor: + title: Zoom towards Cursor + description: If activated the zoom will happen in the direction of your mouse + position, otherwise in the middle of the screen. + mapResourcesScale: + title: Map Resources Size + description: Controls the size of the shapes on the map overview (when zooming + out). rangeSliderPercentage: % keybindings: title: Atalhos - hint: >- - Tip: Utiliza o CTRL, o SHIFT e o ALT! Eles permitem diferentes opções de posicionamento. + hint: "Dica: Utiliza o CTRL, o SHIFT e o ALT! Eles permitem diferentes opções de + posicionamento." resetKeybindings: Resetar Atalhos categoryLabels: general: Aplicação @@ -931,13 +960,12 @@ keybindings: menuClose: Fechar Menu switchLayers: Troca de camadas wire: Fio Elétrico - balancer: Balanceador + balancer: Distribuidor storage: Armazém constant_signal: Sinal Constante logic_gate: Portões Lógicos lever: Interruptor (normal) - lever_wires: Interruptor (fios) - filter: Filtro + filter: Filtro de itens wire_tunnel: Túnel de Fio display: Display reader: Leitor de Tapete @@ -972,13 +1000,15 @@ demo: exportingBase: Exportar base como uma imagem settingNotAvailable: Não disponível no Demo. tips: - - 'O edifício central aceita qualquer entrada, não apenas a forma atual!' + - O edifício central aceita qualquer entrada, não apenas a forma atual! - Tem a certeza que as tuas fábricas são modulares - vai valer a pena! - - 'Não construas demasiado perto do edifício, ou vai ser um grande caos!' - - 'Se empilhar não funciona, tenta trocar as entradas.' - - Podes alternar a direção do planeador de tapete rolante ao pressionar R. + - Não construas demasiado perto do edifício, ou vai ser um grande caos! + - Se empilhar não funciona, tenta trocar as entradas. + - Podes alternar a direção do planeador de tapete rolante ao pressionar + R. - Ao segurar CTRL podes arrastar tapetes rolantes sem auto-orientação. - - 'Os rácios continuam os mesmos, desde que todos os upgrades estejam no mesmo Nível.' + - Os rácios continuam os mesmos, desde que todos os upgrades estejam no + mesmo Nível. - Execução em série é mais eficiente que em paralelo. - Vais desbloquear mais variações de edifícios mais tarde no jogo! - Podes usar T para trocar entre as diferentes variantes. @@ -987,11 +1017,13 @@ tips: - Tenta construir fábricas compactas - vai valer a pena! - O pintor tem uma variante espelhada que podes selectionar com T - Ter os rácios de edifícios corretos vai maximizar a eficiência. - - 'No nível máximo, 5 extratores vão encher um tapete.' + - No nível máximo, 5 extratores vão encher um tapete. - Não te esqueças dos túneis! - Não tens de dividir os itens uniformemente para eficiência máxima. - - Segurar SHIFT vai ativar o planeador de tapetes, deixando-te colocar longas linhas de tapetes facilmente. - - 'Os cortadores cortam sempre verticalmente, independentemente da sua orientação.' + - Segurar SHIFT vai ativar o planeador de tapetes, deixando-te + colocar longas linhas de tapetes facilmente. + - Os cortadores cortam sempre verticalmente, independentemente da sua + orientação. - Para obter branco junta as três cores. - O buffer do armazém prioritiza a primeira saída. - Investe tempo para costruir designs repetiveis - vale a pena! @@ -999,32 +1031,41 @@ tips: - Podes segurar ALT para inverter a direção de tapetes colocados. - Eficiência é a solução! - As formas que estão mais longes do edifício central são mais complexas. - - 'As Máquinas têm uma velocidade limitada, divide-as para eficiência máxima.' + - As Máquinas têm uma velocidade limitada, divide-as para eficiência máxima. - Usa balanceadores para maximizar a tua eficiência. - Organização é importante. Tenta não cruzar tapetes demasiado. - - 'Planeja antecipadamente, ou vai ser um grande caos!' - - Não removas as tuas fábricas antigas! Vais precisar delas para desbloquear upgrades. + - Planeja antecipadamente, ou vai ser um grande caos! + - Não removas as tuas fábricas antigas! Vais precisar delas para desbloquear + upgrades. - Tenta superar o nível 18 sozinho sem procurar ajuda! - - 'Não complicas as coisas, tenta continuar simples e irás muito longe.' - - Talvez precises de reusar fábricas mais tarde no jogo. Planeia as tuas fábricas para serem reutilizáveis. - - Às vezes, podes encontrar uma forma necessária no mapa sem criar-la com empilhadoras. + - Não complicas as coisas, tenta continuar simples e irás muito longe. + - Talvez precises de reusar fábricas mais tarde no jogo. Planeia as tuas + fábricas para serem reutilizáveis. + - Às vezes, podes encontrar uma forma necessária no mapa sem criar-la com + empilhadoras. - Moinhos de vento e cataventos completos nunca aparecem naturalmente. - Pinta as tuas formas antes de cortar-las para eficiência máxima. - - 'Com módulos, o espaço é apenas uma percepção; uma preocupação para pessoas mortais.' + - Com módulos, o espaço é apenas uma percepção; uma preocupação para pessoas + mortais. - Faz uma fábrica de diagramas separada. São importantes para módulos. - - 'Dá uma olhada ao misturador de cores, e as tuas questões serão respondidas.' + - Dá uma olhada ao misturador de cores, e as tuas questões serão respondidas. - Use CTRL + Clique para selecionar uma área. - - Construir demasiado perto do edifício central pode ficar no caminho de projetos futuros. - - O ícone de alfinete perto duma forma na lista de upgrades vai afixar-la ao ecrã. + - Construir demasiado perto do edifício central pode ficar no caminho de + projetos futuros. + - O ícone de alfinete perto duma forma na lista de upgrades vai afixar-la ao + ecrã. - Junta todas as cores primárias juntas para fazer branco! - - 'Tu tens um mapa infinito, não limites a tua fábrica, expande!' + - Tu tens um mapa infinito, não limites a tua fábrica, expande! - Tenta também Factorio! É o meu jogo favorito. - - O cortador quádruplo corta no sentido dos ponteiros começando no canto superior direito! + - O cortador quádruplo corta no sentido dos ponteiros começando no canto + superior direito! - Podes fazer download dos teus savegames no menu principal! - - Este jogo tem muitos atalhos de teclado úteis! Não te esqueças de verificar a página de configurações. - - 'Este jogo tem muitas definições, não te esqueças de as verificar!' - - O marco para o teu edifício central tem uma pequena bússola para indicar a sua direção! - - 'Para limpar tapetes, corta a área e cola-a na mesma localização.' + - Este jogo tem muitos atalhos de teclado úteis! Não te esqueças de + verificar a página de configurações. + - Este jogo tem muitas definições, não te esqueças de as verificar! + - O marco para o teu edifício central tem uma pequena bússola para indicar a + sua direção! + - Para limpar tapetes, corta a área e cola-a na mesma localização. - Pressiona F4 para mostrar os teus FPS e Tick Rate. - Pressiona F4 duas vezes para mostrar a tile do teu rato e câmara. - Podes clicar numa forma afixada no lado direito para desafixar-la. diff --git a/translations/base-ro.yaml b/translations/base-ro.yaml index 54c45291..5a635507 100644 --- a/translations/base-ro.yaml +++ b/translations/base-ro.yaml @@ -204,11 +204,6 @@ dialogs: renameSavegame: title: Rename Savegame desc: You can rename your savegame here. - entityWarning: - title: Performance Warning - desc: You have placed a lot of buildings, this is just a friendly reminder that - the game can not handle an endless count of buildings - So try to - keep your factories compact! ingame: keybindingsOverlay: moveMap: Move @@ -254,27 +249,6 @@ ingame: title: Upgrade-uri buttonUnlock: Upgrade tier: Tier - tierLabels: - - I - - II - - III - - IV - - V - - VI - - VII - - VIII - - IX - - X - - XI - - XII - - XIII - - XIV - - XV - - XVI - - XVII - - XVIII - - XIX - - XX maximumLevel: NIVELUL MAXIM (Speed x) statistics: title: Statistici @@ -298,10 +272,6 @@ ingame: playtime: Timp jucat buildingsPlaced: Construcții beltsPlaced: Benzi - buttons: - continue: Continuă - settings: Setări - menu: Întoarce-te în meniu tutorialHints: title: Ai nevoie de ajutor? showHint: Arată o idee @@ -689,7 +659,7 @@ storyRewards: title: Balancer desc: The multifunctional balancer has been unlocked - It can be used to build bigger factories by splitting and merging - items onto multiple belts!

+ items
onto multiple belts! reward_merger: title: Compact Merger desc: You have unlocked a merger variant of the @@ -910,6 +880,14 @@ settings: title: Enable Mouse Pan description: Allows to move the map by moving the cursor to the edges of the screen. The speed depends on the Movement Speed setting. + zoomToCursor: + title: Zoom towards Cursor + description: If activated the zoom will happen in the direction of your mouse + position, otherwise in the middle of the screen. + mapResourcesScale: + title: Map Resources Size + description: Controls the size of the shapes on the map overview (when zooming + out). rangeSliderPercentage: % keybindings: title: Tastele setate diff --git a/translations/base-ru.yaml b/translations/base-ru.yaml index aa2aaf85..6d07d56c 100644 --- a/translations/base-ru.yaml +++ b/translations/base-ru.yaml @@ -1,51 +1,51 @@ steamPage: shortText: shapez.io — это игра о строительстве фабрик для автоматизации создания и объединения все более сложных фигур на бесконечной карте. - discordLinkShort: Official Discord + discordLinkShort: Официальный Discord сервер intro: >- - Shapez.io is a relaxed game in which you have to build factories for the - automated production of geometric shapes. + Shapez.io - это спокойная игра о создании фабрик для автоматизации + создания сложных геометрических фигур. - As the level increases, the shapes become more and more complex, and you have to spread out on the infinite map. + По мере управления уровня, фигуры становятся все сложнее, так что придется расширять фабрику засчет бесконечной карты. - And as if that wasn't enough, you also have to produce exponentially more to satisfy the demands - the only thing that helps is scaling! + Если этого мало, то Вам так же придется экспоненциально производить все больше и больше фигур, чтобы удовлетворить потребности Вашей фабрики. Ключ к успеху - расширение! - While you only process shapes at the beginning, you have to color them later - for this you have to extract and mix colors! + Вначале игры Вам понадобится производить только фигуры, однако позже фигуры надо будет окрашивать. Для этого добывайте и смешивайте краски! - Buying the game on Steam gives you access to the full version, but you can also play a demo on shapez.io first and decide later! - title_advantages: Standalone Advantages + Приобретение игры в Steam предоставляет доступ к полной версии игры, но вы можете опробовать демоверсию игры на shapez.io и принять решение позже! + title_advantages: Преимущества полной версии advantages: - - 12 New Level for a total of 26 levels - - 18 New Buildings for a fully automated factory! - - 20 Upgrade Tiers for many hours of fun! - - Wires Update for an entirely new dimension! - - Dark Mode! - - Unlimited Savegames - - Unlimited Markers - - Support me! ❤️ - title_future: Planned Content + - 12 новых уровней - всего 26 уровней! + - 18 новых построек для полностью автоматизированной фабрики! + - 20 стадий улучшения для долгих часов веселья! + - Провода - открывает полноценное новое измерение! + - Темная тема! + - Неограниченные сохранения + - Неограниченные маркеры + - Поддержите меня! ❤️ + title_future: Запланированный контент planned: - - Blueprint Library (Standalone Exclusive) - - Steam Achievements - - Puzzle Mode - - Minimap - - Mods - - Sandbox mode - - ... and a lot more! - title_open_source: This game is open source! - title_links: Links + - Библиотека чертежей (только для Полной версии) + - Достижения Steam + - Режим головоломок + - Мини-карта + - Моды + - Режим песочницы + - ... и многое другое! + title_open_source: Эта игра находится в открытом доступе! + title_links: Ссылки links: - discord: Official Discord - roadmap: Roadmap + discord: Официальный Discord сервер + roadmap: Планы subreddit: Subreddit - source_code: Source code (GitHub) - translate: Help translate + source_code: Исходный код (GitHub) + translate: Помочь с переводом text_open_source: >- - Anybody can contribute, I'm actively involved in the community and - attempt to review all suggestions and take feedback into consideration - where possible. + Кто угодно может внести свой вклад в разработку игры - я активно + вовлечен в сообщество и стараюсь просмотреть все предложения и по + возможности прислушиваться к отзывам. - Be sure to check out my trello board for the full roadmap! + Не забудьте заглянуть на мой Trello board, чтобы ознакомиться с планами на будущее! global: loading: Загрузка error: Ошибка @@ -78,7 +78,7 @@ global: shift: SHIFT space: ПРОБЕЛ demoBanners: - title: Демо-версия + title: Демоверсия intro: Приобретите полную версию, чтобы разблокировать все возможности! mainMenu: play: Играть @@ -121,9 +121,8 @@ dialogs: text: Не удалось загрузить сохранение игры. confirmSavegameDelete: title: Подтвердите удаление. - text: Are you sure you want to delete the following game?

- '' at level

This can not be - undone! + text: Вы уверены, что хотите удалить это сохранение?

'' на + уровне

Это не может быть отменено! savegameDeletionError: title: Ошибка удаления text: Не удалось удалить сохранение игры. @@ -142,13 +141,13 @@ dialogs: title: Сброс управления desc: Настройки управления сброшены до соответствующих значений по умолчанию! featureRestriction: - title: Демо-версия + title: Демоверсия desc: Вы попытались получить доступ к функции (), которая недоступна в - демо-версии. Вы можете приобрести полную версию чтобы пользоваться + демоверсии. Вы можете приобрести полную версию чтобы пользоваться всеми функциями! oneSavegameLimit: title: Лимит сохранений - desc: Вы можете иметь только одно сохранение игры в демо-версии. Пожалуйста, + desc: Вы можете иметь только одно сохранение игры в демоверсии. Пожалуйста, удалите существующее или приобретите полную версию! updateSummary: title: Новое обновление! @@ -177,11 +176,12 @@ dialogs: Инвертировать направление размещаемых конвейерных лент.
" createMarker: title: Новый маркер - desc: Give it a meaningful name, you can also include a short - key of a shape (Which you can generate here) + desc: Дайте ему значимое название, вы также можете добавить короткий + ключ фигуры (Который можно сгенерировать + здесь) titleEdit: Редактирование маркера markerDemoLimit: - desc: Вы можете создать только 2 своих маркера в демо-версии. Приобретите полную + desc: Вы можете создать только 2 своих маркера в демоверсии. Приобретите полную версию для безлимитных маркеров. massCutConfirm: title: Подтвердите вырезку @@ -197,18 +197,13 @@ dialogs: desc: Вы не можете позволить себе вставить эту область! Вы уверены, что хотите вырезать ее? editSignal: - title: Set Signal - descItems: "Choose a pre-defined item:" - descShortKey: ... or enter the short key of a shape (Which you - can generate here) + title: Установить Сигнал + descItems: "Выберите объект:" + descShortKey: ... или введите короткий ключ фигуры (Который + можно сгенерировать здесь) renameSavegame: - title: Rename Savegame - desc: You can rename your savegame here. - entityWarning: - title: Performance Warning - desc: You have placed a lot of buildings, this is just a friendly reminder that - the game can not handle an endless count of buildings - So try to - keep your factories compact! + title: Переименовать Сохранение + desc: Здесь вы можете изменить название своего сохранения. ingame: keybindingsOverlay: moveMap: Передвижение @@ -217,7 +212,7 @@ ingame: rotateBuilding: Повернуть постройку placeMultiple: Поставить несколько reverseOrientation: Реверсировать направление - disableAutoOrientation: Отключить авто-определение направления + disableAutoOrientation: Отключить автоопределение направления toggleHud: Переключить HUD placeBuilding: Разместить постройку createMarker: Создать маркер @@ -254,27 +249,6 @@ ingame: title: Улучшения buttonUnlock: Улучшить tier: Уровень - tierLabels: - - I - - II - - III - - IV - - V - - VI - - VII - - VIII - - IX - - X - - XI - - XII - - XIII - - XIV - - XV - - XVI - - XVII - - XVIII - - XIX - - XX maximumLevel: Скорость x (макс.) statistics: title: Статистика @@ -298,10 +272,6 @@ ingame: playtime: Игровое время buildingsPlaced: Постройки beltsPlaced: Конвейеры - buttons: - continue: Продолжить - settings: Настройки - menu: Вернуться в меню tutorialHints: title: Нужна помощь? showHint: Показать подсказку @@ -343,41 +313,41 @@ ingame: empty: Пусто copyKey: Копировать connectedMiners: - one_miner: 1 Miner - n_miners: Miners - limited_items: Limited to + one_miner: 1 Экстрактор + n_miners: Экстрактора(-ов) + limited_items: Ограничено до watermark: - title: Demo version - desc: Click here to see the Steam version advantages! - get_on_steam: Get on steam + title: Демоверсия + desc: Нажмите сюда, чтобы узнать о преимуществах Steam версии! + get_on_steam: Приобрести в Steam standaloneAdvantages: - title: Get the full version! - no_thanks: No, thanks! + title: Приобретите полную версию! + no_thanks: Нет, спасибо! points: levels: - title: 12 New Levels - desc: For a total of 26 levels! + title: 12 Новых Уровней! + desc: Всего 26 уровней! buildings: - title: 18 New Buildings - desc: Fully automate your factory! + title: 18 новых Построек! + desc: Полностью автоматизируйте свою фабрику! savegames: - title: ∞ Savegames - desc: As many as your heart desires! + title: ∞ Сохранений + desc: Сколько Вашей душе угодно! upgrades: - title: 20 Upgrade Tiers - desc: This demo version has only 5! + title: 20 стадий улучшений! + desc: В демоверсии всего 5! markers: - title: ∞ Markers - desc: Never get lost in your factory! + title: ∞ Маркеров! + desc: Никогда не теряйтесь в своей фабрике! wires: - title: Wires - desc: An entirely new dimension! + title: Провода! + desc: Полноценное дополнительное измерение! darkmode: - title: Dark Mode - desc: Stop hurting your eyes! + title: Темная Тема! + desc: Дайте глазам отдохнуть! support: - title: Support me - desc: I develop it in my spare time! + title: Поддержите меня + desc: Я занимаюсь разработкой в свободное время! shopUpgrades: belt: name: Конвейеры, Разделители & Туннели @@ -417,11 +387,11 @@ buildings: default: name: Резак description: Разрезает фигуры сверху вниз и выводит обе половины. Если - Вы собираетесь использовать только одну часть, уничтожьте + вы собираетесь использовать только одну часть, уничтожьте другую, иначе производство остановится! quad: name: Резак (4Вых.) - description: Разрезает фигуры на четыре части. Если Вы собираетесь + description: Разрезает фигуры на четыре части. Если вы собираетесь использовать не все части - уничтожьте оставшиеся, иначе производство остановится! rotater: @@ -432,8 +402,8 @@ buildings: name: Вращатель (Обр.) description: Поворачивает фигуры против часовой стрелки на 90 градусов. rotate180: - name: Rotate (180) - description: Rotates shapes by 180 degrees. + name: Вращатель (180) + description: Поворачивает фигуры на 180 градусов. stacker: default: name: Объединитель @@ -452,9 +422,9 @@ buildings: description: Красит фигуру из левых входов красителем из перпендикулярного. quad: name: Покрасчик (4Вх.) - description: Allows you to color each quadrant of the shape individually. Only - slots with a truthy signal on the wires layer - will be painted! + description: Позволяет отдельно окрашивать каждую часть фигуры. Только ячейки с + положительным сигналом на слое с проводами + будут окрашены! mirrored: name: Покрасчик description: Красит всю фигуру из левого входа красителем из перпендикулярного. @@ -466,67 +436,121 @@ buildings: deliver: Доставить toUnlock: чтобы открыть levelShortcut: Ур. - endOfDemo: End of Demo + endOfDemo: Конец Демо wire: default: name: Энерг. провод description: Позволяет транспортировать энергию. second: - name: Wire - description: Transfers signals, which can be items, colors or booleans (1 / 0). - Different colored wires do not connect. + name: Энерг. провод + description: Передает сигналы, которые могут быть ресурсами, цветами или + логическими значениями (1 / 0). Провода разных цветов не + соединяются. balancer: default: - name: Balancer - description: Multifunctional - Evenly distributes all inputs onto all outputs. + name: Балансер + description: Многофункциональный - равномерно распределяет все входы на выходы. merger: - name: Merger (compact) - description: Merges two conveyor belts into one. + name: Соединитель (компактный) + description: Соединяет две линии ковейера в одну. merger-inverse: - name: Merger (compact) - description: Merges two conveyor belts into one. + name: Соединитель (компактный) + description: Соединяет две линии ковейера в одну. splitter: - name: Splitter (compact) - description: Splits one conveyor belt into two. + name: Разделитель (компактный) + description: Разделяет одну линию конвейера на две. splitter-inverse: - name: Splitter (compact) - description: Splits one conveyor belt into two. + name: Разделитель (компактный) + description: Разделяет одну линию конвейера на две. storage: default: - name: Storage - description: Stores excess items, up to a given capacity. Prioritizes the left - output and can be used as an overflow gate. + name: Буферное Хранилище + description: Хранит излишние ресурсы пока есть место. Левый выход в приоритете, + может быть использован как буфер. wire_tunnel: default: - name: Wire Crossing - description: Allows to cross two wires without connecting them. + name: Пересечение Проводов + description: Позволяет пересекать провода при этом их не соединяя. constant_signal: default: - name: Constant Signal - description: Emits a constant signal, which can be either a shape, color or - boolean (1 / 0). + name: Постоянный Сигнал + description: Издает постоянный сигнал, который может быть ресурсом, цветом или + логическим значением (1 / 0). lever: default: - name: Switch - description: Can be toggled to emit a boolean signal (1 / 0) on the wires layer, - which can then be used to control for example an item filter. + name: Переключатель + description: Может быть переключен, чтобы издавать логический сигнал (1 / 0) на + слое с проводами, который может быть использован для управления + Фильтром, например. logic_gate: default: name: AND Gate - description: Emits a boolean "1" if both inputs are truthy. (Truthy means shape, - color or boolean "1") + description: Издает значение "1" если оба входа положительны. (Положительный - + значит ресурс, цвет или логическое значение "1") not: name: NOT Gate - description: Emits a boolean "1" if the input is not truthy. (Truthy means - shape, color or boolean "1") + description: Издает значение "1" если вход не положительный. (Положительный - + значит ресурс, цвет или логическое значение "1") xor: name: XOR Gate - description: Emits a boolean "1" if one of the inputs is truthy, but not both. - (Truthy means shape, color or boolean "1") + description: Издает значение "1" только один из входов положительный. + (Положительный - значит ресурс, цвет или логическое значение + "1") or: name: OR Gate - description: Emits a boolean "1" if one of the inputs is truthy. (Truthy means - shape, color or boolean "1") + description: Издает значение "1" если хотя бы один вход положительный. + (Положительный - значит ресурс, цвет или логическое значение + "1"). + filter: + default: + name: Фильтр + description: Подключите сигнал, чтобы направить все подходящие ресурсы наверх, а + остальные направо. Также контролируемо логическими сигналами. + display: + default: + name: Экран + description: Подключите сигнал, чтобы отобразить его на экране. Это может + ресурс, цвет или логическое значение (1 / 0). + reader: + default: + name: Измеритель + description: Позволяет измерять среднюю пропускную способность конвейера. + Отображает последний прошедший ресурс на слое с проводами (когда + разблокировано). + analyzer: + default: + name: Анализатор Фигур + description: Анализирует правую верхнюю часть низшего слоя фигуры и возвращает + ее форму и цвет. + comparator: + default: + name: Сравнить + description: Возвращает значение "1" если оба сигнала полностью одинаковы. Может + сравнивать фигуры, цвета и логические значения. + virtual_processor: + default: + name: Виртуальный Резак + description: Виртуально разрезает фигуру пополам. + rotater: + name: Виртуальный Вращатель + description: Виртуально вращает фигуру как по часовой стрелке, так и против + часовой стрелки. + unstacker: + name: Виртуальный Разъединитель + description: Виртуально извлекает самый верхний слой фигуры направо, а все + остальное направо. + stacker: + name: Виртуальный Объединитель + description: Виртуально помещает правый предмет поверх левого. + painter: + name: Виртуальный Покрасчик + description: Виртуально окрашивает фигуру из нижнего входа цветом из правого + входа. + item_producer: + default: + name: Генератор Ресурсов + description: Доступен только в режиме песочницы, производит заданный на слое с + проводами сигнал на обычном слое. transistor: default: name: Transistor @@ -536,64 +560,15 @@ buildings: name: Transistor description: Forwards the bottom input if the side input is truthy (a shape, color or "1"). - filter: - default: - name: Filter - description: Connect a signal to route all matching items to the top and the - remaining to the right. Can be controlled with boolean signals - too. - display: - default: - name: Display - description: Connect a signal to show it on the display - It can be a shape, - color or boolean. - reader: - default: - name: Belt Reader - description: Allows to measure the average belt throughput. Outputs the last - read item on the wires layer (once unlocked). - analyzer: - default: - name: Shape Analyzer - description: Analyzes the top right quadrant of the lowest layer of the shape - and returns its shape and color. - comparator: - default: - name: Compare - description: Returns boolean "1" if both signals are exactly equal. Can compare - shapes, items and booleans. - virtual_processor: - default: - name: Virtual Cutter - description: Virtually cuts the shape into two halves. - rotater: - name: Virtual Rotater - description: Virtually rotates the shape, both clockwise and counter-clockwise. - unstacker: - name: Virtual Unstacker - description: Virtually extracts the topmost layer to the right output and the - remaining ones to the left. - stacker: - name: Virtual Stacker - description: Virtually stacks the right shape onto the left. - painter: - name: Virtual Painter - description: Virtually paints the shape from the bottom input with the shape on - the right input. - item_producer: - default: - name: Item Producer - description: Available in sandbox mode only, outputs the given signal from the - wires layer on the regular layer. storyRewards: reward_cutter_and_trash: title: Разрезание Фигур - desc: You just unlocked the cutter, which cuts shapes in half - from top to bottom regardless of its - orientation!

Be sure to get rid of the waste, or - otherwise it will clog and stall - For this purpose - I have given you the trash, which destroys - everything you put into it! + desc: Разблокирован резак, который разрезает фигуры пополам по + вертикали независимо от ориентации!

Не + забудьте избавляться от излишков, иначе он забьется и + остановится - для этого я также открыл для Вас + мусорку, которая уничтожает все, что в нее + попадает! reward_rotater: title: Вращение desc: Разблокирован вращатель! Он поворачивает фигуры по @@ -616,9 +591,9 @@ storyRewards: правого входа наложится на фигуру из левого! reward_splitter: title: Разделитель / Соединитель - desc: You have unlocked a splitter variant of the - balancer - It accepts one input and splits them - into two! + desc: Разблокирован разделитель один из вариантов + балансера - он принимает один вход и разделяет его + на два! reward_tunnel: title: Туннель desc: Разблокирован туннель! Теперь вы можете транспортировать @@ -630,10 +605,10 @@ storyRewards: нажмите 'T', чтобы переключить вариант! reward_miner_chainable: title: Цепной Экстрактор - desc: "You have unlocked the chained extractor! It can - forward its resources to other extractors so you - can more efficiently extract resources!

PS: The old - extractor has been replaced in your toolbar now!" + desc: "Разблокирован цепной экстрактор! Он может + передавать свои ресурсы другим экстракторам, чтобы + вы могли эффективнее извлекать ресурсы!

PS: Старый + экстрактор был заменен в панели инструментов!" reward_underground_belt_tier_2: title: Туннель II desc: Разблокирован новый вариант туннеля с большей @@ -649,18 +624,19 @@ storyRewards: одновременно, потребляя только один краситель вместо двух! reward_storage: title: Буферное Хранилище - desc: You have unlocked the storage building - It allows you to - store items up to a given capacity!

It priorities the left - output, so you can also use it as an overflow gate! + desc: Разблокировано буферное хранилище - оно позволяет хранить + в нем ресурсы пока есть место!

Левый выход в приоритете, + может быть использован как буфер! reward_freeplay: title: Свободная игра - desc: You did it! You unlocked the free-play mode! This means - that shapes are now randomly generated!

- Since the hub will require a throughput from now - on, I highly recommend to build a machine which automatically - delivers the requested shape!

The HUB outputs the requested - shape on the wires layer, so all you have to do is to analyze it and - automatically configure your factory based on that. + desc: У Вас получилось! Разблокирован свободный режим! Это + означает что теперь фигуры будут генерироваться + случайно!

Так как ХАБ отныне будет + требовать определенную пропускную способность, я + настоятельно рекомендую построить механизм, автоматически + доставляющий запрашиваемую фигуру!

ХАБ выводит запрашиваемую + фигуру на слое с проводами, так что все, что необходимо сделать, - + это проанализировать ее и автоматически настроить вашу фабрику. reward_blueprints: title: Чертежи desc: Теперь вы можете копировать и вставлять части вашей @@ -678,70 +654,72 @@ storyRewards: title: Следующий уровень desc: Поздравляем! Кстати, больше контента планируется для полной версии! reward_balancer: - title: Balancer + title: Балансер desc: The multifunctional balancer has been unlocked - It can be used to build bigger factories by splitting and merging - items onto multiple belts!

+ items
onto multiple belts! reward_merger: - title: Compact Merger - desc: You have unlocked a merger variant of the - balancer - It accepts two inputs and merges them - into one belt! + title: Компактный Соединитель + desc: Разблокирован соединитель - вариант + балансера - он принимает два входа и объединяет их + в один конвейер. reward_belt_reader: - title: Belt reader - desc: You have now unlocked the belt reader! It allows you to - measure the throughput of a belt.

And wait until you unlock - wires - then it gets really useful! + title: Измеритель + desc: Разблокирован измеритель! Он позволяет измерять + пропускную способность конвейера.

А как полезен он будет, + когда вы разблокируете провода! reward_rotater_180: - title: Rotater (180 degrees) - desc: You just unlocked the 180 degress rotater! - It allows - you to rotate a shape by 180 degress (Surprise! :D) + title: Вращатель (180 градусов) + desc: Разблокирован rotater на 180 градусов! - Он позволяет + вращать фигур на 180 градусов (Сюрприз! :D) reward_display: - title: Display - desc: "You have unlocked the Display - Connect a signal on the - wires layer to visualize it!

PS: Did you notice the belt - reader and storage output their last read item? Try showing it on a - display!" + title: Экран + desc: "Разблокирован Экран - Подключите сигнал на слое с + проводами чтобы отобразить его!

PS: Заметили ли вы, что + измеритель и буферное хранилище отображают последний ресурс, + прошедший через них? Попробуйте отобразить его на экране!" reward_constant_signal: - title: Constant Signal - desc: You unlocked the constant signal building on the wires - layer! This is useful to connect it to item filters - for example.

The constant signal can emit a - shape, color or - boolean (1 / 0). + title: Постоянный Сигнал + desc: Разблокирован постоянный сигнал на слое с проводами! Он + полезен для подключения к фильтрам, + например.

Постоянный сигнал может издавать + фигуру, цвет или + логическое значение (1 / 0). reward_logic_gates: - title: Logic Gates - desc: You unlocked logic gates! You don't have to be excited - about this, but it's actually super cool!

With those gates - you can now compute AND, OR, XOR and NOT operations.

As a - bonus on top I also just gave you a transistor! + title: Логические Элементы + desc: Разблокированы логические элементы! Вы не обязаны + радоваться по этому поводу, но вообще это очень круто!

С + этими элементами теперь вы можете производить И, ИЛИ, исключающее + ИЛИ и НЕ операции.

Как бонус, я также дал вам + транзистор! reward_virtual_processing: - title: Virtual Processing - desc: I just gave a whole bunch of new buildings which allow you to - simulate the processing of shapes!

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

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

- Build - something cool with wires.

- Continue to play - regulary.

Whatever you choose, remember to have fun! + title: Виртуальное Производство + desc: Только что я открыл вам множество новых построек, которые позволят + симулировать производство фигур!

Теперь вы + можете симулировать резак, вращатель, объединитель и др. на слое с + проводами! Теперь у вас есть три варианта продолжения игры:

+ - Построить автоматический механизм для + производства любой фигуры, запрашиваемой ХАБ (рекомендую + попробовать!).

- Построить что-то клевое, используя + провода.

- Продолжить обычную игру.

Что бы вы не + выбрали, не забывайте хорошо проводить время! reward_wires_painter_and_levers: - title: Wires & Quad Painter - desc: "You just unlocked the Wires Layer: It is a separate - layer on top of the regular layer and introduces a lot of new - mechanics!

For the beginning I unlocked you the Quad - Painter - Connect the slots you would like to paint with on - the wires layer!

To switch to the wires layer, press - E." + title: Провода & Покрасчик (4 входа) + desc: Разблокирован Слой с Проводами. Это отдельный слой поверх + обычного слоя, добавляющий множество новых механик!

Для + начала я разблокировал Покрасчик на четыре входа - + соедините ячейки, которые вы бы хотели окрасить на слое с + проводами!

Чтобы переключиться на слой слой с проводами, + нажмите E. reward_filter: - title: Item Filter - desc: You unlocked the Item Filter! It will route items either - to the top or the right output depending on whether they match the - signal from the wires layer or not.

You can also pass in a - boolean signal (1 / 0) to entirely activate or disable it. + title: Фильтр + desc: Разблокирован Фильтр! Он направит ресурсы наверх или + направо в зависмости от того, совпадают ли они с установленным + сигналом.

Вы также можете передавать логические значения (1 + / 0), чтобы полностью отключить или включить его. reward_demo_end: - title: End of Demo - desc: You have reached the end of the demo version! + title: Конец Демо + desc: Вы достигли конца демоверсии игры! settings: title: Настройки categories: @@ -835,9 +813,9 @@ settings: description: Включает виньетирование, которое затемняет углы экрана и облегчает чтение текста. autosaveInterval: - title: Интервал авто-сохранения + title: Интервал автосохранения description: Управляет тем, как часто игра автоматически сохраняется. Также - здесь можно полностью отключить авто-сохранение. + здесь можно полностью отключить автосохранение. intervals: one_minute: 1 Минута two_minutes: 2 Минуты @@ -864,47 +842,54 @@ settings: установлен. С этой настройкой может быть удобнее, при частом переключении между различными типами зданий. soundVolume: - title: Sound Volume - description: Set the volume for sound effects + title: Громкость Звука + description: Задает громкость звуковых эффектов. musicVolume: title: Music Volume - description: Set the volume for music + description: Задает громкость музыки. lowQualityMapResources: - title: Low Quality Map Resources - description: Simplifies the rendering of resources on the map when zoomed in to - improve performance. It even looks cleaner, so be sure to try it - out! + title: Низкое качество ресурсов на карте + description: Упрощает отображение ресурсов на карте при приближении для + улучшения производительности. Оно даже выглядит аккуратнее, + поэтому обязательно попробуйте! disableTileGrid: - title: Disable Grid - description: Disabling the tile grid can help with the performance. This also - makes the game look cleaner! + title: Отключить Сетку + description: Отключение разделительной сетки может помочь улучшить + производительность. Кроме того, делает игру визуально проще! clearCursorOnDeleteWhilePlacing: - title: Clear Cursor on Right Click - description: Enabled by default, clears the cursor whenever you right click - while you have a building selected for placement. If disabled, - you can delete buildings by right-clicking while placing a - building. + title: Очистить Курсор на ПКМ + description: Включено по умолчанию, очищает курсор от выбранной постройки при + нажатии на ПКМ. Если отключено, вы можете удалять постройки при + нажатии на ПКМ во время строительства. lowQualityTextures: - title: Low quality textures (Ugly) - description: Uses low quality textures to save performance. This will make the - game look very ugly! + title: Низкое качество текстур (Некрасиво) + description: Использует низкое качество текстур, чтобы улучшить + производительность. Это сделает игру очень некрасивой! displayChunkBorders: - title: Display Chunk Borders - description: The game is divided into chunks of 16x16 tiles, if this setting is - enabled the borders of each chunk are displayed. + title: Отображать границы чанков + description: Эта игра разделена на чанки, состоящие из 16x16 ячеек, если эта + настройка включена, границы чанков будут отображаться. pickMinerOnPatch: - title: Pick miner on resource patch - description: Enabled by default, selects the miner if you use the pipette when - hovering a resource patch. + title: Выбрать Экстрактор над Жилой + description: Включено по умолчанию, выбирает экстрактор, если использовать + пипетку над жилой с ресурсами. simplifiedBelts: - title: Simplified Belts (Ugly) - description: Does not render belt items except when hovering the belt to save - performance. I do not recommend to play with this setting if you - do not absolutely need the performance. + title: Упрощенные Конвейеры (Некрасиво) + description: Не отображает ресурсы, находящиеся на конвейере, если не навести + курсор для улучшения производительности. Не рекомендую играть с + этой настройкой, если вас устраивает производительность. enableMousePan: - title: Enable Mouse Pan - description: Allows to move the map by moving the cursor to the edges of the - screen. The speed depends on the Movement Speed setting. + title: Включить Перемещение Мышкой + description: Позволяет двигать карту, перемещая курсор к краям экрана. Скорость + зависит от настройки Скорости движения. + zoomToCursor: + title: Zoom towards Cursor + description: If activated the zoom will happen in the direction of your mouse + position, otherwise in the middle of the screen. + mapResourcesScale: + title: Map Resources Size + description: Controls the size of the shapes on the map overview (when zooming + out). rangeSliderPercentage: % keybindings: title: Настройки управления @@ -951,9 +936,9 @@ keybindings: massSelectStart: Модификатор для выделения области massSelectSelectMultiple: Выбрать несколько областей massSelectCopy: Копировать область - placementDisableAutoOrientation: Отключить авто-определение направления + placementDisableAutoOrientation: Отключить автоопределение направления placeMultiple: Оставаться в режиме размещения - placeInverse: Инвертировать авто-определение направления конвейеров + placeInverse: Инвертировать автоопределение направления конвейеров pasteLastBlueprint: Вставить последний чертеж massSelectCut: Вырезать область exportScreenshot: Экспорт всей Базы в виде Изображения @@ -964,21 +949,21 @@ keybindings: menuClose: Закрыть меню switchLayers: Переключить слои wire: Энергетический провод - balancer: Balancer - storage: Storage - constant_signal: Constant Signal - logic_gate: Logic Gate - lever: Switch (regular) - filter: Filter - wire_tunnel: Wire Crossing - display: Display - reader: Belt Reader - virtual_processor: Virtual Cutter - transistor: Transistor - analyzer: Shape Analyzer - comparator: Compare - item_producer: Item Producer (Sandbox) - copyWireValue: "Wires: Copy value below cursor" + balancer: Балансер + storage: Буферное Хранилище + constant_signal: Постоянный Сигнал + logic_gate: Логический Элемент + lever: Переключатель (обычный) + filter: Фильтр + wire_tunnel: Пересечение Проводов + display: Экран + reader: Измеритель + virtual_processor: Виртуальный Резак + transistor: Транзистор + analyzer: Анализатор Фигур + comparator: Сравнить + item_producer: Генератор Ресурсов (Песочница) + copyWireValue: "Провода: скопировать значение под курсором" about: title: Об игре body: >- @@ -1002,64 +987,70 @@ demo: oneGameLimit: Ограниченность одним сохранением игры customizeKeybindings: Пользовательская настройка Управления exportingBase: Экспорт всей Базы в виде Изображения - settingNotAvailable: Не доступно в демо-версии. + settingNotAvailable: Недоступно в демоверсии. tips: - - The hub accepts input of any kind, not just the current shape! - - Make sure your factories are modular - it will pay out! - - Don't build too close to the hub, or it will be a huge chaos! - - If stacking does not work, try switching the inputs. - - You can toggle the belt planner direction by pressing R. - - Holding CTRL allows dragging of belts without auto-orientation. - - Ratios stay the same, as long as all upgrades are on the same Tier. - - Serial execution is more efficient than parallel. - - You will unlock more variants of buildings later in the game! - - You can use T to switch between different variants. - - Symmetry is key! - - You can weave different tiers of tunnels. - - Try to build compact factories - it will pay out! - - The painter has a mirrored variant which you can select with T - - Having the right building ratios will maximize efficiency. + - ХАБ принимает любые ресурсы, не только текущую фигуру! + - Старайтесь создавать модульные фабрики - вы не пожалеете! + - Не стройте слишком близко к ХАБ-у, иначе начнется ужасный хаос! + - Если не получается объединить фигуры, попробуйте поменять входы местами! + - Вы можете изменить направление конвейера при строительстве, нажав R. + - Удерживая CTRL, вы можете перемещать конвейеры без авто ориентации. + - Соотношения всегда одинаковы, если уровни улучшений равны. + - Последовательное выполнение эффективнее, чем параллельное. + - Вам будут открываться новые варианты построек по мере прохождения! + - Нажмите T, чтобы переключаться между различными вариантами. + - Симметрия - ключ к успеху! + - Вы можете переплетать между собой туннели разных уровней. + - Попробуйте строить компактные фабрики - вы не пожалеете! + - Покрасчик имеет зеркальный вариант, который может быть выбран, нажав + T. + - Правильные соотношения построек позволяет улучшить эффективность фабрики. - At maximum level, 5 extractors will fill a single belt. - - Don't forget about tunnels! - - You don't need to divide up items evenly for full efficiency. + - Резаки всегда разрезают пополам по вертикали вне зависимости от ориентации. + - Чтобы получить белый цвет, смешайте все три цвета. - Holding SHIFT will activate the belt planner, letting you place long lines of belts easily. - - Cutters always cut vertically, regardless of their orientation. + - Вкладывайте время в строительство повторяемых механизмов - оно того стоит! - To get white mix all three colors. - - The storage buffer priorities the first output. - - Invest time to build repeatable designs - it's worth it! + - The storage buffer prioritises the left output. + - Эффективность - ключ к успеху! - Holding CTRL allows to place multiple buildings. - You can hold ALT to invert the direction of placed belts. - - Efficiency is key! - - Shape patches that are further away from the hub are more complex. - - Machines have a limited speed, divide them up for maximum efficiency. - - Use balancers to maximize your efficiency. - - Organization is important. Try not to cross conveyors too much. - - Plan in advance, or it will be a huge chaos! - - Don't remove your old factories! You'll need them to unlock upgrades. - - Try beating level 20 on your own before seeking for help! - - Don't complicate things, try to stay simple and you'll go far. - - You may need to re-use factories later in the game. Plan your factories to - be re-usable. - - Sometimes, you can find a needed shape in the map without creating it with - stackers. - - Full windmills / pinwheels can never spawn naturally. - - Color your shapes before cutting for maximum efficiency. + - Используйте балансеры, чтобы максимизировать эффективность. + - Организация очень важна, старайтесь не пересекать конвейеры слишком часто. + - Планируйте заранее, иначе начнется ужасный хаос! + - Не удаляйте свои старые фабрики! Они понадобятся вам, чтобы открывать + улучшения. + - Попробуйте пройти 20-ый уровень самостоятельно, прежде чем искать помощи! + - Не усложняйте себе жизнь, старайтесь думать проще и вы достигните больших + высот. + - Вам может снова понадобиться ваша старая фабрика позже в игре. Старайтесь + планировать фабрику, чтобы она могла быть повторно использована. + - Иногда, вы можете найти необходимую фигуру на карте, вместо того, чтобы + создавать ее самостоятельно. + - Полноценные мельницы/вертушки никогда не генерируются натурально. + - Окрашивайте свои фигуры, прежде чем разрезать для максимальной + эффективности. + - С модулями теряется восприятие пространства; забота смертных. + - Создайте отдельную фабрику чертежей. Они очень важны для модулей. + - Взгляните внимательнее на смешиватель и вы найдете ответы на свои вопросы. - With modules, space is merely a perception; a concern for mortal men. - - Make a separate blueprint factory. They're important for modules. - - Have a closer look on the color mixer, and your questions will be answered. + - Строительство вблизи ХАБ-а может помешать будущим проектам. + - Иконка булавки на каждой фигуре закрепляет ее на экране. - Use CTRL + Click to select an area. - - Building too close to the hub can get in the way of later projects. - - The pin icon next to each shape in the upgrade list pins it to the screen. - - Mix all primary colors together to make white! - - You have an infinite map, don't cramp your factory, expand! - - Also try Factorio! It's my favorite game. - - The quad cutter cuts clockwise starting from the top right! - - You can download your savegames in the main menu! - - This game has a lot of useful keybindings! Be sure to check out the - settings page. - - This game has a lot of settings, be sure to check them out! - - The marker to your hub has a small compass to indicate its direction! + - В вашем распоряжении бесконечная карта! Не загромождайте вашу фабрику, + расширяйтесь! + - Также попробуйте Factorio. Это моя любимая игра. + - Резак(4 входа) разрезает по часовой стрелке, начиная с правой верхней + части! + - Вы можете скачать свои сохранения в главном меню! + - В этой игре множество полезных комбинаций клавиш. Загляните в настройки, + чтобы ознакомиться с ними. + - В этой игре множество настроек, не забудьте с ними ознакомиться. + - Маркер ХАБ-а имеет небольшой компас, указывающий его направление. + - Нажмите F4, чтобы показать FPS и Частоту Обновления. + - Нажмите F4 дважды, чтобы показать координаты курсора и камеры. + - Вы можете нажать на закрепленную фигуру слева, чтобы открепить ее. - To clear belts, cut the area and then paste it at the same location. - Press F4 to show your FPS and Tick Rate. - Press F4 twice to show the tile of your mouse and camera. diff --git a/translations/base-sl.yaml b/translations/base-sl.yaml index 9bd566cc..737b64ae 100644 --- a/translations/base-sl.yaml +++ b/translations/base-sl.yaml @@ -199,11 +199,6 @@ dialogs: renameSavegame: title: Rename Savegame desc: You can rename your savegame here. - entityWarning: - title: Performance Warning - desc: You have placed a lot of buildings, this is just a friendly reminder that - the game can not handle an endless count of buildings - So try to - keep your factories compact! ingame: keybindingsOverlay: moveMap: Move @@ -259,27 +254,6 @@ ingame: title: Upgrades buttonUnlock: Upgrade tier: Tier - tierLabels: - - I - - II - - III - - IV - - V - - VI - - VII - - VIII - - IX - - X - - XI - - XII - - XIII - - XIV - - XV - - XVI - - XVII - - XVIII - - XIX - - XX maximumLevel: MAXIMUM LEVEL (Speed x) statistics: title: Statistics @@ -303,10 +277,6 @@ ingame: playtime: Playtime buildingsPlaced: Buildings beltsPlaced: Belts - buttons: - continue: Continue - settings: Settings - menu: Return to menu tutorialHints: title: Need help? showHint: Show hint @@ -676,7 +646,7 @@ storyRewards: title: Balancer desc: The multifunctional balancer has been unlocked - It can be used to build bigger factories by splitting and merging - items onto multiple belts!

+ items onto multiple belts! reward_merger: title: Compact Merger desc: You have unlocked a merger variant of the @@ -893,6 +863,14 @@ settings: title: Enable Mouse Pan description: Allows to move the map by moving the cursor to the edges of the screen. The speed depends on the Movement Speed setting. + zoomToCursor: + title: Zoom towards Cursor + description: If activated the zoom will happen in the direction of your mouse + position, otherwise in the middle of the screen. + mapResourcesScale: + title: Map Resources Size + description: Controls the size of the shapes on the map overview (when zooming + out). rangeSliderPercentage: % keybindings: title: Keybindings diff --git a/translations/base-sr.yaml b/translations/base-sr.yaml index 47aa4947..6a4f5f6f 100644 --- a/translations/base-sr.yaml +++ b/translations/base-sr.yaml @@ -199,11 +199,6 @@ dialogs: renameSavegame: title: Rename Savegame desc: You can rename your savegame here. - entityWarning: - title: Performance Warning - desc: You have placed a lot of buildings, this is just a friendly reminder that - the game can not handle an endless count of buildings - So try to - keep your factories compact! ingame: keybindingsOverlay: moveMap: Kretanje @@ -259,27 +254,6 @@ ingame: title: Nadogradnje buttonUnlock: Nadogradi tier: red - tierLabels: - - I - - II - - III - - IV - - V - - VI - - VII - - VIII - - IX - - X - - XI - - XII - - XIII - - XIV - - XV - - XVI - - XVII - - XVIII - - XIX - - XX maximumLevel: MAKSIMALNI LEVEL (Brzina x) statistics: title: Statistika @@ -303,10 +277,6 @@ ingame: playtime: Vreme igranja buildingsPlaced: Građevine beltsPlaced: Trake - buttons: - continue: Nastavi - settings: Podešavanja - menu: Povratak na glavni meni tutorialHints: title: Potrebna pomoć? showHint: Prikaži savet @@ -674,7 +644,7 @@ storyRewards: title: Balancer desc: The multifunctional balancer has been unlocked - It can be used to build bigger factories by splitting and merging - items onto multiple belts!

+ items onto multiple belts! reward_merger: title: Compact Merger desc: You have unlocked a merger variant of the @@ -891,6 +861,14 @@ settings: title: Enable Mouse Pan description: Allows to move the map by moving the cursor to the edges of the screen. The speed depends on the Movement Speed setting. + zoomToCursor: + title: Zoom towards Cursor + description: If activated the zoom will happen in the direction of your mouse + position, otherwise in the middle of the screen. + mapResourcesScale: + title: Map Resources Size + description: Controls the size of the shapes on the map overview (when zooming + out). rangeSliderPercentage: % keybindings: title: Tasteri diff --git a/translations/base-sv.yaml b/translations/base-sv.yaml index a6828af3..a06e0c95 100644 --- a/translations/base-sv.yaml +++ b/translations/base-sv.yaml @@ -121,8 +121,8 @@ dialogs: text: "Kunde inte ladda sparfil:" confirmSavegameDelete: title: Bekräfta radering - text: Är du säker på att du vill ta bort följande spel?

- '' på nivå

Detta kan inte ångras! + text: Är du säker på att du vill ta bort följande spel?

'' + på nivå

Detta kan inte ångras! savegameDeletionError: title: Kunde inte radera text: "Kunde inte radera sparfil:" @@ -203,9 +203,6 @@ dialogs: renameSavegame: title: Byt namn på sparfil desc: Du kan byta namn på din sparfil här. - entityWarning: - title: Prestanda varning - desc: Du har placerat väldigt många byggnader, det här är bara en vänlig påminnelse att spelet inte klarar av ett oändligt antal av byggnader - så försök hålla dina fabriker kompakta! ingame: keybindingsOverlay: moveMap: Flytta @@ -251,27 +248,6 @@ ingame: title: Upgraderingar buttonUnlock: Upgradera tier: Tier - tierLabels: - - I - - II - - III - - IV - - V - - VI - - VII - - VIII - - IX - - X - - XI - - XII - - XIII - - XIV - - XV - - XVI - - XVII - - XVIII - - XIX - - XX maximumLevel: MAXNIVÅ (Hastighet x) statistics: title: Statistik @@ -295,10 +271,6 @@ ingame: playtime: Speltid buildingsPlaced: Byggnader beltsPlaced: Rullband - buttons: - continue: Fortsätt - settings: Inställningar - menu: Återvänd till meny tutorialHints: title: Behöver hjälp? showHint: Visa tips @@ -665,7 +637,7 @@ storyRewards: title: Ritningar desc: Du kan nu kopiera och klistra in delar av din fabrik! Välj ett område (håll in CTRL, dra sedan med musen), och tryck 'C' - för att kopiera det.

Att klistra in ärinte + för att kopiera det.

Att klistra in är inte gratis, du behöver producera ritningsformer för att ha råd med det! (De du just levererade). @@ -683,7 +655,7 @@ storyRewards: title: Balancer desc: The multifunctional balancer has been unlocked - It can be used to build bigger factories by splitting and merging - items onto multiple belts!

+ items
onto multiple belts! reward_merger: title: Compact Merger desc: You have unlocked a merger variant of the @@ -696,7 +668,8 @@ storyRewards: wires - then it gets really useful! reward_rotater_180: title: Roterare (180 grader) - desc: Du låste precis upp roteraren! - Den låter dig rotera former med 180 grader (Vilken överraskning! :D) + desc: Du låste precis upp roteraren! - Den låter dig rotera + former med 180 grader (Vilken överraskning! :D) reward_display: title: Display desc: "You have unlocked the Display - Connect a signal on the @@ -900,6 +873,14 @@ settings: title: Enable Mouse Pan description: Allows to move the map by moving the cursor to the edges of the screen. The speed depends on the Movement Speed setting. + zoomToCursor: + title: Zoom towards Cursor + description: If activated the zoom will happen in the direction of your mouse + position, otherwise in the middle of the screen. + mapResourcesScale: + title: Map Resources Size + description: Controls the size of the shapes on the map overview (when zooming + out). rangeSliderPercentage: % keybindings: title: Snabbtangenter diff --git a/translations/base-tr.yaml b/translations/base-tr.yaml index 2397cd8f..837f14b3 100644 --- a/translations/base-tr.yaml +++ b/translations/base-tr.yaml @@ -120,9 +120,9 @@ dialogs: text: "Oyun yükleme başarısız:" confirmSavegameDelete: title: Silme işlemini onayla - text: Are you sure you want to delete the following game?

- '' at level

This can not be - undone! + text:

Bu kayıdı silmek istiyor musunuz? + ''

. seviyede. Bu işlem geri + alınamaz! savegameDeletionError: title: Silme başarısız text: "Oyun kaydını silme başarısız:" @@ -174,8 +174,8 @@ dialogs: taşıma bantlarının yönünü ters çevirir.
" createMarker: title: Yeni Konum İşareti - desc: Give it a meaningful name, you can also include a short - key of a shape (Which you can generate here) + desc: Anlamlı bir isim ver. Ayrıca Şekil koduda koyabilirsiniz + (Buradan kod yapabilirisinz ) titleEdit: Konum İşaretini Düzenle markerDemoLimit: desc: Deneme sürümünde sadece iki adet yer imi oluşturabilirsiniz. Sınırsız yer @@ -201,10 +201,6 @@ dialogs: renameSavegame: title: Oyun Kaydının Yeniden Adlandır desc: Oyun kaydını buradan adlandırabilirsiniz. - entityWarning: - title: Performans Uyarısı - desc: Dostça bir uyarı; Çok fazla yapı inşa ettiniz. Oyun sınırsız sayıda - yapıyla başa çıkamaz - Bu yüzden fabrikalarınızı az ama öz tutun! ingame: keybindingsOverlay: moveMap: Hareket Et @@ -250,27 +246,6 @@ ingame: title: Gelİştİrmeler buttonUnlock: Gelİştİr tier: Aşama - tierLabels: - - I - - II - - III - - IV - - V - - VI - - VII - - VIII - - IX - - X - - XI - - XII - - XIII - - XIV - - XV - - XVI - - XVII - - XVIII - - XIX - - XX maximumLevel: SON SEVİYE (Hız x) statistics: title: İstatistikler @@ -293,10 +268,6 @@ ingame: playtime: Oynama zamanı buildingsPlaced: Yapılar beltsPlaced: Taşıma bantları - buttons: - continue: Devam - settings: Ayarlar - menu: Ana Menüye Dön tutorialHints: title: Yardım? showHint: İpucu Göster @@ -585,9 +556,10 @@ storyRewards: reward_cutter_and_trash: title: Şekİllerİ Kesmek desc: Kesici açıldı, bu alet şekilleri yönelimi ne - olursa olsun ortadan ikiye böler!

Çıkan şekilleri kullanmayı veya - çöpe atmayı unutma yoksa makine tıkanır! - Bu nedenle sana gönderdiğin - bütün her şeyi yok eden çöpü de verdim! + olursa olsun ortadan ikiye böler!

Çıkan şekilleri + kullanmayı veya çöpe atmayı unutma yoksa makine + tıkanır! - Bu nedenle sana gönderdiğin bütün her şeyi yok + eden çöpü de verdim! reward_rotater: title: Döndürme desc: Döndürücü açıldı! Döndürücü şekilleri saat yönüne 90 @@ -610,7 +582,8 @@ storyRewards: üzerine kaynaştırılır! reward_splitter: title: Ayırıcı/Bİrleştİrİcİ - desc: Bir taşıma bandını iki çıktı verecek şekilde birleştirir! + desc: Ayırıcıyı açtın! dengeleyicin başka bir + türü - Tek giriş alıp ikiye ayırır reward_tunnel: title: Tünel desc: Tünel açıldı - Artık eşyaları taşıma bantları ve yapılar @@ -622,10 +595,11 @@ storyRewards: seç ve türler arası geçiş yapmak için 'T' tuşuna bas! reward_miner_chainable: - title: Zincirleme Üretİm - desc: "Zincirleme Üreticiyi açtın! Çıkardığı eşyayı önündeki - üreticiye aktarabilir böylelikle madenleri daha efektif kullana bilirsin!

- NOT: Eski üretici Zincirleme Üretici ile değiştirildi!" + title: Zincirleme Üretici + desc: " zincirleme üreticiyiaçtın! bununla + kaynaklarını diğer çıkarıcılarla paylaşıp daha + verimli bir şekilde çıkartabilirsin!

not: Eskilerini + yenileri ile değiştirdim!" reward_underground_belt_tier_2: title: Tünel Aşama II desc: Tünelin başka bir türünü açtın - Bu tünelin menzili @@ -642,9 +616,11 @@ storyRewards: boya yerine sadece bir boya harcar! reward_storage: title: Depo - desc: Depoyu açtınız! - Gönderdiğin eşyaları içinde biriktirir!

- Öncelikli olarak sol tarafından eşyaları gönderir eğer sol taraf tıkanırsa sağ taraftan gödermeye başlar. - Bunu fazlalık eşyaları değerlendirmek için kullanabilirsin! + desc: Depoyu açtınız! - Gönderdiğin eşyaları içinde + biriktirir!

Öncelikli olarak sol tarafından eşyaları + gönderir eğer sol taraf tıkanırsa sağ taraftan göndermeye başlar. + Bunu fazlalık eşyaları değerlendirmek için + kullanabilirsin! reward_blueprints: title: Taslaklar desc: Fabrikanın bölümlerini artık kopyalayıp @@ -655,29 +631,35 @@ storyRewards: (Az önce teslim ettiğin şekiller). no_reward: title: Sonrakİ Sevİye - desc: - "Bu seviyenin bir ödülü yok ama bir sonrakinin olacak!

Not: Şu anki fabrikalarını yok etmemeni öneririm - - Daha sonra Geliştirmeleri açmak için bütün hepsine ihtiyacın olacak!" + desc: "Bu seviyenin bir ödülü yok ama bir sonrakinin olacak!

Not: Şu + anki fabrikalarını yok etmemeni öneririm - Daha sonra + Geliştirmeleri açmak için bütün + hepsine ihtiyacın olacak!" no_reward_freeplay: title: Sonrakİ Sevİye desc: Tebrikler! reward_freeplay: title: Özgür Mod desc: Başardın! Özgür modu açtın! Bu artık gelen şekillerin - rastgele oluşacağı anlamına geliyor!

- Bundan sonra ana bölge belirli bir miktar eşya değil belirli bir miktar eşya geliş hızına - bağlı olarak level atlayacaksın, istenilen şekilleri otomatik olarak yapacak bir fabrika inşa etmeni - öneririm!

Ana bölgenin istediği şekil kablo katmanında sol taraftan sinyal olarak gönderiliyor, - yani sadece bu şekli analiz ederek üretecek tamen otomatik bir alet yapman yeterli. + rastgele oluşacağı anlamına geliyor!

Bundan + sonra ana bölge belirli bir miktar eşya değil belirli bir + miktar eşya geliş hızına bağlı olarak level atlayacaksın, + istenilen şekilleri otomatik olarak yapacak bir fabrika inşa etmeni + öneririm!

Ana bölgenin istediği şekil kablo katmanında sol + taraftan sinyal olarak gönderiliyor, yani sadece bu şekli analiz + ederek üretecek tamen otomatik bir alet yapman yeterli. reward_demo_end: title: Deneme Sürümünün Sonu desc: Deneme sürümünün sonuna geldin! reward_balancer: title: Dengeleyici - desc: Çok işlevli - bütün girdileri eşit olarak bütün çıkışlara dağıtır.!

+ desc: Çok fonksiyonlu dengeleyici açıldı. - Eşyaları + bantlara ayırarak ve bantları birleştirerek daha büyük + fabrikalar kurmak için kullanılabilir! reward_merger: title: Tekil Birleştirici - desc: İki taşıma bandını bir çıktı verecek şekilde birleştirir! + desc: Birleştiriciyi açtın ! dengeleyecinin + bir türü - İki giriş alıp tek banta atar. reward_belt_reader: title: Bant Okuyucu desc: Bant okuyucu açıldı! Bu yapı taşıma bandındaki akış @@ -685,16 +667,21 @@ storyRewards: çok kullanışlı olacak. reward_rotater_180: title: Dödürücü (180 derece) - desc: 180 derece döndürücüyü açtınız! - Şekilleri - 180 derece döndürür (Süpriz! :D) + desc: 180 derece döndürücüyü açtınız! - Şekilleri 180 derece + döndürür (Süpriz! :D) reward_display: title: Ekran - desc: "Ekranda göstermek için bir sinyal bağla - Bu sinyal bir şekil, renk - veya ikili değer (1/0) olabilir" + desc: "Ekranı açtın. - Kablo katmanında bir sinyal + bağla ve onu ekranda göster!

Not: Bant okuyucunun ve + deponun son okudukları eşyayı çıkardığını fark ettin mi? + Bunu ekranda göstermeyi dene!" reward_constant_signal: title: Sabit Sinyal - desc: Şekil, renk veya ikili değer (1 / 0) olan sabit bir sinyal - gönderir. + desc: Kablo katmanında inşa edilebilen sabit sinyal'i açtın! + Bu yapı eşya filtrelerine bağlanabilir. + Sabit sinyal şekil, + renk veya ikili değer (1 veya 0) + gönderelebilir. reward_logic_gates: title: Mantık Kapıları desc: Mantık kapıları açıldı! Çok heyecanlanmana gerek yok, ama @@ -738,6 +725,7 @@ settings: staging: Yükseltme prod: Üretim buildDate: derlendi + rangeSliderPercentage: % labels: uiScale: title: Arayüz Ölçeğİ @@ -878,13 +866,20 @@ settings: kullanılırsa, üreteç yapısı inşa için seçilir. simplifiedBelts: title: Sadeleştİrİlmİş Bantlar (Çirkin) - description: Taşıma bandı üzerindeki eşyalar görüntülenmez, eğer fare imleci - taşıma bandı üzerinde değilse. Eğer gerçekten performansa - ihtiyacınız yoksa bu ayarla oynamanız tavsiye edilmez. + description: Taşıma bandı üzerindeki eşyalar fare imleci üzerinde değilse + görüntülenmez. Eğer gerçekten performansa ihtiyacınız yoksa bu + ayarla oynamanız tavsiye edilmez. enableMousePan: title: Fare Kaydırarak Hareket Etme description: Fareyi ekranın köşelerine getirerek hareket ettirmeyi sağlar. - rangeSliderPercentage: % + zoomToCursor: + title: Farenin Konumuna Yakınlaştırma + description: Eğer etkinleştirilirse zaman ekran yakınlaştırılması fare imlecinin bulunduğu + yere doğru olur. Etkinleştirilmezse yakınlaştırma ekranın ortasına doğru olur. + mapResourcesScale: + title: Uzak Bakışta Kaynakların Büyüklüğü + description: Haritaya uzaktan bakıldığında, haritadaki şekillerin + büyüklüğünü ayarlar. keybindings: title: Tuş Atamaları hint: "İpucu: CTRL, SHIFT ve ALT tuşlarından yararlanın! Farklı yerleştirme diff --git a/translations/base-uk.yaml b/translations/base-uk.yaml index 96c5694a..e7b0d00f 100644 --- a/translations/base-uk.yaml +++ b/translations/base-uk.yaml @@ -202,11 +202,6 @@ dialogs: renameSavegame: title: Rename Savegame desc: You can rename your savegame here. - entityWarning: - title: Performance Warning - desc: You have placed a lot of buildings, this is just a friendly reminder that - the game can not handle an endless count of buildings - So try to - keep your factories compact! ingame: keybindingsOverlay: moveMap: Рухатися @@ -262,27 +257,6 @@ ingame: title: Поліпшення buttonUnlock: Поліпшення tier: Ранг - tierLabels: - - I - - II - - III - - IV - - V - - VI - - VII - - VIII - - IX - - X - - XI - - XII - - XIII - - XIV - - XV - - XVI - - XVII - - XVIII - - XIX - - XX maximumLevel: МАКСИМАЛЬНИЙ РІВЕНЬ (Швидкість x) statistics: title: Статистика @@ -306,10 +280,6 @@ ingame: playtime: У грі buildingsPlaced: Будівлі beltsPlaced: Стрічки - buttons: - continue: Продовжити - settings: Налаштування - menu: Повернутися до меню tutorialHints: title: Потрібна допомога? showHint: Показати підказку @@ -684,7 +654,7 @@ storyRewards: title: Balancer desc: The multifunctional balancer has been unlocked - It can be used to build bigger factories by splitting and merging - items onto multiple belts!

+ items
onto multiple belts! reward_merger: title: Compact Merger desc: You have unlocked a merger variant of the @@ -904,6 +874,14 @@ settings: title: Enable Mouse Pan description: Allows to move the map by moving the cursor to the edges of the screen. The speed depends on the Movement Speed setting. + zoomToCursor: + title: Zoom towards Cursor + description: If activated the zoom will happen in the direction of your mouse + position, otherwise in the middle of the screen. + mapResourcesScale: + title: Map Resources Size + description: Controls the size of the shapes on the map overview (when zooming + out). rangeSliderPercentage: % keybindings: title: Гарячі клавіши diff --git a/translations/base-zh-CN.yaml b/translations/base-zh-CN.yaml index 73933872..771470d0 100644 --- a/translations/base-zh-CN.yaml +++ b/translations/base-zh-CN.yaml @@ -148,8 +148,7 @@ dialogs: desc: 你还没有解锁蓝图功能!完成更多的关卡来解锁蓝图。 keybindingsIntroduction: title: 实用按键 - desc: - "这个游戏有很多能帮助搭建工厂的使用按键。 以下是其中的一些,记得在按键设置中查看其他的!

+ desc: "这个游戏有很多能帮助搭建工厂的使用按键。 以下是其中的一些,记得在按键设置中查看其他的!

CTRL + 拖动:选择区域以复制或删除。
SHIFT: 按住以放置多个。
ALT: 反向放置传送带。
" @@ -177,9 +176,6 @@ dialogs: renameSavegame: title: 重命名存档 desc: 您可以在此重命名存档。 - entityWarning: - title: 性能警告 - desc: 您有非常多建筑,善意的提醒您,这个游戏无法处理过多的建筑 - 尝试将您的工厂压缩! ingame: keybindingsOverlay: moveMap: 移动地图 @@ -225,27 +221,6 @@ ingame: title: 建筑升级 buttonUnlock: 升级 tier: 级 - tierLabels: - - 一 - - 二 - - 三 - - 四 - - 五 - - 六 - - 七 - - 八 - - 九 - - 十 - - 十一 - - 十二 - - 十三 - - 十四 - - 十五 - - 十六 - - 十七 - - 十八 - - 十九 - - 二十 maximumLevel: 最高级(倍效率) statistics: title: 统计信息 @@ -268,10 +243,6 @@ ingame: playtime: 游戏时间 buildingsPlaced: 建筑数量 beltsPlaced: 传送带数量 - buttons: - continue: 继续 - settings: 设置 - menu: 回到主界面 tutorialHints: title: 需要帮助? showHint: 显示帮助 @@ -289,8 +260,7 @@ ingame: hints: 1_1_extractor: 在圆形矿脉上放一个开采机来获取圆形! 1_2_conveyor: 用传送带将你的开采机连接到基地上!

提示:用你的鼠标按下并拖动传送带! - 1_3_expand: - 这不是一个挂机游戏!建造更多的开采机和传送带来更快地完成目标。

提示:按住 + 1_3_expand: 这不是一个挂机游戏!建造更多的开采机和传送带来更快地完成目标。

提示:按住 SHIFT 键来放置多个开采机,用 R 键旋转它们。 colors: red: 红色 @@ -617,7 +587,7 @@ storyRewards: title: Balancer desc: The multifunctional balancer has been unlocked - It can be used to build bigger factories by splitting and merging - items onto multiple belts!

+ items onto multiple belts! reward_merger: title: Compact Merger desc: You have unlocked a merger variant of the @@ -817,6 +787,14 @@ settings: title: Enable Mouse Pan description: Allows to move the map by moving the cursor to the edges of the screen. The speed depends on the Movement Speed setting. + zoomToCursor: + title: Zoom towards Cursor + description: If activated the zoom will happen in the direction of your mouse + position, otherwise in the middle of the screen. + mapResourcesScale: + title: Map Resources Size + description: Controls the size of the shapes on the map overview (when zooming + out). rangeSliderPercentage: % keybindings: title: 按键设置 diff --git a/translations/base-zh-TW.yaml b/translations/base-zh-TW.yaml index 6b48936a..03690ecd 100644 --- a/translations/base-zh-TW.yaml +++ b/translations/base-zh-TW.yaml @@ -1,50 +1,43 @@ steamPage: shortText: shapez.io 是一款在一個無邊際的地圖上建造工廠、自動化生產與組合愈加複雜圖形的遊戲。 - discordLinkShort: Official Discord - intro: >- - Shapez.io is a relaxed game in which you have to build factories for the - automated production of geometric shapes. - - As the level increases, the shapes become more and more complex, and you have to spread out on the infinite map. - - And as if that wasn't enough, you also have to produce exponentially more to satisfy the demands - the only thing that helps is scaling! - - While you only process shapes at the beginning, you have to color them later - for this you have to extract and mix colors! - - Buying the game on Steam gives you access to the full version, but you can also play a demo on shapez.io first and decide later! - title_advantages: Standalone Advantages + discordLinkShort: 官方 Discord 伺服器 + intro: |- + 你喜歡自動化生產的遊戲類型嗎?那你來對地方了! + Shapez.io 是一款建造工廠、自動化生產與組合圖形的休閒遊戲。 + 每當玩家不斷晉級,形狀會越來越複雜,你的工廠將在一個無邊際的地圖上不斷擴展。 + 除此之外,你也必須不斷累加你的生產量來達到升級的需求,達成目標的方法無他,只有不段地擴張! + 遊戲初期只需要組合特定圖形,接著玩家會被要求幫圖形上色,有時甚至需要先混色才能達到目標。 + 玩家可以在 Steam 購買本游戲的單機版,如果還在猶豫,也可以到 shapez.io 先免費試玩再決定! + title_advantages: 單機版優點 advantages: - - 12 New Level for a total of 26 levels - - 18 New Buildings for a fully automated factory! - - 20 Upgrade Tiers for many hours of fun! - - Wires Update for an entirely new dimension! - - Dark Mode! - - Unlimited Savegames - - Unlimited Markers - - Support me! ❤️ - title_future: Planned Content + - 12 個新關卡 (總共有26關) + - 18 個新建築 幫助玩家自動化整個工廠! + - 20 個升級 讓遊玩時間更久! + - 電路更新 帶給玩家新次元體驗! + - 深色模式! + - 無限存檔 + - 無限標記 + - 支持我! ❤️ + title_future: 未來計劃 planned: - - Blueprint Library (Standalone Exclusive) - - Steam Achievements - - Puzzle Mode - - Minimap - - Mods - - Sandbox mode - - ... and a lot more! - title_open_source: This game is open source! - title_links: Links + - 藍圖圖庫(單機版獨有) + - Steam 成就 + - 拼圖模式 + - 迷你地圖 + - 模組 + - 沙盒模式 + - ... 還有更多! + title_open_source: 本遊戲已開源! + title_links: 連結 links: - discord: Official Discord + discord: 官方 Discord 伺服器 roadmap: Roadmap - subreddit: Subreddit + subreddit: Reddit source_code: Source code (GitHub) - translate: Help translate - text_open_source: >- - Anybody can contribute, I'm actively involved in the community and - attempt to review all suggestions and take feedback into consideration - where possible. - - Be sure to check out my trello board for the full roadmap! + translate: 幫助我們翻譯! + text_open_source: |- + 任何人都可以幫助我開發遊戲或提供建議,我很活躍於各個社群媒體,也會讀所有的建議與回饋、並嘗試改善。 + 記得查看我的 trello board 以了解我的完整計畫! global: loading: 加載中 error: 錯誤 @@ -78,22 +71,22 @@ global: space: 空格 demoBanners: title: 演示版 - intro: 獲取獨立版以解鎖所有功能! + intro: 獲取單機版以解鎖所有功能! mainMenu: play: 開始遊戲 changelog: 更新日誌 importSavegame: 導入 openSourceHint: 本遊戲已開源! - discordLink: 官方Discord伺服器 + discordLink: 官方 Discord 伺服器 helpTranslate: 幫助我們翻譯! - browserWarning: 很抱歉, 本遊戲在當前瀏覽器上可能運行緩慢! 使用chrome或者獲取獨立版以得到更好的體驗。 - savegameLevel: Level + browserWarning: 很抱歉, 本遊戲在當前瀏覽器上可能運行緩慢! 使用chrome或者獲取單機版以得到更好的體驗。 + savegameLevel: 級 savegameLevelUnknown: 未知關卡 continue: 繼續 newGame: 新遊戲 madeBy: 作者: subreddit: Reddit - savegameUnnamed: Unnamed + savegameUnnamed: 未命名 dialogs: buttons: ok: 確認 @@ -102,7 +95,7 @@ dialogs: later: 之後 restart: 重啟 reset: 重置 - getStandalone: 獲得獨立版 + getStandalone: 獲得單機版 deleteGame: 我知道我在做什麼 viewUpdate: 查看更新 showUpgrades: 顯示建築升級 @@ -118,9 +111,7 @@ dialogs: text: 存檔載入失敗: confirmSavegameDelete: title: 確認刪除 - text: Are you sure you want to delete the following game?

- '' at level

This can not be - undone! + text: 你確定要刪除這個存檔?

'' 在第

級 不能反悔喔! savegameDeletionError: title: 刪除錯誤 text: 存檔刪除失敗 @@ -138,10 +129,10 @@ dialogs: desc: 成功重置了所有按鍵! featureRestriction: title: 演示版 - desc: 你嘗試使用了 功能。該功能在演示版中不可用。請考慮購買獨立版以獲得更好的體驗。 + desc: 你嘗試使用了 功能。該功能在演示版中不可用。請考慮購買單機版以獲得更好的體驗。 oneSavegameLimit: title: 存檔數量限制 - desc: 演示版中只能保存一份存檔。請刪除舊存檔或者獲取獨立版! + desc: 演示版中只能保存一份存檔。請刪除舊存檔或者獲取單機版! updateSummary: title: 更新了! desc: "以下為自上次遊戲以來更新的內容:" @@ -156,18 +147,18 @@ dialogs: desc: 你還沒有解鎖藍圖功能!完成更多的關卡來解鎖藍圖。 keybindingsIntroduction: title: 實用按鍵 - desc: - "這個遊戲有很多能幫助搭建工廠的使用按鍵。 以下是其中的一些,記得在按鍵設置中查看其他的!

+ desc: "這個遊戲有很多能幫助搭建工廠的使用按鍵。 以下是其中的一些,記得在按鍵設置中查看其他的!

CTRL + 拖動:選擇區域以復製或刪除。
SHIFT: 按住以放置多個。
ALT: 反向放置傳送帶。
" createMarker: title: 創建標記 - desc: Give it a meaningful name, you can also include a short - key of a shape (Which you can generate here) - titleEdit: Edit Marker + desc: 給地圖標記起一個的名字。 + 你可以在名字中加入一個短代碼以加入圖形。 + (你可以在 here 生成短代碼。) + titleEdit: 修改標記 markerDemoLimit: - desc: 在演示版中你只能創建兩個地圖標記。請獲取獨立版以創建更多標記。 + desc: 在演示版中你只能創建兩個地圖標記。請獲取單機版以創建更多標記。 massCutConfirm: title: 確認剪下 desc: 你將要剪下很多建築,準確來說有幢!你確定要這麼做嗎? @@ -175,21 +166,16 @@ dialogs: title: 工廠截圖 desc: 你將要匯出你的工廠的截圖。如果你的基地很大,截圖過程將會很慢,甚至有可能導致遊戲當掉! massCutInsufficientConfirm: - title: Confirm cut - desc: You can not afford to paste this area! Are you sure you want to cut it? + title: 確認剪下 + desc: 你的複製圖形不夠貼上!你確定要剪下嗎? editSignal: title: Set Signal descItems: "Choose a pre-defined item:" descShortKey: ... or enter the short key of a shape (Which you can generate here) renameSavegame: - title: Rename Savegame - desc: You can rename your savegame here. - entityWarning: - title: Performance Warning - desc: You have placed a lot of buildings, this is just a friendly reminder that - the game can not handle an endless count of buildings - So try to - keep your factories compact! + title: 重新命名存檔 + desc: 你可以在這裡重新命名存檔 ingame: keybindingsOverlay: moveMap: 移動 @@ -210,7 +196,7 @@ ingame: copySelection: 複製選取 clearSelection: 清空選取 pipette: 吸附 - switchLayers: Switch layers + switchLayers: 切換層 buildingPlacement: cycleBuildingVariants: 按鍵以選擇建築變體. hotkeyLabel: "快捷鍵: " @@ -223,39 +209,18 @@ ingame: itemsPerSecondDouble: (2倍) tiles: 格 levelCompleteNotification: - levelTitle: Level + levelTitle: 第級 completed: 完成 unlockText: 解鎖! buttonNextLevel: 下一關 notifications: newUpgrade: 有新的更新啦! gameSaved: 遊戲已保存。 - freeplayLevelComplete: Level has been completed! + freeplayLevelComplete: 已完成第級! shop: title: 建築升級 buttonUnlock: 升級 tier: 級 - tierLabels: - - 壹 - - 貳 - - 叁 - - 肆 - - 伍 - - 陸 - - 柒 - - 捌 - - 玖 - - 拾 - - XI - - XII - - XIII - - XIV - - XV - - XVI - - XVII - - XVIII - - XIX - - XX maximumLevel: 最高級(倍效率) statistics: title: 統計資訊 @@ -271,17 +236,13 @@ ingame: description: 顯示圖形送達基地的速度。 noShapesProduced: 你還沒有生產任何圖形。 shapesDisplayUnits: - second: / s - minute: / m - hour: / h + second: / 秒 + minute: / 分 + hour: / 時 settingsMenu: playtime: 遊戲時間 buildingsPlaced: 建築數量 beltsPlaced: 傳送帶數量 - buttons: - continue: 繼續 - settings: 設置 - menu: 回到主選單 tutorialHints: title: 需要幫助? showHint: 顯示 @@ -291,10 +252,8 @@ 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: 在標記上按左鍵以快速移動到標記區域,在標記上按右鍵以刪除該標記。

+ 為當前區域建立地圖標記或按 right-click 為選取區域建立地圖標記。 creationSuccessNotification: 成功創建地圖標記。 interactiveTutorial: title: 教程 @@ -302,8 +261,7 @@ ingame: 1_1_extractor: 在圓形礦脈上放一個開採機來獲取圓形! 1_2_conveyor: 用傳送帶將你的開採機連接到基地上!

提示:用你的游標按下並拖動傳送帶! - 1_3_expand: - 這不是一個放置型遊戲!建造更多的開採機和傳送帶來更快地完成目標。

+ 1_3_expand: 這不是一個放置型遊戲!建造更多的開採機和傳送帶來更快地完成目標。

提示:按住SHIFT鍵來放置多個開採機,用R鍵旋轉它們。 colors: red: 紅 @@ -320,41 +278,41 @@ ingame: empty: 空 copyKey: Copy Key connectedMiners: - one_miner: 1 Miner - n_miners: Miners + one_miner: 1 個開採機 + n_miners: 個開採機 limited_items: Limited to watermark: title: Demo version desc: Click here to see the Steam version advantages! - get_on_steam: Get on steam + get_on_steam: 在 Steam 上取得 standaloneAdvantages: - title: Get the full version! - no_thanks: No, thanks! + title: 取得單機版! + no_thanks: 不用了,謝謝! points: levels: - title: 12 New Levels - desc: For a total of 26 levels! + title: 12 個新關卡 + desc: 總共26關! buildings: - title: 18 New Buildings - desc: Fully automate your factory! + title: 18 個新建築 + desc: 邁向完全自動化! savegames: - title: ∞ Savegames - desc: As many as your heart desires! + title: ∞ 個存檔 + desc: 隨心所欲! upgrades: - title: 20 Upgrade Tiers - desc: This demo version has only 5! + title: 20 個等級 + desc: 試玩版只有 5 個。 markers: - title: ∞ Markers - desc: Never get lost in your factory! + title: ∞ 個地圖標記 + desc: 再也不會迷失在一望無際的地圖裡! wires: - title: Wires - desc: An entirely new dimension! + title: 電路 + desc: 全新次元! darkmode: - title: Dark Mode - desc: Stop hurting your eyes! + title: 深色模式 + desc: 要保養眼球~ support: - title: Support me - desc: I develop it in my spare time! + title: 支持我 + desc: 遊戲使我利用閒暇時間做的! shopUpgrades: belt: name: 傳送帶、平衡機、隧道 @@ -402,8 +360,8 @@ buildings: name: 旋轉機(逆時針) description: 將圖形逆時針旋轉90度。 rotate180: - name: Rotate (180) - description: Rotates shapes by 180 degrees. + name: 旋轉機 (180度) + description: 將圖形旋轉180度。 stacker: default: name: 混合機 @@ -421,9 +379,7 @@ buildings: description: 同時為兩個輸入的圖形上色,每次上色只消耗一份顏色塗料。 quad: name: 上色機(四向) - description: Allows you to color each quadrant of the shape individually. Only - slots with a truthy signal on the wires layer - will be painted! + description: 分別為圖形的四個部分上色。 只有 truthy signal 的格子會被上色。 mirrored: name: 上色機 description: 將整個圖形塗上輸入的顏色。 @@ -435,36 +391,34 @@ buildings: deliver: 交付 toUnlock: 來解鎖 levelShortcut: LVL - endOfDemo: End of Demo + endOfDemo: 試玩結束 wire: default: - name: Energy Wire - description: Allows you to transport energy. + name: 能量電線 + description: 傳輸能量。 second: - name: Wire - description: Transfers signals, which can be items, colors or booleans (1 / 0). - Different colored wires do not connect. + name: 電線 + description: 傳輸訊號,訊號可以是物件,顏色或布林值(0或1)。 不同顏色的電線無法互相連接。 balancer: default: - name: Balancer - description: Multifunctional - Evenly distributes all inputs onto all outputs. + name: 平衡機 + description: 多功能——將所有輸入平均分配到所有輸出。 merger: - name: Merger (compact) - description: Merges two conveyor belts into one. + name: 合流機(右) + description: 將兩個傳送帶整合成一個。 merger-inverse: - name: Merger (compact) - description: Merges two conveyor belts into one. + name: 合流機(左) + description: 將兩個傳送帶整合成一個。 splitter: - name: Splitter (compact) - description: Splits one conveyor belt into two. + name: 分流機(右) + description: 將單個傳送帶分流成兩個。 splitter-inverse: - name: Splitter (compact) - description: Splits one conveyor belt into two. + name: 分流機(左) + description: 將單個傳送帶分流成兩個。 storage: default: - name: Storage - description: Stores excess items, up to a given capacity. Prioritizes the left - output and can be used as an overflow gate. + name: 倉庫 + description: 儲存多餘的物品,有一定儲存上限。優先從左側輸出,可以被用來作為溢流門。 wire_tunnel: default: name: Wire Crossing @@ -476,35 +430,31 @@ buildings: boolean (1 / 0). lever: default: - name: Switch + name: 信號切換器 description: Can be toggled to emit a boolean signal (1 / 0) on the wires layer, which can then be used to control for example an item filter. logic_gate: default: - name: AND Gate - description: Emits a boolean "1" if both inputs are truthy. (Truthy means shape, - color or boolean "1") + name: AND 邏輯閘 + description: 當輸入均為「真」時,輸出才為1。 (「真」值代表:形狀正確、顏色正確或布林值為1) not: - name: NOT Gate - description: Emits a boolean "1" if the input is not truthy. (Truthy means - shape, color or boolean "1") + name: NOT 邏輯閘 + description: 當輸入之ㄧ為「假」時,輸出才為1。 (「假」值代表:形狀不正確、顏色不正確或布林值為0) xor: - name: XOR Gate - description: Emits a boolean "1" if one of the inputs is truthy, but not both. - (Truthy means shape, color or boolean "1") + name: XOR 邏輯閘 + description: 當輸入均為「假」時,輸出才為1。 (「假」值代表:形狀不正確、顏色不正確或布林值為0) or: - name: OR Gate - description: Emits a boolean "1" if one of the inputs is truthy. (Truthy means - shape, color or boolean "1") + name: OR 邏輯閘 + description: 當輸入之ㄧ為「真」時,輸出才為1。 (「真」值代表:形狀正確、顏色正確或布林值為1) transistor: default: - name: Transistor - description: Forwards the bottom input if the side input is truthy (a shape, - color or "1"). + name: 電晶體 + description: 如果基極(側面)的輸入訊號為「真」,則把射極(底部)輸入的真假值複製到集極(頂部)的輸出。 + (「真」值代表:形狀正確、顏色正確或布林值為1) mirrored: - name: Transistor - description: Forwards the bottom input if the side input is truthy (a shape, - color or "1"). + name: 電晶體 + description: 如果基極(側面)的輸入訊號為「真」,則把射極(底部)輸入的真假值複製到集極(頂部)的輸出。 + (「真」值代表:形狀正確、顏色正確或布林值為1) filter: default: name: Filter @@ -518,54 +468,48 @@ buildings: color or boolean. reader: default: - name: Belt Reader + name: 傳送帶讀取機 description: Allows to measure the average belt throughput. Outputs the last read item on the wires layer (once unlocked). analyzer: default: - name: Shape Analyzer + name: 形狀分析機 description: Analyzes the top right quadrant of the lowest layer of the shape and returns its shape and color. comparator: default: - name: Compare + name: 比較機 description: Returns boolean "1" if both signals are exactly equal. Can compare shapes, items and booleans. virtual_processor: default: - name: Virtual Cutter - description: Virtually cuts the shape into two halves. + name: 虛擬切割機 + description: 虛擬地將圖形從上到下切開並輸出。 rotater: - name: Virtual Rotater - description: Virtually rotates the shape, both clockwise and counter-clockwise. + name: 虛擬旋轉機 + description: 虛擬地將圖形順時針或逆時針旋轉。 unstacker: - name: Virtual Unstacker - description: Virtually extracts the topmost layer to the right output and the - remaining ones to the left. + name: 虛擬提取機 + description: 虛擬地提取最上層的圖形到右方輸出,剩下的圖形由左方輸出。 stacker: - name: Virtual Stacker - description: Virtually stacks the right shape onto the left. + name: 虛擬堆疊機 + description: 虛擬地將輸入的圖形拼貼在一起。如果不能被直接拼貼,右邊的圖形會被疊在左邊的圖形上面。 painter: - name: Virtual Painter - description: Virtually paints the shape from the bottom input with the shape on - the right input. + name: 虛擬上色機 + description: 虛擬地將整個圖形塗上輸入的顏色。 item_producer: default: - name: Item Producer - description: Available in sandbox mode only, outputs the given signal from the - wires layer on the regular layer. + name: 物品製造機 + description: 沙盒模式專有,將電路層的輸入轉化成一般層的輸出。 storyRewards: reward_cutter_and_trash: title: 切割圖形 - desc: You just unlocked the cutter, which cuts shapes in half - from top to bottom regardless of its - orientation!

Be sure to get rid of the waste, or - otherwise it will clog and stall - For this purpose - I have given you the trash, which destroys - everything you put into it! + desc: 切割機已解鎖。不論切割機的方向,它都會把圖形垂直地切成兩半。 +

記得把不需要的部分處理掉,否則這個這個建築會停止運作。 + 為此我給你準備了垃圾桶,它會把所有放進去的物品銷毀掉。 reward_rotater: title: 順時針旋轉 - desc: 旋轉機已解鎖。它會順時針旋轉輸入的圖形90度。 + desc: 順時針旋轉機已解鎖。它會順時針旋轉輸入的圖形90度。 reward_painter: title: 上色 desc: 上色機已解鎖。開採一些顏色,用上色機把顏色和圖形混合,就可以為圖形著色。

備註:如果你是色盲,設置中有色盲模式可以選。 @@ -576,7 +520,7 @@ storyRewards: title: 混合 desc: 混合機已解鎖。如果沒有重疊的部分,混合機會嘗試把兩個輸入的圖形拼貼在一起。如果有重疊的部分,右邊的輸入會被到左邊的輸入上方! reward_splitter: - title: 分離/合併 + title: 分流 desc: You have unlocked a splitter variant of the balancer - It accepts one input and splits them into two! @@ -585,63 +529,50 @@ storyRewards: desc: 隧道已解鎖。你現在可以從其他傳送帶或建築底下運送物品了! reward_rotater_ccw: title: 逆時針旋轉 - desc: You have unlocked a variant of the rotater - It allows - you to rotate shapes counter-clockwise! To build it, select the - rotater and press 'T' to cycle through its - variants! + desc: 逆時針旋轉機已解鎖。它會逆時針旋轉輸入的圖形90度。 + 逆時針旋轉機是順時針旋轉機的變體。選擇「順時針旋轉機」並按「T」來切換變體就能創建。 reward_miner_chainable: - title: 鏈式開採機 - desc: "You have unlocked the chained extractor! It can - forward its resources to other extractors so you - can more efficiently extract resources!

PS: The old - extractor has been replaced in your toolbar now!" + title: 鏈式開採 + desc: "鏈式開採機變體已解鎖。它是開採機的一個變體。 + 它可以將開採出來的資源傳遞給其他的開採機,使得資源提取更加高效!

+ PS: 工具列中舊的開採機已被取代。" reward_underground_belt_tier_2: 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 - similar to the regular painter but processes two shapes at - once, consuming just one color instead of two! + title: 雙倍上色 + desc: 您已經解鎖了上色機的變體:雙倍上色機。 + 它的運作方式跟上色機類似,但一次能處理兩個形狀,而且只消耗一種顏色而不是兩種顏色! reward_storage: title: 倉庫 - desc: You have unlocked the storage building - It allows you to - store items up to a given capacity!

It priorities the left - output, so you can also use it as an overflow gate! + desc: 倉庫 已解鎖: + 可以儲存多餘的物品,有一定儲存上限。

優先從左側輸出,可以被用來作為溢流門。 reward_freeplay: title: 自由模式 - desc: You did it! You unlocked the free-play mode! This means - that shapes are now randomly generated!

- Since the hub will require a throughput from now - on, I highly recommend to build a machine which automatically - delivers the requested shape!

The HUB outputs the requested - shape on the wires layer, so all you have to do is to analyze it and - automatically configure your factory based on that. + 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」將其複制。

+ 複製不是免費的,你需要用藍圖形狀來支付!(剛交付的那些)。 no_reward: title: 下一關 desc: "這一關沒有獎勵,但是下一關有!

PS: 你生產過的所有圖形都會被用來升級建築。" no_reward_freeplay: title: 下一關 - desc: 恭喜你!另外,我們已經計劃在獨立版中加入更多內容! + desc: 恭喜你!另外,我們已經計劃在單機版中加入更多內容! reward_balancer: - title: Balancer - 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_merger: - title: Compact Merger + title: 合流 desc: You have unlocked a merger variant of the balancer - It accepts two inputs and merges them into one belt! @@ -736,7 +667,7 @@ settings: description: 改變語言。所有的翻譯皆由玩家提供,且有可能正在施工中! fullscreen: title: 全螢幕 - description: 全螢幕以獲得更好的遊戲體驗。僅在獨立版中可用。 + description: 全螢幕以獲得更好的遊戲體驗。僅在單機版中可用。 soundsMuted: title: 關閉音效 description: 關閉所有音效。 @@ -799,11 +730,11 @@ settings: title: 依建築類型旋轉 description: 每個建築類型,將會分別記住您最後一次使用的旋轉方向。 如果您常常切換不同類型的建築,這樣可能會更方便。 soundVolume: - title: Sound Volume - description: Set the volume for sound effects + title: 音效 + description: 音效設定 musicVolume: - title: Music Volume - description: Set the volume for music + title: 音樂 + description: 音樂設定 lowQualityMapResources: title: Low Quality Map Resources description: Simplifies the rendering of resources on the map when zoomed in to @@ -840,6 +771,14 @@ settings: title: Enable Mouse Pan description: Allows to move the map by moving the cursor to the edges of the screen. The speed depends on the Movement Speed setting. + zoomToCursor: + title: Zoom towards Cursor + description: If activated the zoom will happen in the direction of your mouse + position, otherwise in the middle of the screen. + mapResourcesScale: + title: Map Resources Size + description: Controls the size of the shapes on the map overview (when zooming + out). rangeSliderPercentage: % keybindings: title: 按鍵設置 @@ -896,7 +835,7 @@ keybindings: switchDirectionLockSide: 規劃器:換邊 pipette: Pipette menuClose: Close Menu - switchLayers: Switch layers + switchLayers: 更換層 wire: Energy Wire balancer: Balancer storage: Storage