diff --git a/.gitignore b/.gitignore index be6071af..cdade93f 100644 --- a/.gitignore +++ b/.gitignore @@ -47,6 +47,7 @@ res_built gulp/runnable-texturepacker.jar tmp_standalone_files tmp_standalone_files_china +tmp_standalone_files_wegame # Local config config.local.js diff --git a/Dockerfile b/Dockerfile index 61d54684..b79cac20 100644 --- a/Dockerfile +++ b/Dockerfile @@ -26,6 +26,7 @@ COPY translations ./translations COPY src/js ./src/js COPY res_raw ./res_raw COPY .git ./.git +COPY electron ./electron WORKDIR /shapez.io/gulp ENTRYPOINT ["yarn", "gulp"] diff --git a/README.md b/README.md index 85b5d26b..84be6af7 100644 --- a/README.md +++ b/README.md @@ -5,11 +5,11 @@ This is the source code for shapez.io, an open source base building game inspired by Factorio. Your goal is to produce shapes by cutting, rotating, merging and painting parts of shapes. -- [Trello Board & Roadmap](https://trello.com/b/ISQncpJP/shapezio) -- [Free web version](https://shapez.io) -- [itch.io Page](https://tobspr.itch.io/shapezio) - [Steam Page](https://steam.shapez.io) - [Official Discord](https://discord.com/invite/HN7EVzV) <- _Highly recommended to join!_ +- [Trello Board & Roadmap](https://trello.com/b/ISQncpJP/shapezio) +- [itch.io Page](https://tobspr.itch.io/shapezio) +- [Free web version](https://shapez.io) ## Reporting issues, suggestions, feedback, bugs @@ -35,9 +35,9 @@ Your goal is to produce shapes by cutting, rotating, merging and painting parts You can use [Gitpod](https://www.gitpod.io/) (an Online Open Source VS Code-like IDE which is free for Open Source) for working on issues and making PRs to this project. With a single click it will start a workspace and automatically: -- clone the `shapez.io` repo. -- install all of the dependencies. -- start `gulp` in `gulp/` directory. +- clone the `shapez.io` repo. +- install all of the dependencies. +- start `gulp` in `gulp/` directory. [![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/from-referrer/) diff --git a/electron/index.js b/electron/index.js index b877900c..e7994050 100644 --- a/electron/index.js +++ b/electron/index.js @@ -74,20 +74,8 @@ function createWindow() { win.on("closed", () => { console.log("Window closed"); win = null; - app.quit(); }); - function handleWindowBeforeunload(event) { - const confirmed = dialog.showMessageBox(remote.getCurrentWindow(), options) === 1; - if (confirmed) { - remote.getCurrentWindow().close(); - } else { - event.returnValue = false; - } - } - - win.on("", handleWindowBeforeunload); - if (isDev) { menu = new Menu(); @@ -286,7 +274,10 @@ async function performFsJob(job) { } } -ipcMain.handle("fs-job", (event, arg) => performFsJob(arg)); +ipcMain.on("fs-job", async (event, arg) => { + const result = await performFsJob(arg); + event.reply("fs-response", { id: arg.id, result }); +}); steam.init(isDev); steam.listen(); diff --git a/electron/package.json b/electron/package.json index 893e3609..d21aff71 100644 --- a/electron/package.json +++ b/electron/package.json @@ -10,10 +10,10 @@ "start": "electron --disable-direct-composition --in-process-gpu ." }, "devDependencies": { - "electron": "10.4.0" + "electron": "10.4.3" }, "optionalDependencies": { - "shapez.io-private-artifacts": "github:tobspr/shapez.io-private-artifacts#abi-v85" + "shapez.io-private-artifacts": "github:tobspr/shapez.io-private-artifacts#abi-v82" }, "dependencies": { "async-lock": "^1.2.8" diff --git a/electron/steam.js b/electron/steam.js index c65816f0..1c37e3ca 100644 --- a/electron/steam.js +++ b/electron/steam.js @@ -1,5 +1,5 @@ -const fs = require('fs'); -const path = require('path'); +const fs = require("fs"); +const path = require("path"); const { ipcMain } = require("electron"); let greenworks = null; @@ -11,10 +11,10 @@ try { appId = parseInt(fs.readFileSync(path.join(__dirname, "steam_appid.txt"), "utf8")); } catch (err) { // greenworks is not installed - // throw err; + console.warn("Failed to load steam api:", err); } -function init (isDev) { +function init(isDev) { if (!greenworks) { return; } @@ -34,18 +34,51 @@ function init (isDev) { initialized = true; } -function listen () { +function listen() { ipcMain.handle("steam:is-initialized", isInitialized); - if (!greenworks || !initialized) { - console.log("Ignoring Steam IPC events"); + if (!initialized) { + console.warn("Steam not initialized, won't be able to listen"); return; } + if (!greenworks) { + console.warn("Greenworks not loaded, won't be able to listen"); + return; + } + + console.log("Adding listeners"); + ipcMain.handle("steam:get-achievement-names", getAchievementNames); ipcMain.handle("steam:get-achievement", getAchievement); ipcMain.handle("steam:activate-achievement", activateAchievement); ipcMain.handle("steam:deactivate-achievement", deactivateAchievement); + + function bufferToHex(buffer) { + return Array.from(new Uint8Array(buffer)) + .map(b => b.toString(16).padStart(2, "0")) + .join(""); + } + + ipcMain.handle("steam:get-ticket", (event, arg) => { + console.log("Requested steam ticket ..."); + return new Promise((resolve, reject) => { + greenworks.getAuthSessionTicket( + success => { + const ticketHex = bufferToHex(success.ticket); + resolve(ticketHex); + }, + error => { + console.error("Failed to get steam ticket:", error); + reject(error); + } + ); + }); + }); + + ipcMain.handle("steam:check-app-ownership", (event, appId) => { + return Promise.resolve(greenworks.isDLCInstalled(appId)); + }); } function isInitialized(event) { @@ -55,7 +88,7 @@ function isInitialized(event) { function getAchievementNames(event) { return new Promise((resolve, reject) => { try { - const achievements = greenworks.getAchievementNames() + const achievements = greenworks.getAchievementNames(); resolve(achievements); } catch (err) { reject(err); @@ -65,23 +98,35 @@ function getAchievementNames(event) { function getAchievement(event, id) { return new Promise((resolve, reject) => { - greenworks.getAchievement(id, () => resolve(), err => reject(err)) + greenworks.getAchievement( + id, + () => resolve(), + err => reject(err) + ); }); } function activateAchievement(event, id) { return new Promise((resolve, reject) => { - greenworks.activateAchievement(id, (is_achieved) => resolve(is_achieved), err => reject(err)) + greenworks.activateAchievement( + id, + is_achieved => resolve(is_achieved), + err => reject(err) + ); }); } function deactivateAchievement(event, id) { return new Promise((resolve, reject) => { - greenworks.clearAchievement(id, () => resolve(), err => reject(err)) + greenworks.clearAchievement( + id, + () => resolve(), + err => reject(err) + ); }); } module.exports = { init, - listen + listen, }; diff --git a/electron/yarn.lock b/electron/yarn.lock index 8c5b1dec..db2b6278 100644 --- a/electron/yarn.lock +++ b/electron/yarn.lock @@ -146,10 +146,10 @@ duplexer3@^0.1.4: resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= -electron@10.4.0: - version "10.4.0" - resolved "https://registry.yarnpkg.com/electron/-/electron-10.4.0.tgz#018385914474b56110a5a43087a53c114b67c08d" - integrity sha512-qK8OOCWuNvEFWThmjkukkqDwIpBqULlDuMXVC9MC/2P4UaWJEjIYvBmBuTyxtFcKoE3kWvcWyeRDUuvzVxxXjA== +electron@10.4.3: + version "10.4.3" + resolved "https://registry.yarnpkg.com/electron/-/electron-10.4.3.tgz#8d1c0f5e562d1b78dcec8074c0d59e58137fd508" + integrity sha512-qL8XZBII9KQHr1+YmVMj1AqyTR2I8/lxozvKEWoKKSkF8Hl6GzzxrLXRfISP7aDAvsJEyyhc6b2/42ME8hG5JA== dependencies: "@electron/get" "^1.0.1" "@types/node" "^12.0.12" @@ -503,9 +503,9 @@ serialize-error@^7.0.1: dependencies: type-fest "^0.13.1" -"shapez.io-private-artifacts@github:tobspr/shapez.io-private-artifacts#abi-v85": +"shapez.io-private-artifacts@github:tobspr/shapez.io-private-artifacts#abi-v82": version "0.1.0" - resolved "git+ssh://git@github.com/tobspr/shapez.io-private-artifacts.git#63adf7e0ea4b90c2a29053ce1f0ec9d573b3ac0a" + resolved "git+ssh://git@github.com/tobspr/shapez.io-private-artifacts.git#8aa3bfd3b569eb5695fc8a585a3f2ee3ed2db290" sprintf-js@^1.1.2: version "1.1.2" diff --git a/electron_wegame/.gitignore b/electron_wegame/.gitignore new file mode 100644 index 00000000..475a7c75 --- /dev/null +++ b/electron_wegame/.gitignore @@ -0,0 +1 @@ +wegame_sdk diff --git a/electron_wegame/electron_wegame.code-workspace b/electron_wegame/electron_wegame.code-workspace new file mode 100644 index 00000000..fc9ab864 --- /dev/null +++ b/electron_wegame/electron_wegame.code-workspace @@ -0,0 +1,13 @@ +{ + "folders": [ + { + "path": "." + } + ], + "settings": { + "files.exclude": { + "**/node_modules": true, + "**/typedefs_gen": true + } + } +} \ No newline at end of file diff --git a/electron_wegame/favicon.icns b/electron_wegame/favicon.icns new file mode 100644 index 00000000..13d21f26 Binary files /dev/null and b/electron_wegame/favicon.icns differ diff --git a/electron_wegame/favicon.ico b/electron_wegame/favicon.ico new file mode 100644 index 00000000..54721ebf Binary files /dev/null and b/electron_wegame/favicon.ico differ diff --git a/electron_wegame/favicon.png b/electron_wegame/favicon.png new file mode 100644 index 00000000..44994cc9 Binary files /dev/null and b/electron_wegame/favicon.png differ diff --git a/electron_wegame/index.js b/electron_wegame/index.js new file mode 100644 index 00000000..e58c30d9 --- /dev/null +++ b/electron_wegame/index.js @@ -0,0 +1,288 @@ +/* eslint-disable quotes,no-undef */ + +const { app, BrowserWindow, Menu, MenuItem, ipcMain, shell } = require("electron"); + +app.commandLine.appendSwitch("in-process-gpu"); + +const path = require("path"); +const url = require("url"); +const fs = require("fs"); +const wegame = require("./wegame"); +const asyncLock = require("async-lock"); + +const isDev = process.argv.indexOf("--dev") >= 0; +const isLocal = process.argv.indexOf("--local") >= 0; + +const roamingFolder = + process.env.APPDATA || + (process.platform == "darwin" + ? process.env.HOME + "/Library/Preferences" + : process.env.HOME + "/.local/share"); +let storePath = path.join(roamingFolder, "shapez.io", "saves"); + +if (!fs.existsSync(storePath)) { + // No try-catch by design + fs.mkdirSync(storePath, { recursive: true }); +} + +/** @type {BrowserWindow} */ +let win = null; +let menu = null; + +function createWindow() { + let faviconExtension = ".png"; + if (process.platform === "win32") { + faviconExtension = ".ico"; + } + + win = new BrowserWindow({ + width: 1280, + height: 800, + show: false, + backgroundColor: "#222428", + useContentSize: true, + minWidth: 800, + minHeight: 600, + title: "shapez.io Standalone", + transparent: false, + icon: path.join(__dirname, "favicon" + faviconExtension), + // fullscreen: true, + autoHideMenuBar: true, + webPreferences: { + nodeIntegration: true, + webSecurity: false, + }, + // allowRunningInsecureContent: false, + }); + + if (isLocal) { + win.loadURL("http://localhost:3005"); + } else { + win.loadURL( + url.format({ + pathname: path.join(__dirname, "index.html"), + protocol: "file:", + slashes: true, + }) + ); + } + win.webContents.session.clearCache(() => null); + win.webContents.session.clearStorageData(); + + win.webContents.on("new-window", (event, pth) => { + event.preventDefault(); + shell.openExternal(pth); + }); + + win.on("closed", () => { + console.log("Window closed"); + win = null; + }); + + if (isDev) { + menu = new Menu(); + + const mainItem = new MenuItem({ + label: "Toggle Dev Tools", + click: () => win.webContents.toggleDevTools(), + accelerator: "F12", + }); + menu.append(mainItem); + + const reloadItem = new MenuItem({ + label: "Restart", + click: () => win.reload(), + accelerator: "F5", + }); + menu.append(reloadItem); + + const fullscreenItem = new MenuItem({ + label: "Fullscreen", + click: () => win.setFullScreen(!win.isFullScreen()), + accelerator: "F11", + }); + menu.append(fullscreenItem); + + Menu.setApplicationMenu(menu); + } else { + Menu.setApplicationMenu(null); + } + + win.once("ready-to-show", () => { + win.show(); + win.focus(); + }); +} + +if (!app.requestSingleInstanceLock()) { + app.exit(0); +} else { + app.on("second-instance", (event, commandLine, workingDirectory) => { + // Someone tried to run a second instance, we should focus + if (win) { + if (win.isMinimized()) { + win.restore(); + } + win.focus(); + } + }); +} + +app.on("ready", createWindow); + +app.on("window-all-closed", () => { + console.log("All windows closed"); + app.quit(); +}); + +ipcMain.on("set-fullscreen", (event, flag) => { + win.setFullScreen(flag); +}); + +ipcMain.on("exit-app", (event, flag) => { + win.close(); + app.quit(); +}); + +let renameCounter = 1; + +const fileLock = new asyncLock({ + timeout: 30000, + maxPending: 1000, +}); + +function niceFileName(filename) { + return filename.replace(storePath, "@"); +} + +async function writeFileSafe(filename, contents) { + ++renameCounter; + const prefix = "[ " + renameCounter + ":" + niceFileName(filename) + " ] "; + const transactionId = String(new Date().getTime()) + "." + renameCounter; + + if (fileLock.isBusy()) { + console.warn(prefix, "Concurrent write process on", filename); + } + + await fileLock.acquire(filename, async () => { + console.log(prefix, "Starting write on", niceFileName(filename), "in transaction", transactionId); + + if (!fs.existsSync(filename)) { + // this one is easy + console.log(prefix, "Writing file instantly because it does not exist:", niceFileName(filename)); + fs.writeFileSync(filename, contents, { encoding: "utf8" }); + return; + } + + // first, write a temporary file (.tmp-XXX) + const tempName = filename + ".tmp-" + transactionId; + console.log(prefix, "Writing temporary file", niceFileName(tempName)); + fs.writeFileSync(tempName, contents, { encoding: "utf8" }); + + // now, rename the original file to (.backup-XXX) + const oldTemporaryName = filename + ".backup-" + transactionId; + console.log( + prefix, + "Renaming old file", + niceFileName(filename), + "to", + niceFileName(oldTemporaryName) + ); + fs.renameSync(filename, oldTemporaryName); + + // now, rename the temporary file (.tmp-XXX) to the target + console.log( + prefix, + "Renaming the temporary file", + niceFileName(tempName), + "to the original", + niceFileName(filename) + ); + fs.renameSync(tempName, filename); + + // we are done now, try to create a backup, but don't fail if the backup fails + try { + // check if there is an old backup file + const backupFileName = filename + ".backup"; + if (fs.existsSync(backupFileName)) { + console.log(prefix, "Deleting old backup file", niceFileName(backupFileName)); + // delete the old backup + fs.unlinkSync(backupFileName); + } + + // rename the old file to the new backup file + console.log(prefix, "Moving", niceFileName(oldTemporaryName), "to the backup file location"); + fs.renameSync(oldTemporaryName, backupFileName); + } catch (ex) { + console.error(prefix, "Failed to switch backup files:", ex); + } + }); +} + +async function performFsJob(job) { + const fname = path.join(storePath, job.filename); + + switch (job.type) { + case "read": { + if (!fs.existsSync(fname)) { + return { + // Special FILE_NOT_FOUND error code + error: "file_not_found", + }; + } + + try { + const data = fs.readFileSync(fname, { encoding: "utf8" }); + return { + success: true, + data, + }; + } catch (ex) { + console.error(ex); + return { + error: ex, + }; + } + } + case "write": { + try { + writeFileSafe(fname, job.contents); + return { + success: true, + data: job.contents, + }; + } catch (ex) { + console.error(ex); + return { + error: ex, + }; + } + } + + case "delete": { + try { + fs.unlinkSync(fname); + } catch (ex) { + console.error(ex); + return { + error: ex, + }; + } + + return { + success: true, + data: null, + }; + } + + default: + throw new Error("Unkown fs job: " + job.type); + } +} + +ipcMain.on("fs-job", async (event, arg) => { + const result = await performFsJob(arg); + event.sender.send("fs-response", { id: arg.id, result }); +}); +wegame.init(isDev); +wegame.listen(); diff --git a/electron_wegame/package.json b/electron_wegame/package.json new file mode 100644 index 00000000..173379b6 --- /dev/null +++ b/electron_wegame/package.json @@ -0,0 +1,18 @@ +{ + "name": "electron", + "version": "1.0.0", + "main": "index.js", + "license": "MIT", + "private": true, + "scripts": { + "startDev": "electron --disable-direct-composition --in-process-gpu . --dev --local", + "startDevGpu": "electron --enable-gpu-rasterization --enable-accelerated-2d-canvas --num-raster-threads=8 --enable-zero-copy . --dev --local", + "start": "electron --disable-direct-composition --in-process-gpu ." + }, + "devDependencies": { + "electron": "3.1.13" + }, + "dependencies": { + "async-lock": "^1.2.8" + } +} diff --git a/electron_wegame/wegame.js b/electron_wegame/wegame.js new file mode 100644 index 00000000..69f121e1 --- /dev/null +++ b/electron_wegame/wegame.js @@ -0,0 +1,52 @@ +const railsdk = require("./wegame_sdk/railsdk.js"); +const { dialog } = require("electron"); + +function init(isDev) { + console.log("Step 1: wegame: init"); + + try { + console.log("Step 2: Calling need restart app"); + const need_restart = railsdk.RailNeedRestartAppForCheckingEnvironment( + 2001639, + [`--rail_render_pid=${process.pid}`] //,"--rail_debug_mode", + ); + console.log("Step 3: Needs restart =", need_restart); + if (need_restart) { + console.error("Step 4: Need restart"); + dialog.showErrorBox("加载RailSDK失败", "请先运行WeGame开发者版本"); + return; + } + } catch (err) { + console.error("Rail SDK error:", err); + dialog.showErrorBox("加载RailSDK失败", err); + return; + } + + console.log("Step 5: starting rail sdk"); + if (railsdk.RailInitialize() === false) { + console.error("RailInitialize() = false"); + dialog.showErrorBox("RailInitialize调用失败", "请先运行WeGame开发者版本"); + return; + } + + console.log("Initialize RailSDK success!"); + + railsdk.RailRegisterEvent(railsdk.RailEventID.kRailEventSystemStateChanged, event => { + console.log(event); + if (event.result === railsdk.RailResult.kSuccess) { + if ( + event.state === railsdk.RailSystemState.kSystemStatePlatformOffline || + event.state === railsdk.RailSystemState.kSystemStatePlatformExit || + event.state === railsdk.RailSystemState.kSystemStateGameExitByAntiAddiction + ) { + remote.app.exit(); + } + } + }); +} + +function listen() { + console.log("wegame: listen"); +} + +module.exports = { init, listen }; diff --git a/electron_wegame/yarn.lock b/electron_wegame/yarn.lock new file mode 100644 index 00000000..025549d9 --- /dev/null +++ b/electron_wegame/yarn.lock @@ -0,0 +1,982 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@types/node@^10.1.4": + version "10.17.60" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.60.tgz#35f3d6213daed95da7f0f73e75bcc6980e90597b" + integrity sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw== + +ajv@^6.12.3: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= + +array-find-index@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" + integrity sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E= + +asn1@~0.2.3: + version "0.2.4" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" + integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== + dependencies: + safer-buffer "~2.1.0" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= + +async-lock@^1.2.8: + version "1.2.8" + resolved "https://registry.yarnpkg.com/async-lock/-/async-lock-1.2.8.tgz#7b02bdfa2de603c0713acecd11184cf97bbc7c4c" + integrity sha512-G+26B2jc0Gw0EG/WN2M6IczuGepBsfR1+DtqLnyFSH4p2C668qkOCtEkGNVEaaNAVlYwEMazy1+/jnLxltBkIQ== + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= + +aws-sign2@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= + +aws4@^1.8.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" + integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== + +bcrypt-pbkdf@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= + dependencies: + tweetnacl "^0.14.3" + +buffer-crc32@~0.2.3: + version "0.2.13" + resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" + integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= + +buffer-from@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" + integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== + +camelcase-keys@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7" + integrity sha1-MIvur/3ygRkFHvodkyITyRuPkuc= + dependencies: + camelcase "^2.0.0" + map-obj "^1.0.0" + +camelcase@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" + integrity sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8= + +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= + +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= + +combined-stream@^1.0.6, combined-stream@~1.0.6: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +concat-stream@^1.6.2: + version "1.6.2" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" + integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== + dependencies: + buffer-from "^1.0.0" + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" + +core-util-is@1.0.2, core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= + +currently-unhandled@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" + integrity sha1-mI3zP+qxke95mmE2nddsF635V+o= + dependencies: + array-find-index "^1.0.1" + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= + dependencies: + assert-plus "^1.0.0" + +debug@^2.1.3, debug@^2.2.0, debug@^2.6.9: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@^3.0.0: + version "3.2.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== + dependencies: + ms "^2.1.1" + +decamelize@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= + +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= + +ecc-jsbn@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" + integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= + dependencies: + jsbn "~0.1.0" + safer-buffer "^2.1.0" + +electron-download@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/electron-download/-/electron-download-4.1.1.tgz#02e69556705cc456e520f9e035556ed5a015ebe8" + integrity sha512-FjEWG9Jb/ppK/2zToP+U5dds114fM1ZOJqMAR4aXXL5CvyPE9fiqBK/9YcwC9poIFQTEJk/EM/zyRwziziRZrg== + dependencies: + debug "^3.0.0" + env-paths "^1.0.0" + fs-extra "^4.0.1" + minimist "^1.2.0" + nugget "^2.0.1" + path-exists "^3.0.0" + rc "^1.2.1" + semver "^5.4.1" + sumchecker "^2.0.2" + +electron@3.1.13: + version "3.1.13" + resolved "https://registry.yarnpkg.com/electron/-/electron-3.1.13.tgz#aeb276f4cf5e3785078b6495e982ee46d553a5d2" + integrity sha512-aRNywoUSO1Va/lpU4nz3K6GDyFqYtlOnHGLcERAAHfhB+IJrJ34cUJW4FVBpm43AwvUdAeuCkVKRLtOmrgx5CA== + dependencies: + "@types/node" "^10.1.4" + electron-download "^4.1.0" + extract-zip "^1.0.3" + +env-paths@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-1.0.0.tgz#4168133b42bb05c38a35b1ae4397c8298ab369e0" + integrity sha1-QWgTO0K7BcOKNbGuQ5fIKYqzaeA= + +error-ex@^1.2.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + +extend@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +extract-zip@^1.0.3: + version "1.7.0" + resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.7.0.tgz#556cc3ae9df7f452c493a0cfb51cc30277940927" + integrity sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA== + dependencies: + concat-stream "^1.6.2" + debug "^2.6.9" + mkdirp "^0.5.4" + yauzl "^2.10.0" + +extsprintf@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= + +extsprintf@^1.2.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" + integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= + +fast-deep-equal@^3.1.1: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fd-slicer@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" + integrity sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4= + dependencies: + pend "~1.2.0" + +find-up@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" + integrity sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8= + dependencies: + path-exists "^2.0.0" + pinkie-promise "^2.0.0" + +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= + +form-data@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" + integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.6" + mime-types "^2.1.12" + +fs-extra@^4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.3.tgz#0d852122e5bc5beb453fb028e9c0c9bf36340c94" + integrity sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg== + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +get-stdin@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" + integrity sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4= + +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= + dependencies: + assert-plus "^1.0.0" + +graceful-fs@^4.1.2, graceful-fs@^4.1.6: + version "4.2.6" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" + integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== + +har-schema@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" + integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= + +har-validator@~5.1.3: + version "5.1.5" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd" + integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w== + dependencies: + ajv "^6.12.3" + har-schema "^2.0.0" + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +hosted-git-info@^2.1.4: + version "2.8.9" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" + integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== + +http-signature@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" + integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= + dependencies: + assert-plus "^1.0.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +indent-string@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80" + integrity sha1-ji1INIdCEhtKghi3oTfppSBJ3IA= + dependencies: + repeating "^2.0.0" + +inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +ini@~1.3.0: + version "1.3.8" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" + integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= + +is-core-module@^2.2.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.4.0.tgz#8e9fc8e15027b011418026e98f0e6f4d86305cc1" + integrity sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A== + dependencies: + has "^1.0.3" + +is-finite@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.1.0.tgz#904135c77fb42c0641d6aa1bcdbc4daa8da082f3" + integrity sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w== + +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= + dependencies: + number-is-nan "^1.0.0" + +is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= + +is-utf8@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" + integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= + +isarray@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= + +isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= + +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= + +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-schema@0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" + integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= + +json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= + +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= + optionalDependencies: + graceful-fs "^4.1.6" + +jsprim@^1.2.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" + integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= + dependencies: + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.2.3" + verror "1.10.0" + +load-json-file@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" + integrity sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA= + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + pinkie-promise "^2.0.0" + strip-bom "^2.0.0" + +loud-rejection@^1.0.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" + integrity sha1-W0b4AUft7leIcPCG0Eghz5mOVR8= + dependencies: + currently-unhandled "^0.4.1" + signal-exit "^3.0.0" + +map-obj@^1.0.0, map-obj@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" + integrity sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0= + +meow@^3.1.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" + integrity sha1-cstmi0JSKCkKu/qFaJJYcwioAfs= + dependencies: + camelcase-keys "^2.0.0" + decamelize "^1.1.2" + loud-rejection "^1.0.0" + map-obj "^1.0.1" + minimist "^1.1.3" + normalize-package-data "^2.3.4" + object-assign "^4.0.1" + read-pkg-up "^1.0.1" + redent "^1.0.0" + trim-newlines "^1.0.0" + +mime-db@1.47.0: + version "1.47.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.47.0.tgz#8cb313e59965d3c05cfbf898915a267af46a335c" + integrity sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw== + +mime-types@^2.1.12, mime-types@~2.1.19: + version "2.1.30" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.30.tgz#6e7be8b4c479825f85ed6326695db73f9305d62d" + integrity sha512-crmjA4bLtR8m9qLpHvgxSChT+XoSlZi8J4n/aIdn3z92e/U47Z0V/yl+Wh9W046GgFVAmoNR/fmdbZYcSSIUeg== + dependencies: + mime-db "1.47.0" + +minimist@^1.1.0, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + +mkdirp@^0.5.4: + version "0.5.5" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" + integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== + dependencies: + minimist "^1.2.5" + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + +ms@^2.1.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +normalize-package-data@^2.3.2, normalize-package-data@^2.3.4: + version "2.5.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" + integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== + dependencies: + hosted-git-info "^2.1.4" + resolve "^1.10.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + +nugget@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/nugget/-/nugget-2.0.1.tgz#201095a487e1ad36081b3432fa3cada4f8d071b0" + integrity sha1-IBCVpIfhrTYIGzQy+jytpPjQcbA= + dependencies: + debug "^2.1.3" + minimist "^1.1.0" + pretty-bytes "^1.0.2" + progress-stream "^1.1.0" + request "^2.45.0" + single-line-log "^1.1.2" + throttleit "0.0.2" + +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= + +oauth-sign@~0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" + integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== + +object-assign@^4.0.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= + +object-keys@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-0.4.0.tgz#28a6aae7428dd2c3a92f3d95f21335dd204e0336" + integrity sha1-KKaq50KN0sOpLz2V8hM13SBOAzY= + +parse-json@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" + integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck= + dependencies: + error-ex "^1.2.0" + +path-exists@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" + integrity sha1-D+tsZPD8UY2adU3V77YscCJ2H0s= + dependencies: + pinkie-promise "^2.0.0" + +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= + +path-parse@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" + integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== + +path-type@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" + integrity sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE= + dependencies: + graceful-fs "^4.1.2" + pify "^2.0.0" + pinkie-promise "^2.0.0" + +pend@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" + integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= + +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= + +pify@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= + +pinkie-promise@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o= + dependencies: + pinkie "^2.0.0" + +pinkie@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= + +pretty-bytes@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-1.0.4.tgz#0a22e8210609ad35542f8c8d5d2159aff0751c84" + integrity sha1-CiLoIQYJrTVUL4yNXSFZr/B1HIQ= + dependencies: + get-stdin "^4.0.1" + meow "^3.1.0" + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +progress-stream@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/progress-stream/-/progress-stream-1.2.0.tgz#2cd3cfea33ba3a89c9c121ec3347abe9ab125f77" + integrity sha1-LNPP6jO6OonJwSHsM0er6asSX3c= + dependencies: + speedometer "~0.1.2" + through2 "~0.2.3" + +psl@^1.1.28: + version "1.8.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" + integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== + +punycode@^2.1.0, punycode@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + +qs@~6.5.2: + version "6.5.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" + integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== + +rc@^1.2.1: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + +read-pkg-up@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" + integrity sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI= + dependencies: + find-up "^1.0.0" + read-pkg "^1.0.0" + +read-pkg@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" + integrity sha1-9f+qXs0pyzHAR0vKfXVra7KePyg= + dependencies: + load-json-file "^1.0.0" + normalize-package-data "^2.3.2" + path-type "^1.0.0" + +readable-stream@^2.2.2: + version "2.3.7" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" + integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readable-stream@~1.1.9: + version "1.1.14" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" + integrity sha1-fPTFTvZI44EwhMY23SB54WbAgdk= + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +redent@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" + integrity sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94= + dependencies: + indent-string "^2.1.0" + strip-indent "^1.0.1" + +repeating@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" + integrity sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo= + dependencies: + is-finite "^1.0.0" + +request@^2.45.0: + version "2.88.2" + resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" + integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.8.0" + caseless "~0.12.0" + combined-stream "~1.0.6" + extend "~3.0.2" + forever-agent "~0.6.1" + form-data "~2.3.2" + har-validator "~5.1.3" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.19" + oauth-sign "~0.9.0" + performance-now "^2.1.0" + qs "~6.5.2" + safe-buffer "^5.1.2" + tough-cookie "~2.5.0" + tunnel-agent "^0.6.0" + uuid "^3.3.2" + +resolve@^1.10.0: + version "1.20.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" + integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== + dependencies: + is-core-module "^2.2.0" + path-parse "^1.0.6" + +safe-buffer@^5.0.1, safe-buffer@^5.1.2: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +"semver@2 || 3 || 4 || 5", semver@^5.4.1: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +signal-exit@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" + integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== + +single-line-log@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/single-line-log/-/single-line-log-1.1.2.tgz#c2f83f273a3e1a16edb0995661da0ed5ef033364" + integrity sha1-wvg/Jzo+GhbtsJlWYdoO1e8DM2Q= + dependencies: + string-width "^1.0.1" + +spdx-correct@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" + integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w== + dependencies: + spdx-expression-parse "^3.0.0" + spdx-license-ids "^3.0.0" + +spdx-exceptions@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" + integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== + +spdx-expression-parse@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" + integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== + dependencies: + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + +spdx-license-ids@^3.0.0: + version "3.0.9" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.9.tgz#8a595135def9592bda69709474f1cbeea7c2467f" + integrity sha512-Ki212dKK4ogX+xDo4CtOZBVIwhsKBEfsEEcwmJfLQzirgc2jIWdzg40Unxz/HzEUqM1WFzVlQSMF9kZZ2HboLQ== + +speedometer@~0.1.2: + version "0.1.4" + resolved "https://registry.yarnpkg.com/speedometer/-/speedometer-0.1.4.tgz#9876dbd2a169d3115402d48e6ea6329c8816a50d" + integrity sha1-mHbb0qFp0xFUAtSObqYynIgWpQ0= + +sshpk@^1.7.0: + version "1.16.1" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" + integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + bcrypt-pbkdf "^1.0.0" + dashdash "^1.12.0" + ecc-jsbn "~0.1.1" + getpass "^0.1.1" + jsbn "~0.1.0" + safer-buffer "^2.0.2" + tweetnacl "~0.14.0" + +string-width@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +string_decoder@~0.10.x: + version "0.10.31" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" + integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +strip-ansi@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= + dependencies: + ansi-regex "^2.0.0" + +strip-bom@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" + integrity sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4= + dependencies: + is-utf8 "^0.2.0" + +strip-indent@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2" + integrity sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI= + dependencies: + get-stdin "^4.0.1" + +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= + +sumchecker@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/sumchecker/-/sumchecker-2.0.2.tgz#0f42c10e5d05da5d42eea3e56c3399a37d6c5b3e" + integrity sha1-D0LBDl0F2l1C7qPlbDOZo31sWz4= + dependencies: + debug "^2.2.0" + +throttleit@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-0.0.2.tgz#cfedf88e60c00dd9697b61fdd2a8343a9b680eaf" + integrity sha1-z+34jmDADdlpe2H90qg0OptoDq8= + +through2@~0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/through2/-/through2-0.2.3.tgz#eb3284da4ea311b6cc8ace3653748a52abf25a3f" + integrity sha1-6zKE2k6jEbbMis42U3SKUqvyWj8= + dependencies: + readable-stream "~1.1.9" + xtend "~2.1.1" + +tough-cookie@~2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" + integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== + dependencies: + psl "^1.1.28" + punycode "^2.1.1" + +trim-newlines@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" + integrity sha1-WIeWa7WCpFA6QetST301ARgVphM= + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= + dependencies: + safe-buffer "^5.0.1" + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= + +typedarray@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= + +universalify@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + +uuid@^3.3.2: + version "3.4.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" + integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== + +validate-npm-package-license@^3.0.1: + version "3.0.4" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== + dependencies: + spdx-correct "^3.0.0" + spdx-expression-parse "^3.0.0" + +verror@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + +xtend@~2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-2.1.2.tgz#6efecc2a4dad8e6962c4901b337ce7ba87b5d28b" + integrity sha1-bv7MKk2tjmlixJAbM3znuoe10os= + dependencies: + object-keys "~0.4.0" + +yauzl@^2.10.0: + version "2.10.0" + resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" + integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk= + dependencies: + buffer-crc32 "~0.2.3" + fd-slicer "~1.1.0" diff --git a/gulp/bundle-loader.js b/gulp/bundle-loader.js index d8bb8c24..16db26fa 100644 --- a/gulp/bundle-loader.js +++ b/gulp/bundle-loader.js @@ -54,8 +54,11 @@ document.documentElement.appendChild(element); } - window.addEventListener("error", errorHandler); - window.addEventListener("unhandledrejection", errorHandler); + + if (window.location.host.indexOf("localhost") < 0) { + window.addEventListener("error", errorHandler); + window.addEventListener("unhandledrejection", errorHandler); + } function makeJsTag(src, integrity) { var script = document.createElement("script"); diff --git a/gulp/gulpfile.js b/gulp/gulpfile.js index 06bb627b..0f4f4185 100644 --- a/gulp/gulpfile.js +++ b/gulp/gulpfile.js @@ -139,7 +139,12 @@ gulp.task("main.webserver", () => { ); }); -function serve({ standalone, chineseVersion = false }) { +/** + * + * @param {object} param0 + * @param {"web"|"standalone"|"china"|"wegame"} param0.version + */ +function serve({ version = "web" }) { browserSync.init({ server: buildFolder, port: 3005, @@ -163,7 +168,7 @@ function serve({ standalone, chineseVersion = false }) { gulp.watch(["../src/**/*.scss"], gulp.series("css.dev")); // Watch .html files, those trigger a html rebuild - gulp.watch("../src/**/*.html", gulp.series(standalone ? "html.standalone-dev" : "html.dev")); + gulp.watch("../src/**/*.html", gulp.series(version === "web" ? "html.dev" : "html.standalone-dev")); // Watch sound files // gulp.watch(["../res_raw/sounds/**/*.mp3", "../res_raw/sounds/**/*.wav"], gulp.series("sounds.dev")); @@ -199,14 +204,25 @@ function serve({ standalone, chineseVersion = false }) { return gulp.src(path).pipe(browserSync.reload({ stream: true })); }); - // Start the webpack watching server (Will never return) - if (standalone) { - gulp.series("js.standalone-dev.watch")(() => true); - } else { - if (chineseVersion) { - gulp.series("china.js.dev.watch")(() => true); - } else { + switch (version) { + case "web": { gulp.series("js.dev.watch")(() => true); + break; + } + case "standalone": { + gulp.series("js.standalone-dev.watch")(() => true); + break; + } + case "china": { + gulp.series("china.js.dev.watch")(() => true); + break; + } + case "wegame": { + gulp.series("wegame.js.dev.watch")(() => true); + break; + } + default: { + throw new Error("Unknown version " + version); } } } @@ -294,7 +310,7 @@ gulp.task( // Builds everything (standalone-prod) -for (const prefix of ["", "china."]) { +for (const prefix of ["", "china.", "wegame."]) { gulp.task( prefix + "step.standalone-prod.code", gulp.series("sounds.fullbuildHQ", "translations.fullBuild", prefix + "js.standalone-prod") @@ -327,25 +343,45 @@ gulp.task( ); gulp.task("main.deploy.prod", gulp.series("utils.requireCleanWorkingTree", "build.prod", "ftp.upload.prod")); gulp.task("main.deploy.all", gulp.series("main.deploy.staging", "main.deploy.prod")); + +// steam gulp.task("regular.main.standalone", gulp.series("build.standalone-prod", "standalone.package.prod")); + +// china gulp.task( "china.main.standalone", gulp.series("china.build.standalone-prod", "china.standalone.package.prod") ); -gulp.task("standalone.all", gulp.series("regular.main.standalone", "china.main.standalone")); + +// wegame +gulp.task( + "wegame.main.standalone", + gulp.series("wegame.build.standalone-prod", "wegame.standalone.package.prod") +); + +// all (except wegame) +gulp.task("standalone.steam", gulp.series("regular.main.standalone", "china.main.standalone")); +gulp.task( + "standalone.all", + gulp.series("regular.main.standalone", "china.main.standalone", "wegame.main.standalone") +); // Live-development gulp.task( "main.serveDev", - gulp.series("build.dev", () => serve({ standalone: false })) + gulp.series("build.dev", () => serve({ version: "web" })) ); gulp.task( "main.serveStandalone", - gulp.series("build.standalone.dev", () => serve({ standalone: true })) + gulp.series("build.standalone.dev", () => serve({ version: "standalone" })) ); gulp.task( "china.main.serveDev", - gulp.series("build.dev", () => serve({ standalone: false, chineseVersion: true })) + gulp.series("build.dev", () => serve({ version: "china" })) +); +gulp.task( + "wegame.main.serveDev", + gulp.series("build.dev", () => serve({ version: "wegame" })) ); gulp.task("default", gulp.series("main.serveDev")); diff --git a/gulp/js.js b/gulp/js.js index cfaedb8c..93dab464 100644 --- a/gulp/js.js +++ b/gulp/js.js @@ -59,6 +59,36 @@ function gulptasksJS($, gulp, buildFolder, browserSync) { .pipe(gulp.dest(buildFolder)); }); + //// DEV WEGAME + + gulp.task("wegame.js.dev.watch", () => { + return gulp + .src("../src/js/main.js") + .pipe( + $.webpackStream( + requireUncached("./webpack.config.js")({ + watch: true, + wegameVersion: true, + }) + ) + ) + .pipe(gulp.dest(buildFolder)) + .pipe(browserSync.stream()); + }); + + gulp.task("wegame.js.dev", () => { + return gulp + .src("../src/js/main.js") + .pipe( + $.webpackStream( + requireUncached("./webpack.config.js")({ + wegameVersion: true, + }) + ) + ) + .pipe(gulp.dest(buildFolder)); + }); + //// STAGING gulp.task("js.staging.transpiled", () => { @@ -208,6 +238,23 @@ function gulptasksJS($, gulp, buildFolder, browserSync) { ) .pipe(gulp.dest(buildFolder)); }); + + gulp.task("wegame.js.standalone-prod", () => { + return gulp + .src("../src/js/main.js") + .pipe( + $.webpackStream( + requireUncached("./webpack.production.config.js")({ + enableAssert: false, + environment: "prod", + es6: false, + standalone: true, + wegameVersion: true, + }) + ) + ) + .pipe(gulp.dest(buildFolder)); + }); } module.exports = { diff --git a/gulp/standalone.js b/gulp/standalone.js index ffec8539..81b41929 100644 --- a/gulp/standalone.js +++ b/gulp/standalone.js @@ -9,21 +9,31 @@ const buildutils = require("./buildutils"); const execSync = require("child_process").execSync; function gulptasksStandalone($, gulp) { - const electronBaseDir = path.join(__dirname, "..", "electron"); const targets = [ { tempDestDir: path.join(__dirname, "..", "tmp_standalone_files"), suffix: "", taskPrefix: "", + electronBaseDir: path.join(__dirname, "..", "electron"), + steam: true, }, { tempDestDir: path.join(__dirname, "..", "tmp_standalone_files_china"), suffix: "china", taskPrefix: "china.", + electronBaseDir: path.join(__dirname, "..", "electron"), + steam: true, + }, + { + tempDestDir: path.join(__dirname, "..", "tmp_standalone_files_wegame"), + suffix: "wegame", + taskPrefix: "wegame.", + electronBaseDir: path.join(__dirname, "..", "electron_wegame"), + steam: false, }, ]; - for (const { tempDestDir, suffix, taskPrefix } of targets) { + for (const { tempDestDir, suffix, taskPrefix, electronBaseDir, steam } of targets) { const tempDestBuildDir = path.join(tempDestDir, "built"); gulp.task(taskPrefix + "standalone.prepare.cleanup", () => { @@ -34,13 +44,17 @@ function gulptasksStandalone($, gulp) { const requiredFiles = [ path.join(electronBaseDir, "node_modules", "**", "*.*"), path.join(electronBaseDir, "node_modules", "**", ".*"), - path.join(electronBaseDir, "steam_appid.txt"), + path.join(electronBaseDir, "wegame_sdk", "**", "*.*"), + path.join(electronBaseDir, "wegame_sdk", "**", ".*"), path.join(electronBaseDir, "favicon*"), // fails on platforms which support symlinks // https://github.com/gulpjs/gulp/issues/1427 // path.join(electronBaseDir, "node_modules", "**", "*"), ]; + if (steam) { + requiredFiles.push(path.join(electronBaseDir, "steam_appid.txt")); + } return gulp.src(requiredFiles, { base: electronBaseDir }).pipe(gulp.dest(tempDestBuildDir)); }); @@ -64,6 +78,11 @@ function gulptasksStandalone($, gulp) { }); gulp.task(taskPrefix + "standalone.prepareVDF", cb => { + if (!steam) { + cb(); + return; + } + const hash = buildutils.getRevision(); const steampipeDir = path.join(__dirname, "steampipe", "scripts"); @@ -116,11 +135,10 @@ function gulptasksStandalone($, gulp) { const tomlFile = fs.readFileSync(path.join(__dirname, ".itch.toml")); const privateArtifactsPath = "node_modules/shapez.io-private-artifacts"; - let asar; - if (fs.existsSync(path.join(tempDestBuildDir, privateArtifactsPath))) { + let asar = steam; + if (steam && fs.existsSync(path.join(tempDestBuildDir, privateArtifactsPath))) { + // @ts-expect-error asar = { unpackDir: privateArtifactsPath }; - } else { - asar = true; } packager({ @@ -147,24 +165,26 @@ function gulptasksStandalone($, gulp) { return; } - fs.writeFileSync( - path.join(appPath, "LICENSE"), - fs.readFileSync(path.join(__dirname, "..", "LICENSE")) - ); - - fse.copySync( - path.join(tempDestBuildDir, "steam_appid.txt"), - path.join(appPath, "steam_appid.txt") - ); - - fs.writeFileSync(path.join(appPath, ".itch.toml"), tomlFile); - - if (platform === "linux") { + if (steam) { fs.writeFileSync( - path.join(appPath, "play.sh"), - '#!/usr/bin/env bash\n./shapezio --no-sandbox "$@"\n' + path.join(appPath, "LICENSE"), + fs.readFileSync(path.join(__dirname, "..", "LICENSE")) ); - fs.chmodSync(path.join(appPath, "play.sh"), 0o775); + + fse.copySync( + path.join(tempDestBuildDir, "steam_appid.txt"), + path.join(appPath, "steam_appid.txt") + ); + + fs.writeFileSync(path.join(appPath, ".itch.toml"), tomlFile); + + if (platform === "linux") { + fs.writeFileSync( + path.join(appPath, "play.sh"), + '#!/usr/bin/env bash\n./shapezio --no-sandbox "$@"\n' + ); + fs.chmodSync(path.join(appPath, "play.sh"), 0o775); + } } }); diff --git a/gulp/steampipe/scripts/app.vdf.template b/gulp/steampipe/scripts/app.vdf.template index 32634060..5359acfe 100644 --- a/gulp/steampipe/scripts/app.vdf.template +++ b/gulp/steampipe/scripts/app.vdf.template @@ -2,16 +2,16 @@ { "appid" "1318690" "desc" "$DESC$" - "buildoutput" "C:\work\shapez.io\gulp\steampipe\steamtemp" + "buildoutput" "C:\work\shapez\shapez.io\gulp\steampipe\steamtemp" "contentroot" "" "setlive" "" "preview" "0" "local" "" "depots" { - "1318691" "C:\work\shapez.io\gulp\steampipe\scripts\windows.vdf" - "1318694" "C:\work\shapez.io\gulp\steampipe\scripts\china-windows.vdf" - "1318692" "C:\work\shapez.io\gulp\steampipe\scripts\linux.vdf" - "1318695" "C:\work\shapez.io\gulp\steampipe\scripts\china-linux.vdf" + "1318691" "C:\work\shapez\shapez.io\gulp\steampipe\scripts\windows.vdf" + "1318694" "C:\work\shapez\shapez.io\gulp\steampipe\scripts\china-windows.vdf" + "1318692" "C:\work\shapez\shapez.io\gulp\steampipe\scripts\linux.vdf" + "1318695" "C:\work\shapez\shapez.io\gulp\steampipe\scripts\china-linux.vdf" } } diff --git a/gulp/steampipe/scripts/china-linux.vdf b/gulp/steampipe/scripts/china-linux.vdf index 9882dc96..3906312b 100644 --- a/gulp/steampipe/scripts/china-linux.vdf +++ b/gulp/steampipe/scripts/china-linux.vdf @@ -1,7 +1,7 @@ "DepotBuildConfig" { "DepotID" "1318695" - "contentroot" "C:\work\shapez.io\tmp_standalone_files_china\shapez.io-standalonechina-linux-x64" + "contentroot" "C:\work\shapez\shapez.io\tmp_standalone_files_china\shapez.io-standalonechina-linux-x64" "FileMapping" { "LocalPath" "*" diff --git a/gulp/steampipe/scripts/china-windows.vdf b/gulp/steampipe/scripts/china-windows.vdf index 4b024228..3a098cbc 100644 --- a/gulp/steampipe/scripts/china-windows.vdf +++ b/gulp/steampipe/scripts/china-windows.vdf @@ -1,7 +1,7 @@ "DepotBuildConfig" { "DepotID" "1318694" - "contentroot" "C:\work\shapez.io\tmp_standalone_files_china\shapez.io-standalonechina-win32-x64" + "contentroot" "C:\work\shapez\shapez.io\tmp_standalone_files_china\shapez.io-standalonechina-win32-x64" "FileMapping" { "LocalPath" "*" diff --git a/gulp/steampipe/scripts/linux.vdf b/gulp/steampipe/scripts/linux.vdf index df1a86cc..60dfcca5 100644 --- a/gulp/steampipe/scripts/linux.vdf +++ b/gulp/steampipe/scripts/linux.vdf @@ -1,7 +1,7 @@ "DepotBuildConfig" { "DepotID" "1318692" - "contentroot" "C:\work\shapez.io\tmp_standalone_files\shapez.io-standalone-linux-x64" + "contentroot" "C:\work\shapez\shapez.io\tmp_standalone_files\shapez.io-standalone-linux-x64" "FileMapping" { "LocalPath" "*" diff --git a/gulp/steampipe/scripts/windows.vdf b/gulp/steampipe/scripts/windows.vdf index bf0e8721..7d0db436 100644 --- a/gulp/steampipe/scripts/windows.vdf +++ b/gulp/steampipe/scripts/windows.vdf @@ -1,7 +1,7 @@ "DepotBuildConfig" { "DepotID" "1318691" - "contentroot" "C:\work\shapez.io\tmp_standalone_files\shapez.io-standalone-win32-x64" + "contentroot" "C:\work\shapez\shapez.io\tmp_standalone_files\shapez.io-standalone-win32-x64" "FileMapping" { "LocalPath" "*" diff --git a/gulp/webpack.config.js b/gulp/webpack.config.js index 3f666e73..14987cfa 100644 --- a/gulp/webpack.config.js +++ b/gulp/webpack.config.js @@ -6,7 +6,7 @@ const { getRevision, getVersion, getAllResourceImages } = require("./buildutils" const lzString = require("lz-string"); const CircularDependencyPlugin = require("circular-dependency-plugin"); -module.exports = ({ watch = false, standalone = false, chineseVersion = false }) => { +module.exports = ({ watch = false, standalone = false, chineseVersion = false, wegameVersion = false }) => { return { mode: "development", devtool: "cheap-source-map", @@ -35,6 +35,7 @@ module.exports = ({ watch = false, standalone = false, chineseVersion = false }) lzString.compressToEncodedURIComponent("http://localhost:10005/v1") ), G_CHINA_VERSION: JSON.stringify(chineseVersion), + G_WEGAME_VERSION: JSON.stringify(wegameVersion), G_IS_DEV: "true", G_IS_RELEASE: "false", G_IS_MOBILE_APP: "false", diff --git a/gulp/webpack.production.config.js b/gulp/webpack.production.config.js index 1779a76f..fd7551e0 100644 --- a/gulp/webpack.production.config.js +++ b/gulp/webpack.production.config.js @@ -17,6 +17,7 @@ module.exports = ({ isBrowser = true, mobileApp = false, chineseVersion = false, + wegameVersion = false, }) => { const globalDefs = { assert: enableAssert ? "window.assert" : "false && window.assert", @@ -25,6 +26,7 @@ module.exports = ({ G_IS_DEV: "false", G_CHINA_VERSION: JSON.stringify(chineseVersion), + G_WEGAME_VERSION: JSON.stringify(wegameVersion), G_IS_RELEASE: environment === "prod" ? "true" : "false", G_IS_STANDALONE: standalone ? "true" : "false", G_IS_BROWSER: isBrowser ? "true" : "false", @@ -40,7 +42,7 @@ module.exports = ({ G_ALL_UI_IMAGES: JSON.stringify(getAllResourceImages()), }; - const minifyNames = environment === "prod"; + const minifyNames = false; return { mode: "production", diff --git a/res/logo_wegame.png b/res/logo_wegame.png new file mode 100644 index 00000000..eb7d35fe Binary files /dev/null and b/res/logo_wegame.png differ diff --git a/res/puzzle_dlc_logo.png b/res/puzzle_dlc_logo.png new file mode 100644 index 00000000..1c430c82 Binary files /dev/null and b/res/puzzle_dlc_logo.png differ diff --git a/res/puzzle_dlc_logo_china.png b/res/puzzle_dlc_logo_china.png new file mode 100644 index 00000000..c45b6cdc Binary files /dev/null and b/res/puzzle_dlc_logo_china.png differ diff --git a/res/ui/building_icons/block.png b/res/ui/building_icons/block.png new file mode 100644 index 00000000..a6d914f6 Binary files /dev/null and b/res/ui/building_icons/block.png differ diff --git a/res/ui/building_icons/constant_producer.png b/res/ui/building_icons/constant_producer.png new file mode 100644 index 00000000..f7ac8afa Binary files /dev/null and b/res/ui/building_icons/constant_producer.png differ diff --git a/res/ui/building_icons/goal_acceptor.png b/res/ui/building_icons/goal_acceptor.png new file mode 100644 index 00000000..9087c155 Binary files /dev/null and b/res/ui/building_icons/goal_acceptor.png differ diff --git a/res/ui/building_tutorials/block.png b/res/ui/building_tutorials/block.png new file mode 100644 index 00000000..73925265 Binary files /dev/null and b/res/ui/building_tutorials/block.png differ diff --git a/res/ui/building_tutorials/constant_producer.png b/res/ui/building_tutorials/constant_producer.png new file mode 100644 index 00000000..8af4da33 Binary files /dev/null and b/res/ui/building_tutorials/constant_producer.png differ diff --git a/res/ui/building_tutorials/goal_acceptor.png b/res/ui/building_tutorials/goal_acceptor.png new file mode 100644 index 00000000..054783b6 Binary files /dev/null and b/res/ui/building_tutorials/goal_acceptor.png differ diff --git a/res/ui/icons/puzzle_action_liked_no.png b/res/ui/icons/puzzle_action_liked_no.png new file mode 100644 index 00000000..7b30f81e Binary files /dev/null and b/res/ui/icons/puzzle_action_liked_no.png differ diff --git a/res/ui/icons/puzzle_action_liked_yes.png b/res/ui/icons/puzzle_action_liked_yes.png new file mode 100644 index 00000000..07b8bbcf Binary files /dev/null and b/res/ui/icons/puzzle_action_liked_yes.png differ diff --git a/res/ui/icons/puzzle_complete_indicator.png b/res/ui/icons/puzzle_complete_indicator.png new file mode 100644 index 00000000..e2c95b8b Binary files /dev/null and b/res/ui/icons/puzzle_complete_indicator.png differ diff --git a/res/ui/icons/puzzle_complete_indicator_inverse.png b/res/ui/icons/puzzle_complete_indicator_inverse.png new file mode 100644 index 00000000..f3946efc Binary files /dev/null and b/res/ui/icons/puzzle_complete_indicator_inverse.png differ diff --git a/res/ui/icons/puzzle_completion_rate.png b/res/ui/icons/puzzle_completion_rate.png new file mode 100644 index 00000000..2b07ce22 Binary files /dev/null and b/res/ui/icons/puzzle_completion_rate.png differ diff --git a/res/ui/icons/puzzle_plays.png b/res/ui/icons/puzzle_plays.png new file mode 100644 index 00000000..358b5362 Binary files /dev/null and b/res/ui/icons/puzzle_plays.png differ diff --git a/res/ui/icons/puzzle_upvotes.png b/res/ui/icons/puzzle_upvotes.png new file mode 100644 index 00000000..685d4bd7 Binary files /dev/null and b/res/ui/icons/puzzle_upvotes.png differ diff --git a/res/ui/icons/state_next_button.png b/res/ui/icons/state_next_button.png new file mode 100644 index 00000000..d6e09644 Binary files /dev/null and b/res/ui/icons/state_next_button.png differ diff --git a/res/ui/languages/he.svg b/res/ui/languages/he.svg new file mode 100644 index 00000000..aaa64e98 --- /dev/null +++ b/res/ui/languages/he.svg @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/ui/puzzle_dlc_logo.png b/res/ui/puzzle_dlc_logo.png new file mode 100644 index 00000000..1c430c82 Binary files /dev/null and b/res/ui/puzzle_dlc_logo.png differ diff --git a/res/ui/puzzle_dlc_logo_china.png b/res/ui/puzzle_dlc_logo_china.png new file mode 100644 index 00000000..c45b6cdc Binary files /dev/null and b/res/ui/puzzle_dlc_logo_china.png differ diff --git a/res/ui/puzzle_dlc_logo_inverse.png b/res/ui/puzzle_dlc_logo_inverse.png new file mode 100644 index 00000000..4709f5c4 Binary files /dev/null and b/res/ui/puzzle_dlc_logo_inverse.png differ diff --git a/res_raw/sounds/music/puzzle-full.mp3 b/res_raw/sounds/music/puzzle-full.mp3 new file mode 100644 index 00000000..a2c0d80a Binary files /dev/null and b/res_raw/sounds/music/puzzle-full.mp3 differ diff --git a/res_raw/sprites/blueprints/block.png b/res_raw/sprites/blueprints/block.png new file mode 100644 index 00000000..5a27786e Binary files /dev/null and b/res_raw/sprites/blueprints/block.png differ diff --git a/res_raw/sprites/blueprints/constant_producer.png b/res_raw/sprites/blueprints/constant_producer.png new file mode 100644 index 00000000..417a7886 Binary files /dev/null and b/res_raw/sprites/blueprints/constant_producer.png differ diff --git a/res_raw/sprites/blueprints/goal_acceptor.png b/res_raw/sprites/blueprints/goal_acceptor.png new file mode 100644 index 00000000..58097279 Binary files /dev/null and b/res_raw/sprites/blueprints/goal_acceptor.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 be78107b..15dc6b86 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/buildings/block.png b/res_raw/sprites/buildings/block.png new file mode 100644 index 00000000..90855c82 Binary files /dev/null and b/res_raw/sprites/buildings/block.png differ diff --git a/res_raw/sprites/buildings/constant_producer.png b/res_raw/sprites/buildings/constant_producer.png new file mode 100644 index 00000000..9ea15eaf Binary files /dev/null and b/res_raw/sprites/buildings/constant_producer.png differ diff --git a/res_raw/sprites/buildings/goal_acceptor.png b/res_raw/sprites/buildings/goal_acceptor.png new file mode 100644 index 00000000..17fa224f Binary files /dev/null and b/res_raw/sprites/buildings/goal_acceptor.png differ diff --git a/res_raw/sprites/create_blueprint_previews.py b/res_raw/sprites/create_blueprint_previews.py index 96688fe4..714804d3 100644 --- a/res_raw/sprites/create_blueprint_previews.py +++ b/res_raw/sprites/create_blueprint_previews.py @@ -41,7 +41,7 @@ def process_image(data, outfilename, src_image): if isWire: targetR = 255 targetG = 104 - targetB = 232 + targetB = 232 for x in range(img.width): for y in range(img.height): @@ -85,6 +85,8 @@ def generate_blueprint_sprite(infilename, outfilename): buildings = listdir("buildings") for buildingId in buildings: + if not ".png" in buildingId: + continue if "hub" in buildingId: continue if "wire-" in buildingId: diff --git a/src/css/ingame_hud/beta_overlay.scss b/src/css/ingame_hud/beta_overlay.scss index caadd127..08eba960 100644 --- a/src/css/ingame_hud/beta_overlay.scss +++ b/src/css/ingame_hud/beta_overlay.scss @@ -1,6 +1,6 @@ #ingame_HUD_BetaOverlay { position: fixed; - @include S(top, 10px); + @include S(top, 70px); left: 50%; transform: translateX(-50%); color: $colorRedBright; diff --git a/src/css/ingame_hud/buildings_toolbar.scss b/src/css/ingame_hud/buildings_toolbar.scss index 54205d64..3af5edf4 100644 --- a/src/css/ingame_hud/buildings_toolbar.scss +++ b/src/css/ingame_hud/buildings_toolbar.scss @@ -37,7 +37,7 @@ .building { @include S(width, 30px); - @include S(height, 22px); + @include S(height, 30px); background-size: 45%; &:not(.unlocked) { @@ -49,65 +49,98 @@ } .building { - color: $accentColorDark; display: flex; - flex-direction: column; + @include S(width, 40px); position: relative; - align-items: center; - justify-content: center; - @include S(padding, 5px); - @include S(padding-bottom, 1px); - @include S(width, 35px); @include S(height, 40px); + .icon { + color: $accentColorDark; + display: flex; + flex-direction: column-reverse; + position: relative; + align-items: center; + justify-content: center; + width: 100%; + height: 100%; + padding: 0; + margin: 0; + @include S(border-radius, $globalBorderRadius); - background: center center / 70% no-repeat; + background: center center / 70% no-repeat; + } &:not(.unlocked) { - @include S(width, 20px); - opacity: 0.15; - background-image: none !important; - - &::before { - content: " "; - - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: 4; - & { - /* @load-async */ - background: uiResource("locked_building.png") center center / #{D(20px)} #{D(20px)} - no-repeat; + @include S(width, 25px); + .icon { + opacity: 0.15; + } + &.editor { + .icon { + pointer-events: all; + cursor: pointer; + &:hover { + background-color: rgba(22, 30, 68, 0.1); + } + } + } + &:not(.editor) { + .icon { + background-image: uiResource("locked_building.png") !important; } } } - @include S(border-radius, $globalBorderRadius); - &.unlocked { - pointer-events: all; - transition: all 50ms ease-in-out; - transition-property: background-color, transform; + .icon { + pointer-events: all; + transition: all 50ms ease-in-out; + transition-property: background-color, transform; + cursor: pointer; - cursor: pointer; - &:hover { - background-color: rgba(30, 40, 90, 0.1); + &:hover { + background-color: rgba(30, 40, 90, 0.1); + } + + &.pressed { + transform: scale(0.9) !important; + } } - - &.pressed { - transform: scale(0.9) !important; - } - &.selected { // transform: scale(1.05); background-color: rgba(lighten($colorBlueBright, 9), 0.4); + @include S(border-radius, 2px); .keybinding { color: #111; } } + + .puzzle-lock { + & { + /* @load-async */ + background: uiResource("locked_building.png") center center / 90% no-repeat; + } + + display: grid; + grid-auto-flow: column; + + position: absolute; + @include S(top, -15px); + left: 50%; + transform: translateX(-50%) !important; + transition: all 0.12s ease-in-out; + transition-property: opacity, transform; + + cursor: pointer; + pointer-events: all; + + @include S(width, 12px); + @include S(height, 12px); + + &:hover { + opacity: 0.5; + } + } } } } diff --git a/src/css/ingame_hud/dialogs.scss b/src/css/ingame_hud/dialogs.scss index ad3f76d0..cc742d42 100644 --- a/src/css/ingame_hud/dialogs.scss +++ b/src/css/ingame_hud/dialogs.scss @@ -67,6 +67,14 @@ * { color: #fff; } + + display: flex; + flex-direction: column; + + .text { + text-transform: uppercase; + @include S(margin-bottom, 10px); + } } > .dialogInner { @@ -168,6 +176,11 @@ &.errored { background-color: rgb(250, 206, 206); + + &::placeholder { + color: #fff; + opacity: 0.8; + } } } diff --git a/src/css/ingame_hud/puzzle_back_to_menu.scss b/src/css/ingame_hud/puzzle_back_to_menu.scss new file mode 100644 index 00000000..564b592e --- /dev/null +++ b/src/css/ingame_hud/puzzle_back_to_menu.scss @@ -0,0 +1,41 @@ +#ingame_HUD_PuzzleBackToMenu { + position: absolute; + @include S(top, 10px); + @include S(left, 0px); + + display: flex; + flex-direction: column; + align-items: flex-start; + backdrop-filter: blur(D(1px)); + padding: D(3px); + + > .button { + @include PlainText; + pointer-events: all; + cursor: pointer; + position: relative; + color: #333438; + transition: all 0.12s ease-in-out; + transition-property: opacity, transform; + text-transform: uppercase; + @include PlainText; + @include S(width, 30px); + @include S(height, 30px); + + @include DarkThemeInvert; + + opacity: 1; + &:hover { + opacity: 0.9 !important; + } + + &.pressed { + transform: scale(0.95) !important; + } + + & { + /* @load-async */ + background: uiResource("icons/state_back_button.png") center center / D(15px) no-repeat; + } + } +} diff --git a/src/css/ingame_hud/puzzle_complete_notification.scss b/src/css/ingame_hud/puzzle_complete_notification.scss new file mode 100644 index 00000000..5f36df82 --- /dev/null +++ b/src/css/ingame_hud/puzzle_complete_notification.scss @@ -0,0 +1,171 @@ +#ingame_HUD_PuzzleCompleteNotification { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + overflow: auto; + pointer-events: all; + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + + & { + /* @load-async */ + background: rgba(#333538, 0.95) uiResource("dialog_bg_pattern.png") top left / #{D(10px)} repeat; + } + + @include InlineAnimation(0.1s ease-in-out) { + 0% { + opacity: 0; + } + } + + > .dialog { + // background: rgba(#222428, 0.5); + @include S(border-radius, $globalBorderRadius); + @include S(padding, 30px); + + @include InlineAnimation(0.5s ease-in-out) { + 0% { + opacity: 0; + } + } + + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + color: #fff; + text-align: center; + + > .title { + @include SuperHeading; + text-transform: uppercase; + @include S(font-size, 30px); + @include S(margin-bottom, 40px); + color: $colorGreenBright !important; + + @include InlineAnimation(0.5s ease-in-out) { + 0% { + transform: translateY(-50vh); + } + 50% { + transform: translateY(5vh); + } + 75% { + transform: translateY(-2vh); + } + } + } + + > .contents { + @include InlineAnimation(0.5s ease-in-out) { + 0% { + transform: translateX(-100vw); + } + 50% { + transform: translateX(5vw); + } + + 75% { + transform: translateX(-2vw); + } + } + + display: flex; + flex-direction: column; + align-items: center; + justify-content: flex-start; + + > .stepLike { + display: flex; + flex-direction: column; + @include S(margin-bottom, 10px); + @include SuperSmallText; + + > .buttons { + display: flex; + align-items: center; + justify-content: center; + @include S(margin, 10px, 0); + + > button { + @include S(width, 60px); + @include S(height, 60px); + @include S(margin, 0, 10px); + box-sizing: border-box; + border-radius: 50%; + transition: opacity 0.12s ease-in-out, background-color 0.12s ease-in-out; + @include IncreasedClickArea(0px); + + &.liked-yes { + /* @load-async */ + background: uiResource("icons/puzzle_action_liked_yes.png") center 55% / 60% + no-repeat; + } + + &:hover:not(.active) { + opacity: 0.5 !important; + } + + &.active { + background-color: $colorRedBright !important; + @include InlineAnimation(0.3s ease-in-out) { + 0% { + transform: scale(0); + } + 50% { + transform: scale(1.2); + } + 100% { + transform: scale(1); + } + } + } + &:not(.active) { + opacity: 0.4; + } + } + } + } + + > .buttonBar { + display: flex; + @include S(margin-top, 20px); + + button.continue { + background: #555; + @include S(margin-right, 10px); + } + + button.menu { + background-color: $colorGreenBright; + } + + > button { + @include S(min-width, 100px); + @include S(padding, 10px, 20px); + @include IncreasedClickArea(0px); + } + } + + > .actions { + position: absolute; + @include S(bottom, 40px); + + display: grid; + @include S(grid-gap, 15px); + grid-auto-flow: column; + + button { + @include SuperSmallText; + } + .report { + background-color: $accentColorDark; + } + } + } + } +} diff --git a/src/css/ingame_hud/puzzle_dlc_logo.scss b/src/css/ingame_hud/puzzle_dlc_logo.scss new file mode 100644 index 00000000..c2a64607 --- /dev/null +++ b/src/css/ingame_hud/puzzle_dlc_logo.scss @@ -0,0 +1,24 @@ +#ingame_HUD_PuzzleDLCLogo { + position: absolute; + @include S(width, 120px); + @include S(height, 40px); + @include S(left, 40px); + @include S(top, 7px); + + & { + /* @load-async */ + background: uiResource("puzzle_dlc_logo.png") center center / contain no-repeat; + } + + @include DarkThemeOverride { + & { + /* @load-async */ + background: uiResource("puzzle_dlc_logo_inverse.png") center center / contain no-repeat; + } + } + + &.china { + /* @load-async */ + background: uiResource("puzzle_dlc_logo_china.png") center center / contain no-repeat !important; + } +} diff --git a/src/css/ingame_hud/puzzle_editor_controls.scss b/src/css/ingame_hud/puzzle_editor_controls.scss new file mode 100644 index 00000000..7ce76b41 --- /dev/null +++ b/src/css/ingame_hud/puzzle_editor_controls.scss @@ -0,0 +1,36 @@ +#ingame_HUD_PuzzleEditorControls { + position: absolute; + + @include S(top, 70px); + @include S(left, 10px); + + display: flex; + flex-direction: column; + @include SuperDuperSmallText; + @include S(width, 200px); + + > span { + @include S(margin-bottom, 10px); + + strong { + font-weight: bold; + } + } + + @include DarkThemeInvert; +} + +#ingame_HUD_PuzzleEditorTitle { + position: absolute; + + @include S(top, 18px); + left: 50%; + transform: translateX(-50%); + text-transform: uppercase; + @include Heading; + text-align: center; + + @include DarkThemeOverride { + color: #eee; + } +} diff --git a/src/css/ingame_hud/puzzle_editor_review.scss b/src/css/ingame_hud/puzzle_editor_review.scss new file mode 100644 index 00000000..523d8025 --- /dev/null +++ b/src/css/ingame_hud/puzzle_editor_review.scss @@ -0,0 +1,50 @@ +#ingame_HUD_PuzzleEditorReview { + position: absolute; + @include S(top, 17px); + @include S(right, 10px); + + display: flex; + flex-direction: column; + align-items: flex-end; + backdrop-filter: blur(D(1px)); + padding: D(3px); + + > .button { + @include ButtonText; + @include IncreasedClickArea(0px); + pointer-events: all; + cursor: pointer; + position: relative; + color: #333438; + transition: all 0.12s ease-in-out; + text-transform: uppercase; + transition-property: opacity, transform; + @include PlainText; + @include S(padding-right, 25px); + opacity: 1; + + @include DarkThemeInvert; + + &:hover { + opacity: 0.9 !important; + } + + &.pressed { + transform: scale(0.95) !important; + } + + & { + /* @load-async */ + background: uiResource("icons/state_next_button.png") right center / D(15px) no-repeat; + } + } + + > .content { + @include SuperDuperSmallText; + @include S(width, 180px); + @include S(padding-right, 25px); + text-align: right; + text-transform: uppercase; + color: $accentColorDark; + } +} diff --git a/src/css/ingame_hud/puzzle_editor_settings.scss b/src/css/ingame_hud/puzzle_editor_settings.scss new file mode 100644 index 00000000..9d093c42 --- /dev/null +++ b/src/css/ingame_hud/puzzle_editor_settings.scss @@ -0,0 +1,71 @@ +#ingame_HUD_PuzzleEditorSettings { + position: absolute; + background: $ingameHudBg; + @include S(padding, 10px); + @include S(bottom, 60px); + @include S(left, 10px); + + @include SuperSmallText; + color: #eee; + display: flex; + flex-direction: column; + @include S(border-radius, $globalBorderRadius); + + > .section { + > label { + text-transform: uppercase; + } + + .plusMinus { + @include S(margin-top, 5px); + display: grid; + grid-template-columns: 1fr auto auto auto; + align-items: center; + @include S(grid-gap, 5px); + + label { + @include S(margin-right, 10px); + } + + button { + @include PlainText; + @include S(padding, 0); + display: flex; + align-items: center; + justify-content: center; + @include S(width, 15px); + @include S(height, 15px); + @include IncreasedClickArea(0px); + } + + .value { + text-align: center; + @include S(min-width, 15px); + } + } + + > .buttons { + > .buttonBar { + display: flex; + align-items: center; + @include S(margin-top, 10px); + > button { + @include S(margin-right, 4px); + @include SuperSmallText; + &:last-child { + margin-right: 0; + } + } + } + + > .buildingsButton { + display: grid; + align-items: center; + @include S(margin-top, 4px); + > button { + @include SuperSmallText; + } + } + } + } +} diff --git a/src/css/ingame_hud/puzzle_play_metadata.scss b/src/css/ingame_hud/puzzle_play_metadata.scss new file mode 100644 index 00000000..d0675b13 --- /dev/null +++ b/src/css/ingame_hud/puzzle_play_metadata.scss @@ -0,0 +1,129 @@ +#ingame_HUD_PuzzlePlayMetadata { + position: absolute; + + @include S(top, 70px); + @include S(left, 10px); + + display: flex; + flex-direction: column; + @include S(width, 200px); + + > .info { + display: flex; + flex-direction: column; + @include SuperSmallText; + @include S(margin-bottom, 5px); + + > label { + text-transform: uppercase; + @include SuperDuperSmallText; + color: $accentColorDark; + } + > span { + display: flex; + color: darken($accentColorDark, 25); + @include SuperSmallText; + @include DarkThemeOverride { + color: lighten($accentColorDark, 15); + } + } + } + + > .plays { + display: flex; + align-items: center; + justify-self: end; + align-self: end; + flex-direction: row; + @include S(margin-bottom, 10px); + opacity: 0.8; + @include DarkThemeInvert; + @include DarkThemeOverride { + opacity: 0.8; + } + + > .downloads { + @include SuperSmallText; + color: #000; + align-self: start; + justify-self: start; + font-weight: bold; + @include S(margin-right, 10px); + @include S(padding-left, 14px); + opacity: 0.7; + display: inline-flex; + align-items: center; + justify-content: center; + + & { + /* @load-async */ + background: uiResource("icons/puzzle_plays.png") #{D(2px)} center / #{D(8px)} #{D(8px)} no-repeat; + } + } + + > .likes { + @include SuperSmallText; + align-items: center; + justify-content: center; + color: #000; + align-self: start; + justify-self: start; + font-weight: bold; + @include S(padding-left, 14px); + opacity: 0.7; + & { + /* @load-async */ + background: uiResource("icons/puzzle_upvotes.png") #{D(2px)} center / #{D(8px)} #{D(8px)} no-repeat; + } + } + } + + > .key { + button { + @include S(margin-top, 2px); + } + } + + button { + @include SuperSmallText; + align-self: start; + @include S(min-width, 50px); + + &.report { + background-color: $accentColorDark; + @include SuperDuperSmallText; + } + } + + > .buttons { + display: flex; + flex-direction: column; + + > button { + @include S(margin-bottom, 4px); + } + } +} + +#ingame_HUD_PuzzlePlayTitle { + position: absolute; + + @include S(top, 18px); + left: 50%; + transform: translateX(-50%); + text-transform: uppercase; + @include Heading; + text-align: center; + + display: flex; + flex-direction: column; + + > .name { + @include PlainText; + opacity: 0.5; + } + + @include DarkThemeOverride { + color: #eee; + } +} diff --git a/src/css/ingame_hud/puzzle_play_settings.scss b/src/css/ingame_hud/puzzle_play_settings.scss new file mode 100644 index 00000000..b53d0829 --- /dev/null +++ b/src/css/ingame_hud/puzzle_play_settings.scss @@ -0,0 +1,23 @@ +#ingame_HUD_PuzzlePlaySettings { + position: absolute; + background: $ingameHudBg; + @include S(padding, 10px); + @include S(bottom, 60px); + @include S(left, 10px); + + @include SuperSmallText; + color: #eee; + display: flex; + flex-direction: column; + @include S(border-radius, $globalBorderRadius); + + > .section { + display: grid; + @include S(grid-gap, 5px); + grid-auto-flow: row; + + > button { + @include SuperSmallText; + } + } +} diff --git a/src/css/main.scss b/src/css/main.scss index 15648e3a..1c604be6 100644 --- a/src/css/main.scss +++ b/src/css/main.scss @@ -29,6 +29,7 @@ @import "states/about"; @import "states/mobile_warning"; @import "states/changelog"; +@import "states/puzzle_menu"; @import "ingame_hud/buildings_toolbar"; @import "ingame_hud/building_placer"; @@ -56,12 +57,21 @@ @import "ingame_hud/standalone_advantages"; @import "ingame_hud/cat_memes"; @import "ingame_hud/achievements"; +@import "ingame_hud/puzzle_back_to_menu"; +@import "ingame_hud/puzzle_editor_review"; +@import "ingame_hud/puzzle_dlc_logo"; +@import "ingame_hud/puzzle_editor_controls"; +@import "ingame_hud/puzzle_editor_settings"; +@import "ingame_hud/puzzle_play_settings"; +@import "ingame_hud/puzzle_play_metadata"; +@import "ingame_hud/puzzle_complete_notification"; // prettier-ignore $elements: // Base ingame_Canvas, ingame_VignetteOverlay, +ingame_HUD_PuzzleDLCLogo, // Ingame overlays ingame_HUD_Waypoints, @@ -72,6 +82,14 @@ ingame_HUD_PlacerVariants, ingame_HUD_PinnedShapes, ingame_HUD_GameMenu, ingame_HUD_KeybindingOverlay, +ingame_HUD_PuzzleBackToMenu, +ingame_HUD_PuzzleEditorReview, +ingame_HUD_PuzzleEditorControls, +ingame_HUD_PuzzleEditorTitle, +ingame_HUD_PuzzleEditorSettings, +ingame_HUD_PuzzlePlaySettings, +ingame_HUD_PuzzlePlayMetadata, +ingame_HUD_PuzzlePlayTitle, ingame_HUD_Notifications, ingame_HUD_DebugInfo, ingame_HUD_EntityDebugger, @@ -96,6 +114,7 @@ ingame_HUD_Statistics, ingame_HUD_ShapeViewer, ingame_HUD_StandaloneAdvantages, ingame_HUD_UnlockNotification, +ingame_HUD_PuzzleCompleteNotification, ingame_HUD_SettingsMenu, ingame_HUD_ModalDialogs, ingame_HUD_CatMemes; @@ -115,6 +134,8 @@ body.uiHidden { #ingame_HUD_PlacementHints, #ingame_HUD_GameMenu, #ingame_HUD_PinnedShapes, + #ingame_HUD_PuzzleBackToMenu, + #ingame_HUD_PuzzleEditorReview, #ingame_HUD_Notifications, #ingame_HUD_TutorialHints, #ingame_HUD_Waypoints, diff --git a/src/css/mixins.scss b/src/css/mixins.scss index 43f7a259..888d84d6 100644 --- a/src/css/mixins.scss +++ b/src/css/mixins.scss @@ -15,15 +15,15 @@ $hardwareAcc: null; // ---------------------------------------- /** Increased click area for this element, helpful on mobile */ @mixin IncreasedClickArea($size) { - &::after { - content: ""; - position: absolute; - top: #{D(-$size)}; - bottom: #{D(-$size)}; - left: #{D(-$size)}; - right: #{D(-$size)}; - // background: rgba(255, 0, 0, 0.3); - } + // &::after { + // content: ""; + // position: absolute; + // top: #{D(-$size)}; + // bottom: #{D(-$size)}; + // left: #{D(-$size)}; + // right: #{D(-$size)}; + // // background: rgba(255, 0, 0, 0.3); + // } } button, .increasedClickArea { diff --git a/src/css/resources.scss b/src/css/resources.scss index 7a636a6e..5f3a95e2 100644 --- a/src/css/resources.scss +++ b/src/css/resources.scss @@ -1,11 +1,13 @@ $buildings: belt, cutter, miner, mixer, painter, rotater, balancer, stacker, trash, underground_belt, wire, constant_signal, logic_gate, lever, filter, wire_tunnel, display, virtual_processor, reader, storage, - transistor, analyzer, comparator, item_producer; + transistor, analyzer, comparator, item_producer, constant_producer, goal_acceptor, block; @each $building in $buildings { [data-icon="building_icons/#{$building}.png"] { /* @load-async */ - background-image: uiResource("res/ui/building_icons/#{$building}.png") !important; + .icon { + background-image: uiResource("res/ui/building_icons/#{$building}.png") !important; + } } } @@ -13,7 +15,8 @@ $buildingsAndVariants: belt, balancer, underground_belt, underground_belt-tier2, cutter, cutter-quad, rotater, rotater-ccw, stacker, mixer, painter-double, painter-quad, trash, storage, reader, rotater-rotate180, display, constant_signal, wire, wire_tunnel, logic_gate-or, logic_gate-not, logic_gate-xor, analyzer, virtual_processor-rotater, virtual_processor-unstacker, item_producer, - virtual_processor-stacker, virtual_processor-painter, wire-second, painter, painter-mirrored, comparator; + constant_producer, virtual_processor-stacker, virtual_processor-painter, wire-second, painter, + painter-mirrored, comparator, goal_acceptor, block; @each $building in $buildingsAndVariants { [data-icon="building_tutorials/#{$building}.png"] { /* @load-async */ @@ -81,7 +84,7 @@ $achievements: reset, hidden, belt500Tiles, blueprint100k, blueprint1m, complete } $languages: en, de, cs, da, et, es-419, fr, it, pt-BR, sv, tr, el, ru, uk, zh-TW, zh-CN, nb, mt-MT, ar, nl, vi, - th, hu, pl, ja, kor, no, pt-PT, fi, ro; + th, hu, pl, ja, kor, no, pt-PT, fi, ro, he; @each $language in $languages { [data-languageicon="#{$language}"] { diff --git a/src/css/states/main_menu.scss b/src/css/states/main_menu.scss index 9c2ad37f..07f880f6 100644 --- a/src/css/states/main_menu.scss +++ b/src/css/states/main_menu.scss @@ -94,9 +94,11 @@ @include S(grid-column-gap, 10px); display: grid; - grid-template-columns: 1fr; - &.demo { + &[data-columns="1"] { + grid-template-columns: 1fr; + } + &[data-columns="2"] { grid-template-columns: 1fr 1fr; } @@ -189,7 +191,7 @@ .updateLabel { position: absolute; transform: translateX(50%) rotate(-5deg); - color: #3291e9; + color: #ff590b; @include Heading; font-weight: bold; @include S(right, 40px); @@ -229,9 +231,75 @@ } } + .puzzleContainer { + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + background: $colorBlueBright; + grid-row: 1 / 2; + grid-column: 2 / 3; + position: relative; + @include S(padding, 20px); + @include S(border-radius, $globalBorderRadius); + + > .badge { + color: #fff; + text-transform: uppercase; + font-weight: bold; + position: absolute; + @include S(top, 10px); + @include S(right, 10px); + + transform: translateX(50%) rotate(10deg); + @include Heading; + font-weight: bold; + + @include InlineAnimation(1.3s ease-in-out infinite) { + 50% { + transform: translateX(50%) rotate(12deg) scale(1.1); + } + } + } + + > .hint { + @include SuperDuperSmallText; + @include S(margin-top, 10px); + @include S(width, 200px); + } + + > .dlcLogo { + @include S(width, 190px); + } + + > button { + @include S(margin-top, 20px); + @include Heading; + @include S(padding, 10px, 30px); + background-color: #333; + color: #fff; + } + + &.notOwned { + p { + @include PlainText; + color: #333; + @include S(margin-top, 10px); + @include S(width, 190px); + } + > button { + box-sizing: border-box; + @include S(margin-top, 10px); + @include S(width, 190px); + @include S(padding, 10px, 20px); + } + } + } + .mainContainer { display: flex; align-items: center; + grid-row: 1 / 2; justify-content: center; flex-direction: column; background: #fafafa; @@ -248,6 +316,16 @@ align-items: center; } + .modeButtons { + display: grid; + grid-template-columns: repeat(2, 1fr); + @include S(grid-column-gap, 10px); + align-items: start; + height: 100%; + width: 100%; + box-sizing: border-box; + } + .browserWarning { @include S(margin-bottom, 10px); background-color: $colorRedBright; @@ -291,6 +369,18 @@ @include S(margin-left, 15px); } + .playModeButton { + @include IncreasedClickArea(0px); + @include S(margin-top, 15px); + @include S(margin-left, 15px); + } + + .editModeButton { + @include IncreasedClickArea(0px); + @include S(margin-top, 15px); + @include S(margin-left, 15px); + } + .savegames { @include S(max-height, 105px); overflow-y: auto; @@ -445,6 +535,27 @@ } } + .bottomContainer { + display: flex; + align-items: center; + justify-content: center; + flex-direction: row; + @include S(padding-top, 10px); + height: 100%; + width: 100%; + box-sizing: border-box; + + .buttons { + display: grid; + grid-template-columns: repeat(2, 1fr); + @include S(grid-column-gap, 10px); + align-items: start; + height: 100%; + width: 100%; + box-sizing: border-box; + } + } + .footer { display: grid; flex-grow: 1; diff --git a/src/css/states/preload.scss b/src/css/states/preload.scss index 514c60d2..2e14abd6 100644 --- a/src/css/states/preload.scss +++ b/src/css/states/preload.scss @@ -17,7 +17,7 @@ @include S(border-radius, 3px); @include DarkThemeOverride { - background: #424242; + background: #33343c; } .version { diff --git a/src/css/states/puzzle_menu.scss b/src/css/states/puzzle_menu.scss new file mode 100644 index 00000000..e2deacf0 --- /dev/null +++ b/src/css/states/puzzle_menu.scss @@ -0,0 +1,306 @@ +#state_PuzzleMenuState { + > .headerBar { + display: grid; + grid-template-columns: 1fr auto; + align-items: center; + + > h1 { + justify-self: start; + } + + .createPuzzle { + background-color: $colorGreenBright; + @include S(margin-left, 5px); + } + } + + > .container { + > .mainContent { + overflow: hidden; + + > .categoryChooser { + > .categories { + display: grid; + grid-auto-columns: 1fr; + grid-auto-flow: column; + @include S(grid-gap, 2px); + @include S(padding-right, 10px); + @include S(margin-bottom, 5px); + + .category { + background: $accentColorBright; + border-radius: 0; + color: $accentColorDark; + transition: all 0.12s ease-in-out; + transition-property: opacity, background-color, color; + + &:first-child { + @include S(border-top-left-radius, $globalBorderRadius); + @include S(border-bottom-left-radius, $globalBorderRadius); + } + &:last-child { + border-top-right-radius: $globalBorderRadius; + border-bottom-right-radius: $globalBorderRadius; + } + + &.active { + background: $colorBlueBright; + opacity: 1 !important; + color: #fff; + cursor: default; + } + + @include DarkThemeOverride { + background: $accentColorDark; + color: #bbbbc4; + + &.active { + background: $colorBlueBright; + color: #fff; + } + } + + &.root { + @include S(padding-top, 10px); + @include S(padding-bottom, 10px); + @include Text; + } + &.child { + @include PlainText; + } + } + } + } + + > .puzzles { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(D(240px), 1fr)); + @include S(grid-auto-rows, 65px); + @include S(grid-gap, 7px); + @include S(margin-top, 10px); + @include S(padding-right, 4px); + @include S(height, 320px); + overflow-y: scroll; + pointer-events: all; + position: relative; + + > .puzzle { + width: 100%; + @include S(height, 65px); + background: #f3f3f8; + @include S(border-radius, $globalBorderRadius); + + display: grid; + grid-template-columns: auto 1fr; + grid-template-rows: D(15px) D(15px) 1fr; + @include S(padding, 5px); + @include S(grid-column-gap, 5px); + box-sizing: border-box; + pointer-events: all; + cursor: pointer; + position: relative; + @include S(padding-left, 10px); + + @include DarkThemeOverride { + background: rgba(0, 0, 10, 0.2); + } + + @include InlineAnimation(0.12s ease-in-out) { + 0% { + opacity: 0; + } + 100% { + opacity: 1; + } + } + + &:hover { + background: #f0f0f8; + } + + > .title { + grid-column: 2 / 3; + grid-row: 1 / 2; + @include PlainText; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + align-self: center; + justify-self: start; + width: 100%; + box-sizing: border-box; + @include S(padding, 2px, 5px); + @include S(height, 17px); + } + + > .author { + grid-column: 2 / 2; + grid-row: 2 / 3; + @include SuperSmallText; + color: $accentColorDark; + align-self: center; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + @include S(padding, 2px, 5px); + } + + > .icon { + grid-column: 1 / 2; + grid-row: 1 / 4; + align-self: center; + justify-self: center; + @include S(width, 45px); + @include S(height, 45px); + + canvas { + width: 100%; + height: 100%; + } + } + + > button.delete { + position: absolute; + @include S(top, 5px); + @include S(right, 5px); + background-repeat: no-repeat; + background-position: center center; + background-size: 70%; + background-color: transparent !important; + @include S(width, 20px); + @include S(height, 20px); + padding: 0; + opacity: 0.7; + @include DarkThemeInvert; + + & { + /* @load-async */ + background-image: uiResource("icons/delete.png") !important; + } + } + + > .stats { + grid-column: 2 / 3; + grid-row: 3 / 4; + display: flex; + align-items: center; + justify-self: end; + justify-content: center; + align-self: end; + @include S(height, 14px); + + > .downloads { + @include SuperSmallText; + color: #000; + font-weight: bold; + @include S(margin-right, 5px); + @include S(padding-left, 12px); + opacity: 0.7; + display: inline-flex; + align-items: center; + justify-content: center; + @include DarkThemeInvert; + + & { + /* @load-async */ + background: uiResource("icons/puzzle_plays.png") #{D(2px)} #{D(2.5px)} / #{D( + 8px + )} #{D(8px)} no-repeat; + } + } + + > .likes { + @include SuperSmallText; + align-items: center; + justify-content: center; + color: #000; + font-weight: bold; + @include S(padding-left, 14px); + opacity: 0.7; + @include DarkThemeInvert; + + & { + /* @load-async */ + background: uiResource("icons/puzzle_upvotes.png") #{D(2px)} #{D(2.4px)} / #{D( + 9px + )} #{D(9px)} no-repeat; + } + } + + > .difficulty { + @include SuperSmallText; + align-items: center; + justify-content: center; + color: #000; + font-weight: bold; + @include S(margin-right, 3px); + opacity: 0.7; + text-transform: uppercase; + + &.stage--easy { + color: $colorGreenBright; + } + &.stage--medium { + color: $colorOrangeBright; + } + &.stage--hard { + color: $colorRedBright; + } + } + } + + &.completed { + > .icon, + > .stats, + > .author, + > .title { + opacity: 0.3; + } + + background: #fafafa; + + @include DarkThemeOverride { + background: rgba(0, 0, 0, 0.05); + } + + &::after { + content: ""; + position: absolute; + @include S(top, 10px); + @include S(right, 10px); + @include S(width, 30px); + @include S(height, 30px); + opacity: 0.1; + + & { + /* @load-async */ + background: uiResource("icons/puzzle_complete_indicator.png") center center / + contain no-repeat; + } + } + @include DarkThemeOverride { + &::after { + /* @load-async */ + background: uiResource("icons/puzzle_complete_indicator_inverse.png") center + center / contain no-repeat; + } + } + } + } + + > .loader, + > .empty { + display: flex; + align-items: center; + color: $accentColorDark; + justify-content: center; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + } + } + } + } +} diff --git a/src/css/variables.scss b/src/css/variables.scss index d2798f41..c7b7c17c 100644 --- a/src/css/variables.scss +++ b/src/css/variables.scss @@ -18,8 +18,10 @@ $textLineHeight: 21px; $plainTextFontSize: 13px; $plainTextLineHeight: 17px; -$supersmallTextFontSize: 10px; -$supersmallTextLineHeight: 13px; +$superDuperSmallTextFontSize: 8px; +$superDuperSmallTextLineHeight: 9px; +$superSmallTextFontSize: 10px; +$superSmallTextLineHeight: 13px; $buttonFontSize: 14px; $buttonLineHeight: 18px; @@ -33,6 +35,7 @@ $accentColorDark: #7d808a; $colorGreenBright: #66bb6a; $colorBlueBright: rgb(74, 151, 223); $colorRedBright: #ef5072; +$colorOrangeBright: #ef9d50; $themeColor: #393747; $ingameHudBg: rgba(#333438, 0.9); @@ -76,8 +79,16 @@ $mainFontScale: 1; // } } +@mixin SuperDuperSmallText { + @include ScaleFont($superDuperSmallTextFontSize, $superDuperSmallTextLineHeight); + font-weight: $mainFontWeight; + font-family: $mainFont; + letter-spacing: $mainFontSpacing; + @include DebugText(green); +} + @mixin SuperSmallText { - @include ScaleFont($supersmallTextFontSize, $supersmallTextLineHeight); + @include ScaleFont($superSmallTextFontSize, $superSmallTextLineHeight); font-weight: $mainFontWeight; font-family: $mainFont; letter-spacing: $mainFontSpacing; diff --git a/src/js/application.js b/src/js/application.js index 858d97cd..8f9ec316 100644 --- a/src/js/application.js +++ b/src/js/application.js @@ -32,6 +32,9 @@ import { SettingsState } from "./states/settings"; import { ShapezGameAnalytics } from "./platform/browser/game_analytics"; import { RestrictionManager } from "./core/restriction_manager"; import { AchievementsState } from "./states/achievements"; +import { PuzzleMenuState } from "./states/puzzle_menu"; +import { ClientAPI } from "./platform/api"; +import { LoginState } from "./states/login"; /** * @typedef {import("./platform/achievement_provider").AchievementProviderInterface} AchievementProviderInterface @@ -73,6 +76,7 @@ export class Application { this.savegameMgr = new SavegameManager(this); this.inputMgr = new InputDistributor(this); this.backgroundResourceLoader = new BackgroundResourcesLoader(this); + this.clientApi = new ClientAPI(this); // Restrictions (Like demo etc) this.restrictionMgr = new RestrictionManager(this); @@ -161,6 +165,8 @@ export class Application { AboutState, ChangelogState, AchievementsState, + PuzzleMenuState, + LoginState, ]; for (let i = 0; i < states.length; ++i) { diff --git a/src/js/changelog.js b/src/js/changelog.js index 61c1c79a..24b5d725 100644 --- a/src/js/changelog.js +++ b/src/js/changelog.js @@ -1,12 +1,50 @@ export const CHANGELOG = [ { - version: "1.3.1", - date: "beta", + version: "1.4.2", + date: "24.06.2021", entries: [ - "Fixed savegames getting corrupt in rare conditions", - "Fixed game crashing sometimes since the achievements update", + "Puzzle DLC: Goal acceptors now reset after getting no items for a while (This should prevent being able to 'cheat' puzzles) (by Sense101)", + "Puzzle DLC: Added button to clear all buildings / reset the puzzle (by Sense101)", + "Puzzle DLC: Allow copy-paste in puzzle mode (by Sense101)", + "Updated translations", ], }, + { + version: "1.4.1", + date: "22.06.2021", + entries: [ + "The Puzzle DLC is now available on Steam!", + "The Soundtrack is now also available to wishlist and will be released within the next days, including the new music from the Puzzle DLC!", + ], + }, + { + version: "1.4.0", + date: "04.06.2021", + entries: [ + "Belts in blueprints should now always paste correctly", + "You can now clear belts by selecting them and then pressing 'B'", + "Preparations for the Puzzle DLC, coming June 22nd!", + ], + }, + { + version: "1.3.1", + date: "16.04.2021", + entries: G_CHINA_VERSION + ? [ + "第13关的交付目标更改为:中国古代指南针。(感谢玩家:凯风入心 创作并提供", + "第17关的交付目标更改为:永乐通宝。(感谢玩家:金天赐 创作并提供", + "第22关的交付目标更改为:凤凰。(感谢玩家:我没得眼镜 创作并提供", + "第23关的交付目标更改为:古代车轮。(感谢玩家:我没得眼镜 创作并提供", + "第24关的交付目标更改为:大熊猫。(感谢玩家:窝囸倪现任 创作并提供", + + "修复了一些特定情况下偶尔会发生的存档损坏问题", + "修复了成就更新后有时候游戏崩溃的问题", + ] + : [ + "Fixed savegames getting corrupt in rare conditions", + "Fixed game crashing sometimes since the achievements update", + ], + }, { version: "1.3.0", date: "12.03.2020", diff --git a/src/js/core/animation_frame.js b/src/js/core/animation_frame.js index eeefb4b0..6aa629a5 100644 --- a/src/js/core/animation_frame.js +++ b/src/js/core/animation_frame.js @@ -51,9 +51,12 @@ export class AnimationFrame { dt = resetDtMs; } - this.frameEmitted.dispatch(dt); + try { + this.frameEmitted.dispatch(dt); + } catch (ex) { + console.error(ex); + } this.lastTime = time; - window.requestAnimationFrame(this.boundMethod); } } diff --git a/src/js/core/background_resources_loader.js b/src/js/core/background_resources_loader.js index 46ec20e6..316619c4 100644 --- a/src/js/core/background_resources_loader.js +++ b/src/js/core/background_resources_loader.js @@ -12,8 +12,13 @@ import { cachebust } from "./cachebust"; const logger = createLogger("background_loader"); +export function getLogoSprite() { + // @todo: ugh, in a hurry + return G_WEGAME_VERSION ? "logo_wegame.png" : G_CHINA_VERSION ? "logo_cn.png" : "logo.png"; +} + const essentialMainMenuSprites = [ - G_CHINA_VERSION ? "logo_cn.png" : "logo.png", + getLogoSprite(), ...G_ALL_UI_IMAGES.filter(src => src.startsWith("ui/") && src.indexOf(".gif") < 0), ]; const essentialMainMenuSounds = [ diff --git a/src/js/core/config.js b/src/js/core/config.js index d5dc7089..a4793384 100644 --- a/src/js/core/config.js +++ b/src/js/core/config.js @@ -18,6 +18,7 @@ export const THIRDPARTY_URLS = { shapeViewer: "https://viewer.shapez.io", standaloneStorePage: "https://store.steampowered.com/app/1318690/shapezio/", + puzzleDlcStorePage: "https://store.steampowered.com/app/1625400/shapezio__Puzzle_DLC", levelTutorialVideos: { 21: "https://www.youtube.com/watch?v=0nUfRLMCcgo&", @@ -71,6 +72,13 @@ export const globalConfig = { readerAnalyzeIntervalSeconds: 10, + goalAcceptorItemsRequired: 10, + goalAcceptorsPerProducer: 5, + puzzleModeSpeed: 3, + puzzleMinBoundsSize: 2, + puzzleMaxBoundsSize: 20, + puzzleValidationDurationSeconds: 30, + buildingSpeeds: { cutter: 1 / 4, cutterQuad: 1 / 4, @@ -93,7 +101,7 @@ export const globalConfig = { gameSpeed: 1, warmupTimeSecondsFast: 0.5, - warmupTimeSecondsRegular: 3, + warmupTimeSecondsRegular: 1.5, smoothing: { smoothMainCanvas: smoothCanvas && true, diff --git a/src/js/core/config.local.template.js b/src/js/core/config.local.template.js index 5e3cdad6..fc71c01e 100644 --- a/src/js/core/config.local.template.js +++ b/src/js/core/config.local.template.js @@ -62,6 +62,9 @@ export default { // Allows unlocked achievements to be logged to console in the local build // testAchievements: true, // ----------------------------------------------------------------------------------- + // Enables use of (some) existing flags within the puzzle mode context + // testPuzzleMode: true, + // ----------------------------------------------------------------------------------- // Disables the automatic switch to an overview when zooming out // disableMapOverview: true, // ----------------------------------------------------------------------------------- diff --git a/src/js/core/error_handler.js b/src/js/core/error_handler.js index 686e4e4e..c149ba76 100644 --- a/src/js/core/error_handler.js +++ b/src/js/core/error_handler.js @@ -123,4 +123,6 @@ function catchErrors(message, source, lineno, colno, error) { return true; } -window.onerror = catchErrors; +if (!G_IS_DEV) { + window.onerror = catchErrors; +} diff --git a/src/js/core/global_registries.js b/src/js/core/global_registries.js index ad45850c..723bf567 100644 --- a/src/js/core/global_registries.js +++ b/src/js/core/global_registries.js @@ -5,6 +5,7 @@ import { Factory } from "./factory"; * @typedef {import("../game/time/base_game_speed").BaseGameSpeed} BaseGameSpeed * @typedef {import("../game/component").Component} Component * @typedef {import("../game/base_item").BaseItem} BaseItem + * @typedef {import("../game/game_mode").GameMode} GameMode * @typedef {import("../game/meta_building").MetaBuilding} MetaBuilding @@ -19,6 +20,9 @@ export let gBuildingsByCategory = null; /** @type {FactoryTemplate} */ export let gComponentRegistry = new Factory("component"); +/** @type {FactoryTemplate} */ +export let gGameModeRegistry = new Factory("gameMode"); + /** @type {FactoryTemplate} */ export let gGameSpeedRegistry = new Factory("gamespeed"); diff --git a/src/js/core/modal_dialog_elements.js b/src/js/core/modal_dialog_elements.js index 5f0ed59f..ee552aa9 100644 --- a/src/js/core/modal_dialog_elements.js +++ b/src/js/core/modal_dialog_elements.js @@ -267,7 +267,7 @@ export class Dialog { * Dialog which simply shows a loading spinner */ export class DialogLoading extends Dialog { - constructor(app) { + constructor(app, text = "") { super({ app, title: "", @@ -279,6 +279,8 @@ export class DialogLoading extends Dialog { // Loading dialog can not get closed with back button this.inputReciever.backButton.removeAll(); this.inputReciever.context = "dialog-loading"; + + this.text = text; } createElement() { @@ -287,6 +289,13 @@ export class DialogLoading extends Dialog { elem.classList.add("loadingDialog"); this.element = elem; + if (this.text) { + const text = document.createElement("div"); + text.classList.add("text"); + text.innerText = this.text; + elem.appendChild(text); + } + const loader = document.createElement("div"); loader.classList.add("prefab_LoadingTextWithAnim"); loader.classList.add("loadingIndicator"); @@ -309,7 +318,7 @@ export class DialogOptionChooser extends Dialog {
- ${iconHtml} + ${iconHtml} ${text} ${descHtml}
@@ -444,7 +453,7 @@ export class DialogWithForm extends Dialog { for (let i = 0; i < this.formElements.length; ++i) { const elem = this.formElements[i]; elem.bindEvents(div, this.clickDetectors); - elem.valueChosen.add(this.closeRequested.dispatch, this.closeRequested); + // elem.valueChosen.add(this.closeRequested.dispatch, this.closeRequested); elem.valueChosen.add(this.valueChosen.dispatch, this.valueChosen); } diff --git a/src/js/core/modal_dialog_forms.js b/src/js/core/modal_dialog_forms.js index 1c5b1986..aac81d82 100644 --- a/src/js/core/modal_dialog_forms.js +++ b/src/js/core/modal_dialog_forms.js @@ -117,6 +117,11 @@ export class FormElementInput extends FormElement { return this.element.value; } + setValue(value) { + this.element.value = value; + this.updateErrorState(); + } + focus() { this.element.focus(); } diff --git a/src/js/core/rectangle.js b/src/js/core/rectangle.js index f17825ca..bd3421d9 100644 --- a/src/js/core/rectangle.js +++ b/src/js/core/rectangle.js @@ -44,6 +44,15 @@ export class Rectangle { return new Rectangle(left, top, right - left, bottom - top); } + /** + * + * @param {number} width + * @param {number} height + */ + static centered(width, height) { + return new Rectangle(-Math.ceil(width / 2), -Math.ceil(height / 2), width, height); + } + /** * Returns if a intersects b * @param {Rectangle} a @@ -72,7 +81,7 @@ export class Rectangle { /** * Returns if this rectangle is equal to the other while taking an epsilon into account * @param {Rectangle} other - * @param {number} epsilon + * @param {number} [epsilon] */ equalsEpsilon(other, epsilon) { return ( @@ -287,6 +296,15 @@ export class Rectangle { return Rectangle.fromTRBL(top, right, bottom, left); } + /** + * Returns whether the rectangle fully intersects the given rectangle + * @param {Rectangle} rect + */ + intersectsFully(rect) { + const intersection = this.getIntersection(rect); + return intersection && Math.abs(intersection.w * intersection.h - rect.w * rect.h) < 0.001; + } + /** * Returns the union of this rectangle with another * @param {Rectangle} rect diff --git a/src/js/core/signal.js b/src/js/core/signal.js index 7daae4ea..2dbc9f93 100644 --- a/src/js/core/signal.js +++ b/src/js/core/signal.js @@ -17,6 +17,17 @@ export class Signal { ++this.modifyCount; } + /** + * Adds a new signal listener + * @param {function} receiver + * @param {object} scope + */ + addToTop(receiver, scope = null) { + assert(receiver, "receiver is null"); + this.receivers.unshift({ receiver, scope }); + ++this.modifyCount; + } + /** * Dispatches the signal * @param {...any} payload diff --git a/src/js/core/state_manager.js b/src/js/core/state_manager.js index 3c49ada9..2e55f5d4 100644 --- a/src/js/core/state_manager.js +++ b/src/js/core/state_manager.js @@ -89,10 +89,15 @@ export class StateManager { const dialogParent = document.createElement("div"); dialogParent.classList.add("modalDialogParent"); document.body.appendChild(dialogParent); + try { + this.currentState.internalEnterCallback(payload); + } catch (ex) { + console.error(ex); + throw ex; + } this.app.sound.playThemeMusic(this.currentState.getThemeMusic()); - this.currentState.internalEnterCallback(payload); this.currentState.onResized(this.app.screenWidth, this.app.screenHeight); this.app.analytics.trackStateEnter(key); diff --git a/src/js/core/utils.js b/src/js/core/utils.js index 6eed9c34..842e6151 100644 --- a/src/js/core/utils.js +++ b/src/js/core/utils.js @@ -573,12 +573,14 @@ export function round1DigitLocalized(speed, separator = T.global.decimalSeparato * @param {string=} separator The decimal separator for numbers like 50.1 (separator='.') */ export function formatItemsPerSecond(speed, double = false, separator = T.global.decimalSeparator) { - return speed === 1.0 - ? T.ingame.buildingPlacement.infoTexts.oneItemPerSecond - : T.ingame.buildingPlacement.infoTexts.itemsPerSecond.replace( - "", - round2Digits(speed).toString().replace(".", separator) - ) + (double ? " " + T.ingame.buildingPlacement.infoTexts.itemsPerSecondDouble : ""); + return ( + (speed === 1.0 + ? T.ingame.buildingPlacement.infoTexts.oneItemPerSecond + : T.ingame.buildingPlacement.infoTexts.itemsPerSecond.replace( + "", + round2Digits(speed).toString().replace(".", separator) + )) + (double ? " " + T.ingame.buildingPlacement.infoTexts.itemsPerSecondDouble : "") + ); } /** diff --git a/src/js/game/achievement_proxy.js b/src/js/game/achievement_proxy.js index ed05b700..9077b283 100644 --- a/src/js/game/achievement_proxy.js +++ b/src/js/game/achievement_proxy.js @@ -32,6 +32,12 @@ export class AchievementProxy { } onLoad() { + if (!this.root.gameMode.hasAchievements()) { + logger.log("Disabling achievements because game mode does not have achievements"); + this.disabled = true; + return; + } + this.provider .onLoad(this.root) .then(() => { diff --git a/src/js/game/base_item.js b/src/js/game/base_item.js index 0075e6c1..d74ff834 100644 --- a/src/js/game/base_item.js +++ b/src/js/game/base_item.js @@ -11,6 +11,7 @@ export const itemTypes = ["shape", "color", "boolean"]; export class BaseItem extends BasicSerializableObject { constructor() { super(); + this._type = this.getItemType(); } static getId() { diff --git a/src/js/game/belt_path.js b/src/js/game/belt_path.js index dde81549..8ad4f7e3 100644 --- a/src/js/game/belt_path.js +++ b/src/js/game/belt_path.js @@ -13,8 +13,6 @@ import { GameRoot } from "./root"; const logger = createLogger("belt_path"); // Helpers for more semantic access into interleaved arrays -const _nextDistance = 0; -const _item = 1; const DEBUG = G_IS_DEV && false; @@ -110,6 +108,15 @@ export class BeltPath extends BasicSerializableObject { } } + /** + * Clears all items + */ + clearAllItems() { + this.items = []; + this.spacingToFirstItem = this.totalLength; + this.numCompressedItemsAfterFirstItem = 0; + } + /** * Returns whether this path can accept a new item * @returns {boolean} @@ -174,7 +181,7 @@ export class BeltPath extends BasicSerializableObject { * Recomputes cache variables once the path was changed */ onPathChanged() { - this.acceptorTarget = this.computeAcceptingEntityAndSlot(); + this.boundAcceptor = this.computeAcceptingEntityAndSlot(); /** * How many items past the first item are compressed @@ -192,7 +199,7 @@ export class BeltPath extends BasicSerializableObject { /** * Finds the entity which accepts our items * @param {boolean=} debug_Silent Whether debug output should be silent - * @return {{ entity: Entity, slot: number, direction?: enumDirection }} + * @return { (BaseItem, number?) => boolean } */ computeAcceptingEntityAndSlot(debug_Silent = false) { DEBUG && !debug_Silent && logger.log("Recomputing acceptor target"); @@ -214,55 +221,142 @@ export class BeltPath extends BasicSerializableObject { "regular" ); - if (targetEntity) { - DEBUG && !debug_Silent && logger.log(" Found target entity", targetEntity.uid); - const targetStaticComp = targetEntity.components.StaticMapEntity; - const targetBeltComp = targetEntity.components.Belt; + if (!targetEntity) { + return; + } - // Check for belts (special case) - if (targetBeltComp) { - const beltAcceptingDirection = targetStaticComp.localDirectionToWorld(enumDirection.top); - DEBUG && - !debug_Silent && - logger.log( - " Entity is accepting items from", - ejectSlotWsDirection, - "vs", - beltAcceptingDirection, - "Rotation:", - targetStaticComp.rotation + const noSimplifiedBelts = !this.root.app.settings.getAllSettings().simplifiedBelts; + + DEBUG && !debug_Silent && logger.log(" Found target entity", targetEntity.uid); + const targetStaticComp = targetEntity.components.StaticMapEntity; + const targetBeltComp = targetEntity.components.Belt; + + // Check for belts (special case) + if (targetBeltComp) { + const beltAcceptingDirection = targetStaticComp.localDirectionToWorld(enumDirection.top); + DEBUG && + !debug_Silent && + logger.log( + " Entity is accepting items from", + ejectSlotWsDirection, + "vs", + beltAcceptingDirection, + "Rotation:", + targetStaticComp.rotation + ); + if (ejectSlotWsDirection === beltAcceptingDirection) { + return item => { + const path = targetBeltComp.assignedPath; + assert(path, "belt has no path"); + return path.tryAcceptItem(item); + }; + } + } + + // Check for item acceptors + const targetAcceptorComp = targetEntity.components.ItemAcceptor; + if (!targetAcceptorComp) { + // Entity doesn't accept items + return; + } + + const ejectingDirection = targetStaticComp.worldDirectionToLocal(ejectSlotWsDirection); + const matchingSlot = targetAcceptorComp.findMatchingSlot( + targetStaticComp.worldToLocalTile(ejectSlotTargetWsTile), + ejectingDirection + ); + + if (!matchingSlot) { + // No matching slot found + return; + } + + const matchingSlotIndex = matchingSlot.index; + const passOver = this.computePassOverFunctionWithoutBelts(targetEntity, matchingSlotIndex); + if (!passOver) { + return; + } + + const matchingDirection = enumInvertedDirections[ejectingDirection]; + const filter = matchingSlot.slot.filter; + + return function (item, remainingProgress = 0.0) { + // Check if the acceptor has a filter + if (filter && item._type !== filter) { + return false; + } + + // Try to pass over + if (passOver(item, matchingSlotIndex)) { + // Trigger animation on the acceptor comp + if (noSimplifiedBelts) { + targetAcceptorComp.onItemAccepted( + matchingSlotIndex, + matchingDirection, + item, + remainingProgress ); - if (ejectSlotWsDirection === beltAcceptingDirection) { - return { - entity: targetEntity, - direction: null, - slot: 0, - }; } + return true; } + return false; + }; + } - // Check for item acceptors - const targetAcceptorComp = targetEntity.components.ItemAcceptor; - if (!targetAcceptorComp) { - // Entity doesn't accept items - return; - } + /** + * Computes a method to pass over the item to the entity + * @param {Entity} entity + * @param {number} matchingSlotIndex + * @returns {(item: BaseItem, slotIndex: number) => boolean | void} + */ + computePassOverFunctionWithoutBelts(entity, matchingSlotIndex) { + const systems = this.root.systemMgr.systems; + const hubGoals = this.root.hubGoals; - const ejectingDirection = targetStaticComp.worldDirectionToLocal(ejectSlotWsDirection); - const matchingSlot = targetAcceptorComp.findMatchingSlot( - targetStaticComp.worldToLocalTile(ejectSlotTargetWsTile), - ejectingDirection - ); + // NOTICE: THIS IS COPIED FROM THE ITEM EJECTOR SYSTEM FOR PEROFMANCE REASONS - if (!matchingSlot) { - // No matching slot found - return; - } + const itemProcessorComp = entity.components.ItemProcessor; + if (itemProcessorComp) { + // Its an item processor .. + return function (item) { + // Check for potential filters + if (!systems.itemProcessor.checkRequirements(entity, item, matchingSlotIndex)) { + return; + } + return itemProcessorComp.tryTakeItem(item, matchingSlotIndex); + }; + } - return { - entity: targetEntity, - slot: matchingSlot.index, - direction: enumInvertedDirections[ejectingDirection], + const undergroundBeltComp = entity.components.UndergroundBelt; + if (undergroundBeltComp) { + // Its an underground belt. yay. + return function (item) { + return undergroundBeltComp.tryAcceptExternalItem( + item, + hubGoals.getUndergroundBeltBaseSpeed() + ); + }; + } + + const storageComp = entity.components.Storage; + if (storageComp) { + // It's a storage + return function (item) { + if (storageComp.canAcceptItem(item)) { + storageComp.takeItem(item); + return true; + } + }; + } + + const filterComp = entity.components.Filter; + if (filterComp) { + // It's a filter! Unfortunately the filter has to know a lot about it's + // surrounding state and components, so it can't be within the component itself. + return function (item) { + if (systems.filter.tryAcceptItem(entity, matchingSlotIndex, item)) { + return true; + } }; } } @@ -365,17 +459,17 @@ export class BeltPath extends BasicSerializableObject { for (let i = 0; i < this.items.length; ++i) { const item = this.items[i]; - if (item[_nextDistance] < 0 || item[_nextDistance] > this.totalLength + 0.02) { + if (item[0 /* nextDistance */] < 0 || item[0 /* nextDistance */] > this.totalLength + 0.02) { return fail( "Item has invalid offset to next item: ", - item[_nextDistance], + item[0 /* nextDistance */], "(total length:", this.totalLength, ")" ); } - currentPos += item[_nextDistance]; + currentPos += item[0 /* nextDistance */]; } // Check the total sum matches @@ -387,7 +481,7 @@ export class BeltPath extends BasicSerializableObject { this.spacingToFirstItem, ") and items does not match total length (", this.totalLength, - ") -> items: " + this.items.map(i => i[_nextDistance]).join("|") + ") -> items: " + this.items.map(i => i[0 /* nextDistance */]).join("|") ); } @@ -399,43 +493,14 @@ export class BeltPath extends BasicSerializableObject { // Check acceptor const acceptor = this.computeAcceptingEntityAndSlot(true); - if (!!acceptor !== !!this.acceptorTarget) { - return fail("Acceptor target mismatch, acceptor", !!acceptor, "vs stored", !!this.acceptorTarget); - } - - if (acceptor) { - if (this.acceptorTarget.entity !== acceptor.entity) { - return fail( - "Mismatching entity on acceptor target:", - acceptor.entity.uid, - "vs", - this.acceptorTarget.entity.uid - ); - } - - if (this.acceptorTarget.slot !== acceptor.slot) { - return fail( - "Mismatching entity on acceptor target:", - acceptor.slot, - "vs stored", - this.acceptorTarget.slot - ); - } - - if (this.acceptorTarget.direction !== acceptor.direction) { - return fail( - "Mismatching direction on acceptor target:", - acceptor.direction, - "vs stored", - this.acceptorTarget.direction - ); - } + if (!!acceptor !== !!this.boundAcceptor) { + return fail("Acceptor target mismatch, acceptor", !!acceptor, "vs stored", !!this.boundAcceptor); } // Check first nonzero offset let firstNonzero = 0; for (let i = this.items.length - 2; i >= 0; --i) { - if (this.items[i][_nextDistance] < globalConfig.itemSpacingOnBelts + 1e-5) { + if (this.items[i][0 /* nextDistance */] < globalConfig.itemSpacingOnBelts + 1e-5) { ++firstNonzero; } else { break; @@ -483,11 +548,11 @@ export class BeltPath extends BasicSerializableObject { DEBUG && logger.log( " Extended spacing of last item from", - lastItem[_nextDistance], + lastItem[0 /* nextDistance */], "to", - lastItem[_nextDistance] + additionalLength + lastItem[0 /* nextDistance */] + additionalLength ); - lastItem[_nextDistance] += additionalLength; + lastItem[0 /* nextDistance */] += additionalLength; } // Assign reference @@ -618,7 +683,7 @@ export class BeltPath extends BasicSerializableObject { DEBUG && logger.log( "Old items are", - this.items.map(i => i[_nextDistance]) + this.items.map(i => i[0 /* nextDistance */]) ); // Create second path @@ -628,7 +693,7 @@ export class BeltPath extends BasicSerializableObject { let itemPos = this.spacingToFirstItem; for (let i = 0; i < this.items.length; ++i) { const item = this.items[i]; - const distanceToNext = item[_nextDistance]; + const distanceToNext = item[0 /* nextDistance */]; DEBUG && logger.log(" Checking item at", itemPos, "with distance of", distanceToNext, "to next"); @@ -643,7 +708,7 @@ export class BeltPath extends BasicSerializableObject { // Check if its on the second path (otherwise its on the removed belt and simply lost) if (itemPos >= secondPathStart) { // Put item on second path - secondPath.items.push([distanceToNext, item[_item]]); + secondPath.items.push([distanceToNext, item[1 /* item */]]); DEBUG && logger.log( " Put item to second path @", @@ -672,7 +737,7 @@ export class BeltPath extends BasicSerializableObject { "to", clampedDistanceToNext ); - item[_nextDistance] = clampedDistanceToNext; + item[0 /* nextDistance */] = clampedDistanceToNext; } } @@ -683,13 +748,13 @@ export class BeltPath extends BasicSerializableObject { DEBUG && logger.log( "New items are", - this.items.map(i => i[_nextDistance]) + this.items.map(i => i[0 /* nextDistance */]) ); DEBUG && logger.log( "And second path items are", - secondPath.items.map(i => i[_nextDistance]) + secondPath.items.map(i => i[0 /* nextDistance */]) ); // Adjust our total length @@ -776,9 +841,17 @@ export class BeltPath extends BasicSerializableObject { continue; } - DEBUG && logger.log("Item", i, "is at", itemOffset, "with next offset", item[_nextDistance]); + DEBUG && + logger.log( + "Item", + i, + "is at", + itemOffset, + "with next offset", + item[0 /* nextDistance */] + ); lastItemOffset = itemOffset; - itemOffset += item[_nextDistance]; + itemOffset += item[0 /* nextDistance */]; } // If we still have an item, make sure the last item matches @@ -805,7 +878,7 @@ export class BeltPath extends BasicSerializableObject { this.totalLength, ")" ); - this.items[this.items.length - 1][_nextDistance] = lastDistance; + this.items[this.items.length - 1][0 /* nextDistance */] = lastDistance; } else { DEBUG && logger.log(" Removed all items so we'll update spacing to total length"); @@ -893,7 +966,7 @@ export class BeltPath extends BasicSerializableObject { DEBUG && logger.log( " Items:", - this.items.map(i => i[_nextDistance]) + this.items.map(i => i[0 /* nextDistance */]) ); // Find offset to first item @@ -912,7 +985,7 @@ export class BeltPath extends BasicSerializableObject { // This item must be dropped this.items.splice(i, 1); i -= 1; - itemOffset += item[_nextDistance]; + itemOffset += item[0 /* nextDistance */]; continue; } else { // This item can be kept, thus its the first we know @@ -990,9 +1063,13 @@ export class BeltPath extends BasicSerializableObject { // Now, update the distance of our last item if (this.items.length !== 0) { const lastItem = this.items[this.items.length - 1]; - lastItem[_nextDistance] += otherPath.spacingToFirstItem; + lastItem[0 /* nextDistance */] += otherPath.spacingToFirstItem; DEBUG && - logger.log(" Add distance to last item, effectively being", lastItem[_nextDistance], "now"); + logger.log( + " Add distance to last item, effectively being", + lastItem[0 /* nextDistance */], + "now" + ); } else { // Seems we have no items, update our first item distance this.spacingToFirstItem = oldLength + otherPath.spacingToFirstItem; @@ -1012,7 +1089,7 @@ export class BeltPath extends BasicSerializableObject { // Aaand push the other paths items for (let i = 0; i < otherPath.items.length; ++i) { const item = otherPath.items[i]; - this.items.push([item[_nextDistance], item[_item]]); + this.items.push([item[0 /* nextDistance */], item[1 /* item */]]); } // Update bounds @@ -1046,6 +1123,11 @@ export class BeltPath extends BasicSerializableObject { this.debug_checkIntegrity("pre-update"); } + // Skip empty belts + if (this.items.length === 0) { + return; + } + // Divide by item spacing on belts since we use throughput and not speed let beltSpeed = this.root.hubGoals.getBeltBaseSpeed() * @@ -1074,30 +1156,40 @@ export class BeltPath extends BasicSerializableObject { lastItemProcessed === this.items.length - 1 ? 0 : globalConfig.itemSpacingOnBelts; // Compute how much we can advance - const clampedProgress = Math.max( - 0, - Math.min(remainingVelocity, nextDistanceAndItem[_nextDistance] - minimumSpacing) - ); + let clampedProgress = nextDistanceAndItem[0 /* nextDistance */] - minimumSpacing; + + // Make sure we don't advance more than the remaining velocity has stored + if (remainingVelocity < clampedProgress) { + clampedProgress = remainingVelocity; + } + + // Make sure we don't advance back + if (clampedProgress < 0) { + clampedProgress = 0; + } // Reduce our velocity by the amount we consumed remainingVelocity -= clampedProgress; // Reduce the spacing - nextDistanceAndItem[_nextDistance] -= clampedProgress; + nextDistanceAndItem[0 /* nextDistance */] -= clampedProgress; // Advance all items behind by the progress we made this.spacingToFirstItem += clampedProgress; // If the last item can be ejected, eject it and reduce the spacing, because otherwise // we lose velocity - if (isFirstItemProcessed && nextDistanceAndItem[_nextDistance] < 1e-7) { + if (isFirstItemProcessed && nextDistanceAndItem[0 /* nextDistance */] < 1e-7) { // Store how much velocity we "lost" because we bumped the item to the end of the // belt but couldn't move it any farther. We need this to tell the item acceptor // animation to start a tad later, so everything matches up. Yes I'm a perfectionist. const excessVelocity = beltSpeed - clampedProgress; // Try to directly get rid of the item - if (this.tryHandOverItem(nextDistanceAndItem[_item], excessVelocity)) { + if ( + this.boundAcceptor && + this.boundAcceptor(nextDistanceAndItem[1 /* item */], excessVelocity) + ) { this.items.pop(); const itemBehind = this.items[lastItemProcessed - 1]; @@ -1108,11 +1200,11 @@ export class BeltPath extends BasicSerializableObject { // Also see #999 const fixupProgress = Math.max( 0, - Math.min(remainingVelocity, itemBehind[_nextDistance]) + Math.min(remainingVelocity, itemBehind[0 /* nextDistance */]) ); // See above - itemBehind[_nextDistance] -= fixupProgress; + itemBehind[0 /* nextDistance */] -= fixupProgress; remainingVelocity -= fixupProgress; this.spacingToFirstItem += fixupProgress; } @@ -1145,8 +1237,8 @@ export class BeltPath extends BasicSerializableObject { // Check if we have an item which is ready to be emitted const lastItem = this.items[this.items.length - 1]; - if (lastItem && lastItem[_nextDistance] === 0 && this.acceptorTarget) { - if (this.tryHandOverItem(lastItem[_item])) { + if (lastItem && lastItem[0 /* nextDistance */] === 0) { + if (this.boundAcceptor && this.boundAcceptor(lastItem[1 /* item */])) { this.items.pop(); this.numCompressedItemsAfterFirstItem = Math.max( 0, @@ -1160,50 +1252,6 @@ export class BeltPath extends BasicSerializableObject { } } - /** - * Tries to hand over the item to the end entity - * @param {BaseItem} item - */ - tryHandOverItem(item, remainingProgress = 0.0) { - if (!this.acceptorTarget) { - return; - } - - const targetAcceptorComp = this.acceptorTarget.entity.components.ItemAcceptor; - - // Check if the acceptor has a filter for example - if (targetAcceptorComp && !targetAcceptorComp.canAcceptItem(this.acceptorTarget.slot, item)) { - // Well, this item is not accepted - return false; - } - - // Try to pass over - if ( - this.root.systemMgr.systems.itemEjector.tryPassOverItem( - item, - this.acceptorTarget.entity, - this.acceptorTarget.slot - ) - ) { - // Trigger animation on the acceptor comp - const targetAcceptorComp = this.acceptorTarget.entity.components.ItemAcceptor; - if (targetAcceptorComp) { - if (!this.root.app.settings.getAllSettings().simplifiedBelts) { - targetAcceptorComp.onItemAccepted( - this.acceptorTarget.slot, - this.acceptorTarget.direction, - item, - remainingProgress - ); - } - } - - return true; - } - - return false; - } - /** * Computes a world space position from the given progress * @param {number} progress @@ -1270,11 +1318,11 @@ export class BeltPath extends BasicSerializableObject { parameters.context.font = "6px GameFont"; parameters.context.fillStyle = "#111"; parameters.context.fillText( - "" + round4Digits(nextDistanceAndItem[_nextDistance]), + "" + round4Digits(nextDistanceAndItem[0 /* nextDistance */]), worldPos.x + 5, worldPos.y + 2 ); - progress += nextDistanceAndItem[_nextDistance]; + progress += nextDistanceAndItem[0 /* nextDistance */]; if (this.items.length - 1 - this.numCompressedItemsAfterFirstItem === i) { parameters.context.fillStyle = "red"; @@ -1370,7 +1418,7 @@ export class BeltPath extends BasicSerializableObject { const centerPos = staticComp.localTileToWorld(centerPosLocal).toWorldSpaceCenterOfTile(); parameters.context.globalAlpha = 0.5; - firstItem[_item].drawItemCenteredClipped(centerPos.x, centerPos.y, parameters); + firstItem[1 /* item */].drawItemCenteredClipped(centerPos.x, centerPos.y, parameters); parameters.context.globalAlpha = 1; } @@ -1402,7 +1450,7 @@ export class BeltPath extends BasicSerializableObject { const distanceAndItem = this.items[currentItemIndex]; - distanceAndItem[_item].drawItemCenteredClipped( + distanceAndItem[1 /* item */].drawItemCenteredClipped( worldPos.x, worldPos.y, parameters, @@ -1410,7 +1458,7 @@ export class BeltPath extends BasicSerializableObject { ); // Check for the next item - currentItemPos += distanceAndItem[_nextDistance]; + currentItemPos += distanceAndItem[0 /* nextDistance */]; ++currentItemIndex; if (currentItemIndex >= this.items.length) { diff --git a/src/js/game/blueprint.js b/src/js/game/blueprint.js index 3aaef831..795b27c3 100644 --- a/src/js/game/blueprint.js +++ b/src/js/game/blueprint.js @@ -101,8 +101,12 @@ export class Blueprint { const entity = this.entities[i]; const staticComp = entity.components.StaticMapEntity; + // Actually keeping this in as an easter egg to rotate the trash can + // if (staticComp.getMetaBuilding().getIsRotateable()) { staticComp.rotation = (staticComp.rotation + 90) % 360; staticComp.originalRotation = (staticComp.originalRotation + 90) % 360; + // } + staticComp.origin = staticComp.origin.rotateFastMultipleOf90(90); } } @@ -139,6 +143,9 @@ export class Blueprint { * @param {GameRoot} root */ canAfford(root) { + if (root.gameMode.getHasFreeCopyPaste()) { + return true; + } return root.hubGoals.getShapesStoredByKey(root.gameMode.getBlueprintShapeKey()) >= this.getCost(); } @@ -149,29 +156,31 @@ export class Blueprint { */ tryPlace(root, tile) { return root.logic.performBulkOperation(() => { - let count = 0; - for (let i = 0; i < this.entities.length; ++i) { - const entity = this.entities[i]; - if (!root.logic.checkCanPlaceEntity(entity, tile)) { - continue; + return root.logic.performImmutableOperation(() => { + let count = 0; + for (let i = 0; i < this.entities.length; ++i) { + const entity = this.entities[i]; + if (!root.logic.checkCanPlaceEntity(entity, tile)) { + continue; + } + + const clone = entity.clone(); + clone.components.StaticMapEntity.origin.addInplace(tile); + root.logic.freeEntityAreaBeforeBuild(clone); + root.map.placeStaticEntity(clone); + root.entityMgr.registerEntity(clone); + count++; } - const clone = entity.clone(); - clone.components.StaticMapEntity.origin.addInplace(tile); - root.logic.freeEntityAreaBeforeBuild(clone); - root.map.placeStaticEntity(clone); - root.entityMgr.registerEntity(clone); - count++; - } + root.signals.bulkAchievementCheck.dispatch( + ACHIEVEMENTS.placeBlueprint, + count, + ACHIEVEMENTS.placeBp1000, + count + ); - root.signals.bulkAchievementCheck.dispatch( - ACHIEVEMENTS.placeBlueprint, - count, - ACHIEVEMENTS.placeBp1000, - count - ); - - return count !== 0; + return count !== 0; + }); }); } } diff --git a/src/js/game/buildings/balancer.js b/src/js/game/buildings/balancer.js index 2f14e36c..38a568e1 100644 --- a/src/js/game/buildings/balancer.js +++ b/src/js/game/buildings/balancer.js @@ -66,6 +66,10 @@ export class MetaBalancerBuilding extends MetaBuilding { * @returns {Array<[string, string]>} */ getAdditionalStatistics(root, variant) { + if (root.gameMode.throughputDoesNotMatter()) { + return []; + } + let speedMultiplier = 2; switch (variant) { case enumBalancerVariants.merger: @@ -88,9 +92,11 @@ export class MetaBalancerBuilding extends MetaBuilding { * @param {GameRoot} root */ getAvailableVariants(root) { - let available = [defaultBuildingVariant]; + const deterministic = root.gameMode.getIsDeterministic(); - if (root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_merger)) { + let available = deterministic ? [] : [defaultBuildingVariant]; + + if (!deterministic && root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_merger)) { available.push(enumBalancerVariants.merger, enumBalancerVariants.mergerInverse); } diff --git a/src/js/game/buildings/belt.js b/src/js/game/buildings/belt.js index 84646b19..f4e31ba9 100644 --- a/src/js/game/buildings/belt.js +++ b/src/js/game/buildings/belt.js @@ -55,6 +55,9 @@ export class MetaBeltBuilding extends MetaBuilding { * @returns {Array<[string, string]>} */ getAdditionalStatistics(root, variant) { + if (root.gameMode.throughputDoesNotMatter()) { + return []; + } const beltSpeed = root.hubGoals.getBeltBaseSpeed(); return [[T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(beltSpeed)]]; } diff --git a/src/js/game/buildings/block.js b/src/js/game/buildings/block.js new file mode 100644 index 00000000..d6499648 --- /dev/null +++ b/src/js/game/buildings/block.js @@ -0,0 +1,30 @@ +/* typehints:start */ +import { Entity } from "../entity"; +/* typehints:end */ + +import { MetaBuilding } from "../meta_building"; + +export class MetaBlockBuilding extends MetaBuilding { + constructor() { + super("block"); + } + + getSilhouetteColor() { + return "#333"; + } + + /** + * + * @param {import("../../savegame/savegame_serializer").GameRoot} root + * @returns + */ + getIsRemovable(root) { + return root.gameMode.getIsEditor(); + } + + /** + * Creates the entity at the given location + * @param {Entity} entity + */ + setupEntityComponents(entity) {} +} diff --git a/src/js/game/buildings/constant_producer.js b/src/js/game/buildings/constant_producer.js new file mode 100644 index 00000000..c1c502d0 --- /dev/null +++ b/src/js/game/buildings/constant_producer.js @@ -0,0 +1,42 @@ +/* typehints:start */ +import { Entity } from "../entity"; +/* typehints:end */ + +import { enumDirection, Vector } from "../../core/vector"; +import { ConstantSignalComponent } from "../components/constant_signal"; +import { ItemEjectorComponent } from "../components/item_ejector"; +import { ItemProducerComponent } from "../components/item_producer"; +import { MetaBuilding } from "../meta_building"; + +export class MetaConstantProducerBuilding extends MetaBuilding { + constructor() { + super("constant_producer"); + } + + getSilhouetteColor() { + return "#bfd630"; + } + + /** + * + * @param {import("../../savegame/savegame_serializer").GameRoot} root + * @returns + */ + getIsRemovable(root) { + return root.gameMode.getIsEditor(); + } + + /** + * Creates the entity at the given location + * @param {Entity} entity + */ + setupEntityComponents(entity) { + entity.addComponent( + new ItemEjectorComponent({ + slots: [{ pos: new Vector(0, 0), direction: enumDirection.top }], + }) + ); + entity.addComponent(new ItemProducerComponent({})); + entity.addComponent(new ConstantSignalComponent({})); + } +} diff --git a/src/js/game/buildings/cutter.js b/src/js/game/buildings/cutter.js index 7dee4449..37264c9d 100644 --- a/src/js/game/buildings/cutter.js +++ b/src/js/game/buildings/cutter.js @@ -38,6 +38,9 @@ export class MetaCutterBuilding extends MetaBuilding { * @returns {Array<[string, string]>} */ getAdditionalStatistics(root, variant) { + if (root.gameMode.throughputDoesNotMatter()) { + return []; + } const speed = root.hubGoals.getProcessorBaseSpeed( variant === enumCutterVariants.quad ? enumItemProcessorTypes.cutterQuad diff --git a/src/js/game/buildings/filter.js b/src/js/game/buildings/filter.js index 2d81ce83..08296853 100644 --- a/src/js/game/buildings/filter.js +++ b/src/js/game/buildings/filter.js @@ -40,6 +40,9 @@ export class MetaFilterBuilding extends MetaBuilding { * @returns {Array<[string, string]>} */ getAdditionalStatistics(root, variant) { + if (root.gameMode.throughputDoesNotMatter()) { + return []; + } const beltSpeed = root.hubGoals.getBeltBaseSpeed(); return [[T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(beltSpeed)]]; } diff --git a/src/js/game/buildings/goal_acceptor.js b/src/js/game/buildings/goal_acceptor.js new file mode 100644 index 00000000..dde720e3 --- /dev/null +++ b/src/js/game/buildings/goal_acceptor.js @@ -0,0 +1,54 @@ +/* typehints:start */ +import { Entity } from "../entity"; +/* typehints:end */ + +import { enumDirection, Vector } from "../../core/vector"; +import { GoalAcceptorComponent } from "../components/goal_acceptor"; +import { ItemAcceptorComponent } from "../components/item_acceptor"; +import { enumItemProcessorTypes, ItemProcessorComponent } from "../components/item_processor"; +import { MetaBuilding } from "../meta_building"; + +export class MetaGoalAcceptorBuilding extends MetaBuilding { + constructor() { + super("goal_acceptor"); + } + + getSilhouetteColor() { + return "#ce418a"; + } + + /** + * + * @param {import("../../savegame/savegame_serializer").GameRoot} root + * @returns + */ + getIsRemovable(root) { + return root.gameMode.getIsEditor(); + } + + /** + * Creates the entity at the given location + * @param {Entity} entity + */ + setupEntityComponents(entity) { + entity.addComponent( + new ItemAcceptorComponent({ + slots: [ + { + pos: new Vector(0, 0), + directions: [enumDirection.bottom], + filter: "shape", + }, + ], + }) + ); + + entity.addComponent( + new ItemProcessorComponent({ + processorType: enumItemProcessorTypes.goal, + }) + ); + + entity.addComponent(new GoalAcceptorComponent({})); + } +} diff --git a/src/js/game/buildings/item_producer.js b/src/js/game/buildings/item_producer.js index 477ed603..1140c8f1 100644 --- a/src/js/game/buildings/item_producer.js +++ b/src/js/game/buildings/item_producer.js @@ -39,6 +39,6 @@ export class MetaItemProducerBuilding extends MetaBuilding { ], }) ); - entity.addComponent(new ItemProducerComponent()); + entity.addComponent(new ItemProducerComponent({})); } } diff --git a/src/js/game/buildings/miner.js b/src/js/game/buildings/miner.js index f0b837a1..473aa262 100644 --- a/src/js/game/buildings/miner.js +++ b/src/js/game/buildings/miner.js @@ -31,6 +31,9 @@ export class MetaMinerBuilding extends MetaBuilding { * @returns {Array<[string, string]>} */ getAdditionalStatistics(root, variant) { + if (root.gameMode.throughputDoesNotMatter()) { + return []; + } const speed = root.hubGoals.getMinerBaseSpeed(); return [[T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(speed)]]; } diff --git a/src/js/game/buildings/mixer.js b/src/js/game/buildings/mixer.js index cbde309e..e572bbba 100644 --- a/src/js/game/buildings/mixer.js +++ b/src/js/game/buildings/mixer.js @@ -35,6 +35,9 @@ export class MetaMixerBuilding extends MetaBuilding { * @returns {Array<[string, string]>} */ getAdditionalStatistics(root, variant) { + if (root.gameMode.throughputDoesNotMatter()) { + return []; + } const speed = root.hubGoals.getProcessorBaseSpeed(enumItemProcessorTypes.mixer); return [[T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(speed)]]; } diff --git a/src/js/game/buildings/painter.js b/src/js/game/buildings/painter.js index 6e941403..e7a0b72d 100644 --- a/src/js/game/buildings/painter.js +++ b/src/js/game/buildings/painter.js @@ -46,6 +46,9 @@ export class MetaPainterBuilding extends MetaBuilding { * @returns {Array<[string, string]>} */ getAdditionalStatistics(root, variant) { + if (root.gameMode.throughputDoesNotMatter()) { + return []; + } switch (variant) { case defaultBuildingVariant: case enumPainterVariants.mirrored: { @@ -71,7 +74,10 @@ export class MetaPainterBuilding extends MetaBuilding { if (root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_painter_double)) { variants.push(enumPainterVariants.double); } - if (root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_wires_painter_and_levers)) { + if ( + root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_wires_painter_and_levers) && + root.gameMode.getSupportsWires() + ) { variants.push(enumPainterVariants.quad); } return variants; diff --git a/src/js/game/buildings/rotater.js b/src/js/game/buildings/rotater.js index 7df94d16..f24fee14 100644 --- a/src/js/game/buildings/rotater.js +++ b/src/js/game/buildings/rotater.js @@ -48,6 +48,9 @@ export class MetaRotaterBuilding extends MetaBuilding { * @returns {Array<[string, string]>} */ getAdditionalStatistics(root, variant) { + if (root.gameMode.throughputDoesNotMatter()) { + return []; + } switch (variant) { case defaultBuildingVariant: { const speed = root.hubGoals.getProcessorBaseSpeed(enumItemProcessorTypes.rotater); diff --git a/src/js/game/buildings/stacker.js b/src/js/game/buildings/stacker.js index 40a9c5ae..6b70365d 100644 --- a/src/js/game/buildings/stacker.js +++ b/src/js/game/buildings/stacker.js @@ -28,6 +28,9 @@ export class MetaStackerBuilding extends MetaBuilding { * @returns {Array<[string, string]>} */ getAdditionalStatistics(root, variant) { + if (root.gameMode.throughputDoesNotMatter()) { + return []; + } const speed = root.hubGoals.getProcessorBaseSpeed(enumItemProcessorTypes.stacker); return [[T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(speed)]]; } diff --git a/src/js/game/buildings/underground_belt.js b/src/js/game/buildings/underground_belt.js index 2761443d..12e887c9 100644 --- a/src/js/game/buildings/underground_belt.js +++ b/src/js/game/buildings/underground_belt.js @@ -72,13 +72,21 @@ export class MetaUndergroundBeltBuilding extends MetaBuilding { globalConfig.undergroundBeltMaxTilesByTier[enumUndergroundBeltVariantToTier[variant]]; const beltSpeed = root.hubGoals.getUndergroundBeltBaseSpeed(); - return [ + + /** @type {Array<[string, string]>} */ + const stats = [ [ T.ingame.buildingPlacement.infoTexts.range, T.ingame.buildingPlacement.infoTexts.tiles.replace("", "" + rangeTiles), ], - [T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(beltSpeed)], ]; + + if (root.gameMode.throughputDoesNotMatter()) { + return stats; + } + stats.push([T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(beltSpeed)]); + + return stats; } /** diff --git a/src/js/game/camera.js b/src/js/game/camera.js index 107d1fb4..fc90f4de 100644 --- a/src/js/game/camera.js +++ b/src/js/game/camera.js @@ -392,13 +392,20 @@ export class Camera extends BasicSerializableObject { return rect.containsPoint(point.x, point.y); } + getMaximumZoom() { + return this.root.gameMode.getMaximumZoom(); + } + + getMinimumZoom() { + return this.root.gameMode.getMinimumZoom(); + } + /** * Returns if we can further zoom in * @returns {boolean} */ canZoomIn() { - const maxLevel = this.root.app.platformWrapper.getMaximumZoom(); - return this.zoomLevel <= maxLevel - 0.01; + return this.zoomLevel <= this.getMaximumZoom() - 0.01; } /** @@ -406,8 +413,7 @@ export class Camera extends BasicSerializableObject { * @returns {boolean} */ canZoomOut() { - const minLevel = this.root.app.platformWrapper.getMinimumZoom(); - return this.zoomLevel >= minLevel + 0.01; + return this.zoomLevel >= this.getMinimumZoom() + 0.01; } // EVENTS @@ -468,6 +474,7 @@ export class Camera extends BasicSerializableObject { // Clamp everything afterwards this.clampZoomLevel(); + this.clampToBounds(); return false; } @@ -743,17 +750,29 @@ export class Camera extends BasicSerializableObject { if (G_IS_DEV && globalConfig.debug.disableZoomLimits) { return; } - const wrapper = this.root.app.platformWrapper; - assert(Number.isFinite(this.zoomLevel), "Invalid zoom level *before* clamp: " + this.zoomLevel); - this.zoomLevel = clamp(this.zoomLevel, wrapper.getMinimumZoom(), wrapper.getMaximumZoom()); + this.zoomLevel = clamp(this.zoomLevel, this.getMinimumZoom(), this.getMaximumZoom()); assert(Number.isFinite(this.zoomLevel), "Invalid zoom level *after* clamp: " + this.zoomLevel); if (this.desiredZoom) { - this.desiredZoom = clamp(this.desiredZoom, wrapper.getMinimumZoom(), wrapper.getMaximumZoom()); + this.desiredZoom = clamp(this.desiredZoom, this.getMinimumZoom(), this.getMaximumZoom()); } } + /** + * Clamps the center within set boundaries + */ + clampToBounds() { + const bounds = this.root.gameMode.getCameraBounds(); + if (!bounds) { + return; + } + + const tileScaleBounds = this.root.gameMode.getCameraBounds().allScaled(globalConfig.tileSize); + this.center.x = clamp(this.center.x, tileScaleBounds.x, tileScaleBounds.x + tileScaleBounds.w); + this.center.y = clamp(this.center.y, tileScaleBounds.y, tileScaleBounds.y + tileScaleBounds.h); + } + /** * Updates the camera * @param {number} dt Delta time in milliseconds @@ -857,6 +876,7 @@ export class Camera extends BasicSerializableObject { // Panning this.currentPan = mixVector(this.currentPan, this.desiredPan, 0.06); this.center = this.center.add(this.currentPan.multiplyScalar((0.5 * dt) / this.zoomLevel)); + this.clampToBounds(); } } @@ -921,6 +941,8 @@ export class Camera extends BasicSerializableObject { ((0.5 * dt) / this.zoomLevel) * this.root.app.settings.getMovementSpeed() ) ); + + this.clampToBounds(); } /** @@ -1006,6 +1028,8 @@ export class Camera extends BasicSerializableObject { this.center.x += moveAmount * forceX * movementSpeed; this.center.y += moveAmount * forceY * movementSpeed; + + this.clampToBounds(); } } } diff --git a/src/js/game/component.js b/src/js/game/component.js index 46b1b545..cff14d62 100644 --- a/src/js/game/component.js +++ b/src/js/game/component.js @@ -23,6 +23,11 @@ export class Component extends BasicSerializableObject { */ copyAdditionalStateTo(otherComponent) {} + /** + * Clears all items and state + */ + clear() {} + /* dev:start */ /** diff --git a/src/js/game/component_registry.js b/src/js/game/component_registry.js index f094e60d..9c9247e6 100644 --- a/src/js/game/component_registry.js +++ b/src/js/game/component_registry.js @@ -19,6 +19,7 @@ import { DisplayComponent } from "./components/display"; import { BeltReaderComponent } from "./components/belt_reader"; import { FilterComponent } from "./components/filter"; import { ItemProducerComponent } from "./components/item_producer"; +import { GoalAcceptorComponent } from "./components/goal_acceptor"; export function initComponentRegistry() { gComponentRegistry.register(StaticMapEntityComponent); @@ -41,6 +42,7 @@ export function initComponentRegistry() { gComponentRegistry.register(BeltReaderComponent); gComponentRegistry.register(FilterComponent); gComponentRegistry.register(ItemProducerComponent); + gComponentRegistry.register(GoalAcceptorComponent); // IMPORTANT ^^^^^ UPDATE ENTITY COMPONENT STORAGE AFTERWARDS diff --git a/src/js/game/components/belt.js b/src/js/game/components/belt.js index 138c4775..3144ad96 100644 --- a/src/js/game/components/belt.js +++ b/src/js/game/components/belt.js @@ -57,6 +57,12 @@ export class BeltComponent extends Component { this.assignedPath = null; } + clear() { + if (this.assignedPath) { + this.assignedPath.clearAllItems(); + } + } + /** * Returns the effective length of this belt in tile space * @returns {number} diff --git a/src/js/game/components/belt_reader.js b/src/js/game/components/belt_reader.js index d451bab5..187e53cd 100644 --- a/src/js/game/components/belt_reader.js +++ b/src/js/game/components/belt_reader.js @@ -3,6 +3,12 @@ import { BaseItem } from "../base_item"; import { typeItemSingleton } from "../item_resolver"; import { types } from "../../savegame/serialization"; +/** @enum {string} */ +export const enumBeltReaderType = { + wired: "wired", + wireless: "wireless", +}; + export class BeltReaderComponent extends Component { static getId() { return "BeltReader"; @@ -16,7 +22,10 @@ export class BeltReaderComponent extends Component { constructor() { super(); + this.clear(); + } + clear() { /** * Which items went through the reader, we only store the time * @type {Array} diff --git a/src/js/game/components/constant_signal.js b/src/js/game/components/constant_signal.js index 286108be..3f00ccba 100644 --- a/src/js/game/components/constant_signal.js +++ b/src/js/game/components/constant_signal.js @@ -1,7 +1,6 @@ -import { gItemRegistry } from "../../core/global_registries"; import { types } from "../../savegame/serialization"; -import { Component } from "../component"; import { BaseItem } from "../base_item"; +import { Component } from "../component"; import { typeItemSingleton } from "../item_resolver"; export class ConstantSignalComponent extends Component { diff --git a/src/js/game/components/filter.js b/src/js/game/components/filter.js index cffee969..8a22a076 100644 --- a/src/js/game/components/filter.js +++ b/src/js/game/components/filter.js @@ -40,6 +40,10 @@ export class FilterComponent extends Component { constructor() { super(); + this.clear(); + } + + clear() { /** * Items in queue to leave through * @type {Array} diff --git a/src/js/game/components/goal_acceptor.js b/src/js/game/components/goal_acceptor.js new file mode 100644 index 00000000..bb13ee61 --- /dev/null +++ b/src/js/game/components/goal_acceptor.js @@ -0,0 +1,59 @@ +import { globalConfig } from "../../core/config"; +import { BaseItem } from "../base_item"; +import { Component } from "../component"; +import { typeItemSingleton } from "../item_resolver"; + +export class GoalAcceptorComponent extends Component { + static getId() { + return "GoalAcceptor"; + } + + static getSchema() { + return { + item: typeItemSingleton, + }; + } + + /** + * @param {object} param0 + * @param {BaseItem=} param0.item + * @param {number=} param0.rate + */ + constructor({ item = null, rate = null }) { + super(); + + // ths item to produce + /** @type {BaseItem | undefined} */ + this.item = item; + + this.clear(); + } + + clear() { + /** + * The last item we delivered + * @type {{ item: BaseItem; time: number; } | null} */ + this.lastDelivery = null; + + // The amount of items we delivered so far + this.currentDeliveredItems = 0; + + // Used for animations + this.displayPercentage = 0; + } + + /** + * Clears items but doesn't instantly reset the progress bar + */ + clearItems() { + this.lastDelivery = null; + this.currentDeliveredItems = 0; + } + + getRequiredSecondsPerItem() { + return ( + globalConfig.goalAcceptorsPerProducer / + (globalConfig.puzzleModeSpeed * globalConfig.beltSpeedItemsPerSecond) + ); + } +} diff --git a/src/js/game/components/item_acceptor.js b/src/js/game/components/item_acceptor.js index 7dbd9677..354f9024 100644 --- a/src/js/game/components/item_acceptor.js +++ b/src/js/game/components/item_acceptor.js @@ -36,6 +36,11 @@ export class ItemAcceptorComponent extends Component { constructor({ slots = [] }) { super(); + this.setSlots(slots); + this.clear(); + } + + clear() { /** * Fixes belt animations * @type {Array<{ @@ -46,8 +51,6 @@ export class ItemAcceptorComponent extends Component { * }>} */ this.itemConsumptionAnimations = []; - - this.setSlots(slots); } /** @@ -71,6 +74,8 @@ export class ItemAcceptorComponent extends Component { /** * Returns if this acceptor can accept a new item at slot N + * + * NOTICE: The belt path ignores this for performance reasons and does his own check * @param {number} slotIndex * @param {BaseItem=} item */ diff --git a/src/js/game/components/item_ejector.js b/src/js/game/components/item_ejector.js index 47253b4b..719925af 100644 --- a/src/js/game/components/item_ejector.js +++ b/src/js/game/components/item_ejector.js @@ -48,6 +48,13 @@ export class ItemEjectorComponent extends Component { this.renderFloatingItems = renderFloatingItems; } + clear() { + for (const slot of this.slots) { + slot.item = null; + slot.progress = 0; + } + } + /** * @param {Array<{pos: Vector, direction: enumDirection }>} slots The slots to eject on */ diff --git a/src/js/game/components/item_processor.js b/src/js/game/components/item_processor.js index fd466662..4c0e1835 100644 --- a/src/js/game/components/item_processor.js +++ b/src/js/game/components/item_processor.js @@ -19,6 +19,7 @@ export const enumItemProcessorTypes = { hub: "hub", filter: "filter", reader: "reader", + goal: "goal", }; /** @enum {string} */ @@ -63,10 +64,8 @@ export class ItemProcessorComponent extends Component { }) { super(); - // Which slot to emit next, this is only a preference and if it can't emit - // it will take the other one. Some machines ignore this (e.g. the balancer) to make - // sure the outputs always match - this.nextOutputSlot = 0; + // How many inputs we need for one charge + this.inputsPerCharge = inputsPerCharge; // Type of the processor this.type = processorType; @@ -74,8 +73,14 @@ export class ItemProcessorComponent extends Component { // Type of processing requirement this.processingRequirement = processingRequirement; - // How many inputs we need for one charge - this.inputsPerCharge = inputsPerCharge; + this.clear(); + } + + clear() { + // Which slot to emit next, this is only a preference and if it can't emit + // it will take the other one. Some machines ignore this (e.g. the balancer) to make + // sure the outputs always match + this.nextOutputSlot = 0; /** * Our current inputs @@ -104,7 +109,11 @@ export class ItemProcessorComponent extends Component { * @param {number} sourceSlot */ tryTakeItem(item, sourceSlot) { - if (this.type === enumItemProcessorTypes.hub || this.type === enumItemProcessorTypes.trash) { + if ( + this.type === enumItemProcessorTypes.hub || + this.type === enumItemProcessorTypes.trash || + this.type === enumItemProcessorTypes.goal + ) { // Hub has special logic .. not really nice but efficient. this.inputSlots.push({ item, sourceSlot }); return true; diff --git a/src/js/game/components/miner.js b/src/js/game/components/miner.js index ab87760f..5321ae11 100644 --- a/src/js/game/components/miner.js +++ b/src/js/game/components/miner.js @@ -24,13 +24,6 @@ export class MinerComponent extends Component { this.lastMiningTime = 0; this.chainable = chainable; - /** - * Stores items from other miners which were chained to this - * miner. - * @type {Array} - */ - this.itemChainBuffer = []; - /** * @type {BaseItem} */ @@ -42,6 +35,17 @@ export class MinerComponent extends Component { * @type {Entity|null|false} */ this.cachedChainedMiner = null; + + this.clear(); + } + + clear() { + /** + * Stores items from other miners which were chained to this + * miner. + * @type {Array} + */ + this.itemChainBuffer = []; } /** diff --git a/src/js/game/components/static_map_entity.js b/src/js/game/components/static_map_entity.js index 7e2f5314..c76a298e 100644 --- a/src/js/game/components/static_map_entity.js +++ b/src/js/game/components/static_map_entity.js @@ -71,6 +71,14 @@ export class StaticMapEntityComponent extends Component { return getBuildingDataFromCode(this.code).variant; } + /** + * Returns the buildings rotation variant + * @returns {number} + */ + getRotationVariant() { + return getBuildingDataFromCode(this.code).rotationVariant; + } + /** * Copy the current state to another component * @param {Component} otherComponent diff --git a/src/js/game/components/underground_belt.js b/src/js/game/components/underground_belt.js index a3e883ec..2b744edd 100644 --- a/src/js/game/components/underground_belt.js +++ b/src/js/game/components/underground_belt.js @@ -41,6 +41,17 @@ export class UndergroundBeltComponent extends Component { this.mode = mode; this.tier = tier; + /** + * The linked entity, used to speed up performance. This contains either + * the entrance or exit depending on the tunnel type + * @type {LinkedUndergroundBelt} + */ + this.cachedLinkedEntity = null; + + this.clear(); + } + + clear() { /** @type {Array<{ item: BaseItem, progress: number }>} */ this.consumptionAnimations = []; @@ -51,13 +62,6 @@ export class UndergroundBeltComponent extends Component { * @type {Array<[BaseItem, number]>} Format is [Item, ingame time to eject the item] */ this.pendingItems = []; - - /** - * The linked entity, used to speed up performance. This contains either - * the entrance or exit depending on the tunnel type - * @type {LinkedUndergroundBelt} - */ - this.cachedLinkedEntity = null; } /** diff --git a/src/js/game/core.js b/src/js/game/core.js index f4b3e9ee..a0ee3713 100644 --- a/src/js/game/core.js +++ b/src/js/game/core.js @@ -31,7 +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 { GameMode } from "./game_mode"; import { ProductionAnalytics } from "./production_analytics"; import { GameRoot } from "./root"; import { ShapeDefinitionManager } from "./shape_definition_manager"; @@ -82,7 +82,9 @@ export class GameCore { * @param {import("../states/ingame").InGameState} parentState * @param {Savegame} savegame */ - initializeRoot(parentState, savegame) { + initializeRoot(parentState, savegame, gameModeId) { + logger.log("initializing root"); + // Construct the root element, this is the data representation of the game this.root = new GameRoot(this.app); this.root.gameState = parentState; @@ -100,12 +102,12 @@ export class GameCore { // This isn't nice, but we need it right here root.keyMapper = new KeyActionMapper(root, this.root.gameState.inputReciever); + // Init game mode + root.gameMode = GameMode.create(root, gameModeId, parentState.creationPayload.gameModeParameters); + // 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); @@ -157,6 +159,8 @@ export class GameCore { } }); } + + logger.log("root initialized"); } /** @@ -168,6 +172,10 @@ export class GameCore { this.root.gameIsFresh = true; this.root.map.seed = randomInt(0, 100000); + if (!this.root.gameMode.hasHub()) { + return; + } + // Place the hub const hub = gMetaBuildingRegistry.findByClass(MetaHubBuilding).createEntity({ root: this.root, @@ -447,7 +455,9 @@ export class GameCore { systems.hub.draw(params); // Green wires overlay - root.hud.parts.wiresOverlay.draw(params); + if (root.hud.parts.wiresOverlay) { + root.hud.parts.wiresOverlay.draw(params); + } if (this.root.currentLayer === "wires") { // Static map entities diff --git a/src/js/game/dynamic_tickrate.js b/src/js/game/dynamic_tickrate.js index 3e29aba3..c76fa2e1 100644 --- a/src/js/game/dynamic_tickrate.js +++ b/src/js/game/dynamic_tickrate.js @@ -23,10 +23,16 @@ export class DynamicTickrate { this.averageFps = 60; - this.setTickRate(this.root.app.settings.getDesiredFps()); + const fixedRate = this.root.gameMode.getFixedTickrate(); + if (fixedRate) { + logger.log("Setting fixed tickrate of", fixedRate); + this.setTickRate(fixedRate); + } else { + this.setTickRate(this.root.app.settings.getDesiredFps()); - if (G_IS_DEV && globalConfig.debug.renderForTrailer) { - this.setTickRate(300); + if (G_IS_DEV && globalConfig.debug.renderForTrailer) { + this.setTickRate(300); + } } } @@ -99,9 +105,7 @@ export class DynamicTickrate { this.averageTickDuration = average; - const desiredFps = this.root.app.settings.getDesiredFps(); - - // Disabled for now: Dynamicall adjusting tick rate + // Disabled for now: Dynamically adjusting tick rate // if (this.averageFps > desiredFps * 0.9) { // // if (average < maxTickDuration) { // this.increaseTickRate(); diff --git a/src/js/game/entity_components.js b/src/js/game/entity_components.js index 7dee590a..163be9f9 100644 --- a/src/js/game/entity_components.js +++ b/src/js/game/entity_components.js @@ -19,6 +19,7 @@ import { DisplayComponent } from "./components/display"; import { BeltReaderComponent } from "./components/belt_reader"; import { FilterComponent } from "./components/filter"; import { ItemProducerComponent } from "./components/item_producer"; +import { GoalAcceptorComponent } from "./components/goal_acceptor"; /* typehints:end */ /** @@ -89,6 +90,9 @@ export class EntityComponentStorage { /** @type {ItemProducerComponent} */ this.ItemProducer; + /** @type {GoalAcceptorComponent} */ + this.GoalAcceptor; + /* typehints:end */ } } diff --git a/src/js/game/game_mode.js b/src/js/game/game_mode.js index 15403eb5..bb60d8a6 100644 --- a/src/js/game/game_mode.js +++ b/src/js/game/game_mode.js @@ -1,71 +1,197 @@ /* typehints:start */ -import { enumHubGoalRewards } from "./tutorial_goals"; +import { GameRoot } from "./root"; /* typehints:end */ -import { GameRoot } from "./root"; +import { Rectangle } from "../core/rectangle"; +import { gGameModeRegistry } from "../core/global_registries"; +import { types, BasicSerializableObject } from "../savegame/serialization"; +import { MetaBuilding } from "./meta_building"; +import { MetaItemProducerBuilding } from "./buildings/item_producer"; +import { BaseHUDPart } from "./hud/base_hud_part"; -/** @typedef {{ - * shape: string, - * amount: number - * }} UpgradeRequirement */ +/** @enum {string} */ +export const enumGameModeIds = { + puzzleEdit: "puzzleEditMode", + puzzlePlay: "puzzlePlayMode", + regular: "regularMode", +}; -/** @typedef {{ - * required: Array - * improvement?: number, - * excludePrevious?: boolean - * }} TierRequirement */ +/** @enum {string} */ +export const enumGameModeTypes = { + default: "defaultModeType", + puzzle: "puzzleModeType", +}; -/** @typedef {Array} UpgradeTiers */ +export class GameMode extends BasicSerializableObject { + /** @returns {string} */ + static getId() { + abstract; + return "unknownMode"; + } + + /** @returns {string} */ + static getType() { + abstract; + return "unknownType"; + } + /** + * @param {GameRoot} root + * @param {string} [id=Regular] + * @param {object|undefined} payload + */ + static create(root, id = enumGameModeIds.regular, payload = undefined) { + return new (gGameModeRegistry.findById(id))(root, payload); + } -/** @typedef {{ - * shape: string, - * required: number, - * reward: enumHubGoalRewards, - * throughputOnly?: boolean - * }} LevelDefinition */ - -export class GameMode { /** - * * @param {GameRoot} root */ constructor(root) { + super(); this.root = root; + + /** + * @type {Record} + */ + this.additionalHudParts = {}; + + /** @type {typeof MetaBuilding[]} */ + this.hiddenBuildings = [MetaItemProducerBuilding]; + } + + /** @returns {object} */ + serialize() { + return { + $: this.getId(), + data: super.serialize(), + }; + } + + /** @param {object} savedata */ + deserialize({ data }) { + super.deserialize(data, this.root); + } + + /** @returns {string} */ + getId() { + // @ts-ignore + return this.constructor.getId(); + } + + /** @returns {string} */ + getType() { + // @ts-ignore + return this.constructor.getType(); } /** - * 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 + * @param {typeof MetaBuilding} building - Class name of building * @returns {boolean} */ - getIsFreeplayAvailable() { + isBuildingExcluded(building) { + return this.hiddenBuildings.indexOf(building) >= 0; + } + + /** @returns {undefined|Rectangle[]} */ + getBuildableZones() { + return; + } + + /** @returns {Rectangle|undefined} */ + getCameraBounds() { + return; + } + + /** @returns {boolean} */ + hasHub() { return true; } + + /** @returns {boolean} */ + hasResources() { + return true; + } + + /** @returns {boolean} */ + hasAchievements() { + return false; + } + + /** @returns {number} */ + getMinimumZoom() { + return 0.1; + } + + /** @returns {number} */ + getMaximumZoom() { + return 3.5; + } + + /** @returns {Object} */ + getUpgrades() { + return { + belt: [], + miner: [], + processors: [], + painting: [], + }; + } + + throughputDoesNotMatter() { + return false; + } + + /** + * @param {number} w + * @param {number} h + */ + adjustZone(w = 0, h = 0) { + abstract; + return; + } + + /** @returns {array} */ + getLevelDefinitions() { + return []; + } + + /** @returns {boolean} */ + getIsFreeplayAvailable() { + return false; + } + + /** @returns {boolean} */ + getIsSaveable() { + return true; + } + + /** @returns {boolean} */ + getHasFreeCopyPaste() { + return false; + } + + /** @returns {boolean} */ + getSupportsWires() { + return true; + } + + /** @returns {boolean} */ + getIsEditor() { + return false; + } + + /** @returns {boolean} */ + getIsDeterministic() { + return false; + } + + /** @returns {number | undefined} */ + getFixedTickrate() { + return; + } + + /** @returns {string} */ + getBlueprintShapeKey() { + return "CbCbCbRb:CwCwCwCw"; + } } diff --git a/src/js/game/game_mode_registry.js b/src/js/game/game_mode_registry.js new file mode 100644 index 00000000..03daceb0 --- /dev/null +++ b/src/js/game/game_mode_registry.js @@ -0,0 +1,10 @@ +import { gGameModeRegistry } from "../core/global_registries"; +import { PuzzleEditGameMode } from "./modes/puzzle_edit"; +import { PuzzlePlayGameMode } from "./modes/puzzle_play"; +import { RegularGameMode } from "./modes/regular"; + +export function initGameModeRegistry() { + gGameModeRegistry.register(PuzzleEditGameMode); + gGameModeRegistry.register(PuzzlePlayGameMode); + gGameModeRegistry.register(RegularGameMode); +} diff --git a/src/js/game/game_system_manager.js b/src/js/game/game_system_manager.js index 74ba798f..08609f89 100644 --- a/src/js/game/game_system_manager.js +++ b/src/js/game/game_system_manager.js @@ -24,6 +24,9 @@ import { ItemProcessorOverlaysSystem } from "./systems/item_processor_overlays"; import { BeltReaderSystem } from "./systems/belt_reader"; import { FilterSystem } from "./systems/filter"; import { ItemProducerSystem } from "./systems/item_producer"; +import { ConstantProducerSystem } from "./systems/constant_producer"; +import { GoalAcceptorSystem } from "./systems/goal_acceptor"; +import { ZoneSystem } from "./systems/zone"; const logger = createLogger("game_system_manager"); @@ -100,6 +103,15 @@ export class GameSystemManager { /** @type {ItemProducerSystem} */ itemProducer: null, + /** @type {ConstantProducerSystem} */ + ConstantProducer: null, + + /** @type {GoalAcceptorSystem} */ + GoalAcceptor: null, + + /** @type {ZoneSystem} */ + zone: null, + /* typehints:end */ }; this.systemUpdateOrder = []; @@ -138,7 +150,9 @@ export class GameSystemManager { add("itemEjector", ItemEjectorSystem); - add("mapResources", MapResourcesSystem); + if (this.root.gameMode.hasResources()) { + add("mapResources", MapResourcesSystem); + } add("hub", HubSystem); @@ -165,6 +179,14 @@ export class GameSystemManager { add("itemProcessorOverlays", ItemProcessorOverlaysSystem); + add("constantProducer", ConstantProducerSystem); + + add("goalAcceptor", GoalAcceptorSystem); + + if (this.root.gameMode.getBuildableZones()) { + add("zone", ZoneSystem); + } + logger.log("📦 There are", this.systemUpdateOrder.length, "game systems"); } diff --git a/src/js/game/hub_goals.js b/src/js/game/hub_goals.js index 327b6da7..8351775e 100644 --- a/src/js/game/hub_goals.js +++ b/src/js/game/hub_goals.js @@ -110,7 +110,7 @@ export class HubGoals extends BasicSerializableObject { // Allow quickly switching goals in dev mode if (G_IS_DEV) { window.addEventListener("keydown", ev => { - if (ev.key === "b") { + if (ev.key === "p") { // root is not guaranteed to exist within ~0.5s after loading in if (this.root && this.root.app && this.root.app.gameAnalytics) { if (!this.isEndOfDemoReached()) { @@ -195,6 +195,10 @@ export class HubGoals extends BasicSerializableObject { if (G_IS_DEV && globalConfig.debug.allBuildingsUnlocked) { return true; } + if (this.root.gameMode.getLevelDefinitions().length < 1) { + // no story, so always unlocked + return true; + } return !!this.gainedRewards[reward]; } @@ -472,6 +476,9 @@ export class HubGoals extends BasicSerializableObject { * @returns {number} items / sec */ getBeltBaseSpeed() { + if (this.root.gameMode.throughputDoesNotMatter()) { + return globalConfig.beltSpeedItemsPerSecond * globalConfig.puzzleModeSpeed; + } return globalConfig.beltSpeedItemsPerSecond * this.upgradeImprovements.belt; } @@ -480,6 +487,9 @@ export class HubGoals extends BasicSerializableObject { * @returns {number} items / sec */ getUndergroundBeltBaseSpeed() { + if (this.root.gameMode.throughputDoesNotMatter()) { + return globalConfig.beltSpeedItemsPerSecond * globalConfig.puzzleModeSpeed; + } return globalConfig.beltSpeedItemsPerSecond * this.upgradeImprovements.belt; } @@ -488,6 +498,9 @@ export class HubGoals extends BasicSerializableObject { * @returns {number} items / sec */ getMinerBaseSpeed() { + if (this.root.gameMode.throughputDoesNotMatter()) { + return globalConfig.minerSpeedItemsPerSecond * globalConfig.puzzleModeSpeed; + } return globalConfig.minerSpeedItemsPerSecond * this.upgradeImprovements.miner; } @@ -497,9 +510,14 @@ export class HubGoals extends BasicSerializableObject { * @returns {number} items / sec */ getProcessorBaseSpeed(processorType) { + if (this.root.gameMode.throughputDoesNotMatter()) { + return globalConfig.beltSpeedItemsPerSecond * globalConfig.puzzleModeSpeed * 10; + } + switch (processorType) { case enumItemProcessorTypes.trash: case enumItemProcessorTypes.hub: + case enumItemProcessorTypes.goal: return 1e30; case enumItemProcessorTypes.balancer: return globalConfig.beltSpeedItemsPerSecond * this.upgradeImprovements.belt * 2; diff --git a/src/js/game/hud/hud.js b/src/js/game/hud/hud.js index f3c448dd..88dc0d62 100644 --- a/src/js/game/hud/hud.js +++ b/src/js/game/hud/hud.js @@ -1,55 +1,32 @@ -/* typehints:start */ -import { GameRoot } from "../root"; -/* typehints:end */ - -/* dev:start */ -import { TrailerMaker } from "./trailer_maker"; -/* dev:end */ - -import { Signal } from "../../core/signal"; +import { globalConfig } from "../../core/config"; import { DrawParameters } from "../../core/draw_parameters"; +import { Signal } from "../../core/signal"; +import { KEYMAPPINGS } from "../key_action_mapper"; +import { MetaBuilding } from "../meta_building"; +import { GameRoot } from "../root"; +import { ShapeDefinition } from "../shape_definition"; +import { HUDAchievements } from "./parts/achievements"; +import { HUDBetaOverlay } from "./parts/beta_overlay"; import { HUDBuildingsToolbar } from "./parts/buildings_toolbar"; import { HUDBuildingPlacer } from "./parts/building_placer"; -import { HUDBlueprintPlacer } from "./parts/blueprint_placer"; -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 } from "../../core/config"; -import { HUDMassSelector } from "./parts/mass_selector"; -import { HUDVignetteOverlay } from "./parts/vignette_overlay"; -import { HUDStatistics } from "./parts/statistics"; -import { MetaBuilding } from "../meta_building"; -import { HUDPinnedShapes } from "./parts/pinned_shapes"; -import { ShapeDefinition } from "../shape_definition"; -import { HUDNotifications, enumNotificationType } from "./parts/notifications"; -import { HUDSettingsMenu } from "./parts/settings_menu"; +import { HUDColorBlindHelper } from "./parts/color_blind_helper"; +import { HUDConstantSignalEdit } from "./parts/constant_signal_edit"; +import { HUDChangesDebugger } from "./parts/debug_changes"; import { HUDDebugInfo } from "./parts/debug_info"; import { HUDEntityDebugger } from "./parts/entity_debugger"; -import { KEYMAPPINGS } from "../key_action_mapper"; -import { HUDWatermark } from "./parts/watermark"; -import { HUDModalDialogs } from "./parts/modal_dialogs"; -import { HUDPartTutorialHints } from "./parts/tutorial_hints"; -import { HUDWaypoints } from "./parts/waypoints"; -import { HUDInteractiveTutorial } from "./parts/interactive_tutorial"; -import { HUDScreenshotExporter } from "./parts/screenshot_exporter"; -import { HUDColorBlindHelper } from "./parts/color_blind_helper"; -import { HUDShapeViewer } from "./parts/shape_viewer"; -import { HUDWiresOverlay } from "./parts/wires_overlay"; -import { HUDChangesDebugger } from "./parts/debug_changes"; -import { queryParamOptions } from "../../core/query_parameters"; -import { HUDSandboxController } from "./parts/sandbox_controller"; -import { HUDWiresToolbar } from "./parts/wires_toolbar"; -import { HUDWireInfo } from "./parts/wire_info"; +import { HUDGameMenu } from "./parts/game_menu"; 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 { HUDStandaloneAdvantages } from "./parts/standalone_advantages"; -import { HUDCatMemes } from "./parts/cat_memes"; -import { HUDTutorialVideoOffer } from "./parts/tutorial_video_offer"; -import { HUDConstantSignalEdit } from "./parts/constant_signal_edit"; -import { HUDAchievements } from "./parts/achievements"; +import { HUDMassSelector } from "./parts/mass_selector"; +import { HUDModalDialogs } from "./parts/modal_dialogs"; +import { enumNotificationType } from "./parts/notifications"; +import { HUDSettingsMenu } from "./parts/settings_menu"; +import { HUDShop } from "./parts/shop"; +import { HUDStatistics } from "./parts/statistics"; +import { HUDUnlockNotification } from "./parts/unlock_notification"; +import { HUDVignetteOverlay } from "./parts/vignette_overlay"; +import { HUDWaypoints } from "./parts/waypoints"; +import { HUDWireInfo } from "./parts/wire_info"; +import { TrailerMaker } from "./trailer_maker"; export class GameHUD { /** @@ -77,8 +54,6 @@ export class GameHUD { this.parts = { buildingsToolbar: new HUDBuildingsToolbar(this.root), - wiresToolbar: new HUDWiresToolbar(this.root), - blueprintPlacer: new HUDBlueprintPlacer(this.root), buildingPlacer: new HUDBuildingPlacer(this.root), unlockNotification: new HUDUnlockNotification(this.root), gameMenu: new HUDGameMenu(this.root), @@ -92,19 +67,9 @@ export class GameHUD { constantSignalEdit: new HUDConstantSignalEdit(this.root), // Must always exist - pinnedShapes: new HUDPinnedShapes(this.root), - notifications: new HUDNotifications(this.root), settingsMenu: new HUDSettingsMenu(this.root), debugInfo: new HUDDebugInfo(this.root), dialogs: new HUDModalDialogs(this.root), - screenshotExporter: new HUDScreenshotExporter(this.root), - shapeViewer: new HUDShapeViewer(this.root), - - wiresOverlay: new HUDWiresOverlay(this.root), - layerPreview: new HUDLayerPreview(this.root), - - minerHighlight: new HUDMinerHighlight(this.root), - tutorialVideoOffer: new HUDTutorialVideoOffer(this.root), // Typing hints /* typehints:start */ @@ -113,29 +78,14 @@ export class GameHUD { /* typehints:end */ }; - if (!IS_MOBILE) { - this.parts.keybindingOverlay = new HUDKeybindingOverlay(this.root); - } - if (G_IS_DEV && globalConfig.debug.enableEntityInspector) { this.parts.entityDebugger = new HUDEntityDebugger(this.root); } - 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); - } - if (G_IS_DEV && globalConfig.debug.renderChanges) { this.parts.changesDebugger = new HUDChangesDebugger(this.root); } - if (this.root.app.settings.getAllSettings().offerHints) { - this.parts.tutorialHints = new HUDPartTutorialHints(this.root); - this.parts.interactiveTutorial = new HUDInteractiveTutorial(this.root); - } - if (this.root.app.settings.getAllSettings().vignette) { this.parts.vignetteOverlay = new HUDVignetteOverlay(this.root); } @@ -144,14 +94,15 @@ export class GameHUD { this.parts.colorBlindHelper = new HUDColorBlindHelper(this.root); } - if (queryParamOptions.sandboxMode || G_IS_DEV) { - this.parts.sandboxController = new HUDSandboxController(this.root); - } - if (!G_IS_RELEASE && !G_IS_DEV) { this.parts.betaOverlay = new HUDBetaOverlay(this.root); } + const additionalParts = this.root.gameMode.additionalHudParts; + for (const [partId, part] of Object.entries(additionalParts)) { + this.parts[partId] = new part(this.root); + } + const frag = document.createDocumentFragment(); for (const key in this.parts) { this.parts[key].createElements(frag); diff --git a/src/js/game/hud/parts/base_toolbar.js b/src/js/game/hud/parts/base_toolbar.js index b3f5abfc..15faad66 100644 --- a/src/js/game/hud/parts/base_toolbar.js +++ b/src/js/game/hud/parts/base_toolbar.js @@ -1,6 +1,10 @@ import { gMetaBuildingRegistry } from "../../../core/global_registries"; import { STOP_PROPAGATION } from "../../../core/signal"; import { makeDiv, safeModulo } from "../../../core/utils"; +import { MetaBlockBuilding } from "../../buildings/block"; +import { MetaConstantProducerBuilding } from "../../buildings/constant_producer"; +import { MetaGoalAcceptorBuilding } from "../../buildings/goal_acceptor"; +import { StaticMapEntityComponent } from "../../components/static_map_entity"; import { KEYMAPPINGS } from "../../key_action_mapper"; import { MetaBuilding } from "../../meta_building"; import { GameRoot } from "../../root"; @@ -23,8 +27,8 @@ export class HUDBaseToolbar extends BaseHUDPart { ) { super(root); - this.primaryBuildings = primaryBuildings; - this.secondaryBuildings = secondaryBuildings; + this.primaryBuildings = this.filterBuildings(primaryBuildings); + this.secondaryBuildings = this.filterBuildings(secondaryBuildings); this.visibilityCondition = visibilityCondition; this.htmlElementId = htmlElementId; this.layer = layer; @@ -35,6 +39,7 @@ export class HUDBaseToolbar extends BaseHUDPart { * selected: boolean, * element: HTMLElement, * index: number + * puzzleLocked: boolean; * }>} */ this.buildingHandles = {}; } @@ -47,6 +52,24 @@ export class HUDBaseToolbar extends BaseHUDPart { this.element = makeDiv(parent, this.htmlElementId, ["ingame_buildingsToolbar"], ""); } + /** + * @param {Array} buildings + * @returns {Array} + */ + filterBuildings(buildings) { + const filtered = []; + + for (let i = 0; i < buildings.length; i++) { + if (this.root.gameMode.isBuildingExcluded(buildings[i])) { + continue; + } + + filtered.push(buildings[i]); + } + + return filtered; + } + /** * Returns all buildings * @returns {Array} @@ -87,19 +110,31 @@ export class HUDBaseToolbar extends BaseHUDPart { ); itemContainer.setAttribute("data-icon", "building_icons/" + metaBuilding.getId() + ".png"); itemContainer.setAttribute("data-id", metaBuilding.getId()); - binding.add(() => this.selectBuildingForPlacement(metaBuilding)); - this.trackClicks(itemContainer, () => this.selectBuildingForPlacement(metaBuilding), { + const icon = makeDiv(itemContainer, null, ["icon"]); + + this.trackClicks(icon, () => this.selectBuildingForPlacement(metaBuilding), { clickSound: null, }); + //lock icon for puzzle editor + if (this.root.gameMode.getIsEditor() && !this.inRequiredBuildings(metaBuilding)) { + const puzzleLock = makeDiv(itemContainer, null, ["puzzle-lock"]); + + itemContainer.classList.toggle("editor", true); + this.trackClicks(puzzleLock, () => this.toggleBuildingLock(metaBuilding), { + clickSound: null, + }); + } + this.buildingHandles[metaBuilding.id] = { - metaBuilding, + metaBuilding: metaBuilding, element: itemContainer, unlocked: false, selected: false, index: i, + puzzleLocked: false, }; } @@ -127,7 +162,7 @@ export class HUDBaseToolbar extends BaseHUDPart { let recomputeSecondaryToolbarVisibility = false; for (const buildingId in this.buildingHandles) { const handle = this.buildingHandles[buildingId]; - const newStatus = handle.metaBuilding.getIsUnlocked(this.root); + const newStatus = !handle.puzzleLocked && handle.metaBuilding.getIsUnlocked(this.root); if (handle.unlocked !== newStatus) { handle.unlocked = newStatus; handle.element.classList.toggle("unlocked", newStatus); @@ -216,6 +251,14 @@ export class HUDBaseToolbar extends BaseHUDPart { return STOP_PROPAGATION; } + const handle = this.buildingHandles[metaBuilding.getId()]; + if (handle.puzzleLocked) { + handle.puzzleLocked = false; + handle.element.classList.toggle("unlocked", false); + this.root.soundProxy.playUiClick(); + return; + } + // Allow clicking an item again to deselect it for (const buildingId in this.buildingHandles) { const handle = this.buildingHandles[buildingId]; @@ -229,4 +272,51 @@ export class HUDBaseToolbar extends BaseHUDPart { this.root.hud.signals.buildingSelectedForPlacement.dispatch(metaBuilding); this.onSelectedPlacementBuildingChanged(metaBuilding); } + + /** + * @param {MetaBuilding} metaBuilding + */ + toggleBuildingLock(metaBuilding) { + if (!this.visibilityCondition()) { + // Not active + return; + } + + if (this.inRequiredBuildings(metaBuilding) || !metaBuilding.getIsUnlocked(this.root)) { + this.root.soundProxy.playUiError(); + return STOP_PROPAGATION; + } + + const handle = this.buildingHandles[metaBuilding.getId()]; + handle.puzzleLocked = !handle.puzzleLocked; + handle.element.classList.toggle("unlocked", !handle.puzzleLocked); + this.root.soundProxy.playUiClick(); + + const entityManager = this.root.entityMgr; + for (const entity of entityManager.getAllWithComponent(StaticMapEntityComponent)) { + const staticComp = entity.components.StaticMapEntity; + if (staticComp.getMetaBuilding().id === metaBuilding.id) { + this.root.map.removeStaticEntity(entity); + entityManager.destroyEntity(entity); + } + } + entityManager.processDestroyList(); + + const currentMetaBuilding = this.root.hud.parts.buildingPlacer.currentMetaBuilding; + if (currentMetaBuilding.get() == metaBuilding) { + currentMetaBuilding.set(null); + } + } + + /** + * @param {MetaBuilding} metaBuilding + */ + inRequiredBuildings(metaBuilding) { + const requiredBuildings = [ + gMetaBuildingRegistry.findByClass(MetaConstantProducerBuilding), + gMetaBuildingRegistry.findByClass(MetaGoalAcceptorBuilding), + gMetaBuildingRegistry.findByClass(MetaBlockBuilding), + ]; + return requiredBuildings.includes(metaBuilding); + } } diff --git a/src/js/game/hud/parts/blueprint_placer.js b/src/js/game/hud/parts/blueprint_placer.js index 54e2e3b7..4b2bafb2 100644 --- a/src/js/game/hud/parts/blueprint_placer.js +++ b/src/js/game/hud/parts/blueprint_placer.js @@ -50,6 +50,10 @@ export class HUDBlueprintPlacer extends BaseHUDPart { this.trackedCanAfford = new TrackedState(this.onCanAffordChanged, this); } + getHasFreeCopyPaste() { + return this.root.gameMode.getHasFreeCopyPaste(); + } + abortPlacement() { if (this.currentBlueprint.get()) { this.currentBlueprint.set(null); @@ -82,7 +86,9 @@ export class HUDBlueprintPlacer extends BaseHUDPart { update() { const currentBlueprint = this.currentBlueprint.get(); - this.domAttach.update(currentBlueprint && currentBlueprint.getCost() > 0); + this.domAttach.update( + !this.getHasFreeCopyPaste() && currentBlueprint && currentBlueprint.getCost() > 0 + ); this.trackedCanAfford.set(currentBlueprint && currentBlueprint.canAfford(this.root)); } @@ -114,7 +120,7 @@ export class HUDBlueprintPlacer extends BaseHUDPart { return; } - if (!blueprint.canAfford(this.root)) { + if (!this.getHasFreeCopyPaste() && !blueprint.canAfford(this.root)) { this.root.soundProxy.playUiError(); return; } @@ -122,8 +128,10 @@ export class HUDBlueprintPlacer extends BaseHUDPart { 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); + if (!this.getHasFreeCopyPaste()) { + const cost = blueprint.getCost(); + this.root.hubGoals.takeShapeByKey(this.root.gameMode.getBlueprintShapeKey(), cost); + } this.root.soundProxy.playUi(SOUNDS.placeBuilding); } return STOP_PROPAGATION; @@ -131,7 +139,7 @@ export class HUDBlueprintPlacer extends BaseHUDPart { } /** - * Mose move handler + * Mouse move handler */ onMouseMove() { // Prevent movement while blueprint is selected diff --git a/src/js/game/hud/parts/building_placer.js b/src/js/game/hud/parts/building_placer.js index 7d618b6b..33e6ebc2 100644 --- a/src/js/game/hud/parts/building_placer.js +++ b/src/js/game/hud/parts/building_placer.js @@ -234,7 +234,7 @@ export class HUDBuildingPlacer extends HUDBuildingPlacerLogic { * @param {DrawParameters} parameters */ draw(parameters) { - if (this.root.camera.zoomLevel < globalConfig.mapChunkOverviewMinZoom) { + if (this.root.camera.getIsMapOverlayActive()) { // Dont allow placing in overview mode this.domAttach.update(false); this.variantsAttach.update(false); @@ -275,11 +275,13 @@ export class HUDBuildingPlacer extends HUDBuildingPlacerLogic { const worldPosition = this.root.camera.screenToWorld(mousePosition); // Draw peeker - this.root.hud.parts.layerPreview.renderPreview( - parameters, - worldPosition, - 1 / this.root.camera.zoomLevel - ); + if (this.root.hud.parts.layerPreview) { + this.root.hud.parts.layerPreview.renderPreview( + parameters, + worldPosition, + 1 / this.root.camera.zoomLevel + ); + } } /** diff --git a/src/js/game/hud/parts/building_placer_logic.js b/src/js/game/hud/parts/building_placer_logic.js index 1e88abc7..9e91f372 100644 --- a/src/js/game/hud/parts/building_placer_logic.js +++ b/src/js/game/hud/parts/building_placer_logic.js @@ -366,7 +366,8 @@ export class HUDBuildingPlacerLogic extends BaseHUDPart { if ( tileBelow && this.root.app.settings.getAllSettings().pickMinerOnPatch && - this.root.currentLayer === "regular" + this.root.currentLayer === "regular" && + this.root.gameMode.hasResources() ) { this.currentMetaBuilding.set(gMetaBuildingRegistry.findByClass(MetaMinerBuilding)); @@ -390,6 +391,12 @@ export class HUDBuildingPlacerLogic extends BaseHUDPart { return; } + // Disallow picking excluded buildings + if (this.root.gameMode.isBuildingExcluded(extracted.metaClass)) { + this.currentMetaBuilding.set(null); + return; + } + // If the building we are picking is the same as the one we have, clear the cursor. if ( this.currentMetaBuilding.get() && @@ -430,7 +437,7 @@ export class HUDBuildingPlacerLogic extends BaseHUDPart { * @param {Vector} tile */ tryPlaceCurrentBuildingAt(tile) { - if (this.root.camera.zoomLevel < globalConfig.mapChunkOverviewMinZoom) { + if (this.root.camera.getIsMapOverlayActive()) { // Dont allow placing in overview mode return; } diff --git a/src/js/game/hud/parts/buildings_toolbar.js b/src/js/game/hud/parts/buildings_toolbar.js index 05ffc795..994a70ed 100644 --- a/src/js/game/hud/parts/buildings_toolbar.js +++ b/src/js/game/hud/parts/buildings_toolbar.js @@ -15,23 +15,28 @@ import { MetaUndergroundBeltBuilding } from "../../buildings/underground_belt"; import { HUDBaseToolbar } from "./base_toolbar"; import { MetaStorageBuilding } from "../../buildings/storage"; import { MetaItemProducerBuilding } from "../../buildings/item_producer"; -import { queryParamOptions } from "../../../core/query_parameters"; +import { MetaConstantProducerBuilding } from "../../buildings/constant_producer"; +import { MetaGoalAcceptorBuilding } from "../../buildings/goal_acceptor"; +import { MetaBlockBuilding } from "../../buildings/block"; export class HUDBuildingsToolbar extends HUDBaseToolbar { constructor(root) { super(root, { primaryBuildings: [ + MetaConstantProducerBuilding, + MetaGoalAcceptorBuilding, MetaBeltBuilding, MetaBalancerBuilding, MetaUndergroundBeltBuilding, MetaMinerBuilding, + MetaBlockBuilding, MetaCutterBuilding, MetaRotaterBuilding, MetaStackerBuilding, MetaMixerBuilding, MetaPainterBuilding, MetaTrashBuilding, - ...(queryParamOptions.sandboxMode || G_IS_DEV ? [MetaItemProducerBuilding] : []), + MetaItemProducerBuilding, ], secondaryBuildings: [ MetaStorageBuilding, diff --git a/src/js/game/hud/parts/keybinding_overlay.js b/src/js/game/hud/parts/keybinding_overlay.js index 65919d3c..2384ab84 100644 --- a/src/js/game/hud/parts/keybinding_overlay.js +++ b/src/js/game/hud/parts/keybinding_overlay.js @@ -254,6 +254,13 @@ export class HUDKeybindingOverlay extends BaseHUDPart { condition: () => this.anythingSelectedOnMap, }, + { + // [SELECTION] Clear + label: T.ingame.keybindingsOverlay.clearBelts, + keys: [k.massSelect.massSelectClear], + condition: () => this.anythingSelectedOnMap, + }, + { // Switch layers label: T.ingame.keybindingsOverlay.switchLayers, diff --git a/src/js/game/hud/parts/mass_selector.js b/src/js/game/hud/parts/mass_selector.js index d73e3be3..b8283d55 100644 --- a/src/js/game/hud/parts/mass_selector.js +++ b/src/js/game/hud/parts/mass_selector.js @@ -1,20 +1,22 @@ -import { BaseHUDPart } from "../base_hud_part"; -import { Vector } from "../../../core/vector"; -import { STOP_PROPAGATION } from "../../../core/signal"; -import { DrawParameters } from "../../../core/draw_parameters"; -import { Entity } from "../../entity"; -import { Loader } from "../../../core/loader"; import { globalConfig } from "../../../core/config"; -import { makeDiv, formatBigNumber, formatBigNumberFull } from "../../../core/utils"; -import { DynamicDomAttach } from "../dynamic_dom_attach"; +import { DrawParameters } from "../../../core/draw_parameters"; +import { gMetaBuildingRegistry } from "../../../core/global_registries"; import { createLogger } from "../../../core/logging"; +import { STOP_PROPAGATION } from "../../../core/signal"; +import { formatBigNumberFull } from "../../../core/utils"; +import { Vector } from "../../../core/vector"; import { ACHIEVEMENTS } from "../../../platform/achievement_provider"; -import { enumMouseButton } from "../../camera"; import { T } from "../../../translations"; +import { Blueprint } from "../../blueprint"; +import { MetaBlockBuilding } from "../../buildings/block"; +import { MetaConstantProducerBuilding } from "../../buildings/constant_producer"; +import { enumMouseButton } from "../../camera"; +import { Component } from "../../component"; +import { Entity } from "../../entity"; import { KEYMAPPINGS } from "../../key_action_mapper"; import { THEME } from "../../theme"; import { enumHubGoalRewards } from "../../tutorial_goals"; -import { Blueprint } from "../../blueprint"; +import { BaseHUDPart } from "../base_hud_part"; const logger = createLogger("hud/mass_selector"); @@ -33,12 +35,13 @@ export class HUDMassSelector extends BaseHUDPart { this.root.camera.movePreHandler.add(this.onMouseMove, this); this.root.camera.upPostHandler.add(this.onMouseUp, this); - this.root.keyMapper.getBinding(KEYMAPPINGS.general.back).add(this.onBack, this); + this.root.keyMapper.getBinding(KEYMAPPINGS.general.back).addToTop(this.onBack, this); this.root.keyMapper .getBinding(KEYMAPPINGS.massSelect.confirmMassDelete) .add(this.confirmDelete, this); this.root.keyMapper.getBinding(KEYMAPPINGS.massSelect.massSelectCut).add(this.confirmCut, this); this.root.keyMapper.getBinding(KEYMAPPINGS.massSelect.massSelectCopy).add(this.startCopy, this); + this.root.keyMapper.getBinding(KEYMAPPINGS.massSelect.massSelectClear).add(this.clearBelts, this); this.root.hud.signals.selectedPlacementBuildingChanged.add(this.clearSelection, this); this.root.signals.editModeChanged.add(this.clearSelection, this); @@ -142,6 +145,16 @@ export class HUDMassSelector extends BaseHUDPart { } } + clearBelts() { + for (const uid of this.selectedUids) { + const entity = this.root.entityMgr.findByUid(uid); + for (const component of Object.values(entity.components)) { + /** @type {Component} */ (component).clear(); + } + } + this.selectedUids = new Set(); + } + confirmCut() { if (!this.root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_blueprints)) { this.root.hud.parts.dialogs.showInfo( @@ -250,7 +263,14 @@ export class HUDMassSelector extends BaseHUDPart { for (let x = realTileStart.x; x <= realTileEnd.x; ++x) { for (let y = realTileStart.y; y <= realTileEnd.y; ++y) { const contents = this.root.map.getLayerContentXY(x, y, this.root.currentLayer); + if (contents && this.root.logic.canDeleteBuilding(contents)) { + const staticComp = contents.components.StaticMapEntity; + + if (!staticComp.getMetaBuilding().getIsRemovable(this.root)) { + continue; + } + this.selectedUids.add(contents.uid); } } @@ -310,6 +330,11 @@ export class HUDMassSelector extends BaseHUDPart { renderedUids.add(uid); const staticComp = contents.components.StaticMapEntity; + + if (!staticComp.getMetaBuilding().getIsRemovable(this.root)) { + continue; + } + const bounds = staticComp.getTileSpaceBounds(); parameters.context.beginRoundedRect( bounds.x * globalConfig.tileSize + boundsBorder, diff --git a/src/js/game/hud/parts/modal_dialogs.js b/src/js/game/hud/parts/modal_dialogs.js index 263b23dd..a43260e3 100644 --- a/src/js/game/hud/parts/modal_dialogs.js +++ b/src/js/game/hud/parts/modal_dialogs.js @@ -29,11 +29,14 @@ export class HUDModalDialogs extends BaseHUDPart { } shouldPauseRendering() { - return this.dialogStack.length > 0; + // return this.dialogStack.length > 0; + // @todo: Check if change this affects anything + return false; } shouldPauseGame() { - return this.shouldPauseRendering(); + // @todo: Check if this change affects anything + return false; } createElements(parent) { @@ -139,8 +142,8 @@ export class HUDModalDialogs extends BaseHUDPart { } // Returns method to be called when laoding finishd - showLoadingDialog() { - const dialog = new DialogLoading(this.app); + showLoadingDialog(text = "") { + const dialog = new DialogLoading(this.app, text); this.internalShowDialog(dialog); return this.closeDialog.bind(this, dialog); } diff --git a/src/js/game/hud/parts/pinned_shapes.js b/src/js/game/hud/parts/pinned_shapes.js index 4a9fce0d..a53ecbe5 100644 --- a/src/js/game/hud/parts/pinned_shapes.js +++ b/src/js/game/hud/parts/pinned_shapes.js @@ -55,7 +55,7 @@ export class HUDPinnedShapes extends BaseHUDPart { */ deserialize(data) { if (!data || !data.shapes || !Array.isArray(data.shapes)) { - return "Invalid pinned shapes data"; + return "Invalid pinned shapes data: " + JSON.stringify(data); } this.pinnedShapes = data.shapes; } diff --git a/src/js/game/hud/parts/puzzle_back_to_menu.js b/src/js/game/hud/parts/puzzle_back_to_menu.js new file mode 100644 index 00000000..bde5b66d --- /dev/null +++ b/src/js/game/hud/parts/puzzle_back_to_menu.js @@ -0,0 +1,21 @@ +import { makeDiv } from "../../../core/utils"; +import { BaseHUDPart } from "../base_hud_part"; + +export class HUDPuzzleBackToMenu extends BaseHUDPart { + createElements(parent) { + const key = this.root.gameMode.getId(); + + this.element = makeDiv(parent, "ingame_HUD_PuzzleBackToMenu"); + this.button = document.createElement("button"); + this.button.classList.add("button"); + this.element.appendChild(this.button); + + this.trackClicks(this.button, this.back); + } + + initialize() {} + + back() { + this.root.gameState.goBackToMenu(); + } +} diff --git a/src/js/game/hud/parts/puzzle_complete_notification.js b/src/js/game/hud/parts/puzzle_complete_notification.js new file mode 100644 index 00000000..f223c1d6 --- /dev/null +++ b/src/js/game/hud/parts/puzzle_complete_notification.js @@ -0,0 +1,112 @@ +/* typehints:start */ +import { PuzzlePlayGameMode } from "../../modes/puzzle_play"; +/* typehints:end */ + +import { InputReceiver } from "../../../core/input_receiver"; +import { makeDiv } from "../../../core/utils"; +import { SOUNDS } from "../../../platform/sound"; +import { T } from "../../../translations"; +import { enumColors } from "../../colors"; +import { ColorItem } from "../../items/color_item"; +import { finalGameShape, rocketShape } from "../../modes/regular"; +import { BaseHUDPart } from "../base_hud_part"; +import { DynamicDomAttach } from "../dynamic_dom_attach"; +import { ShapeItem } from "../../items/shape_item"; +import { ShapeDefinition } from "../../shape_definition"; + +export class HUDPuzzleCompleteNotification extends BaseHUDPart { + initialize() { + this.visible = false; + + this.domAttach = new DynamicDomAttach(this.root, this.element, { + timeToKeepSeconds: 0, + }); + + this.root.signals.puzzleComplete.add(this.show, this); + + this.userDidLikePuzzle = false; + this.timeOfCompletion = 0; + } + + createElements(parent) { + this.inputReciever = new InputReceiver("puzzle-complete"); + + this.element = makeDiv(parent, "ingame_HUD_PuzzleCompleteNotification", ["noBlur"]); + + const dialog = makeDiv(this.element, null, ["dialog"]); + + this.elemTitle = makeDiv(dialog, null, ["title"], T.ingame.puzzleCompletion.title); + this.elemContents = makeDiv(dialog, null, ["contents"]); + this.elemActions = makeDiv(dialog, null, ["actions"]); + + const stepLike = makeDiv(this.elemContents, null, ["step", "stepLike"]); + makeDiv(stepLike, null, ["title"], T.ingame.puzzleCompletion.titleLike); + + const likeButtons = makeDiv(stepLike, null, ["buttons"]); + + this.buttonLikeYes = document.createElement("button"); + this.buttonLikeYes.classList.add("liked-yes"); + likeButtons.appendChild(this.buttonLikeYes); + this.trackClicks(this.buttonLikeYes, () => { + this.userDidLikePuzzle = !this.userDidLikePuzzle; + this.updateState(); + }); + + const buttonBar = document.createElement("div"); + buttonBar.classList.add("buttonBar"); + this.elemContents.appendChild(buttonBar); + + this.continueBtn = document.createElement("button"); + this.continueBtn.classList.add("continue", "styledButton"); + this.continueBtn.innerText = T.ingame.puzzleCompletion.continueBtn; + buttonBar.appendChild(this.continueBtn); + this.trackClicks(this.continueBtn, () => { + this.close(false); + }); + + this.menuBtn = document.createElement("button"); + this.menuBtn.classList.add("menu", "styledButton"); + this.menuBtn.innerText = T.ingame.puzzleCompletion.menuBtn; + buttonBar.appendChild(this.menuBtn); + + this.trackClicks(this.menuBtn, () => { + this.close(true); + }); + } + + updateState() { + this.buttonLikeYes.classList.toggle("active", this.userDidLikePuzzle === true); + } + + show() { + this.root.soundProxy.playUi(SOUNDS.levelComplete); + this.root.app.inputMgr.makeSureAttachedAndOnTop(this.inputReciever); + this.visible = true; + this.timeOfCompletion = this.root.time.now(); + } + + cleanup() { + this.root.app.inputMgr.makeSureDetached(this.inputReciever); + } + + isBlockingOverlay() { + return this.visible; + } + + close(toMenu) { + /** @type {PuzzlePlayGameMode} */ (this.root.gameMode) + .trackCompleted(this.userDidLikePuzzle, Math.round(this.timeOfCompletion)) + .then(() => { + if (toMenu) { + this.root.gameState.moveToState("PuzzleMenuState"); + } else { + this.visible = false; + this.cleanup(); + } + }); + } + + update() { + this.domAttach.update(this.visible); + } +} diff --git a/src/js/game/hud/parts/puzzle_dlc_logo.js b/src/js/game/hud/parts/puzzle_dlc_logo.js new file mode 100644 index 00000000..69519c0d --- /dev/null +++ b/src/js/game/hud/parts/puzzle_dlc_logo.js @@ -0,0 +1,14 @@ +import { makeDiv } from "../../../core/utils"; +import { BaseHUDPart } from "../base_hud_part"; + +export class HUDPuzzleDLCLogo extends BaseHUDPart { + createElements(parent) { + this.element = makeDiv(parent, "ingame_HUD_PuzzleDLCLogo"); + this.element.classList.toggle("china", G_CHINA_VERSION || G_WEGAME_VERSION); + parent.appendChild(this.element); + } + + initialize() {} + + next() {} +} diff --git a/src/js/game/hud/parts/puzzle_editor_controls.js b/src/js/game/hud/parts/puzzle_editor_controls.js new file mode 100644 index 00000000..d8055f11 --- /dev/null +++ b/src/js/game/hud/parts/puzzle_editor_controls.js @@ -0,0 +1,18 @@ +import { makeDiv } from "../../../core/utils"; +import { T } from "../../../translations"; +import { BaseHUDPart } from "../base_hud_part"; + +export class HUDPuzzleEditorControls extends BaseHUDPart { + createElements(parent) { + this.element = makeDiv(parent, "ingame_HUD_PuzzleEditorControls"); + + this.element.innerHTML = T.ingame.puzzleEditorControls.instructions + .map(text => `${text}`) + .join(""); + + this.titleElement = makeDiv(parent, "ingame_HUD_PuzzleEditorTitle"); + this.titleElement.innerText = T.ingame.puzzleEditorControls.title; + } + + initialize() {} +} diff --git a/src/js/game/hud/parts/puzzle_editor_review.js b/src/js/game/hud/parts/puzzle_editor_review.js new file mode 100644 index 00000000..727006d6 --- /dev/null +++ b/src/js/game/hud/parts/puzzle_editor_review.js @@ -0,0 +1,233 @@ +import { globalConfig, THIRDPARTY_URLS } from "../../../core/config"; +import { createLogger } from "../../../core/logging"; +import { DialogWithForm } from "../../../core/modal_dialog_elements"; +import { FormElementInput, FormElementItemChooser } from "../../../core/modal_dialog_forms"; +import { STOP_PROPAGATION } from "../../../core/signal"; +import { fillInLinkIntoTranslation, makeDiv } from "../../../core/utils"; +import { PuzzleSerializer } from "../../../savegame/puzzle_serializer"; +import { T } from "../../../translations"; +import { ConstantSignalComponent } from "../../components/constant_signal"; +import { GoalAcceptorComponent } from "../../components/goal_acceptor"; +import { StaticMapEntityComponent } from "../../components/static_map_entity"; +import { ShapeItem } from "../../items/shape_item"; +import { ShapeDefinition } from "../../shape_definition"; +import { BaseHUDPart } from "../base_hud_part"; + +const trim = require("trim"); +const logger = createLogger("puzzle-review"); + +export class HUDPuzzleEditorReview extends BaseHUDPart { + constructor(root) { + super(root); + } + + createElements(parent) { + const key = this.root.gameMode.getId(); + + this.element = makeDiv(parent, "ingame_HUD_PuzzleEditorReview"); + this.button = document.createElement("button"); + this.button.classList.add("button"); + this.button.textContent = T.puzzleMenu.reviewPuzzle; + this.element.appendChild(this.button); + + this.trackClicks(this.button, this.startReview); + } + + initialize() {} + + startReview() { + const validationError = this.validatePuzzle(); + if (validationError) { + this.root.hud.parts.dialogs.showWarning(T.puzzleMenu.validation.title, validationError); + return; + } + + const closeLoading = this.root.hud.parts.dialogs.showLoadingDialog(T.puzzleMenu.validatingPuzzle); + + // Wait a bit, so the user sees the puzzle actually got validated + setTimeout(() => { + // Manually simulate ticks + this.root.logic.clearAllBeltsAndItems(); + + const maxTicks = + this.root.gameMode.getFixedTickrate() * globalConfig.puzzleValidationDurationSeconds; + const deltaMs = this.root.dynamicTickrate.deltaMs; + logger.log("Simulating up to", maxTicks, "ticks, start=", this.root.time.now().toFixed(1)); + const now = performance.now(); + + let simulatedTicks = 0; + for (let i = 0; i < maxTicks; ++i) { + // Perform logic tick + this.root.time.performTicks(deltaMs, this.root.gameState.core.boundInternalTick); + simulatedTicks++; + + if (simulatedTicks % 100 == 0 && !this.validatePuzzle()) { + break; + } + } + + const duration = performance.now() - now; + logger.log( + "Simulated", + simulatedTicks, + "ticks, end=", + this.root.time.now().toFixed(1), + "duration=", + duration.toFixed(2), + "ms" + ); + + console.log("duration: " + duration); + closeLoading(); + + //if it took so little ticks that it must have autocompeted + if (simulatedTicks <= 300) { + this.root.hud.parts.dialogs.showWarning( + T.puzzleMenu.validation.title, + T.puzzleMenu.validation.autoComplete + ); + return; + } + + //if we reached maximum ticks and the puzzle still isn't completed + const validationError = this.validatePuzzle(); + if (simulatedTicks == maxTicks && validationError) { + this.root.hud.parts.dialogs.showWarning(T.puzzleMenu.validation.title, validationError); + return; + } + this.startSubmit(); + }, 750); + } + + startSubmit(title = "", shortKey = "") { + const regex = /^[a-zA-Z0-9_\- ]{4,20}$/; + const nameInput = new FormElementInput({ + id: "nameInput", + label: T.dialogs.submitPuzzle.descName, + placeholder: T.dialogs.submitPuzzle.placeholderName, + defaultValue: title, + validator: val => trim(val).match(regex) && trim(val).length > 0, + }); + + let items = new Set(); + const acceptors = this.root.entityMgr.getAllWithComponent(GoalAcceptorComponent); + for (const acceptor of acceptors) { + const item = acceptor.components.GoalAcceptor.item; + if (item.getItemType() === "shape") { + items.add(item); + } + } + + while (items.size < 8) { + // add some randoms + const item = this.root.hubGoals.computeFreeplayShape(Math.round(10 + Math.random() * 10000)); + items.add(new ShapeItem(item)); + } + + const itemInput = new FormElementItemChooser({ + id: "signalItem", + label: fillInLinkIntoTranslation(T.dialogs.submitPuzzle.descIcon, THIRDPARTY_URLS.shapeViewer), + items: Array.from(items), + }); + + const shapeKeyInput = new FormElementInput({ + id: "shapeKeyInput", + label: null, + placeholder: "CuCuCuCu", + defaultValue: shortKey, + validator: val => ShapeDefinition.isValidShortKey(trim(val)), + }); + + const dialog = new DialogWithForm({ + app: this.root.app, + title: T.dialogs.submitPuzzle.title, + desc: "", + formElements: [nameInput, itemInput, shapeKeyInput], + buttons: ["ok:good:enter"], + }); + + itemInput.valueChosen.add(value => { + shapeKeyInput.setValue(value.definition.getHash()); + }); + + this.root.hud.parts.dialogs.internalShowDialog(dialog); + + dialog.buttonSignals.ok.add(() => { + const title = trim(nameInput.getValue()); + const shortKey = trim(shapeKeyInput.getValue()); + this.doSubmitPuzzle(title, shortKey); + }); + } + + doSubmitPuzzle(title, shortKey) { + const serialized = new PuzzleSerializer().generateDumpFromGameRoot(this.root); + + logger.log("Submitting puzzle, title=", title, "shortKey=", shortKey); + if (G_IS_DEV) { + logger.log("Serialized data:", serialized); + } + + const closeLoading = this.root.hud.parts.dialogs.showLoadingDialog(T.puzzleMenu.submittingPuzzle); + + this.root.app.clientApi + .apiSubmitPuzzle({ + title, + shortKey, + data: serialized, + }) + .then( + () => { + closeLoading(); + const { ok } = this.root.hud.parts.dialogs.showInfo( + T.dialogs.puzzleSubmitOk.title, + T.dialogs.puzzleSubmitOk.desc + ); + ok.add(() => this.root.gameState.moveToState("PuzzleMenuState")); + }, + err => { + closeLoading(); + logger.warn("Failed to submit puzzle:", err); + const signals = this.root.hud.parts.dialogs.showWarning( + T.dialogs.puzzleSubmitError.title, + T.dialogs.puzzleSubmitError.desc + " " + err, + ["cancel", "retry:good"] + ); + signals.retry.add(() => this.startSubmit(title, shortKey)); + } + ); + } + + validatePuzzle() { + // Check there is at least one constant producer and goal acceptor + const producers = this.root.entityMgr.getAllWithComponent(ConstantSignalComponent); + const acceptors = this.root.entityMgr.getAllWithComponent(GoalAcceptorComponent); + + if (producers.length === 0) { + return T.puzzleMenu.validation.noProducers; + } + + if (acceptors.length === 0) { + return T.puzzleMenu.validation.noGoalAcceptors; + } + + // Check if all acceptors satisfy the constraints + for (const acceptor of acceptors) { + const goalComp = acceptor.components.GoalAcceptor; + if (!goalComp.item) { + return T.puzzleMenu.validation.goalAcceptorNoItem; + } + const required = globalConfig.goalAcceptorItemsRequired; + if (goalComp.currentDeliveredItems < required) { + return T.puzzleMenu.validation.goalAcceptorRateNotMet; + } + } + + // Check if all buildings are within the area + const entities = this.root.entityMgr.getAllWithComponent(StaticMapEntityComponent); + for (const entity of entities) { + if (this.root.systemMgr.systems.zone.prePlacementCheck(entity) === STOP_PROPAGATION) { + return T.puzzleMenu.validation.buildingOutOfBounds; + } + } + } +} diff --git a/src/js/game/hud/parts/puzzle_editor_settings.js b/src/js/game/hud/parts/puzzle_editor_settings.js new file mode 100644 index 00000000..11b046bf --- /dev/null +++ b/src/js/game/hud/parts/puzzle_editor_settings.js @@ -0,0 +1,227 @@ +/* typehints:start */ +/* typehints:end */ +import { globalConfig } from "../../../core/config"; +import { gMetaBuildingRegistry } from "../../../core/global_registries"; +import { createLogger } from "../../../core/logging"; +import { Rectangle } from "../../../core/rectangle"; +import { makeDiv } from "../../../core/utils"; +import { T } from "../../../translations"; +import { MetaBlockBuilding } from "../../buildings/block"; +import { MetaConstantProducerBuilding } from "../../buildings/constant_producer"; +import { MetaGoalAcceptorBuilding } from "../../buildings/goal_acceptor"; +import { StaticMapEntityComponent } from "../../components/static_map_entity"; +import { PuzzleGameMode } from "../../modes/puzzle"; +import { BaseHUDPart } from "../base_hud_part"; + +const logger = createLogger("puzzle-editor"); + +export class HUDPuzzleEditorSettings extends BaseHUDPart { + createElements(parent) { + this.element = makeDiv(parent, "ingame_HUD_PuzzleEditorSettings"); + + if (this.root.gameMode.getBuildableZones()) { + const bind = (selector, handler) => + this.trackClicks(this.element.querySelector(selector), handler); + this.zone = makeDiv( + this.element, + null, + ["section", "zone"], + ` + + +
+
+ + + + +
+ +
+ + + + +
+ +
+ + +
+ +
+ +
+ +
` + ); + + bind(".zoneWidth .minus", () => this.modifyZone(-1, 0)); + bind(".zoneWidth .plus", () => this.modifyZone(1, 0)); + bind(".zoneHeight .minus", () => this.modifyZone(0, -1)); + bind(".zoneHeight .plus", () => this.modifyZone(0, 1)); + bind("button.trim", this.trim); + bind("button.clearItems", this.clearItems); + bind("button.clearBuildings", this.clearBuildings); + } + } + + clearItems() { + this.root.logic.clearAllBeltsAndItems(); + } + + clearBuildings() { + for (const entity of this.root.entityMgr.getAllWithComponent(StaticMapEntityComponent)) { + const staticComp = entity.components.StaticMapEntity; + + if ( + [MetaGoalAcceptorBuilding, MetaConstantProducerBuilding, MetaBlockBuilding] + .map(metaClass => gMetaBuildingRegistry.findByClass(metaClass).id) + .includes(staticComp.getMetaBuilding().id) + ) { + continue; + } + + this.root.map.removeStaticEntity(entity); + this.root.entityMgr.destroyEntity(entity); + } + this.root.entityMgr.processDestroyList(); + } + + trim() { + // Now, find the center + const buildings = this.root.entityMgr.entities.slice(); + + if (buildings.length === 0) { + // nothing to do + return; + } + + let minRect = null; + + for (const building of buildings) { + const staticComp = building.components.StaticMapEntity; + const bounds = staticComp.getTileSpaceBounds(); + + if (!minRect) { + minRect = bounds; + } else { + minRect = minRect.getUnion(bounds); + } + } + + const mode = /** @type {PuzzleGameMode} */ (this.root.gameMode); + const moveByInverse = minRect.getCenter().round(); + + // move buildings + if (moveByInverse.length() > 0) { + // increase area size + mode.zoneWidth = globalConfig.puzzleMaxBoundsSize; + mode.zoneHeight = globalConfig.puzzleMaxBoundsSize; + + // First, remove any items etc + this.root.logic.clearAllBeltsAndItems(); + + this.root.logic.performImmutableOperation(() => { + // 1. remove all buildings + for (const building of buildings) { + if (!this.root.logic.tryDeleteBuilding(building)) { + assertAlways(false, "Failed to remove building in trim"); + } + } + + // 2. place them again, but centered + for (const building of buildings) { + const staticComp = building.components.StaticMapEntity; + const result = this.root.logic.tryPlaceBuilding({ + origin: staticComp.origin.sub(moveByInverse), + building: staticComp.getMetaBuilding(), + originalRotation: staticComp.originalRotation, + rotation: staticComp.rotation, + rotationVariant: staticComp.getRotationVariant(), + variant: staticComp.getVariant(), + }); + if (!result) { + this.root.bulkOperationRunning = false; + assertAlways(false, "Failed to re-place building in trim"); + } + + if (building.components.ConstantSignal) { + result.components.ConstantSignal.signal = building.components.ConstantSignal.signal; + } + } + }); + } + + // 3. Actually trim + let w = mode.zoneWidth; + let h = mode.zoneHeight; + + while (!this.anyBuildingOutsideZone(w - 1, h)) { + --w; + } + + while (!this.anyBuildingOutsideZone(w, h - 1)) { + --h; + } + + mode.zoneWidth = w; + mode.zoneHeight = h; + this.updateZoneValues(); + } + + initialize() { + this.visible = true; + this.updateZoneValues(); + } + + anyBuildingOutsideZone(width, height) { + if (Math.min(width, height) < globalConfig.puzzleMinBoundsSize) { + return true; + } + const newZone = Rectangle.centered(width, height); + const entities = this.root.entityMgr.getAllWithComponent(StaticMapEntityComponent); + + for (const entity of entities) { + const staticComp = entity.components.StaticMapEntity; + const bounds = staticComp.getTileSpaceBounds(); + if (!newZone.intersectsFully(bounds)) { + return true; + } + } + } + + modifyZone(deltaW, deltaH) { + const mode = /** @type {PuzzleGameMode} */ (this.root.gameMode); + + const newWidth = mode.zoneWidth + deltaW; + const newHeight = mode.zoneHeight + deltaH; + + if (Math.min(newWidth, newHeight) < globalConfig.puzzleMinBoundsSize) { + return; + } + + if (Math.max(newWidth, newHeight) > globalConfig.puzzleMaxBoundsSize) { + return; + } + + if (this.anyBuildingOutsideZone(newWidth, newHeight)) { + this.root.hud.parts.dialogs.showWarning( + T.dialogs.puzzleResizeBadBuildings.title, + T.dialogs.puzzleResizeBadBuildings.desc + ); + return; + } + + mode.zoneWidth = newWidth; + mode.zoneHeight = newHeight; + this.updateZoneValues(); + } + + updateZoneValues() { + const mode = /** @type {PuzzleGameMode} */ (this.root.gameMode); + + this.element.querySelector(".zoneWidth > .value").textContent = String(mode.zoneWidth); + this.element.querySelector(".zoneHeight > .value").textContent = String(mode.zoneHeight); + } +} diff --git a/src/js/game/hud/parts/puzzle_play_metadata.js b/src/js/game/hud/parts/puzzle_play_metadata.js new file mode 100644 index 00000000..3550a1e6 --- /dev/null +++ b/src/js/game/hud/parts/puzzle_play_metadata.js @@ -0,0 +1,72 @@ +/* typehints:start */ +import { PuzzlePlayGameMode } from "../../modes/puzzle_play"; +/* typehints:end */ + +import { formatBigNumberFull, formatSeconds, makeDiv } from "../../../core/utils"; +import { T } from "../../../translations"; +import { BaseHUDPart } from "../base_hud_part"; + +const copy = require("clipboard-copy"); + +export class HUDPuzzlePlayMetadata extends BaseHUDPart { + createElements(parent) { + this.titleElement = makeDiv(parent, "ingame_HUD_PuzzlePlayTitle"); + this.titleElement.innerText = "PUZZLE"; + + const mode = /** @type {PuzzlePlayGameMode} */ (this.root.gameMode); + const puzzle = mode.puzzle; + + this.puzzleNameElement = makeDiv(this.titleElement, null, ["name"]); + this.puzzleNameElement.innerText = puzzle.meta.title; + + this.element = makeDiv(parent, "ingame_HUD_PuzzlePlayMetadata"); + this.element.innerHTML = ` + +
+ ${formatBigNumberFull(puzzle.meta.downloads)} + +
+ + +
+
+ ${puzzle.meta.shortKey} +
+
+ + ${puzzle.meta.averageTime ? formatSeconds(puzzle.meta.averageTime) : "-"} +
+
+ + ${ + puzzle.meta.downloads > 0 + ? ((puzzle.meta.completions / puzzle.meta.downloads) * 100.0).toFixed(1) + "%" + : "-" + } +
+ +
+ + +
+ `; + + this.trackClicks(this.element.querySelector("button.share"), this.share); + this.trackClicks(this.element.querySelector("button.report"), this.report); + + /** @type {HTMLElement} */ (this.element.querySelector(".author span")).innerText = + puzzle.meta.author; + } + + initialize() {} + + share() { + const mode = /** @type {PuzzlePlayGameMode} */ (this.root.gameMode); + mode.sharePuzzle(); + } + + report() { + const mode = /** @type {PuzzlePlayGameMode} */ (this.root.gameMode); + mode.reportPuzzle(); + } +} diff --git a/src/js/game/hud/parts/puzzle_play_settings.js b/src/js/game/hud/parts/puzzle_play_settings.js new file mode 100644 index 00000000..8ae28166 --- /dev/null +++ b/src/js/game/hud/parts/puzzle_play_settings.js @@ -0,0 +1,61 @@ +import { gMetaBuildingRegistry } from "../../../core/global_registries"; +import { createLogger } from "../../../core/logging"; +import { makeDiv } from "../../../core/utils"; +import { T } from "../../../translations"; +import { MetaBlockBuilding } from "../../buildings/block"; +import { MetaConstantProducerBuilding } from "../../buildings/constant_producer"; +import { MetaGoalAcceptorBuilding } from "../../buildings/goal_acceptor"; +import { StaticMapEntityComponent } from "../../components/static_map_entity"; +import { BaseHUDPart } from "../base_hud_part"; + +const logger = createLogger("puzzle-play"); + +export class HUDPuzzlePlaySettings extends BaseHUDPart { + createElements(parent) { + this.element = makeDiv(parent, "ingame_HUD_PuzzlePlaySettings"); + + if (this.root.gameMode.getBuildableZones()) { + const bind = (selector, handler) => + this.trackClicks(this.element.querySelector(selector), handler); + makeDiv( + this.element, + null, + ["section"], + ` + + + + ` + ); + + bind("button.clearItems", this.clearItems); + bind("button.clearBuildings", this.clearBuildings); + } + } + + clearItems() { + this.root.logic.clearAllBeltsAndItems(); + } + + clearBuildings() { + for (const entity of this.root.entityMgr.getAllWithComponent(StaticMapEntityComponent)) { + const staticComp = entity.components.StaticMapEntity; + + if ( + [MetaGoalAcceptorBuilding, MetaConstantProducerBuilding, MetaBlockBuilding] + .map(metaClass => gMetaBuildingRegistry.findByClass(metaClass).id) + .includes(staticComp.getMetaBuilding().id) + ) { + continue; + } + + this.root.map.removeStaticEntity(entity); + this.root.entityMgr.destroyEntity(entity); + } + this.root.entityMgr.processDestroyList(); + } + + initialize() { + this.visible = true; + } +} diff --git a/src/js/game/hud/parts/sandbox_controller.js b/src/js/game/hud/parts/sandbox_controller.js index 592487ee..3689fa36 100644 --- a/src/js/game/hud/parts/sandbox_controller.js +++ b/src/js/game/hud/parts/sandbox_controller.js @@ -1,3 +1,4 @@ +import { queryParamOptions } from "../../../core/query_parameters"; import { makeDiv } from "../../../core/utils"; import { BaseHUDPart } from "../base_hud_part"; import { DynamicDomAttach } from "../dynamic_dom_attach"; @@ -19,25 +20,25 @@ export class HUDSandboxController extends BaseHUDPart { - +
- +
- +
- +
@@ -117,7 +118,9 @@ export class HUDSandboxController extends BaseHUDPart { // Clear all shapes of this level hubGoals.storedShapes[hubGoals.currentGoal.definition.getHash()] = 0; - this.root.hud.parts.pinnedShapes.rerenderFull(); + if (this.root.hud.parts.pinnedShapes) { + this.root.hud.parts.pinnedShapes.rerenderFull(); + } // Compute gained rewards hubGoals.gainedRewards = {}; @@ -144,7 +147,7 @@ export class HUDSandboxController extends BaseHUDPart { } }); - this.visible = !G_IS_DEV; + this.visible = false; this.domAttach = new DynamicDomAttach(this.root, this.element); } diff --git a/src/js/game/hud/parts/settings_menu.js b/src/js/game/hud/parts/settings_menu.js index eb902934..16da0440 100644 --- a/src/js/game/hud/parts/settings_menu.js +++ b/src/js/game/hud/parts/settings_menu.js @@ -13,17 +13,19 @@ export class HUDSettingsMenu extends BaseHUDPart { this.menuElement = makeDiv(this.background, null, ["menuElement"]); - this.statsElement = makeDiv( - this.background, - null, - ["statsElement"], - ` + if (this.root.gameMode.hasHub()) { + this.statsElement = makeDiv( + this.background, + null, + ["statsElement"], + ` ${T.ingame.settingsMenu.beltsPlaced} ${T.ingame.settingsMenu.buildingsPlaced} ${T.ingame.settingsMenu.playtime} ` - ); + ); + } this.buttonContainer = makeDiv(this.menuElement, null, ["buttons"]); @@ -94,23 +96,25 @@ export class HUDSettingsMenu extends BaseHUDPart { const totalMinutesPlayed = Math.ceil(this.root.time.now() / 60); - /** @type {HTMLElement} */ - const playtimeElement = this.statsElement.querySelector(".playtime"); - /** @type {HTMLElement} */ - const buildingsPlacedElement = this.statsElement.querySelector(".buildingsPlaced"); - /** @type {HTMLElement} */ - const beltsPlacedElement = this.statsElement.querySelector(".beltsPlaced"); + if (this.root.gameMode.hasHub()) { + /** @type {HTMLElement} */ + const playtimeElement = this.statsElement.querySelector(".playtime"); + /** @type {HTMLElement} */ + const buildingsPlacedElement = this.statsElement.querySelector(".buildingsPlaced"); + /** @type {HTMLElement} */ + const beltsPlacedElement = this.statsElement.querySelector(".beltsPlaced"); - playtimeElement.innerText = T.global.time.xMinutes.replace("", `${totalMinutesPlayed}`); + playtimeElement.innerText = T.global.time.xMinutes.replace("", `${totalMinutesPlayed}`); - buildingsPlacedElement.innerText = formatBigNumberFull( - this.root.entityMgr.getAllWithComponent(StaticMapEntityComponent).length - + buildingsPlacedElement.innerText = formatBigNumberFull( + this.root.entityMgr.getAllWithComponent(StaticMapEntityComponent).length - + this.root.entityMgr.getAllWithComponent(BeltComponent).length + ); + + beltsPlacedElement.innerText = formatBigNumberFull( this.root.entityMgr.getAllWithComponent(BeltComponent).length - ); - - beltsPlacedElement.innerText = formatBigNumberFull( - this.root.entityMgr.getAllWithComponent(BeltComponent).length - ); + ); + } } close() { diff --git a/src/js/game/hud/parts/waypoints.js b/src/js/game/hud/parts/waypoints.js index 1a0e3739..2e0bc159 100644 --- a/src/js/game/hud/parts/waypoints.js +++ b/src/js/game/hud/parts/waypoints.js @@ -100,16 +100,14 @@ export class HUDWaypoints extends BaseHUDPart { this.directionIndicatorSprite = Loader.getSprite("sprites/misc/hub_direction_indicator.png"); - /** @type {Array} - */ - this.waypoints = [ - { - label: null, - center: { x: 0, y: 0 }, - zoomLevel: 3, - layer: gMetaBuildingRegistry.findByClass(MetaHubBuilding).getLayer(), - }, - ]; + /** @type {Array} */ + this.waypoints = []; + this.waypoints.push({ + label: null, + center: { x: 0, y: 0 }, + zoomLevel: 3, + layer: gMetaBuildingRegistry.findByClass(MetaHubBuilding).getLayer(), + }); // Create a buffer we can use to measure text this.dummyBuffer = makeOffscreenBuffer(1, 1, { diff --git a/src/js/game/hud/parts/wires_overlay.js b/src/js/game/hud/parts/wires_overlay.js index 2fd3092c..328d6689 100644 --- a/src/js/game/hud/parts/wires_overlay.js +++ b/src/js/game/hud/parts/wires_overlay.js @@ -28,6 +28,9 @@ export class HUDWiresOverlay extends BaseHUDPart { * Switches between layers */ switchLayers() { + if (!this.root.gameMode.getSupportsWires()) { + return; + } if (this.root.currentLayer === "regular") { if ( this.root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_wires_painter_and_levers) || diff --git a/src/js/game/key_action_mapper.js b/src/js/game/key_action_mapper.js index 86445fd1..81170c65 100644 --- a/src/js/game/key_action_mapper.js +++ b/src/js/game/key_action_mapper.js @@ -50,6 +50,11 @@ export const KEYMAPPINGS = { }, buildings: { + // Puzzle buildings + constant_producer: { keyCode: key("H") }, + goal_acceptor: { keyCode: key("N") }, + block: { keyCode: key("4") }, + // Primary Toolbar belt: { keyCode: key("1") }, balancer: { keyCode: key("2") }, @@ -103,6 +108,7 @@ export const KEYMAPPINGS = { massSelectSelectMultiple: { keyCode: 16 }, // SHIFT massSelectCopy: { keyCode: key("C") }, massSelectCut: { keyCode: key("X") }, + massSelectClear: { keyCode: key("B") }, confirmMassDelete: { keyCode: 46 }, // DEL pasteLastBlueprint: { keyCode: key("V") }, }, @@ -263,6 +269,8 @@ export function getStringForKeyCode(code) { return "."; case 191: return "/"; + case 192: + return "`"; case 219: return "["; case 220: @@ -323,6 +331,15 @@ export class Keybinding { this.signal.add(receiver, scope); } + /** + * Adds an event listener + * @param {function() : void} receiver + * @param {object=} scope + */ + addToTop(receiver, scope = null) { + this.signal.addToTop(receiver, scope); + } + /** * @param {Element} elem * @returns {HTMLElement} the created element, or null if the keybindings are not shown diff --git a/src/js/game/logic.js b/src/js/game/logic.js index 7ec7b8ab..20caca31 100644 --- a/src/js/game/logic.js +++ b/src/js/game/logic.js @@ -4,6 +4,7 @@ import { STOP_PROPAGATION } from "../core/signal"; import { round2Digits } from "../core/utils"; import { enumDirection, enumDirectionToVector, enumInvertedDirections, Vector } from "../core/vector"; import { getBuildingDataFromCode } from "./building_codes"; +import { Component } from "./component"; import { enumWireVariant } from "./components/wire"; import { Entity } from "./entity"; import { CHUNK_OVERLAY_RES } from "./map_chunk_view"; @@ -161,13 +162,34 @@ export class GameLogic { return returnValue; } + /** + * Performs a immutable operation, causing no recalculations + * @param {function} operation + */ + performImmutableOperation(operation) { + logger.warn("Running immutable operation ..."); + assert(!this.root.immutableOperationRunning, "Can not run two immutalbe operations twice"); + this.root.immutableOperationRunning = true; + const now = performance.now(); + const returnValue = operation(); + const duration = performance.now() - now; + logger.log("Done in", round2Digits(duration), "ms"); + assert( + this.root.immutableOperationRunning, + "Immutable operation = false while immutable operation was running" + ); + this.root.immutableOperationRunning = false; + this.root.signals.immutableOperationFinished.dispatch(); + return returnValue; + } + /** * Returns whether the given building can get removed * @param {Entity} building */ canDeleteBuilding(building) { const staticComp = building.components.StaticMapEntity; - return staticComp.getMetaBuilding().getIsRemovable(); + return staticComp.getMetaBuilding().getIsRemovable(this.root); } /** @@ -342,8 +364,6 @@ export class GameLogic { return !!overlayMatrix[localPosition.x + localPosition.y * 3]; } - g(tile, edge) {} - /** * Returns the acceptors and ejectors which affect the current tile * @param {Vector} tile @@ -425,4 +445,15 @@ export class GameLogic { } return { ejectors, acceptors }; } + + /** + * Clears all belts and items + */ + clearAllBeltsAndItems() { + for (const entity of this.root.entityMgr.entities) { + for (const component of Object.values(entity.components)) { + /** @type {Component} */ (component).clear(); + } + } + } } diff --git a/src/js/game/map_chunk_view.js b/src/js/game/map_chunk_view.js index 848afbab..131ce37b 100644 --- a/src/js/game/map_chunk_view.js +++ b/src/js/game/map_chunk_view.js @@ -41,7 +41,14 @@ export class MapChunkView extends MapChunk { */ drawBackgroundLayer(parameters) { const systems = this.root.systemMgr.systems; - systems.mapResources.drawChunk(parameters, this); + if (systems.zone) { + systems.zone.drawChunk(parameters, this); + } + + if (this.root.gameMode.hasResources()) { + systems.mapResources.drawChunk(parameters, this); + } + systems.beltUnderlays.drawChunk(parameters, this); systems.belt.drawChunk(parameters, this); } @@ -69,6 +76,8 @@ export class MapChunkView extends MapChunk { systems.lever.drawChunk(parameters, this); systems.display.drawChunk(parameters, this); systems.storage.drawChunk(parameters, this); + systems.constantProducer.drawChunk(parameters, this); + systems.goalAcceptor.drawChunk(parameters, this); systems.itemProcessorOverlays.drawChunk(parameters, this); } diff --git a/src/js/game/map_view.js b/src/js/game/map_view.js index 296291e9..f4372a51 100644 --- a/src/js/game/map_view.js +++ b/src/js/game/map_view.js @@ -186,7 +186,7 @@ export class MapView extends BaseMap { */ drawBackground(parameters) { // Render tile grid - if (!this.root.app.settings.getAllSettings().disableTileGrid) { + if (!this.root.app.settings.getAllSettings().disableTileGrid || !this.root.gameMode.hasResources()) { const dpi = this.backgroundCacheDPI; parameters.context.scale(1 / dpi, 1 / dpi); diff --git a/src/js/game/meta_building.js b/src/js/game/meta_building.js index 9deee272..7bfbce25 100644 --- a/src/js/game/meta_building.js +++ b/src/js/game/meta_building.js @@ -108,9 +108,10 @@ export class MetaBuilding { /** * Returns whether this building is removable + * @param {GameRoot} root * @returns {boolean} */ - getIsRemovable() { + getIsRemovable(root) { return true; } @@ -157,10 +158,9 @@ export class MetaBuilding { /** * Returns whether this building is rotateable - * @param {string} variant * @returns {boolean} */ - getIsRotateable(variant) { + getIsRotateable() { return true; } @@ -242,7 +242,7 @@ export class MetaBuilding { * @return {{ rotation: number, rotationVariant: number, connectedEntities?: Array }} */ computeOptimalDirectionAndRotationVariantAtTile({ root, tile, rotation, variant, layer }) { - if (!this.getIsRotateable(variant)) { + if (!this.getIsRotateable()) { return { rotation: 0, rotationVariant: 0, diff --git a/src/js/game/meta_building_registry.js b/src/js/game/meta_building_registry.js index 0613103e..0c93153d 100644 --- a/src/js/game/meta_building_registry.js +++ b/src/js/game/meta_building_registry.js @@ -4,11 +4,14 @@ import { T } from "../translations"; import { MetaAnalyzerBuilding } from "./buildings/analyzer"; import { enumBalancerVariants, MetaBalancerBuilding } from "./buildings/balancer"; import { MetaBeltBuilding } from "./buildings/belt"; +import { MetaBlockBuilding } from "./buildings/block"; import { MetaComparatorBuilding } from "./buildings/comparator"; +import { MetaConstantProducerBuilding } from "./buildings/constant_producer"; import { MetaConstantSignalBuilding } from "./buildings/constant_signal"; import { enumCutterVariants, MetaCutterBuilding } from "./buildings/cutter"; import { MetaDisplayBuilding } from "./buildings/display"; import { MetaFilterBuilding } from "./buildings/filter"; +import { MetaGoalAcceptorBuilding } from "./buildings/goal_acceptor"; import { MetaHubBuilding } from "./buildings/hub"; import { MetaItemProducerBuilding } from "./buildings/item_producer"; import { MetaLeverBuilding } from "./buildings/lever"; @@ -45,6 +48,7 @@ export function initMetaBuildingRegistry() { gMetaBuildingRegistry.register(MetaStorageBuilding); gMetaBuildingRegistry.register(MetaBeltBuilding); gMetaBuildingRegistry.register(MetaUndergroundBeltBuilding); + gMetaBuildingRegistry.register(MetaGoalAcceptorBuilding); gMetaBuildingRegistry.register(MetaHubBuilding); gMetaBuildingRegistry.register(MetaWireBuilding); gMetaBuildingRegistry.register(MetaConstantSignalBuilding); @@ -59,6 +63,8 @@ export function initMetaBuildingRegistry() { gMetaBuildingRegistry.register(MetaAnalyzerBuilding); gMetaBuildingRegistry.register(MetaComparatorBuilding); gMetaBuildingRegistry.register(MetaItemProducerBuilding); + gMetaBuildingRegistry.register(MetaConstantProducerBuilding); + gMetaBuildingRegistry.register(MetaBlockBuilding); // Belt registerBuildingVariant(1, MetaBeltBuilding, defaultBuildingVariant, 0); @@ -165,6 +171,15 @@ export function initMetaBuildingRegistry() { // Item producer registerBuildingVariant(61, MetaItemProducerBuilding); + // Constant producer + registerBuildingVariant(62, MetaConstantProducerBuilding); + + // Goal acceptor + registerBuildingVariant(63, MetaGoalAcceptorBuilding); + + // Block + registerBuildingVariant(64, MetaBlockBuilding); + // Propagate instances for (const key in gBuildingVariants) { gBuildingVariants[key].metaInstance = gMetaBuildingRegistry.findByClass( diff --git a/src/js/game/modes/puzzle.js b/src/js/game/modes/puzzle.js new file mode 100644 index 00000000..75a47ee2 --- /dev/null +++ b/src/js/game/modes/puzzle.js @@ -0,0 +1,110 @@ +/* typehints:start */ +import { GameRoot } from "../root"; +/* typehints:end */ + +import { Rectangle } from "../../core/rectangle"; +import { types } from "../../savegame/serialization"; +import { enumGameModeTypes, GameMode } from "../game_mode"; +import { HUDPuzzleBackToMenu } from "../hud/parts/puzzle_back_to_menu"; +import { HUDPuzzleDLCLogo } from "../hud/parts/puzzle_dlc_logo"; +import { HUDBlueprintPlacer } from "../hud/parts/blueprint_placer"; +import { HUDMassSelector } from "../hud/parts/mass_selector"; + +export class PuzzleGameMode extends GameMode { + static getType() { + return enumGameModeTypes.puzzle; + } + + /** @returns {object} */ + static getSchema() { + return { + zoneHeight: types.uint, + zoneWidth: types.uint, + }; + } + + /** @param {GameRoot} root */ + constructor(root) { + super(root); + + const data = this.getSaveData(); + + this.additionalHudParts = { + puzzleBackToMenu: HUDPuzzleBackToMenu, + puzzleDlcLogo: HUDPuzzleDLCLogo, + blueprintPlacer: HUDBlueprintPlacer, + massSelector: HUDMassSelector, + }; + + this.zoneWidth = data.zoneWidth || 8; + this.zoneHeight = data.zoneHeight || 6; + } + + /** + * @param {typeof import("../meta_building").MetaBuilding} building + */ + isBuildingExcluded(building) { + return this.hiddenBuildings.indexOf(building) >= 0; + } + + getSaveData() { + const save = this.root.savegame.getCurrentDump(); + if (!save) { + return {}; + } + return save.gameMode.data; + } + + getCameraBounds() { + return Rectangle.centered(this.zoneWidth + 20, this.zoneHeight + 20); + } + + getBuildableZones() { + return [Rectangle.centered(this.zoneWidth, this.zoneHeight)]; + } + + hasHub() { + return false; + } + + hasResources() { + return false; + } + + getMinimumZoom() { + return 1; + } + + getMaximumZoom() { + return 4; + } + + getIsSaveable() { + return false; + } + + getHasFreeCopyPaste() { + return true; + } + + throughputDoesNotMatter() { + return true; + } + + getSupportsWires() { + return false; + } + + getFixedTickrate() { + return 300; + } + + getIsDeterministic() { + return true; + } + + /** @returns {boolean} */ + getIsFreeplayAvailable() { + return true; + } +} diff --git a/src/js/game/modes/puzzle_edit.js b/src/js/game/modes/puzzle_edit.js new file mode 100644 index 00000000..e3d2e40d --- /dev/null +++ b/src/js/game/modes/puzzle_edit.js @@ -0,0 +1,66 @@ +/* typehints:start */ +import { GameRoot } from "../root"; +/* typehints:end */ + +import { enumGameModeIds } from "../game_mode"; +import { PuzzleGameMode } from "./puzzle"; +import { MetaStorageBuilding } from "../buildings/storage"; +import { MetaReaderBuilding } from "../buildings/reader"; +import { MetaFilterBuilding } from "../buildings/filter"; +import { MetaDisplayBuilding } from "../buildings/display"; +import { MetaLeverBuilding } from "../buildings/lever"; +import { MetaItemProducerBuilding } from "../buildings/item_producer"; +import { MetaMinerBuilding } from "../buildings/miner"; +import { MetaWireBuilding } from "../buildings/wire"; +import { MetaWireTunnelBuilding } from "../buildings/wire_tunnel"; +import { MetaConstantSignalBuilding } from "../buildings/constant_signal"; +import { MetaLogicGateBuilding } from "../buildings/logic_gate"; +import { MetaVirtualProcessorBuilding } from "../buildings/virtual_processor"; +import { MetaAnalyzerBuilding } from "../buildings/analyzer"; +import { MetaComparatorBuilding } from "../buildings/comparator"; +import { MetaTransistorBuilding } from "../buildings/transistor"; +import { HUDPuzzleEditorControls } from "../hud/parts/puzzle_editor_controls"; +import { HUDPuzzleEditorReview } from "../hud/parts/puzzle_editor_review"; +import { HUDPuzzleEditorSettings } from "../hud/parts/puzzle_editor_settings"; + +export class PuzzleEditGameMode extends PuzzleGameMode { + static getId() { + return enumGameModeIds.puzzleEdit; + } + + static getSchema() { + return {}; + } + + /** @param {GameRoot} root */ + constructor(root) { + super(root); + + this.hiddenBuildings = [ + MetaStorageBuilding, + MetaReaderBuilding, + MetaFilterBuilding, + MetaDisplayBuilding, + MetaLeverBuilding, + MetaItemProducerBuilding, + MetaMinerBuilding, + + MetaWireBuilding, + MetaWireTunnelBuilding, + MetaConstantSignalBuilding, + MetaLogicGateBuilding, + MetaVirtualProcessorBuilding, + MetaAnalyzerBuilding, + MetaComparatorBuilding, + MetaTransistorBuilding, + ]; + + this.additionalHudParts.puzzleEditorControls = HUDPuzzleEditorControls; + this.additionalHudParts.puzzleEditorReview = HUDPuzzleEditorReview; + this.additionalHudParts.puzzleEditorSettings = HUDPuzzleEditorSettings; + } + + getIsEditor() { + return true; + } +} diff --git a/src/js/game/modes/puzzle_play.js b/src/js/game/modes/puzzle_play.js new file mode 100644 index 00000000..46480c51 --- /dev/null +++ b/src/js/game/modes/puzzle_play.js @@ -0,0 +1,193 @@ +/* typehints:start */ +import { GameRoot } from "../root"; +/* typehints:end */ + +import { enumGameModeIds } from "../game_mode"; +import { PuzzleGameMode } from "./puzzle"; +import { MetaStorageBuilding } from "../buildings/storage"; +import { MetaReaderBuilding } from "../buildings/reader"; +import { MetaFilterBuilding } from "../buildings/filter"; +import { MetaDisplayBuilding } from "../buildings/display"; +import { MetaLeverBuilding } from "../buildings/lever"; +import { MetaItemProducerBuilding } from "../buildings/item_producer"; +import { MetaMinerBuilding } from "../buildings/miner"; +import { MetaWireBuilding } from "../buildings/wire"; +import { MetaWireTunnelBuilding } from "../buildings/wire_tunnel"; +import { MetaConstantSignalBuilding } from "../buildings/constant_signal"; +import { MetaLogicGateBuilding } from "../buildings/logic_gate"; +import { MetaVirtualProcessorBuilding } from "../buildings/virtual_processor"; +import { MetaAnalyzerBuilding } from "../buildings/analyzer"; +import { MetaComparatorBuilding } from "../buildings/comparator"; +import { MetaTransistorBuilding } from "../buildings/transistor"; +import { MetaConstantProducerBuilding } from "../buildings/constant_producer"; +import { MetaGoalAcceptorBuilding } from "../buildings/goal_acceptor"; +import { PuzzleSerializer } from "../../savegame/puzzle_serializer"; +import { T } from "../../translations"; +import { HUDPuzzlePlayMetadata } from "../hud/parts/puzzle_play_metadata"; +import { createLogger } from "../../core/logging"; +import { HUDPuzzleCompleteNotification } from "../hud/parts/puzzle_complete_notification"; +import { HUDPuzzlePlaySettings } from "../hud/parts/puzzle_play_settings"; +import { MetaBlockBuilding } from "../buildings/block"; +import { MetaBuilding } from "../meta_building"; +import { gMetaBuildingRegistry } from "../../core/global_registries"; + +const logger = createLogger("puzzle-play"); +const copy = require("clipboard-copy"); + +export class PuzzlePlayGameMode extends PuzzleGameMode { + static getId() { + return enumGameModeIds.puzzlePlay; + } + + /** + * @param {GameRoot} root + * @param {object} payload + * @param {import("../../savegame/savegame_typedefs").PuzzleFullData} payload.puzzle + */ + constructor(root, { puzzle }) { + super(root); + + /** @type {Array} */ + let excludedBuildings = [ + MetaConstantProducerBuilding, + MetaGoalAcceptorBuilding, + MetaBlockBuilding, + + MetaStorageBuilding, + MetaReaderBuilding, + MetaFilterBuilding, + MetaDisplayBuilding, + MetaLeverBuilding, + MetaItemProducerBuilding, + MetaMinerBuilding, + + MetaWireBuilding, + MetaWireTunnelBuilding, + MetaConstantSignalBuilding, + MetaLogicGateBuilding, + MetaVirtualProcessorBuilding, + MetaAnalyzerBuilding, + MetaComparatorBuilding, + MetaTransistorBuilding, + ]; + + if (puzzle.game.excludedBuildings) { + /** + * @type {any} + */ + const puzzleHidden = puzzle.game.excludedBuildings + .map(id => { + if (!gMetaBuildingRegistry.hasId(id)) { + return; + } + return gMetaBuildingRegistry.findById(id).constructor; + }) + .filter(x => !!x); + excludedBuildings = excludedBuildings.concat(puzzleHidden); + } + + this.hiddenBuildings = excludedBuildings; + + this.additionalHudParts.puzzlePlayMetadata = HUDPuzzlePlayMetadata; + this.additionalHudParts.puzzlePlaySettings = HUDPuzzlePlaySettings; + this.additionalHudParts.puzzleCompleteNotification = HUDPuzzleCompleteNotification; + + root.signals.postLoadHook.add(this.loadPuzzle, this); + + this.puzzle = puzzle; + } + + loadPuzzle() { + let errorText; + logger.log("Loading puzzle", this.puzzle); + + try { + this.zoneWidth = this.puzzle.game.bounds.w; + this.zoneHeight = this.puzzle.game.bounds.h; + errorText = new PuzzleSerializer().deserializePuzzle(this.root, this.puzzle.game); + } catch (ex) { + errorText = ex.message || ex; + } + + if (errorText) { + this.root.gameState.moveToState("PuzzleMenuState", { + error: { + title: T.dialogs.puzzleLoadError.title, + desc: T.dialogs.puzzleLoadError.desc + " " + errorText, + }, + }); + // const signals = this.root.hud.parts.dialogs.showWarning( + // T.dialogs.puzzleLoadError.title, + // T.dialogs.puzzleLoadError.desc + " " + errorText + // ); + // signals.ok.add(() => this.root.gameState.moveToState("PuzzleMenuState")); + } + } + + /** + * + * @param {boolean} liked + * @param {number} time + */ + trackCompleted(liked, time) { + const closeLoading = this.root.hud.parts.dialogs.showLoadingDialog(); + + return this.root.app.clientApi + .apiCompletePuzzle(this.puzzle.meta.id, { + time, + liked, + }) + .catch(err => { + logger.warn("Failed to complete puzzle:", err); + }) + .then(() => { + closeLoading(); + }); + } + + sharePuzzle() { + copy(this.puzzle.meta.shortKey); + + this.root.hud.parts.dialogs.showInfo( + T.dialogs.puzzleShare.title, + T.dialogs.puzzleShare.desc.replace("", this.puzzle.meta.shortKey) + ); + } + + reportPuzzle() { + const { optionSelected } = this.root.hud.parts.dialogs.showOptionChooser( + T.dialogs.puzzleReport.title, + { + options: [ + { value: "profane", text: T.dialogs.puzzleReport.options.profane }, + { value: "unsolvable", text: T.dialogs.puzzleReport.options.unsolvable }, + { value: "trolling", text: T.dialogs.puzzleReport.options.trolling }, + ], + } + ); + + return new Promise(resolve => { + optionSelected.add(option => { + const closeLoading = this.root.hud.parts.dialogs.showLoadingDialog(); + + this.root.app.clientApi.apiReportPuzzle(this.puzzle.meta.id, option).then( + () => { + closeLoading(); + const { ok } = this.root.hud.parts.dialogs.showInfo( + T.dialogs.puzzleReportComplete.title, + T.dialogs.puzzleReportComplete.desc + ); + ok.add(resolve); + }, + err => { + closeLoading(); + const { ok } = this.root.hud.parts.dialogs.showInfo( + T.dialogs.puzzleReportError.title, + T.dialogs.puzzleReportError.desc + " " + err + ); + } + ); + }); + }); + } +} diff --git a/src/js/game/modes/regular.js b/src/js/game/modes/regular.js index e99f4a7c..47ee1355 100644 --- a/src/js/game/modes/regular.js +++ b/src/js/game/modes/regular.js @@ -1,19 +1,77 @@ +/* typehints:start */ +import { GameRoot } from "../root"; +import { MetaBuilding } from "../meta_building"; +/* typehints:end */ + import { findNiceIntegerValue } from "../../core/utils"; -import { GameMode } from "../game_mode"; +import { MetaConstantProducerBuilding } from "../buildings/constant_producer"; +import { MetaGoalAcceptorBuilding } from "../buildings/goal_acceptor"; +import { enumGameModeIds, enumGameModeTypes, GameMode } from "../game_mode"; import { ShapeDefinition } from "../shape_definition"; import { enumHubGoalRewards } from "../tutorial_goals"; +import { HUDWiresToolbar } from "../hud/parts/wires_toolbar"; +import { HUDBlueprintPlacer } from "../hud/parts/blueprint_placer"; +import { HUDUnlockNotification } from "../hud/parts/unlock_notification"; +import { HUDMassSelector } from "../hud/parts/mass_selector"; +import { HUDShop } from "../hud/parts/shop"; +import { HUDWaypoints } from "../hud/parts/waypoints"; +import { HUDStatistics } from "../hud/parts/statistics"; +import { HUDWireInfo } from "../hud/parts/wire_info"; +import { HUDLeverToggle } from "../hud/parts/lever_toggle"; +import { HUDPinnedShapes } from "../hud/parts/pinned_shapes"; +import { HUDNotifications } from "../hud/parts/notifications"; +import { HUDScreenshotExporter } from "../hud/parts/screenshot_exporter"; +import { HUDWiresOverlay } from "../hud/parts/wires_overlay"; +import { HUDShapeViewer } from "../hud/parts/shape_viewer"; +import { HUDLayerPreview } from "../hud/parts/layer_preview"; +import { HUDTutorialVideoOffer } from "../hud/parts/tutorial_video_offer"; +import { HUDMinerHighlight } from "../hud/parts/miner_highlight"; +import { HUDGameMenu } from "../hud/parts/game_menu"; +import { HUDConstantSignalEdit } from "../hud/parts/constant_signal_edit"; +import { IS_MOBILE } from "../../core/config"; +import { HUDKeybindingOverlay } from "../hud/parts/keybinding_overlay"; +import { HUDWatermark } from "../hud/parts/watermark"; +import { HUDStandaloneAdvantages } from "../hud/parts/standalone_advantages"; +import { HUDCatMemes } from "../hud/parts/cat_memes"; +import { HUDPartTutorialHints } from "../hud/parts/tutorial_hints"; +import { HUDInteractiveTutorial } from "../hud/parts/interactive_tutorial"; +import { HUDSandboxController } from "../hud/parts/sandbox_controller"; +import { queryParamOptions } from "../../core/query_parameters"; +import { MetaBlockBuilding } from "../buildings/block"; +import { MetaItemProducerBuilding } from "../buildings/item_producer"; -const rocketShape = "CbCuCbCu:Sr------:--CrSrCr:CwCwCwCw"; -const finalGameShape = "RuCw--Cw:----Ru--"; +/** @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 const rocketShape = "CbCuCbCu:Sr------:--CrSrCr:CwCwCwCw"; +export 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; +const chinaShapes = G_WEGAME_VERSION || G_CHINA_VERSION; + /** * Generates all upgrades - * @returns {Object} */ + * @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; @@ -87,7 +145,14 @@ function generateUpgrades(limitedVersion = false) { required: [{ shape: "CwCwCwCw:WbWbWbWb", amount: 23000 }], }, { - required: [{ shape: "CbRbRbCb:CwCwCwCw:WbWbWbWb", amount: 50000 }], + required: [ + { + shape: chinaShapes + ? "CyCyCyCy:CyCyCyCy:RyRyRyRy:RuRuRuRu" + : "CbRbRbCb:CwCwCwCw:WbWbWbWb", + amount: 50000, + }, + ], }, { required: [{ shape: preparementShape, amount: 25000 }], @@ -141,7 +206,12 @@ function generateUpgrades(limitedVersion = false) { required: [{ shape: "WrWrWrWr", amount: 3800 }], }, { - required: [{ shape: "RpRpRpRp:CwCwCwCw", amount: 6500 }], + required: [ + { + shape: chinaShapes ? "CuCuCuCu:CwCwCwCw:Sb--Sr--" : "RpRpRpRp:CwCwCwCw", + amount: 6500, + }, + ], }, { required: [{ shape: "WpWpWpWp:CwCwCwCw:WpWpWpWp", amount: 25000 }], @@ -315,7 +385,7 @@ export function generateLevelDefinitions(limitedVersion = false) { // 13 // Tunnel Tier 2 { - shape: "RpRpRpRp:CwCwCwCw", // painting t3 + shape: chinaShapes ? "CuCuCuCu:CwCwCwCw:Sb--Sr--" : "RpRpRpRp:CwCwCwCw", // painting t3 required: 3800, reward: enumHubGoalRewards.reward_underground_belt_tier_2, }, @@ -324,7 +394,7 @@ export function generateLevelDefinitions(limitedVersion = false) { ...(limitedVersion ? [ { - shape: "RpRpRpRp:CwCwCwCw", + shape: chinaShapes ? "CuCuCuCu:CwCwCwCw:Sb--Sr--" : "RpRpRpRp:CwCwCwCw", required: 0, reward: enumHubGoalRewards.reward_demo_end, }, @@ -358,7 +428,9 @@ export function generateLevelDefinitions(limitedVersion = false) { // 17 // Double painter { - shape: "CbRbRbCb:CwCwCwCw:WbWbWbWb", // miner t4 (two variants) + shape: chinaShapes + ? "CyCyCyCy:CyCyCyCy:RyRyRyRy:RuRuRuRu" + : "CbRbRbCb:CwCwCwCw:WbWbWbWb", // miner t4 (two variants) required: 20000, reward: enumHubGoalRewards.reward_painter_double, }, @@ -398,7 +470,9 @@ export function generateLevelDefinitions(limitedVersion = false) { // 22 // Constant signal { - shape: "Cg----Cr:Cw----Cw:Sy------:Cy----Cy", + shape: chinaShapes + ? "RrSySrSy:RyCrCwCr:CyCyRyCy" + : "Cg----Cr:Cw----Cw:Sy------:Cy----Cy", required: 25000, reward: enumHubGoalRewards.reward_constant_signal, }, @@ -406,14 +480,18 @@ export function generateLevelDefinitions(limitedVersion = false) { // 23 // Display { - shape: "CcSyCcSy:SyCcSyCc:CcSyCcSy", + shape: chinaShapes + ? "CrCrCrCr:CwCwCwCw:WwWwWwWw:CrCrCrCr" + : "CcSyCcSy:SyCcSyCc:CcSyCcSy", required: 25000, reward: enumHubGoalRewards.reward_display, }, // 24 Logic gates { - shape: "CcRcCcRc:RwCwRwCw:Sr--Sw--:CyCyCyCy", + shape: chinaShapes + ? "Su----Su:RwRwRwRw:Cu----Cu:CwCwCwCw" + : "CcRcCcRc:RwCwRwCw:Sr--Sw--:CyCyCyCy", required: 25000, reward: enumHubGoalRewards.reward_logic_gates, }, @@ -454,27 +532,100 @@ const fullVersionLevels = generateLevelDefinitions(false); const demoVersionLevels = generateLevelDefinitions(true); export class RegularGameMode extends GameMode { - constructor(root) { - super(root); + static getId() { + return enumGameModeIds.regular; } + static getType() { + return enumGameModeTypes.default; + } + + /** @param {GameRoot} root */ + constructor(root) { + super(root); + + this.additionalHudParts = { + wiresToolbar: HUDWiresToolbar, + blueprintPlacer: HUDBlueprintPlacer, + unlockNotification: HUDUnlockNotification, + massSelector: HUDMassSelector, + shop: HUDShop, + statistics: HUDStatistics, + waypoints: HUDWaypoints, + wireInfo: HUDWireInfo, + leverToggle: HUDLeverToggle, + pinnedShapes: HUDPinnedShapes, + notifications: HUDNotifications, + screenshotExporter: HUDScreenshotExporter, + wiresOverlay: HUDWiresOverlay, + shapeViewer: HUDShapeViewer, + layerPreview: HUDLayerPreview, + minerHighlight: HUDMinerHighlight, + tutorialVideoOffer: HUDTutorialVideoOffer, + gameMenu: HUDGameMenu, + constantSignalEdit: HUDConstantSignalEdit, + }; + + if (!IS_MOBILE) { + this.additionalHudParts.keybindingOverlay = HUDKeybindingOverlay; + } + + if (this.root.app.restrictionMgr.getIsStandaloneMarketingActive()) { + this.additionalHudParts.watermark = HUDWatermark; + this.additionalHudParts.standaloneAdvantages = HUDStandaloneAdvantages; + this.additionalHudParts.catMemes = HUDCatMemes; + } + + if (this.root.app.settings.getAllSettings().offerHints) { + this.additionalHudParts.tutorialHints = HUDPartTutorialHints; + this.additionalHudParts.interactiveTutorial = HUDInteractiveTutorial; + } + + // @ts-ignore + if (queryParamOptions.sandboxMode || window.sandboxMode || G_IS_DEV) { + this.additionalHudParts.sandboxController = HUDSandboxController; + } + + /** @type {(typeof MetaBuilding)[]} */ + this.hiddenBuildings = [ + MetaConstantProducerBuilding, + MetaGoalAcceptorBuilding, + MetaBlockBuilding, + MetaItemProducerBuilding, + ]; + } + + /** + * Should return all available upgrades + * @returns {Object} + */ getUpgrades() { return this.root.app.restrictionMgr.getHasExtendedUpgrades() ? fullVersionUpgrades : demoVersionUpgrades; } - getIsFreeplayAvailable() { - return this.root.app.restrictionMgr.getHasExtendedLevelsAndFreeplay(); - } - - getBlueprintShapeKey() { - return blueprintShape; - } - + /** + * Returns the goals for all levels including their reward + * @returns {Array} + */ getLevelDefinitions() { return this.root.app.restrictionMgr.getHasExtendedLevelsAndFreeplay() ? fullVersionLevels : demoVersionLevels; } + + /** + * Should return whether free play is available or if the game stops + * after the predefined levels + * @returns {boolean} + */ + getIsFreeplayAvailable() { + return this.root.app.restrictionMgr.getHasExtendedLevelsAndFreeplay(); + } + + /** @returns {boolean} */ + hasAchievements() { + return true; + } } diff --git a/src/js/game/root.js b/src/js/game/root.js index c63d7a23..78d4c7ed 100644 --- a/src/js/game/root.js +++ b/src/js/game/root.js @@ -79,6 +79,11 @@ export class GameRoot { */ this.bulkOperationRunning = false; + /** + * Whether a immutable operation is running + */ + this.immutableOperationRunning = false; + //////// Other properties /////// /** @type {Camera} */ @@ -169,6 +174,7 @@ export class GameRoot { itemProduced: /** @type {TypedSignal<[BaseItem]>} */ (new Signal()), bulkOperationFinished: /** @type {TypedSignal<[]>} */ (new Signal()), + immutableOperationFinished: /** @type {TypedSignal<[]>} */ (new Signal()), editModeChanged: /** @type {TypedSignal<[Layer]>} */ (new Signal()), @@ -184,6 +190,9 @@ export class GameRoot { achievementCheck: /** @type {TypedSignal<[string, any]>} */ (new Signal()), bulkAchievementCheck: /** @type {TypedSignal<(string|any)[]>} */ (new Signal()), achievementCompleted: /** @type {TypedSignal<[string, any]>} */ (new Signal()), + + // Puzzle mode + puzzleComplete: /** @type {TypedSignal<[]>} */ (new Signal()), }; // RNG's diff --git a/src/js/game/systems/belt.js b/src/js/game/systems/belt.js index 10543e6c..00491eff 100644 --- a/src/js/game/systems/belt.js +++ b/src/js/game/systems/belt.js @@ -164,7 +164,10 @@ export class BeltSystem extends GameSystemWithFilter { // Compute delta to see if anything changed const newDirection = arrayBeltVariantToRotation[rotationVariant]; - if (targetStaticComp.rotation !== rotation || newDirection !== targetBeltComp.direction) { + if ( + !this.root.immutableOperationRunning && + (targetStaticComp.rotation !== rotation || newDirection !== targetBeltComp.direction) + ) { const originalPath = targetBeltComp.assignedPath; // Ok, first remove it from its current path diff --git a/src/js/game/systems/belt_reader.js b/src/js/game/systems/belt_reader.js index fbd00b6c..41211c05 100644 --- a/src/js/game/systems/belt_reader.js +++ b/src/js/game/systems/belt_reader.js @@ -14,7 +14,6 @@ export class BeltReaderSystem extends GameSystemWithFilter { const minimumTimeForThroughput = now - 1; for (let i = 0; i < this.allEntities.length; ++i) { const entity = this.allEntities[i]; - const readerComp = entity.components.BeltReader; const pinsComp = entity.components.WiredPins; @@ -23,12 +22,14 @@ export class BeltReaderSystem extends GameSystemWithFilter { readerComp.lastItemTimes.shift(); } - pinsComp.slots[1].value = readerComp.lastItem; - pinsComp.slots[0].value = - (readerComp.lastItemTimes[readerComp.lastItemTimes.length - 1] || 0) > - minimumTimeForThroughput - ? BOOL_TRUE_SINGLETON - : BOOL_FALSE_SINGLETON; + if (pinsComp) { + pinsComp.slots[1].value = readerComp.lastItem; + pinsComp.slots[0].value = + (readerComp.lastItemTimes[readerComp.lastItemTimes.length - 1] || 0) > + minimumTimeForThroughput + ? BOOL_TRUE_SINGLETON + : BOOL_FALSE_SINGLETON; + } if (now - readerComp.lastThroughputComputation > 0.5) { // Compute throughput diff --git a/src/js/game/systems/constant_producer.js b/src/js/game/systems/constant_producer.js new file mode 100644 index 00000000..5c10b409 --- /dev/null +++ b/src/js/game/systems/constant_producer.js @@ -0,0 +1,62 @@ +import { globalConfig } from "../../core/config"; +import { DrawParameters } from "../../core/draw_parameters"; +import { Vector } from "../../core/vector"; +import { ConstantSignalComponent } from "../components/constant_signal"; +import { ItemProducerComponent } from "../components/item_producer"; +import { GameSystemWithFilter } from "../game_system_with_filter"; +import { MapChunk } from "../map_chunk"; +import { GameRoot } from "../root"; + +export class ConstantProducerSystem extends GameSystemWithFilter { + /** @param {GameRoot} root */ + constructor(root) { + super(root, [ConstantSignalComponent, ItemProducerComponent]); + } + + update() { + for (let i = 0; i < this.allEntities.length; ++i) { + const entity = this.allEntities[i]; + const signalComp = entity.components.ConstantSignal; + const ejectorComp = entity.components.ItemEjector; + if (!ejectorComp) { + continue; + } + ejectorComp.tryEject(0, signalComp.signal); + } + } + + /** + * + * @param {DrawParameters} parameters + * @param {MapChunk} chunk + * @returns + */ + drawChunk(parameters, chunk) { + const contents = chunk.containedEntitiesByLayer.regular; + for (let i = 0; i < contents.length; ++i) { + const producerComp = contents[i].components.ItemProducer; + const signalComp = contents[i].components.ConstantSignal; + + if (!producerComp || !signalComp) { + continue; + } + + const staticComp = contents[i].components.StaticMapEntity; + const item = signalComp.signal; + + if (!item) { + continue; + } + + const center = staticComp.getTileSpaceBounds().getCenter().toWorldSpace(); + + const localOffset = new Vector(0, 1).rotateFastMultipleOf90(staticComp.rotation); + item.drawItemCenteredClipped( + center.x + localOffset.x, + center.y + localOffset.y, + parameters, + globalConfig.tileSize * 0.65 + ); + } + } +} diff --git a/src/js/game/systems/constant_signal.js b/src/js/game/systems/constant_signal.js index d698c1d5..5fabb80e 100644 --- a/src/js/game/systems/constant_signal.js +++ b/src/js/game/systems/constant_signal.js @@ -26,9 +26,12 @@ export class ConstantSignalSystem extends GameSystemWithFilter { // Set signals for (let i = 0; i < this.allEntities.length; ++i) { const entity = this.allEntities[i]; - const pinsComp = entity.components.WiredPins; const signalComp = entity.components.ConstantSignal; - pinsComp.slots[0].value = signalComp.signal; + const pinsComp = entity.components.WiredPins; + + if (pinsComp) { + pinsComp.slots[0].value = signalComp.signal; + } } } @@ -51,31 +54,51 @@ export class ConstantSignalSystem extends GameSystemWithFilter { label: fillInLinkIntoTranslation(T.dialogs.editSignal.descShortKey, THIRDPARTY_URLS.shapeViewer), placeholder: "", defaultValue: "", - validator: val => this.parseSignalCode(val), + validator: val => this.parseSignalCode(entity, val), }); + const items = [...Object.values(COLOR_ITEM_SINGLETONS)]; + + if (entity.components.WiredPins) { + items.unshift(BOOL_FALSE_SINGLETON, BOOL_TRUE_SINGLETON); + items.push( + this.root.shapeDefinitionMgr.getShapeItemFromShortKey( + this.root.gameMode.getBlueprintShapeKey() + ) + ); + } else { + // producer which can produce virtually anything + const shapes = ["CuCuCuCu", "RuRuRuRu", "WuWuWuWu", "SuSuSuSu"]; + items.unshift( + ...shapes.reverse().map(key => this.root.shapeDefinitionMgr.getShapeItemFromShortKey(key)) + ); + } + + if (this.root.gameMode.hasHub()) { + items.push( + this.root.shapeDefinitionMgr.getShapeItemFromDefinition( + this.root.hubGoals.currentGoal.definition + ) + ); + } + + if (this.root.hud.parts.pinnedShapes) { + items.push( + ...this.root.hud.parts.pinnedShapes.pinnedShapes.map(key => + this.root.shapeDefinitionMgr.getShapeItemFromShortKey(key) + ) + ); + } + const itemInput = new FormElementItemChooser({ id: "signalItem", label: null, - items: [ - BOOL_FALSE_SINGLETON, - BOOL_TRUE_SINGLETON, - ...Object.values(COLOR_ITEM_SINGLETONS), - this.root.shapeDefinitionMgr.getShapeItemFromDefinition( - this.root.hubGoals.currentGoal.definition - ), - this.root.shapeDefinitionMgr.getShapeItemFromShortKey( - this.root.gameMode.getBlueprintShapeKey() - ), - ...this.root.hud.parts.pinnedShapes.pinnedShapes.map(key => - this.root.shapeDefinitionMgr.getShapeItemFromShortKey(key) - ), - ], + items, }); const dialog = new DialogWithForm({ app: this.root.app, - title: T.dialogs.editSignal.title, + title: T.dialogs.editConstantProducer.title, desc: T.dialogs.editSignal.descItems, formElements: [itemInput, signalValueInput], buttons: ["cancel:bad:escape", "ok:good:enter"], @@ -103,15 +126,19 @@ export class ConstantSignalSystem extends GameSystemWithFilter { } if (itemInput.chosenItem) { - console.log(itemInput.chosenItem); constantComp.signal = itemInput.chosenItem; } else { - constantComp.signal = this.parseSignalCode(signalValueInput.getValue()); + constantComp.signal = this.parseSignalCode(entity, signalValueInput.getValue()); } }; - dialog.buttonSignals.ok.add(closeHandler); - dialog.valueChosen.add(closeHandler); + dialog.buttonSignals.ok.add(() => { + closeHandler(); + }); + dialog.valueChosen.add(() => { + dialog.closeRequested.dispatch(); + closeHandler(); + }); // When cancelled, destroy the entity again if (deleteOnCancel) { @@ -140,10 +167,11 @@ export class ConstantSignalSystem extends GameSystemWithFilter { /** * Tries to parse a signal code + * @param {Entity} entity * @param {string} code * @returns {BaseItem} */ - parseSignalCode(code) { + parseSignalCode(entity, code) { if (!this.root || !this.root.shapeDefinitionMgr) { // Stale reference return null; @@ -155,12 +183,15 @@ export class ConstantSignalSystem extends GameSystemWithFilter { if (enumColors[codeLower]) { return COLOR_ITEM_SINGLETONS[codeLower]; } - if (code === "1" || codeLower === "true") { - return BOOL_TRUE_SINGLETON; - } - if (code === "0" || codeLower === "false") { - return BOOL_FALSE_SINGLETON; + if (entity.components.WiredPins) { + if (code === "1" || codeLower === "true") { + return BOOL_TRUE_SINGLETON; + } + + if (code === "0" || codeLower === "false") { + return BOOL_FALSE_SINGLETON; + } } if (ShapeDefinition.isValidShortKey(code)) { diff --git a/src/js/game/systems/goal_acceptor.js b/src/js/game/systems/goal_acceptor.js new file mode 100644 index 00000000..40100324 --- /dev/null +++ b/src/js/game/systems/goal_acceptor.js @@ -0,0 +1,135 @@ +import { globalConfig } from "../../core/config"; +import { DrawParameters } from "../../core/draw_parameters"; +import { clamp, lerp } from "../../core/utils"; +import { Vector } from "../../core/vector"; +import { GoalAcceptorComponent } from "../components/goal_acceptor"; +import { GameSystemWithFilter } from "../game_system_with_filter"; +import { MapChunk } from "../map_chunk"; +import { GameRoot } from "../root"; + +export class GoalAcceptorSystem extends GameSystemWithFilter { + /** @param {GameRoot} root */ + constructor(root) { + super(root, [GoalAcceptorComponent]); + + this.puzzleCompleted = false; + } + + update() { + const now = this.root.time.now(); + + let allAccepted = true; + + for (let i = 0; i < this.allEntities.length; ++i) { + const entity = this.allEntities[i]; + const goalComp = entity.components.GoalAcceptor; + + if (!goalComp.lastDelivery) { + allAccepted = false; + continue; + } + + if (now - goalComp.lastDelivery.time > goalComp.getRequiredSecondsPerItem()) { + goalComp.clearItems(); + } + + if (goalComp.currentDeliveredItems < globalConfig.goalAcceptorItemsRequired) { + allAccepted = false; + } + } + + if ( + !this.puzzleCompleted && + this.root.gameInitialized && + allAccepted && + !this.root.gameMode.getIsEditor() + ) { + this.root.signals.puzzleComplete.dispatch(); + this.puzzleCompleted = true; + } + } + + /** + * + * @param {DrawParameters} parameters + * @param {MapChunk} chunk + * @returns + */ + drawChunk(parameters, chunk) { + const contents = chunk.containedEntitiesByLayer.regular; + for (let i = 0; i < contents.length; ++i) { + const goalComp = contents[i].components.GoalAcceptor; + + if (!goalComp) { + continue; + } + + const staticComp = contents[i].components.StaticMapEntity; + const item = goalComp.item; + + const requiredItemsForSuccess = globalConfig.goalAcceptorItemsRequired; + const percentage = clamp(goalComp.currentDeliveredItems / requiredItemsForSuccess, 0, 1); + + const center = staticComp.getTileSpaceBounds().getCenter().toWorldSpace(); + if (item) { + const localOffset = new Vector(0, -1.8).rotateFastMultipleOf90(staticComp.rotation); + item.drawItemCenteredClipped( + center.x + localOffset.x, + center.y + localOffset.y, + parameters, + globalConfig.tileSize * 0.65 + ); + } + + const isValid = item && goalComp.currentDeliveredItems >= requiredItemsForSuccess; + + parameters.context.translate(center.x, center.y); + parameters.context.rotate((staticComp.rotation / 180) * Math.PI); + + parameters.context.lineWidth = 1; + parameters.context.fillStyle = "#8de255"; + parameters.context.strokeStyle = "#64666e"; + parameters.context.lineCap = "round"; + + // progress arc + + goalComp.displayPercentage = lerp(goalComp.displayPercentage, percentage, 0.2); + + const startAngle = Math.PI * 0.595; + const maxAngle = Math.PI * 1.82; + parameters.context.beginPath(); + parameters.context.arc( + 0.25, + -1.5, + 11.6, + startAngle, + startAngle + goalComp.displayPercentage * maxAngle, + false + ); + parameters.context.arc( + 0.25, + -1.5, + 15.5, + startAngle + goalComp.displayPercentage * maxAngle, + startAngle, + true + ); + parameters.context.closePath(); + parameters.context.fill(); + parameters.context.stroke(); + parameters.context.lineCap = "butt"; + + // LED indicator + + parameters.context.lineWidth = 1.2; + parameters.context.strokeStyle = "#64666e"; + parameters.context.fillStyle = isValid ? "#8de255" : "#ff666a"; + parameters.context.beginCircle(10, 11.8, 5); + parameters.context.fill(); + parameters.context.stroke(); + + parameters.context.rotate((-staticComp.rotation / 180) * Math.PI); + parameters.context.translate(-center.x, -center.y); + } + } +} diff --git a/src/js/game/systems/item_ejector.js b/src/js/game/systems/item_ejector.js index 1daaad6b..db37455a 100644 --- a/src/js/game/systems/item_ejector.js +++ b/src/js/game/systems/item_ejector.js @@ -239,6 +239,14 @@ export class ItemEjectorSystem extends GameSystemWithFilter { return false; } + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + // + // NOTICE ! THIS CODE IS DUPLICATED IN THE BELT PATH FOR PERFORMANCE REASONS + // + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + const itemProcessorComp = receiver.components.ItemProcessor; if (itemProcessorComp) { // Check for potential filters diff --git a/src/js/game/systems/item_processor.js b/src/js/game/systems/item_processor.js index 9775afde..3794473f 100644 --- a/src/js/game/systems/item_processor.js +++ b/src/js/game/systems/item_processor.js @@ -1,3 +1,4 @@ +import { globalConfig } from "../../core/config"; import { BaseItem } from "../base_item"; import { enumColorMixingResults, enumColors } from "../colors"; import { @@ -59,6 +60,7 @@ export class ItemProcessorSystem extends GameSystemWithFilter { [enumItemProcessorTypes.painterQuad]: this.process_PAINTER_QUAD, [enumItemProcessorTypes.hub]: this.process_HUB, [enumItemProcessorTypes.reader]: this.process_READER, + [enumItemProcessorTypes.goal]: this.process_GOAL, }; // Bind all handlers @@ -562,4 +564,32 @@ export class ItemProcessorSystem extends GameSystemWithFilter { this.root.hubGoals.handleDefinitionDelivered(item.definition); } } + + /** + * @param {ProcessorImplementationPayload} payload + */ + process_GOAL(payload) { + const goalComp = payload.entity.components.GoalAcceptor; + const item = payload.items[0].item; + const now = this.root.time.now(); + + if (goalComp.item && !item.equals(goalComp.item)) { + goalComp.clearItems(); + } else { + goalComp.currentDeliveredItems = Math.min( + goalComp.currentDeliveredItems + 1, + globalConfig.goalAcceptorItemsRequired + ); + } + + if (this.root.gameMode.getIsEditor()) { + // while playing in editor, assign the item + goalComp.item = payload.items[0].item; + } + + goalComp.lastDelivery = { + item, + time: now, + }; + } } diff --git a/src/js/game/systems/item_producer.js b/src/js/game/systems/item_producer.js index 52edf5d1..0a385907 100644 --- a/src/js/game/systems/item_producer.js +++ b/src/js/game/systems/item_producer.js @@ -1,15 +1,26 @@ +/* typehints:start */ +import { GameRoot } from "../root"; +/* typehints:end */ + import { ItemProducerComponent } from "../components/item_producer"; import { GameSystemWithFilter } from "../game_system_with_filter"; export class ItemProducerSystem extends GameSystemWithFilter { + /** @param {GameRoot} root */ constructor(root) { super(root, [ItemProducerComponent]); + this.item = null; } update() { for (let i = 0; i < this.allEntities.length; ++i) { const entity = this.allEntities[i]; + const ejectorComp = entity.components.ItemEjector; const pinsComp = entity.components.WiredPins; + if (!pinsComp) { + continue; + } + const pin = pinsComp.slots[0]; const network = pin.linkedNetwork; @@ -17,8 +28,8 @@ export class ItemProducerSystem extends GameSystemWithFilter { continue; } - const ejectorComp = entity.components.ItemEjector; - ejectorComp.tryEject(0, network.currentValue); + this.item = network.currentValue; + ejectorComp.tryEject(0, this.item); } } } diff --git a/src/js/game/systems/underground_belt.js b/src/js/game/systems/underground_belt.js index 7a7609f8..9b31eec1 100644 --- a/src/js/game/systems/underground_belt.js +++ b/src/js/game/systems/underground_belt.js @@ -224,13 +224,16 @@ export class UndergroundBeltSystem extends GameSystemWithFilter { update() { this.staleAreaWatcher.update(); + const sender = enumUndergroundBeltMode.sender; + const now = this.root.time.now(); + for (let i = 0; i < this.allEntities.length; ++i) { const entity = this.allEntities[i]; const undergroundComp = entity.components.UndergroundBelt; - if (undergroundComp.mode === enumUndergroundBeltMode.sender) { + if (undergroundComp.mode === sender) { this.handleSender(entity); } else { - this.handleReceiver(entity); + this.handleReceiver(entity, now); } } } @@ -327,14 +330,15 @@ export class UndergroundBeltSystem extends GameSystemWithFilter { /** * * @param {Entity} entity + * @param {number} now */ - handleReceiver(entity) { + handleReceiver(entity, now) { const undergroundComp = entity.components.UndergroundBelt; // Try to eject items, we only check the first one because it is sorted by remaining time const nextItemAndDuration = undergroundComp.pendingItems[0]; if (nextItemAndDuration) { - if (this.root.time.now() > nextItemAndDuration[1]) { + if (now > nextItemAndDuration[1]) { const ejectorComp = entity.components.ItemEjector; const nextSlotIndex = ejectorComp.getFirstFreeSlot(); diff --git a/src/js/game/systems/zone.js b/src/js/game/systems/zone.js new file mode 100644 index 00000000..109f5166 --- /dev/null +++ b/src/js/game/systems/zone.js @@ -0,0 +1,105 @@ +/* typehints:start */ +import { DrawParameters } from "../../core/draw_parameters"; +import { MapChunkView } from "../map_chunk_view"; +import { GameRoot } from "../root"; +/* typehints:end */ + +import { globalConfig } from "../../core/config"; +import { STOP_PROPAGATION } from "../../core/signal"; +import { GameSystem } from "../game_system"; +import { THEME } from "../theme"; +import { Entity } from "../entity"; +import { Vector } from "../../core/vector"; + +export class ZoneSystem extends GameSystem { + /** @param {GameRoot} root */ + constructor(root) { + super(root); + this.drawn = false; + this.root.signals.prePlacementCheck.add(this.prePlacementCheck, this); + + this.root.signals.gameFrameStarted.add(() => { + this.drawn = false; + }); + } + + /** + * + * @param {Entity} entity + * @param {Vector | undefined} tile + * @returns + */ + prePlacementCheck(entity, tile = null) { + const staticComp = entity.components.StaticMapEntity; + + if (!staticComp) { + return; + } + + const mode = this.root.gameMode; + + const zones = mode.getBuildableZones(); + if (!zones) { + return; + } + + const transformed = staticComp.getTileSpaceBounds(); + if (tile) { + transformed.x += tile.x; + transformed.y += tile.y; + } + + if (!zones.some(zone => zone.intersectsFully(transformed))) { + return STOP_PROPAGATION; + } + } + + /** + * Draws the zone + * @param {DrawParameters} parameters + * @param {MapChunkView} chunk + */ + drawChunk(parameters, chunk) { + if (this.drawn) { + // oof + return; + } + this.drawn = true; + + const mode = this.root.gameMode; + + const zones = mode.getBuildableZones(); + if (!zones) { + return; + } + + const zone = zones[0].allScaled(globalConfig.tileSize); + const context = parameters.context; + + context.lineWidth = 2; + context.strokeStyle = THEME.map.zone.borderSolid; + context.beginPath(); + context.rect(zone.x - 1, zone.y - 1, zone.w + 2, zone.h + 2); + context.stroke(); + + const outer = zone; + const padding = 40 * globalConfig.tileSize; + context.fillStyle = THEME.map.zone.outerColor; + context.fillRect(outer.x + outer.w, outer.y, padding, outer.h); + context.fillRect(outer.x - padding, outer.y, padding, outer.h); + context.fillRect( + outer.x - padding - globalConfig.tileSize, + outer.y - padding, + 2 * padding + zone.w + 2 * globalConfig.tileSize, + padding + ); + context.fillRect( + outer.x - padding - globalConfig.tileSize, + outer.y + outer.h, + 2 * padding + zone.w + 2 * globalConfig.tileSize, + padding + ); + + context.globalAlpha = 1; + } +} diff --git a/src/js/game/themes/dark.json b/src/js/game/themes/dark.json index 733b7682..cb111430 100644 --- a/src/js/game/themes/dark.json +++ b/src/js/game/themes/dark.json @@ -47,6 +47,11 @@ "textColor": "#fff", "textColorCapped": "#ef5072", "background": "rgba(40, 50, 60, 0.8)" + }, + + "zone": { + "borderSolid": "rgba(23, 192, 255, 1)", + "outerColor": "rgba(20 , 20, 25, 0.5)" } }, diff --git a/src/js/game/themes/light.json b/src/js/game/themes/light.json index 0c793c26..0962eb93 100644 --- a/src/js/game/themes/light.json +++ b/src/js/game/themes/light.json @@ -48,6 +48,11 @@ "textColor": "#fff", "textColorCapped": "#ef5072", "background": "rgba(40, 50, 60, 0.8)" + }, + + "zone": { + "borderSolid": "rgba(23, 192, 255, 1)", + "outerColor": "rgba(240, 240, 255, 0.5)" } }, diff --git a/src/js/globals.d.ts b/src/js/globals.d.ts index d1fb5305..bf870fab 100644 --- a/src/js/globals.d.ts +++ b/src/js/globals.d.ts @@ -20,6 +20,7 @@ declare const G_ALL_UI_IMAGES: Array; declare const G_IS_RELEASE: boolean; declare const G_CHINA_VERSION: boolean; +declare const G_WEGAME_VERSION: boolean; // Polyfills declare interface String { @@ -185,6 +186,7 @@ declare const STOP_PROPAGATION = "stop_propagation"; declare interface TypedSignal> { add(receiver: (...args: T) => /* STOP_PROPAGATION */ string | void, scope?: object); + addToTop(receiver: (...args: T) => /* STOP_PROPAGATION */ string | void, scope?: object); remove(receiver: (...args: T) => /* STOP_PROPAGATION */ string | void); dispatch(...args: T): /* STOP_PROPAGATION */ string | void; diff --git a/src/js/languages.js b/src/js/languages.js index 6899ef09..4dfb15d4 100644 --- a/src/js/languages.js +++ b/src/js/languages.js @@ -184,4 +184,12 @@ export const LANGUAGES = { code: "uk", region: "", }, + + "he": { + // hebrew + name: "עברית", + data: require("./built-temp/base-he.json"), + code: "he", + region: "", + }, }; diff --git a/src/js/main.js b/src/js/main.js index 5b9df699..94f3d37a 100644 --- a/src/js/main.js +++ b/src/js/main.js @@ -9,6 +9,7 @@ import { initComponentRegistry } from "./game/component_registry"; import { initDrawUtils } from "./core/draw_utils"; import { initItemRegistry } from "./game/item_registry"; import { initMetaBuildingRegistry } from "./game/meta_building_registry"; +import { initGameModeRegistry } from "./game/game_mode_registry"; import { initGameSpeedRegistry } from "./game/game_speed_registry"; const logger = createLogger("main"); @@ -81,6 +82,7 @@ initDrawUtils(); initComponentRegistry(); initItemRegistry(); initMetaBuildingRegistry(); +initGameModeRegistry(); initGameSpeedRegistry(); let app = null; diff --git a/src/js/platform/api.js b/src/js/platform/api.js new file mode 100644 index 00000000..db27360d --- /dev/null +++ b/src/js/platform/api.js @@ -0,0 +1,232 @@ +/* typehints:start */ +import { Application } from "../application"; +/* typehints:end */ +import { createLogger } from "../core/logging"; +import { compressX64 } from "../core/lzstring"; +import { getIPCRenderer } from "../core/utils"; +import { T } from "../translations"; + +const logger = createLogger("puzzle-api"); + +export class ClientAPI { + /** + * + * @param {Application} app + */ + constructor(app) { + this.app = app; + + /** + * The current users session token + * @type {string|null} + */ + this.token = null; + } + + getEndpoint() { + if (G_IS_DEV) { + return "http://localhost:15001"; + } + if (window.location.host === "beta.shapez.io") { + return "https://api-staging.shapez.io"; + } + return "https://api.shapez.io"; + } + + isLoggedIn() { + return Boolean(this.token); + } + + /** + * + * @param {string} endpoint + * @param {object} options + * @param {"GET"|"POST"=} options.method + * @param {any=} options.body + */ + _request(endpoint, options) { + const headers = { + "x-api-key": "d5c54aaa491f200709afff082c153ef2", + "Content-Type": "application/json", + }; + + if (this.token) { + headers["x-token"] = this.token; + } + + return Promise.race([ + fetch(this.getEndpoint() + endpoint, { + cache: "no-cache", + mode: "cors", + headers, + method: options.method || "GET", + body: options.body ? JSON.stringify(options.body) : undefined, + }) + .then(res => { + if (res.status !== 200) { + throw "bad-status: " + res.status + " / " + res.statusText; + } + return res; + }) + .then(res => res.json()), + new Promise((resolve, reject) => setTimeout(() => reject("timeout"), 15000)), + ]) + .then(data => { + if (data && data.error) { + logger.warn("Got error from api:", data); + throw T.backendErrors[data.error] || data.error; + } + return data; + }) + .catch(err => { + logger.warn("Failure:", endpoint, ":", err); + throw err; + }); + } + + tryLogin() { + return this.apiTryLogin() + .then(({ token }) => { + this.token = token; + return true; + }) + .catch(err => { + logger.warn("Failed to login:", err); + return false; + }); + } + + /** + * @returns {Promise<{token: string}>} + */ + apiTryLogin() { + if (!G_IS_STANDALONE) { + let token = window.localStorage.getItem("dev_api_auth_token"); + if (!token) { + token = window.prompt( + "Please enter the auth token for the puzzle DLC (If you have none, you can't login):" + ); + } + if (token) { + window.localStorage.setItem("dev_api_auth_token", token); + } + return Promise.resolve({ token }); + } + + const renderer = getIPCRenderer(); + + return renderer.invoke("steam:get-ticket").then( + ticket => { + logger.log("Got auth ticket:", ticket); + return this._request("/v1/public/login", { + method: "POST", + body: { + token: ticket, + }, + }); + }, + err => { + logger.error("Failed to get auth ticket from steam: ", err); + throw err; + } + ); + } + + /** + * @param {"new"|"top-rated"|"mine"} category + * @returns {Promise} + */ + apiListPuzzles(category) { + if (!this.isLoggedIn()) { + return Promise.reject("not-logged-in"); + } + return this._request("/v1/puzzles/list/" + category, {}); + } + + /** + * @param {number} puzzleId + * @returns {Promise} + */ + apiDownloadPuzzle(puzzleId) { + if (!this.isLoggedIn()) { + return Promise.reject("not-logged-in"); + } + return this._request("/v1/puzzles/download/" + puzzleId, {}); + } + + /** + * @param {number} puzzleId + * @returns {Promise} + */ + apiDeletePuzzle(puzzleId) { + if (!this.isLoggedIn()) { + return Promise.reject("not-logged-in"); + } + return this._request("/v1/puzzles/delete/" + puzzleId, { + method: "POST", + body: {}, + }); + } + + /** + * @param {number} shortKey + * @returns {Promise} + */ + apiDownloadPuzzleByKey(shortKey) { + if (!this.isLoggedIn()) { + return Promise.reject("not-logged-in"); + } + return this._request("/v1/puzzles/download/" + shortKey, {}); + } + + /** + * @param {number} puzzleId + * @returns {Promise} + */ + apiReportPuzzle(puzzleId, reason) { + if (!this.isLoggedIn()) { + return Promise.reject("not-logged-in"); + } + return this._request("/v1/puzzles/report/" + puzzleId, { + method: "POST", + body: { reason }, + }); + } + + /** + * @param {number} puzzleId + * @param {object} payload + * @param {number} payload.time + * @param {boolean} payload.liked + * @returns {Promise<{ success: true }>} + */ + apiCompletePuzzle(puzzleId, payload) { + if (!this.isLoggedIn()) { + return Promise.reject("not-logged-in"); + } + return this._request("/v1/puzzles/complete/" + puzzleId, { + method: "POST", + body: payload, + }); + } + + /** + * @param {object} payload + * @param {string} payload.title + * @param {string} payload.shortKey + * @param {import("../savegame/savegame_typedefs").PuzzleGameData} payload.data + * @returns {Promise<{ success: true }>} + */ + apiSubmitPuzzle(payload) { + if (!this.isLoggedIn()) { + return Promise.reject("not-logged-in"); + } + return this._request("/v1/puzzles/submit", { + method: "POST", + body: { + ...payload, + data: compressX64(JSON.stringify(payload.data)), + }, + }); + } +} diff --git a/src/js/platform/browser/game_analytics.js b/src/js/platform/browser/game_analytics.js index a3947be6..e4226ae0 100644 --- a/src/js/platform/browser/game_analytics.js +++ b/src/js/platform/browser/game_analytics.js @@ -3,6 +3,7 @@ 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 { RegularGameMode } from "../../game/modes/regular"; import { GameRoot } from "../../game/root"; import { InGameState } from "../../states/ingame"; import { GameAnalyticsInterface } from "../game_analytics"; @@ -52,6 +53,10 @@ export class ShapezGameAnalytics extends GameAnalyticsInterface { initialize() { this.syncKey = null; + if (G_WEGAME_VERSION) { + return; + } + setInterval(() => this.sendTimePoints(), 60 * 1000); // Retrieve sync key from player @@ -135,6 +140,10 @@ export class ShapezGameAnalytics extends GameAnalyticsInterface { * @param {string} value */ sendGameEvent(category, value) { + if (G_WEGAME_VERSION) { + return; + } + if (!this.syncKey) { logger.warn("Can not send event due to missing sync key"); return; @@ -163,6 +172,10 @@ export class ShapezGameAnalytics extends GameAnalyticsInterface { return; } + if (!(root.gameMode instanceof RegularGameMode)) { + return; + } + logger.log("Sending event", category, value); this.sendToApi("/v1/game-event", { diff --git a/src/js/platform/electron/steam_achievement_provider.js b/src/js/platform/electron/steam_achievement_provider.js index 06b0758d..b9166c45 100644 --- a/src/js/platform/electron/steam_achievement_provider.js +++ b/src/js/platform/electron/steam_achievement_provider.js @@ -119,6 +119,10 @@ export class SteamAchievementProvider extends AchievementProviderInterface { return Promise.resolve(); } + if (G_WEGAME_VERSION) { + return Promise.resolve(); + } + this.ipc = getIPCRenderer(); return this.ipc.invoke("steam:is-initialized").then(initialized => { @@ -155,6 +159,10 @@ export class SteamAchievementProvider extends AchievementProviderInterface { activate(key) { let promise; + if (G_WEGAME_VERSION) { + return Promise.resolve(); + } + if (!this.initialized) { promise = Promise.resolve(); } else { diff --git a/src/js/platform/electron/storage.js b/src/js/platform/electron/storage.js index bf4ed9ac..41ed1746 100644 --- a/src/js/platform/electron/storage.js +++ b/src/js/platform/electron/storage.js @@ -7,6 +7,24 @@ const logger = createLogger("electron-storage"); export class StorageImplElectron extends StorageInterface { constructor(app) { super(app); + + /** @type {Object.} */ + this.jobs = {}; + this.jobId = 0; + + getIPCRenderer().on("fs-response", (event, arg) => { + const id = arg.id; + if (!this.jobs[id]) { + logger.warn("Got unhandled FS response, job not known:", id); + return; + } + const { resolve, reject } = this.jobs[id]; + if (arg.result.success) { + resolve(arg.result.data); + } else { + reject(arg.result.error); + } + }); } initialize() { @@ -15,53 +33,43 @@ export class StorageImplElectron extends StorageInterface { writeFileAsync(filename, contents) { return new Promise((resolve, reject) => { - getIPCRenderer() - .invoke("fs-job", { - type: "write", - filename, - contents, - }) - .then(result => { - if (result.success) { - resolve(result.data); - } else { - reject(result.error); - } - }); + // ipcMain + const jobId = ++this.jobId; + this.jobs[jobId] = { resolve, reject }; + + getIPCRenderer().send("fs-job", { + type: "write", + filename, + contents, + id: jobId, + }); }); } readFileAsync(filename) { return new Promise((resolve, reject) => { - getIPCRenderer() - .invoke("fs-job", { - type: "read", - filename, - }) - .then(result => { - if (result.success) { - resolve(result.data); - } else { - reject(result.error); - } - }); + // ipcMain + const jobId = ++this.jobId; + this.jobs[jobId] = { resolve, reject }; + + getIPCRenderer().send("fs-job", { + type: "read", + filename, + id: jobId, + }); }); } deleteFileAsync(filename) { return new Promise((resolve, reject) => { - getIPCRenderer() - .invoke("fs-job", { - type: "delete", - filename, - }) - .then(result => { - if (result.success) { - resolve(result.data); - } else { - reject(result.error); - } - }); + // ipcMain + const jobId = ++this.jobId; + this.jobs[jobId] = { resolve, reject }; + getIPCRenderer().send("fs-job", { + type: "delete", + filename, + id: jobId, + }); }); } } diff --git a/src/js/platform/electron/wrapper.js b/src/js/platform/electron/wrapper.js index 501b7258..eac4328b 100644 --- a/src/js/platform/electron/wrapper.js +++ b/src/js/platform/electron/wrapper.js @@ -10,6 +10,10 @@ const logger = createLogger("electron-wrapper"); export class PlatformWrapperImplElectron extends PlatformWrapperImplBrowser { initialize() { + this.dlcs = { + puzzle: false, + }; + this.steamOverlayCanvasFix = document.createElement("canvas"); this.steamOverlayCanvasFix.width = 1; this.steamOverlayCanvasFix.height = 1; @@ -23,9 +27,9 @@ export class PlatformWrapperImplElectron extends PlatformWrapperImplBrowser { this.app.storage = new StorageImplElectron(this); this.app.achievementProvider = new SteamAchievementProvider(this.app); - return this.initializeAchievementProvider().then(() => - PlatformWrapperInterface.prototype.initialize.call(this) - ); + return this.initializeAchievementProvider() + .then(() => this.initializeDlcStatus()) + .then(() => PlatformWrapperInterface.prototype.initialize.call(this)); } steamOverlayFixRedrawCanvas() { @@ -66,6 +70,26 @@ export class PlatformWrapperImplElectron extends PlatformWrapperImplBrowser { }); } + initializeDlcStatus() { + const renderer = getIPCRenderer(); + + if (G_WEGAME_VERSION) { + return Promise.resolve(); + } + + logger.log("Checking DLC ownership ..."); + // @todo: Don't hardcode the app id + return renderer.invoke("steam:check-app-ownership", 1625400).then( + res => { + logger.log("Got DLC ownership:", res); + this.dlcs.puzzle = Boolean(res); + }, + err => { + logger.error("Failed to get DLC ownership:", err); + } + ); + } + getSupportsFullscreen() { return true; } diff --git a/src/js/platform/sound.js b/src/js/platform/sound.js index 9d5a8461..d43c76c2 100644 --- a/src/js/platform/sound.js +++ b/src/js/platform/sound.js @@ -35,6 +35,10 @@ export const MUSIC = { menu: "menu", }; +if (G_IS_STANDALONE || G_IS_DEV) { + MUSIC.puzzle = "puzzle-full"; +} + export class SoundInstanceInterface { constructor(key, url) { this.key = key; diff --git a/src/js/savegame/puzzle_serializer.js b/src/js/savegame/puzzle_serializer.js new file mode 100644 index 00000000..c502142b --- /dev/null +++ b/src/js/savegame/puzzle_serializer.js @@ -0,0 +1,209 @@ +/* typehints:start */ +import { GameRoot } from "../game/root"; +import { PuzzleGameMode } from "../game/modes/puzzle"; +/* typehints:end */ +import { StaticMapEntityComponent } from "../game/components/static_map_entity"; +import { ShapeItem } from "../game/items/shape_item"; +import { Vector } from "../core/vector"; +import { MetaConstantProducerBuilding } from "../game/buildings/constant_producer"; +import { defaultBuildingVariant, MetaBuilding } from "../game/meta_building"; +import { gMetaBuildingRegistry } from "../core/global_registries"; +import { MetaGoalAcceptorBuilding } from "../game/buildings/goal_acceptor"; +import { createLogger } from "../core/logging"; +import { BaseItem } from "../game/base_item"; +import trim from "trim"; +import { enumColors } from "../game/colors"; +import { COLOR_ITEM_SINGLETONS } from "../game/items/color_item"; +import { ShapeDefinition } from "../game/shape_definition"; +import { MetaBlockBuilding } from "../game/buildings/block"; + +const logger = createLogger("puzzle-serializer"); + +export class PuzzleSerializer { + /** + * Serializes the game root into a dump + * @param {GameRoot} root + * @returns {import("./savegame_typedefs").PuzzleGameData} + */ + generateDumpFromGameRoot(root) { + console.log("serializing", root); + + /** + * @type {import("./savegame_typedefs").PuzzleGameData["buildings"]} + */ + let buildings = []; + for (const entity of root.entityMgr.getAllWithComponent(StaticMapEntityComponent)) { + const staticComp = entity.components.StaticMapEntity; + const signalComp = entity.components.ConstantSignal; + + if (signalComp) { + assert(["shape", "color"].includes(signalComp.signal.getItemType()), "not a shape signal"); + buildings.push({ + type: "emitter", + item: signalComp.signal.getAsCopyableKey(), + pos: { + x: staticComp.origin.x, + y: staticComp.origin.y, + r: staticComp.rotation, + }, + }); + continue; + } + + const goalComp = entity.components.GoalAcceptor; + if (goalComp) { + assert(goalComp.item, "goals is missing item"); + assert(goalComp.item.getItemType() === "shape", "goal is not an item"); + buildings.push({ + type: "goal", + item: goalComp.item.getAsCopyableKey(), + pos: { + x: staticComp.origin.x, + y: staticComp.origin.y, + r: staticComp.rotation, + }, + }); + continue; + } + + if (staticComp.getMetaBuilding().id === gMetaBuildingRegistry.findByClass(MetaBlockBuilding).id) { + buildings.push({ + type: "block", + pos: { + x: staticComp.origin.x, + y: staticComp.origin.y, + r: staticComp.rotation, + }, + }); + } + } + + const mode = /** @type {PuzzleGameMode} */ (root.gameMode); + + const handles = root.hud.parts.buildingsToolbar.buildingHandles; + const ids = gMetaBuildingRegistry.getAllIds(); + + /** @type {Array} */ + let excludedBuildings = []; + for (let i = 0; i < ids.length; ++i) { + const handle = handles[ids[i]]; + if (handle && handle.puzzleLocked) { + // @ts-ignore + excludedBuildings.push(handle.metaBuilding.getId()); + } + } + + return { + version: 1, + buildings, + bounds: { + w: mode.zoneWidth, + h: mode.zoneHeight, + }, + //read from the toolbar when making a puzzle + excludedBuildings, + }; + } + + /** + * Tries to parse a signal code + * @param {GameRoot} root + * @param {string} code + * @returns {BaseItem} + */ + parseItemCode(root, code) { + if (!root || !root.shapeDefinitionMgr) { + // Stale reference + return null; + } + + code = trim(code); + const codeLower = code.toLowerCase(); + + if (enumColors[codeLower]) { + return COLOR_ITEM_SINGLETONS[codeLower]; + } + + if (ShapeDefinition.isValidShortKey(code)) { + return root.shapeDefinitionMgr.getShapeItemFromShortKey(code); + } + + return null; + } + /** + * @param {GameRoot} root + * @param {import("./savegame_typedefs").PuzzleGameData} puzzle + */ + deserializePuzzle(root, puzzle) { + if (puzzle.version !== 1) { + return "invalid-version"; + } + + for (const building of puzzle.buildings) { + switch (building.type) { + case "emitter": { + const item = this.parseItemCode(root, building.item); + if (!item) { + return "bad-item:" + building.item; + } + + const entity = root.logic.tryPlaceBuilding({ + origin: new Vector(building.pos.x, building.pos.y), + building: gMetaBuildingRegistry.findByClass(MetaConstantProducerBuilding), + originalRotation: building.pos.r, + rotation: building.pos.r, + rotationVariant: 0, + variant: defaultBuildingVariant, + }); + if (!entity) { + logger.warn("Failed to place emitter:", building); + return "failed-to-place-emitter"; + } + + entity.components.ConstantSignal.signal = item; + break; + } + case "goal": { + const item = this.parseItemCode(root, building.item); + if (!item) { + return "bad-item:" + building.item; + } + const entity = root.logic.tryPlaceBuilding({ + origin: new Vector(building.pos.x, building.pos.y), + building: gMetaBuildingRegistry.findByClass(MetaGoalAcceptorBuilding), + originalRotation: building.pos.r, + rotation: building.pos.r, + rotationVariant: 0, + variant: defaultBuildingVariant, + }); + if (!entity) { + logger.warn("Failed to place goal:", building); + return "failed-to-place-goal"; + } + + entity.components.GoalAcceptor.item = item; + break; + } + case "block": { + const entity = root.logic.tryPlaceBuilding({ + origin: new Vector(building.pos.x, building.pos.y), + building: gMetaBuildingRegistry.findByClass(MetaBlockBuilding), + originalRotation: building.pos.r, + rotation: building.pos.r, + rotationVariant: 0, + variant: defaultBuildingVariant, + }); + if (!entity) { + logger.warn("Failed to place block:", building); + return "failed-to-place-block"; + } + break; + } + default: { + // @ts-ignore + return "invalid-building-type: " + building.type; + } + } + } + } +} diff --git a/src/js/savegame/savegame.js b/src/js/savegame/savegame.js index e56ae1dc..999b90ec 100644 --- a/src/js/savegame/savegame.js +++ b/src/js/savegame/savegame.js @@ -13,6 +13,7 @@ import { SavegameInterface_V1005 } from "./schemas/1005"; import { SavegameInterface_V1006 } from "./schemas/1006"; import { SavegameInterface_V1007 } from "./schemas/1007"; import { SavegameInterface_V1008 } from "./schemas/1008"; +import { SavegameInterface_V1009 } from "./schemas/1009"; const logger = createLogger("savegame"); @@ -53,7 +54,7 @@ export class Savegame extends ReadWriteProxy { * @returns {number} */ static getCurrentVersion() { - return 1008; + return 1009; } /** @@ -136,6 +137,11 @@ export class Savegame extends ReadWriteProxy { data.version = 1008; } + if (data.version === 1008) { + SavegameInterface_V1009.migrate1008to1009(data); + data.version = 1009; + } + return ExplainedResult.good(); } diff --git a/src/js/savegame/savegame_compressor.js b/src/js/savegame/savegame_compressor.js index b0b92aa8..d8797bad 100644 --- a/src/js/savegame/savegame_compressor.js +++ b/src/js/savegame/savegame_compressor.js @@ -13,15 +13,20 @@ function compressInt(i) { // Zero value breaks i += 1; - if (compressionCache[i]) { - return compressionCache[i]; + // save `i` as the cache key + // to avoid it being modified by the + // rest of the function. + const cache_key = i; + + if (compressionCache[cache_key]) { + return compressionCache[cache_key]; } let result = ""; do { result += charmap[i % charmap.length]; i = Math.floor(i / charmap.length); } while (i > 0); - return (compressionCache[i] = result); + return (compressionCache[cache_key] = result); } /** diff --git a/src/js/savegame/savegame_interface_registry.js b/src/js/savegame/savegame_interface_registry.js index 395040b3..b4dc4233 100644 --- a/src/js/savegame/savegame_interface_registry.js +++ b/src/js/savegame/savegame_interface_registry.js @@ -9,6 +9,7 @@ import { SavegameInterface_V1005 } from "./schemas/1005"; import { SavegameInterface_V1006 } from "./schemas/1006"; import { SavegameInterface_V1007 } from "./schemas/1007"; import { SavegameInterface_V1008 } from "./schemas/1008"; +import { SavegameInterface_V1009 } from "./schemas/1009"; /** @type {Object.} */ export const savegameInterfaces = { @@ -21,6 +22,7 @@ export const savegameInterfaces = { 1006: SavegameInterface_V1006, 1007: SavegameInterface_V1007, 1008: SavegameInterface_V1008, + 1009: SavegameInterface_V1009, }; const logger = createLogger("savegame_interface_registry"); diff --git a/src/js/savegame/savegame_serializer.js b/src/js/savegame/savegame_serializer.js index c1247225..3230cdd5 100644 --- a/src/js/savegame/savegame_serializer.js +++ b/src/js/savegame/savegame_serializer.js @@ -2,6 +2,8 @@ import { ExplainedResult } from "../core/explained_result"; import { createLogger } from "../core/logging"; import { gComponentRegistry } from "../core/global_registries"; import { SerializerInternal } from "./serializer_internal"; +import { HUDPinnedShapes } from "../game/hud/parts/pinned_shapes"; +import { HUDWaypoints } from "../game/hud/parts/waypoints"; /** * @typedef {import("../game/component").Component} Component @@ -33,12 +35,13 @@ export class SavegameSerializer { camera: root.camera.serialize(), time: root.time.serialize(), map: root.map.serialize(), + gameMode: root.gameMode.serialize(), entityMgr: root.entityMgr.serialize(), hubGoals: root.hubGoals.serialize(), - pinnedShapes: root.hud.parts.pinnedShapes.serialize(), - waypoints: root.hud.parts.waypoints.serialize(), entities: this.internal.serializeEntityArray(root.entityMgr.entities), beltPaths: root.systemMgr.systems.belt.serializePaths(), + pinnedShapes: root.hud.parts.pinnedShapes ? root.hud.parts.pinnedShapes.serialize() : null, + waypoints: root.hud.parts.waypoints ? root.hud.parts.waypoints.serialize() : null, }; if (G_IS_DEV) { @@ -130,12 +133,19 @@ 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.gameMode.deserialize(savegame.gameMode); 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); errorReason = errorReason || root.systemMgr.systems.belt.deserializePaths(savegame.beltPaths); + if (root.hud.parts.pinnedShapes) { + errorReason = errorReason || root.hud.parts.pinnedShapes.deserialize(savegame.pinnedShapes); + } + + if (root.hud.parts.waypoints) { + errorReason = errorReason || root.hud.parts.waypoints.deserialize(savegame.waypoints); + } + // Check for errors if (errorReason) { return ExplainedResult.bad(errorReason); diff --git a/src/js/savegame/savegame_typedefs.js b/src/js/savegame/savegame_typedefs.js index fb872113..c5e0e5c5 100644 --- a/src/js/savegame/savegame_typedefs.js +++ b/src/js/savegame/savegame_typedefs.js @@ -12,6 +12,7 @@ * time: any, * entityMgr: any, * map: any, + * gameMode: object, * hubGoals: any, * pinnedShapes: any, * waypoints: any, @@ -40,4 +41,61 @@ * }} SavegamesData */ +import { MetaBuilding } from "../game/meta_building"; + +// Notice: Update backend too +/** + * @typedef {{ + * id: number; + * shortKey: string; + * likes: number; + * downloads: number; + * completions: number; + * difficulty: number | null; + * averageTime: number | null; + * title: string; + * author: string; + * completed: boolean; + * }} PuzzleMetadata + */ + +/** + * @typedef {{ + * type: "emitter"; + * item: string; + * pos: { x: number; y: number; r: number } + * }} PuzzleGameBuildingConstantProducer + */ + +/** + * @typedef {{ + * type: "goal"; + * item: string; + * pos: { x: number; y: number; r: number } + * }} PuzzleGameBuildingGoal + */ + +/** + * @typedef {{ + * type: "block"; + * pos: { x: number; y: number; r: number } + * }} PuzzleGameBuildingBlock + */ + +/** + * @typedef {{ + * version: number; + * bounds: { w: number; h: number; }, + * buildings: (PuzzleGameBuildingGoal | PuzzleGameBuildingConstantProducer | PuzzleGameBuildingBlock)[], + * excludedBuildings: Array, + * }} PuzzleGameData + */ + +/** + * @typedef {{ + * meta: PuzzleMetadata, + * game: PuzzleGameData + * }} PuzzleFullData + */ + export default {}; diff --git a/src/js/savegame/schemas/1009.js b/src/js/savegame/schemas/1009.js new file mode 100644 index 00000000..e6e1abc6 --- /dev/null +++ b/src/js/savegame/schemas/1009.js @@ -0,0 +1,34 @@ +import { createLogger } from "../../core/logging.js"; +import { RegularGameMode } from "../../game/modes/regular.js"; +import { SavegameInterface_V1008 } from "./1008.js"; + +const schema = require("./1009.json"); +const logger = createLogger("savegame_interface/1009"); + +export class SavegameInterface_V1009 extends SavegameInterface_V1008 { + getVersion() { + return 1009; + } + + getSchemaUncached() { + return schema; + } + + /** + * @param {import("../savegame_typedefs.js").SavegameData} data + */ + static migrate1008to1009(data) { + logger.log("Migrating 1008 to 1009"); + const dump = data.dump; + if (!dump) { + return true; + } + + dump.gameMode = { + mode: { + id: RegularGameMode.getId(), + data: {}, + }, + }; + } +} diff --git a/src/js/savegame/schemas/1009.json b/src/js/savegame/schemas/1009.json new file mode 100644 index 00000000..6682f615 --- /dev/null +++ b/src/js/savegame/schemas/1009.json @@ -0,0 +1,5 @@ +{ + "type": "object", + "required": [], + "additionalProperties": true +} diff --git a/src/js/savegame/serialization.js b/src/js/savegame/serialization.js index 801b26ab..78642ceb 100644 --- a/src/js/savegame/serialization.js +++ b/src/js/savegame/serialization.js @@ -275,7 +275,7 @@ export function deserializeSchema(obj, schema, data, baseclassErrorResult = null return baseclassErrorResult; } - if (!data) { + if (data === null || typeof data === "undefined") { logger.error("Got 'NULL' data for", obj, "and schema", schema, "!"); return "Got null data"; } diff --git a/src/js/states/about.js b/src/js/states/about.js index b8f465b7..4380b02c 100644 --- a/src/js/states/about.js +++ b/src/js/states/about.js @@ -2,6 +2,7 @@ import { TextualGameState } from "../core/textual_game_state"; import { T } from "../translations"; import { THIRDPARTY_URLS } from "../core/config"; import { cachebust } from "../core/cachebust"; +import { getLogoSprite } from "../core/background_resources_loader"; export class AboutState extends TextualGameState { constructor() { @@ -15,9 +16,7 @@ export class AboutState extends TextualGameState { getMainContentHTML() { return `
- shapez.io Logo + shapez.io Logo
${T.about.body diff --git a/src/js/states/ingame.js b/src/js/states/ingame.js index 316c536c..0dd6c72a 100644 --- a/src/js/states/ingame.js +++ b/src/js/states/ingame.js @@ -8,6 +8,7 @@ import { KeyActionMapper } from "../game/key_action_mapper"; import { Savegame } from "../savegame/savegame"; import { GameCore } from "../game/core"; import { MUSIC } from "../platform/sound"; +import { enumGameModeIds } from "../game/game_mode"; const logger = createLogger("state/ingame"); @@ -39,8 +40,14 @@ export class GameCreationPayload { /** @type {boolean|undefined} */ this.fastEnter; + /** @type {string} */ + this.gameModeId; + /** @type {Savegame} */ this.savegame; + + /** @type {object|undefined} */ + this.gameModeParameters; } } @@ -97,6 +104,9 @@ export class InGameState extends GameState { } getThemeMusic() { + if (this.creationPayload.gameModeId && this.creationPayload.gameModeId.includes("puzzle")) { + return MUSIC.puzzle; + } return MUSIC.theme; } @@ -147,7 +157,11 @@ export class InGameState extends GameState { * Goes back to the menu state */ goBackToMenu() { - this.saveThenGoToState("MainMenuState"); + if ([enumGameModeIds.puzzleEdit, enumGameModeIds.puzzlePlay].includes(this.gameModeId)) { + this.saveThenGoToState("PuzzleMenuState"); + } else { + this.saveThenGoToState("MainMenuState"); + } } /** @@ -220,7 +234,7 @@ export class InGameState extends GameState { logger.log("Creating new game core"); this.core = new GameCore(this.app); - this.core.initializeRoot(this, this.savegame); + this.core.initializeRoot(this, this.savegame, this.gameModeId); if (this.savegame.hasGameDump()) { this.stage4bResumeGame(); @@ -354,6 +368,7 @@ export class InGameState extends GameState { this.creationPayload = payload; this.savegame = payload.savegame; + this.gameModeId = payload.gameModeId; this.loadingOverlay = new GameLoadingOverlay(this.app, this.getDivElement()); this.loadingOverlay.showBasic(); @@ -361,7 +376,13 @@ export class InGameState extends GameState { // Remove unneded default element document.body.querySelector(".modalDialogParent").remove(); - this.asyncChannel.watch(waitNextFrame()).then(() => this.stage3CreateCore()); + this.asyncChannel + .watch(waitNextFrame()) + .then(() => this.stage3CreateCore()) + .catch(ex => { + logger.error(ex); + throw ex; + }); } /** @@ -433,6 +454,11 @@ export class InGameState extends GameState { logger.warn("Skipping double save and returning same promise"); return this.currentSavePromise; } + + if (!this.core.root.gameMode.getIsSaveable()) { + return Promise.resolve(); + } + logger.log("Starting to save game ..."); this.savegame.updateData(this.core.root); diff --git a/src/js/states/login.js b/src/js/states/login.js new file mode 100644 index 00000000..64f599e4 --- /dev/null +++ b/src/js/states/login.js @@ -0,0 +1,102 @@ +import { GameState } from "../core/game_state"; +import { getRandomHint } from "../game/hints"; +import { HUDModalDialogs } from "../game/hud/parts/modal_dialogs"; +import { T } from "../translations"; + +export class LoginState extends GameState { + constructor() { + super("LoginState"); + } + + getInnerHTML() { + return ` +
+
+ ${T.global.loggingIn} +
+
+ + `; + } + + /** + * + * @param {object} payload + * @param {string} payload.nextStateId + */ + onEnter(payload) { + this.payload = payload; + if (!this.payload.nextStateId) { + throw new Error("No next state id"); + } + + if (this.app.clientApi.isLoggedIn()) { + this.finishLoading(); + return; + } + + this.dialogs = new HUDModalDialogs(null, this.app); + const dialogsElement = document.body.querySelector(".modalDialogParent"); + this.dialogs.initializeToElement(dialogsElement); + + this.htmlElement.classList.add("prefab_LoadingState"); + + /** @type {HTMLElement} */ + this.hintsText = this.htmlElement.querySelector(".prefab_GameHint"); + this.lastHintShown = -1000; + this.nextHintDuration = 0; + + this.tryLogin(); + } + + tryLogin() { + this.app.clientApi.tryLogin().then(success => { + console.log("Logged in:", success); + + if (!success) { + const signals = this.dialogs.showWarning( + T.dialogs.offlineMode.title, + T.dialogs.offlineMode.desc, + ["retry", "playOffline:bad"] + ); + signals.retry.add(() => setTimeout(() => this.tryLogin(), 2000), this); + signals.playOffline.add(this.finishLoading, this); + } else { + this.finishLoading(); + } + }); + } + + finishLoading() { + this.moveToState(this.payload.nextStateId); + } + + getDefaultPreviousState() { + return "MainMenuState"; + } + + update() { + const now = performance.now(); + if (now - this.lastHintShown > this.nextHintDuration) { + this.lastHintShown = now; + const hintText = getRandomHint(); + + this.hintsText.innerHTML = hintText; + + /** + * Compute how long the user will need to read the hint. + * We calculate with 130 words per minute, with an average of 5 chars + * that is 650 characters / minute + */ + this.nextHintDuration = Math.max(2500, (hintText.length / 650) * 60 * 1000); + } + } + + onRender() { + this.update(); + } + + onBackgroundTick() { + this.update(); + } +} diff --git a/src/js/states/main_menu.js b/src/js/states/main_menu.js index a368068c..bfd658c7 100644 --- a/src/js/states/main_menu.js +++ b/src/js/states/main_menu.js @@ -1,3 +1,4 @@ +import { getLogoSprite } from "../core/background_resources_loader"; import { cachebust } from "../core/cachebust"; import { A_B_TESTING_LINK_TYPE, globalConfig, THIRDPARTY_URLS } from "../core/config"; import { GameState } from "../core/game_state"; @@ -16,6 +17,7 @@ import { waitNextFrame, } from "../core/utils"; import { HUDModalDialogs } from "../game/hud/parts/modal_dialogs"; +import { PlatformWrapperImplElectron } from "../platform/electron/wrapper"; import { getApplicationSettingById } from "../profile/application_settings"; import { T } from "../translations"; @@ -40,10 +42,15 @@ export class MainMenuState extends GameState { const showDemoBadges = this.app.restrictionMgr.getIsStandaloneMarketingActive(); + const puzzleDlc = + G_IS_STANDALONE && + /** @type { PlatformWrapperImplElectron + }*/ (this.app.platformWrapper).dlcs.puzzle; + return `
${ - G_CHINA_VERSION + G_CHINA_VERSION || G_WEGAME_VERSION ? "" : `` } @@ -64,28 +71,69 @@ export class MainMenuState extends GameState { -
+
${showDemoBadges ? `
${bannerHtml}
` : ""}
${ - isSupportedBrowser() + G_IS_STANDALONE || isSupportedBrowser() ? "" : `
${T.mainMenu.browserWarning}
` }
+ + ${ + (!G_WEGAME_VERSION && G_IS_STANDALONE && puzzleDlc) || G_IS_DEV + ? ` +
+ + +
` + : "" + } + + ${ + !G_WEGAME_VERSION && G_IS_STANDALONE && !puzzleDlc + ? ` +
+ + ${T.puzzleMenu.categories.new} + + +

${T.mainMenu.puzzleDlcText}

+ + + ${T.puzzleMenu.dlcHint} + +
` + : "" + }
- + ` + } `; } @@ -204,6 +254,11 @@ export class MainMenuState extends GameState { const qs = this.htmlElement.querySelector.bind(this.htmlElement); + if (G_IS_DEV && globalConfig.debug.testPuzzleMode) { + this.onPuzzleModeButtonClicked(true); + return; + } + if (G_IS_DEV && globalConfig.debug.fastGameEnter) { const games = this.app.savegameMgr.getSavegamesMetaData(); if (games.length > 0 && globalConfig.debug.resumeGameOnFastEnter) { @@ -225,7 +280,7 @@ export class MainMenuState extends GameState { this.trackClicks(qs(".settingsButton"), this.onSettingsButtonClicked); this.trackClicks(qs(".achievementsButton"), this.onAchievementsButtonClicked); - if (!G_CHINA_VERSION) { + if (!G_CHINA_VERSION && !G_WEGAME_VERSION) { this.trackClicks(qs(".languageChoose"), this.onLanguageChooseClicked); this.trackClicks(qs(".redditLink"), this.onRedditClicked); this.trackClicks(qs(".changelog"), this.onChangelogClicked); @@ -245,14 +300,16 @@ export class MainMenuState extends GameState { } const discordLink = this.htmlElement.querySelector(".discordLink"); - this.trackClicks( - discordLink, - () => { - this.app.analytics.trackUiClick("main_menu_link_discord"); - this.app.platformWrapper.openExternalLink(THIRDPARTY_URLS.discord); - }, - { preventClick: true } - ); + if (discordLink) { + this.trackClicks( + discordLink, + () => { + this.app.analytics.trackUiClick("main_menu_link_discord"); + this.app.platformWrapper.openExternalLink(THIRDPARTY_URLS.discord); + }, + { preventClick: true } + ); + } const githubLink = this.htmlElement.querySelector(".githubLink"); if (githubLink) { @@ -267,9 +324,25 @@ export class MainMenuState extends GameState { } const producerLink = this.htmlElement.querySelector(".producerLink"); - this.trackClicks(producerLink, () => this.app.platformWrapper.openExternalLink("https://tobspr.io"), { - preventClick: true, - }); + if (producerLink) { + this.trackClicks( + producerLink, + () => this.app.platformWrapper.openExternalLink("https://tobspr.io"), + { + preventClick: true, + } + ); + } + + const puzzleModeButton = qs(".puzzleDlcPlayButton"); + if (puzzleModeButton) { + this.trackClicks(puzzleModeButton, () => this.onPuzzleModeButtonClicked()); + } + + const puzzleWishlistButton = qs(".puzzleDlcGetButton"); + if (puzzleWishlistButton) { + this.trackClicks(puzzleWishlistButton, () => this.onPuzzleWishlistButtonClicked()); + } } renderMainMenu() { @@ -308,6 +381,35 @@ export class MainMenuState extends GameState { } } + onPuzzleModeButtonClicked(force = false) { + const hasUnlockedBlueprints = this.app.savegameMgr.getSavegamesMetaData().some(s => s.level >= 12); + console.log(hasUnlockedBlueprints); + if (!force && !hasUnlockedBlueprints) { + const { ok } = this.dialogs.showWarning( + T.dialogs.puzzlePlayRegularRecommendation.title, + T.dialogs.puzzlePlayRegularRecommendation.desc, + ["cancel:good", "ok:bad:timeout"] + ); + ok.add(() => this.onPuzzleModeButtonClicked(true)); + return; + } + + this.moveToState("LoginState", { + nextStateId: "PuzzleMenuState", + }); + } + + onPuzzleWishlistButtonClicked() { + this.app.platformWrapper.openExternalLink( + THIRDPARTY_URLS.puzzleDlcStorePage + "?ref=mmsl2&prc=" + A_B_TESTING_LINK_TYPE + ); + } + + onBackButtonClicked() { + this.renderMainMenu(); + this.renderSavegames(); + } + onSteamLinkClicked() { this.app.analytics.trackUiClick("main_menu_steam_link_" + A_B_TESTING_LINK_TYPE); this.app.platformWrapper.openExternalLink( diff --git a/src/js/states/mobile_warning.js b/src/js/states/mobile_warning.js index ce29b68b..0a54aa5a 100644 --- a/src/js/states/mobile_warning.js +++ b/src/js/states/mobile_warning.js @@ -1,6 +1,7 @@ import { GameState } from "../core/game_state"; import { cachebust } from "../core/cachebust"; import { THIRDPARTY_URLS } from "../core/config"; +import { getLogoSprite } from "../core/background_resources_loader"; export class MobileWarningState extends GameState { constructor() { @@ -10,9 +11,7 @@ export class MobileWarningState extends GameState { getInnerHTML() { return ` - +

I'm sorry, but shapez.io is not available on mobile devices yet! diff --git a/src/js/states/preload.js b/src/js/states/preload.js index 8f36738e..8bbeb88d 100644 --- a/src/js/states/preload.js +++ b/src/js/states/preload.js @@ -1,4 +1,5 @@ import { CHANGELOG } from "../changelog"; +import { getLogoSprite } from "../core/background_resources_loader"; import { cachebust } from "../core/cachebust"; import { globalConfig } from "../core/config"; import { GameState } from "../core/game_state"; @@ -19,7 +20,7 @@ export class PreloadState extends GameState { return `

- ${G_CHINA_VERSION ? "加载中" : "Booting"} + ${G_CHINA_VERSION || G_WEGAME_VERSION ? "加载中" : "Booting"}
@@ -57,8 +58,6 @@ export class PreloadState extends GameState { this.lastHintShown = -1000; this.nextHintDuration = 0; - this.currentStatus = "booting"; - this.startLoading(); } @@ -121,7 +120,7 @@ export class PreloadState extends GameState { .then(() => this.setStatus("Initializing language")) .then(() => { - if (G_CHINA_VERSION) { + if (G_CHINA_VERSION || G_WEGAME_VERSION) { return this.app.settings.updateLanguage("zh-CN"); } @@ -173,7 +172,7 @@ export class PreloadState extends GameState { return; } - if (G_CHINA_VERSION) { + if (G_CHINA_VERSION || G_WEGAME_VERSION) { return; } @@ -236,7 +235,7 @@ export class PreloadState extends GameState { } update() { - if (G_CHINA_VERSION) { + if (G_CHINA_VERSION || G_WEGAME_VERSION) { return; } const now = performance.now(); @@ -269,7 +268,7 @@ export class PreloadState extends GameState { */ setStatus(text) { logger.log("✅ " + text); - if (G_CHINA_VERSION) { + if (G_CHINA_VERSION || G_WEGAME_VERSION) { return Promise.resolve(); } this.currentStatus = text; @@ -287,9 +286,7 @@ export class PreloadState extends GameState { subElement.innerHTML = `
diff --git a/src/js/states/puzzle_menu.js b/src/js/states/puzzle_menu.js new file mode 100644 index 00000000..bded8e1e --- /dev/null +++ b/src/js/states/puzzle_menu.js @@ -0,0 +1,460 @@ +import { createLogger } from "../core/logging"; +import { DialogWithForm } from "../core/modal_dialog_elements"; +import { FormElementInput } from "../core/modal_dialog_forms"; +import { TextualGameState } from "../core/textual_game_state"; +import { formatBigNumberFull } from "../core/utils"; +import { enumGameModeIds } from "../game/game_mode"; +import { ShapeDefinition } from "../game/shape_definition"; +import { MUSIC } from "../platform/sound"; +import { Savegame } from "../savegame/savegame"; +import { T } from "../translations"; + +const navigation = { + categories: ["official", "top-rated", "trending", "trending-weekly", "new"], + difficulties: ["easy", "medium", "hard"], + account: ["mine", "completed"], +}; + +const logger = createLogger("puzzle-menu"); + +let lastCategory = "official"; + +export class PuzzleMenuState extends TextualGameState { + constructor() { + super("PuzzleMenuState"); + this.loading = false; + this.activeCategory = ""; + } + + getThemeMusic() { + return MUSIC.puzzle; + } + + getStateHeaderTitle() { + return T.puzzleMenu.title; + } + /** + * Overrides the GameState implementation to provide our own html + */ + internalGetFullHtml() { + let headerHtml = ` +
+

${this.getStateHeaderTitle()}

+ +
+ + +
+ +
`; + + return ` + ${headerHtml} +
+ ${this.getInnerHTML()} +
+ `; + } + + getMainContentHTML() { + let html = ` +
+ +
+ ${Object.keys(navigation) + .map( + rootCategory => + `` + ) + .join("")} +
+ +
+
+ +
+ + +
+ `; + + return html; + } + + selectCategory(category) { + lastCategory = category; + if (category === this.activeCategory) { + return; + } + + if (this.loading) { + return; + } + + this.loading = true; + this.activeCategory = category; + + const activeCategory = this.htmlElement.querySelector(".active[data-category]"); + if (activeCategory) { + activeCategory.classList.remove("active"); + } + + this.htmlElement.querySelector(`[data-category="${category}"]`).classList.add("active"); + + const container = this.htmlElement.querySelector("#mainContainer"); + while (container.firstChild) { + container.removeChild(container.firstChild); + } + + const loadingElement = document.createElement("div"); + loadingElement.classList.add("loader"); + loadingElement.innerText = T.global.loading + "..."; + container.appendChild(loadingElement); + + this.asyncChannel + .watch(this.getPuzzlesForCategory(category)) + .then( + puzzles => this.renderPuzzles(puzzles), + error => { + this.dialogs.showWarning( + T.dialogs.puzzleLoadFailed.title, + T.dialogs.puzzleLoadFailed.desc + " " + error + ); + this.renderPuzzles([]); + } + ) + .then(() => (this.loading = false)); + } + + /** + * Selects a root category + * @param {string} rootCategory + * @param {string=} category + */ + selectRootCategory(rootCategory, category) { + const subCategory = category || navigation[rootCategory][0]; + console.warn("Select root category", rootCategory, category, "->", subCategory); + + if (this.loading) { + return; + } + if (this.activeCategory === subCategory) { + return; + } + + const activeCategory = this.htmlElement.querySelector(".active[data-root-category]"); + if (activeCategory) { + activeCategory.classList.remove("active"); + } + + const newActiveCategory = this.htmlElement.querySelector(`[data-root-category="${rootCategory}"]`); + if (newActiveCategory) { + newActiveCategory.classList.add("active"); + } + + // Rerender buttons + + const subContainer = this.htmlElement.querySelector(".subCategories"); + while (subContainer.firstChild) { + subContainer.removeChild(subContainer.firstChild); + } + + const children = navigation[rootCategory]; + for (const category of children) { + const button = document.createElement("button"); + button.setAttribute("data-category", category); + button.classList.add("styledButton", "category", "child"); + button.innerText = T.puzzleMenu.categories[category]; + this.trackClicks(button, () => this.selectCategory(category)); + subContainer.appendChild(button); + } + + this.selectCategory(subCategory); + } + + /** + * + * @param {import("../savegame/savegame_typedefs").PuzzleMetadata[]} puzzles + */ + renderPuzzles(puzzles) { + const container = this.htmlElement.querySelector("#mainContainer"); + while (container.firstChild) { + container.removeChild(container.firstChild); + } + + for (const puzzle of puzzles) { + const elem = document.createElement("div"); + elem.classList.add("puzzle"); + elem.setAttribute("data-puzzle-id", String(puzzle.id)); + + if (this.activeCategory !== "mine") { + elem.classList.toggle("completed", puzzle.completed); + } + + if (puzzle.title) { + const title = document.createElement("div"); + title.classList.add("title"); + title.innerText = puzzle.title; + elem.appendChild(title); + } + + if (puzzle.author && !["official", "mine"].includes(this.activeCategory)) { + const author = document.createElement("div"); + author.classList.add("author"); + author.innerText = "by " + puzzle.author; + elem.appendChild(author); + } + + const stats = document.createElement("div"); + stats.classList.add("stats"); + elem.appendChild(stats); + + if ( + puzzle.downloads > 3 && + !["official", "easy", "medium", "hard"].includes(this.activeCategory) + ) { + const difficulty = document.createElement("div"); + difficulty.classList.add("difficulty"); + + const completionPercentage = Math.max( + 0, + Math.min(100, Math.round((puzzle.completions / puzzle.downloads) * 100.0)) + ); + difficulty.innerText = completionPercentage + "%"; + stats.appendChild(difficulty); + + if (completionPercentage < 40) { + difficulty.classList.add("stage--hard"); + difficulty.innerText = T.puzzleMenu.difficulties.hard; + } else if (completionPercentage < 80) { + difficulty.classList.add("stage--medium"); + difficulty.innerText = T.puzzleMenu.difficulties.medium; + } else { + difficulty.classList.add("stage--easy"); + difficulty.innerText = T.puzzleMenu.difficulties.easy; + } + } + + if (this.activeCategory === "mine") { + const downloads = document.createElement("div"); + downloads.classList.add("downloads"); + downloads.innerText = String(puzzle.downloads); + stats.appendChild(downloads); + stats.classList.add("withDownloads"); + } + + const likes = document.createElement("div"); + likes.classList.add("likes"); + likes.innerText = formatBigNumberFull(puzzle.likes); + stats.appendChild(likes); + + const definition = ShapeDefinition.fromShortKey(puzzle.shortKey); + const canvas = definition.generateAsCanvas(100 * this.app.getEffectiveUiScale()); + + const icon = document.createElement("div"); + icon.classList.add("icon"); + icon.appendChild(canvas); + elem.appendChild(icon); + + if (this.activeCategory === "mine") { + const deleteButton = document.createElement("button"); + deleteButton.classList.add("styledButton", "delete"); + this.trackClicks( + deleteButton, + () => { + this.tryDeletePuzzle(puzzle); + }, + { + consumeEvents: true, + preventClick: true, + preventDefault: true, + } + ); + elem.appendChild(deleteButton); + } + + container.appendChild(elem); + + this.trackClicks(elem, () => this.playPuzzle(puzzle)); + } + + if (puzzles.length === 0) { + const elem = document.createElement("div"); + elem.classList.add("empty"); + elem.innerText = T.puzzleMenu.noPuzzles; + container.appendChild(elem); + } + } + + /** + * @param {import("../savegame/savegame_typedefs").PuzzleMetadata} puzzle + */ + tryDeletePuzzle(puzzle) { + const signals = this.dialogs.showWarning( + T.dialogs.puzzleDelete.title, + T.dialogs.puzzleDelete.desc.replace("", puzzle.title), + ["delete:bad", "cancel:good"] + ); + signals.delete.add(() => { + const closeLoading = this.dialogs.showLoadingDialog(); + + this.asyncChannel + .watch(this.app.clientApi.apiDeletePuzzle(puzzle.id)) + .then(() => { + const element = this.htmlElement.querySelector("[data-puzzle-id='" + puzzle.id + "']"); + if (element) { + element.remove(); + } + }) + .catch(err => { + this.dialogs.showWarning(T.global.error, String(err)); + }) + .then(closeLoading); + }); + } + + /** + * + * @param {*} category + * @returns {Promise<import("../savegame/savegame_typedefs").PuzzleMetadata[]>} + */ + getPuzzlesForCategory(category) { + const result = this.app.clientApi.apiListPuzzles(category); + return result.catch(err => { + logger.error("Failed to get", category, ":", err); + throw err; + }); + } + + /** + * + * @param {import("../savegame/savegame_typedefs").PuzzleMetadata} puzzle + */ + playPuzzle(puzzle) { + const closeLoading = this.dialogs.showLoadingDialog(); + + this.app.clientApi.apiDownloadPuzzle(puzzle.id).then( + puzzleData => { + closeLoading(); + logger.log("Got puzzle:", puzzleData); + this.startLoadedPuzzle(puzzleData); + }, + err => { + closeLoading(); + logger.error("Failed to download puzzle", puzzle.id, ":", err); + this.dialogs.showWarning( + T.dialogs.puzzleDownloadError.title, + T.dialogs.puzzleDownloadError.desc + " " + err + ); + } + ); + } + + /** + * + * @param {import("../savegame/savegame_typedefs").PuzzleFullData} puzzle + */ + startLoadedPuzzle(puzzle) { + const savegame = this.createEmptySavegame(); + this.moveToState("InGameState", { + gameModeId: enumGameModeIds.puzzlePlay, + gameModeParameters: { + puzzle, + }, + savegame, + }); + } + + onEnter(payload) { + // Find old category + let rootCategory = "categories"; + for (const [id, children] of Object.entries(navigation)) { + if (children.includes(lastCategory)) { + rootCategory = id; + break; + } + } + + this.selectRootCategory(rootCategory, lastCategory); + + if (payload && payload.error) { + this.dialogs.showWarning(payload.error.title, payload.error.desc); + } + + for (const rootCategory of Object.keys(navigation)) { + const button = this.htmlElement.querySelector(`[data-root-category="${rootCategory}"]`); + this.trackClicks(button, () => this.selectRootCategory(rootCategory)); + } + + this.trackClicks(this.htmlElement.querySelector("button.createPuzzle"), () => this.createNewPuzzle()); + this.trackClicks(this.htmlElement.querySelector("button.loadPuzzle"), () => this.loadPuzzle()); + } + + createEmptySavegame() { + return new Savegame(this.app, { + internalId: "puzzle", + metaDataRef: { + internalId: "puzzle", + lastUpdate: 0, + version: 0, + level: 0, + name: "puzzle", + }, + }); + } + + loadPuzzle() { + const shortKeyInput = new FormElementInput({ + id: "shortKey", + label: null, + placeholder: "", + defaultValue: "", + validator: val => ShapeDefinition.isValidShortKey(val), + }); + + const dialog = new DialogWithForm({ + app: this.app, + title: T.dialogs.puzzleLoadShortKey.title, + desc: T.dialogs.puzzleLoadShortKey.desc, + formElements: [shortKeyInput], + buttons: ["ok:good:enter"], + }); + this.dialogs.internalShowDialog(dialog); + + dialog.buttonSignals.ok.add(() => { + const closeLoading = this.dialogs.showLoadingDialog(); + + this.app.clientApi.apiDownloadPuzzleByKey(shortKeyInput.getValue()).then( + puzzle => { + closeLoading(); + this.startLoadedPuzzle(puzzle); + }, + err => { + closeLoading(); + this.dialogs.showWarning( + T.dialogs.puzzleDownloadError.title, + T.dialogs.puzzleDownloadError.desc + " " + err + ); + } + ); + }); + } + + createNewPuzzle(force = false) { + if (!force && !this.app.clientApi.isLoggedIn()) { + const signals = this.dialogs.showWarning( + T.dialogs.puzzleCreateOffline.title, + T.dialogs.puzzleCreateOffline.desc, + ["cancel:good", "continue:bad"] + ); + signals.continue.add(() => this.createNewPuzzle(true)); + return; + } + + const savegame = this.createEmptySavegame(); + this.moveToState("InGameState", { + gameModeId: enumGameModeIds.puzzleEdit, + savegame, + }); + } +} diff --git a/src/js/states/settings.js b/src/js/states/settings.js index a817de47..85b31fbb 100644 --- a/src/js/states/settings.js +++ b/src/js/states/settings.js @@ -30,7 +30,7 @@ export class SettingsState extends TextualGameState { <div class="other"> ${ - G_CHINA_VERSION + G_CHINA_VERSION || G_WEGAME_VERSION ? "" : ` <button class="styledButton about">${T.about.title}</button> @@ -74,7 +74,7 @@ export class SettingsState extends TextualGameState { for (let i = 0; i < allApplicationSettings.length; ++i) { const setting = allApplicationSettings[i]; - if (G_CHINA_VERSION && setting.id === "language") { + if ((G_CHINA_VERSION || G_WEGAME_VERSION) && setting.id === "language") { continue; } @@ -105,7 +105,7 @@ export class SettingsState extends TextualGameState { onEnter(payload) { this.renderBuildText(); - if (!G_CHINA_VERSION) { + if (!G_CHINA_VERSION && !G_WEGAME_VERSION) { this.trackClicks(this.htmlElement.querySelector(".about"), this.onAboutClicked, { preventDefault: false, }); @@ -144,7 +144,7 @@ export class SettingsState extends TextualGameState { initSettings() { allApplicationSettings.forEach(setting => { - if (G_CHINA_VERSION && setting.id === "language") { + if ((G_CHINA_VERSION || G_WEGAME_VERSION) && setting.id === "language") { return; } diff --git a/translations/base-ar.yaml b/translations/base-ar.yaml index 2ac7d78f..91e1aa9e 100644 --- a/translations/base-ar.yaml +++ b/translations/base-ar.yaml @@ -50,6 +50,7 @@ global: escape: ESC shift: SHIFT space: SPACE + loggingIn: Logging in demoBanners: title: Demo Version intro: Get the standalone to unlock all features! @@ -69,6 +70,12 @@ mainMenu: savegameLevel: Level <x> savegameLevelUnknown: Unknown Level savegameUnnamed: Unnamed + puzzleMode: Puzzle Mode + back: Back + puzzleDlcText: Do you enjoy compacting and optimizing factories? Get the Puzzle + DLC now on Steam for even more fun! + puzzleDlcWishlist: Wishlist now! + puzzleDlcViewNow: View Dlc dialogs: buttons: ok: OK @@ -82,6 +89,9 @@ dialogs: viewUpdate: View Update showUpgrades: Show Upgrades showKeybindings: Show Keybindings + retry: Retry + continue: Continue + playOffline: Play Offline importSavegameError: title: Import Error text: "Failed to import your savegame:" @@ -179,6 +189,70 @@ dialogs: title: Tutorial Available desc: There is a tutorial video available for this level, but it is only available in English. Would you like to watch it? + editConstantProducer: + title: Set Item + puzzleLoadFailed: + title: Puzzles failed to load + desc: "Unfortunately the puzzles could not be loaded:" + submitPuzzle: + title: Submit Puzzle + descName: "Give your puzzle a name:" + descIcon: "Please enter a unique short key, which will be shown as the icon of + your puzzle (You can generate them <link>here</link>, or choose one + of the randomly suggested shapes below):" + placeholderName: Puzzle Title + puzzleResizeBadBuildings: + title: Resize not possible + desc: You can't make the zone any smaller, because then some buildings would be + outside the zone. + puzzleLoadError: + title: Bad Puzzle + desc: "The puzzle failed to load:" + offlineMode: + title: Offline Mode + desc: We couldn't reach the servers, so the game has to run in offline mode. + Please make sure you have an active internet connection. + puzzleDownloadError: + title: Download Error + desc: "Failed to download the puzzle:" + puzzleSubmitError: + title: Submission Error + desc: "Failed to submit your puzzle:" + puzzleSubmitOk: + title: Puzzle Published + desc: Congratulations! Your puzzle has been published and can now be played by + others. You can now find it in the "My puzzles" section. + puzzleCreateOffline: + title: Offline Mode + desc: Since you are offline, you will not be able to save and/or publish your + puzzle. Would you still like to continue? + puzzlePlayRegularRecommendation: + title: Recommendation + desc: I <strong>strongly</strong> recommend playing the normal game to level 12 + before attempting the puzzle DLC, otherwise you may encounter + mechanics not yet introduced. Do you still want to continue? + puzzleShare: + title: Short Key Copied + desc: The short key of the puzzle (<key>) has been copied to your clipboard! It + can be entered in the puzzle menu to access the puzzle. + puzzleReport: + title: Report Puzzle + options: + profane: Profane + unsolvable: Not solvable + trolling: Trolling + puzzleReportComplete: + title: Thank you for your feedback! + desc: The puzzle has been flagged. + puzzleReportError: + title: Failed to report + desc: "Your report could not get processed:" + puzzleLoadShortKey: + title: Enter short key + desc: Enter the short key of the puzzle to load it. + puzzleDelete: + title: Delete Puzzle? + desc: Are you sure you want to delete '<title>'? This can not be undone! ingame: keybindingsOverlay: moveMap: Move @@ -200,6 +274,7 @@ ingame: clearSelection: Clear selection pipette: Pipette switchLayers: Switch layers + clearBelts: Clear belts colors: red: Red green: Green @@ -347,6 +422,46 @@ ingame: achievements: title: Achievements desc: Hunt them all! + puzzleEditorSettings: + zoneTitle: Zone + zoneWidth: Width + zoneHeight: Height + trimZone: Trim + clearItems: Clear Items + share: Share + report: Report + clearBuildings: Clear Buildings + resetPuzzle: Reset Puzzle + puzzleEditorControls: + title: Puzzle Creator + instructions: + - 1. Place <strong>Constant Producers</strong> to provide shapes and + colors to the player + - 2. Build one or more shapes you want the player to build later and + deliver it to one or more <strong>Goal Acceptors</strong> + - 3. Once a Goal Acceptor receives a shape for a certain amount of + time, it <strong>saves it as a goal</strong> that the player must + produce later (Indicated by the <strong>green badge</strong>). + - 4. Click the <strong>lock button</strong> on a building to disable + it. + - 5. Once you click review, your puzzle will be validated and you + can publish it. + - 6. Upon release, <strong>all buildings will be removed</strong> + except for the Producers and Goal Acceptors - That's the part that + the player is supposed to figure out for themselves, after all :) + puzzleCompletion: + title: Puzzle Completed! + titleLike: "Click the heart if you liked the puzzle:" + titleRating: How difficult did you find the puzzle? + titleRatingDesc: Your rating will help me to make you better suggestions in the future + continueBtn: Keep Playing + menuBtn: Menu + puzzleMetadata: + author: Author + shortKey: Short Key + rating: Difficulty score + averageDuration: Avg. Duration + completionRate: Completion rate shopUpgrades: belt: name: Belts, Distributor & Tunnels @@ -553,6 +668,18 @@ buildings: name: Item Producer description: Available in sandbox mode only, outputs the given signal from the wires layer on the regular layer. + constant_producer: + default: + name: Constant Producer + description: Constantly outputs a specified shape or color. + goal_acceptor: + default: + name: Goal Acceptor + description: Deliver shapes to the goal acceptor to set them as a goal. + block: + default: + name: Block + description: Allows you to block a tile. storyRewards: reward_cutter_and_trash: title: Cutting Shapes @@ -954,6 +1081,10 @@ keybindings: rotateToDown: "Rotate: Point Down" rotateToRight: "Rotate: Point Right" rotateToLeft: "Rotate: Point Left" + constant_producer: Constant Producer + goal_acceptor: Goal Acceptor + block: Block + massSelectClear: Clear belts about: title: About this Game body: >- @@ -1039,3 +1170,73 @@ tips: - 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. +puzzleMenu: + play: Play + edit: Edit + title: Puzzle Mode + createPuzzle: Create Puzzle + loadPuzzle: Load + reviewPuzzle: Review & Publish + validatingPuzzle: Validating Puzzle + submittingPuzzle: Submitting Puzzle + noPuzzles: There are currently no puzzles in this section. + categories: + levels: Levels + new: New + top-rated: Top Rated + mine: My Puzzles + easy: Easy + hard: Hard + completed: Completed + medium: Medium + official: Official + trending: Trending today + trending-weekly: Trending weekly + categories: Categories + difficulties: By Difficulty + account: My Puzzles + search: Search + validation: + title: Invalid Puzzle + noProducers: Please place a Constant Producer! + noGoalAcceptors: Please place a Goal Acceptor! + goalAcceptorNoItem: One or more Goal Acceptors have not yet assigned an item. + Deliver a shape to them to set a goal. + goalAcceptorRateNotMet: One or more Goal Acceptors are not getting enough items. + Make sure that the indicators are green for all acceptors. + buildingOutOfBounds: One or more buildings are outside of the buildable area. + Either increase the area or remove them. + autoComplete: Your puzzle autocompletes itself! Please make sure your constant + producers are not directly delivering to your goal acceptors. + difficulties: + easy: Easy + medium: Medium + hard: Hard + dlcHint: Purchased the DLC already? Make sure it is activated by right clicking + shapez.io in your library, selecting Properties > DLCs. +backendErrors: + ratelimit: You are performing your actions too frequent. Please wait a bit. + invalid-api-key: Failed to communicate with the backend, please try to + update/restart the game (Invalid Api Key). + unauthorized: Failed to communicate with the backend, please try to + update/restart the game (Unauthorized). + bad-token: Failed to communicate with the backend, please try to update/restart + the game (Bad Token). + bad-id: Invalid puzzle identifier. + not-found: The given puzzle could not be found. + bad-category: The given category could not be found. + bad-short-key: The given short key is invalid. + profane-title: Your puzzle title contains profane words. + bad-title-too-many-spaces: Your puzzle title is too short. + bad-shape-key-in-emitter: A constant producer has an invalid item. + bad-shape-key-in-goal: A goal acceptor has an invalid item. + no-emitters: Your puzzle does not contain any constant producers. + no-goals: Your puzzle does not contain any goal acceptors. + short-key-already-taken: This short key is already taken, please use another one. + can-not-report-your-own-puzzle: You can not report your own puzzle. + bad-payload: The request contains invalid data. + bad-building-placement: Your puzzle contains invalid placed buildings. + timeout: The request timed out. + too-many-likes-already: The puzzle alreay got too many likes. If you still want + to remove it, please contact support@shapez.io! + no-permission: You do not have the permission to perform this action. diff --git a/translations/base-cat.yaml b/translations/base-cat.yaml index 385aac27..2d7b371c 100644 --- a/translations/base-cat.yaml +++ b/translations/base-cat.yaml @@ -53,6 +53,7 @@ global: escape: ESC shift: SHIFT space: ESPAI + loggingIn: Logging in demoBanners: title: Demo - Versió de prova intro: Aconsegueix el joc complet per obtenir totes les característiques! @@ -73,6 +74,12 @@ mainMenu: savegameLevel: Nivell <x> savegameLevelUnknown: Nivell desconegut savegameUnnamed: Unnamed + puzzleMode: Puzzle Mode + back: Back + puzzleDlcText: Do you enjoy compacting and optimizing factories? Get the Puzzle + DLC now on Steam for even more fun! + puzzleDlcWishlist: Wishlist now! + puzzleDlcViewNow: View Dlc dialogs: buttons: ok: OK @@ -86,6 +93,9 @@ dialogs: viewUpdate: Veure actualitzacions showUpgrades: Mostrar millores showKeybindings: Mostrar dreceres de teclat + retry: Retry + continue: Continue + playOffline: Play Offline importSavegameError: title: Error en importar text: "Ha ocurrit un error intentant importar la teva partida:" @@ -187,6 +197,70 @@ dialogs: title: Tutorial Available desc: There is a tutorial video available for this level, but it is only available in English. Would you like to watch it? + editConstantProducer: + title: Set Item + puzzleLoadFailed: + title: Puzzles failed to load + desc: "Unfortunately the puzzles could not be loaded:" + submitPuzzle: + title: Submit Puzzle + descName: "Give your puzzle a name:" + descIcon: "Please enter a unique short key, which will be shown as the icon of + your puzzle (You can generate them <link>here</link>, or choose one + of the randomly suggested shapes below):" + placeholderName: Puzzle Title + puzzleResizeBadBuildings: + title: Resize not possible + desc: You can't make the zone any smaller, because then some buildings would be + outside the zone. + puzzleLoadError: + title: Bad Puzzle + desc: "The puzzle failed to load:" + offlineMode: + title: Offline Mode + desc: We couldn't reach the servers, so the game has to run in offline mode. + Please make sure you have an active internet connection. + puzzleDownloadError: + title: Download Error + desc: "Failed to download the puzzle:" + puzzleSubmitError: + title: Submission Error + desc: "Failed to submit your puzzle:" + puzzleSubmitOk: + title: Puzzle Published + desc: Congratulations! Your puzzle has been published and can now be played by + others. You can now find it in the "My puzzles" section. + puzzleCreateOffline: + title: Offline Mode + desc: Since you are offline, you will not be able to save and/or publish your + puzzle. Would you still like to continue? + puzzlePlayRegularRecommendation: + title: Recommendation + desc: I <strong>strongly</strong> recommend playing the normal game to level 12 + before attempting the puzzle DLC, otherwise you may encounter + mechanics not yet introduced. Do you still want to continue? + puzzleShare: + title: Short Key Copied + desc: The short key of the puzzle (<key>) has been copied to your clipboard! It + can be entered in the puzzle menu to access the puzzle. + puzzleReport: + title: Report Puzzle + options: + profane: Profane + unsolvable: Not solvable + trolling: Trolling + puzzleReportComplete: + title: Thank you for your feedback! + desc: The puzzle has been flagged. + puzzleReportError: + title: Failed to report + desc: "Your report could not get processed:" + puzzleLoadShortKey: + title: Enter short key + desc: Enter the short key of the puzzle to load it. + puzzleDelete: + title: Delete Puzzle? + desc: Are you sure you want to delete '<title>'? This can not be undone! ingame: keybindingsOverlay: moveMap: Moure @@ -208,6 +282,7 @@ ingame: clearSelection: Buidar selecció pipette: Pipeta switchLayers: Intercanviar capes + clearBelts: Clear belts colors: red: Roig green: Verd @@ -357,6 +432,46 @@ ingame: achievements: title: Achievements desc: Hunt them all! + puzzleEditorSettings: + zoneTitle: Zone + zoneWidth: Width + zoneHeight: Height + trimZone: Trim + clearItems: Clear Items + share: Share + report: Report + clearBuildings: Clear Buildings + resetPuzzle: Reset Puzzle + puzzleEditorControls: + title: Puzzle Creator + instructions: + - 1. Place <strong>Constant Producers</strong> to provide shapes and + colors to the player + - 2. Build one or more shapes you want the player to build later and + deliver it to one or more <strong>Goal Acceptors</strong> + - 3. Once a Goal Acceptor receives a shape for a certain amount of + time, it <strong>saves it as a goal</strong> that the player must + produce later (Indicated by the <strong>green badge</strong>). + - 4. Click the <strong>lock button</strong> on a building to disable + it. + - 5. Once you click review, your puzzle will be validated and you + can publish it. + - 6. Upon release, <strong>all buildings will be removed</strong> + except for the Producers and Goal Acceptors - That's the part that + the player is supposed to figure out for themselves, after all :) + puzzleCompletion: + title: Puzzle Completed! + titleLike: "Click the heart if you liked the puzzle:" + titleRating: How difficult did you find the puzzle? + titleRatingDesc: Your rating will help me to make you better suggestions in the future + continueBtn: Keep Playing + menuBtn: Menu + puzzleMetadata: + author: Author + shortKey: Short Key + rating: Difficulty score + averageDuration: Avg. Duration + completionRate: Completion rate shopUpgrades: belt: name: Cintes transportadores, Distribuidors i Túnels @@ -569,6 +684,18 @@ buildings: name: Productor d'ítems description: Només avaliable en mode "sandbox", emet la senyal de la capa de cablejat a la capa normal. + constant_producer: + default: + name: Constant Producer + description: Constantly outputs a specified shape or color. + goal_acceptor: + default: + name: Goal Acceptor + description: Deliver shapes to the goal acceptor to set them as a goal. + block: + default: + name: Block + description: Allows you to block a tile. storyRewards: reward_cutter_and_trash: title: Tallar figures @@ -981,6 +1108,10 @@ keybindings: rotateToDown: "Rotate: Point Down" rotateToRight: "Rotate: Point Right" rotateToLeft: "Rotate: Point Left" + constant_producer: Constant Producer + goal_acceptor: Goal Acceptor + block: Block + massSelectClear: Clear belts about: title: Sobre aquest Joc body: >- @@ -1081,3 +1212,73 @@ tips: - Premeu F4 per mostrar la vostra tarifa FPS i Tick. - Premeu F4 dues vegades per mostrar el mosaic del ratolí i la càmera. - Podeu fer clic a una forma fixada al costat esquerre per desenganxar-la. +puzzleMenu: + play: Play + edit: Edit + title: Puzzle Mode + createPuzzle: Create Puzzle + loadPuzzle: Load + reviewPuzzle: Review & Publish + validatingPuzzle: Validating Puzzle + submittingPuzzle: Submitting Puzzle + noPuzzles: There are currently no puzzles in this section. + categories: + levels: Levels + new: New + top-rated: Top Rated + mine: My Puzzles + easy: Easy + hard: Hard + completed: Completed + medium: Medium + official: Official + trending: Trending today + trending-weekly: Trending weekly + categories: Categories + difficulties: By Difficulty + account: My Puzzles + search: Search + validation: + title: Invalid Puzzle + noProducers: Please place a Constant Producer! + noGoalAcceptors: Please place a Goal Acceptor! + goalAcceptorNoItem: One or more Goal Acceptors have not yet assigned an item. + Deliver a shape to them to set a goal. + goalAcceptorRateNotMet: One or more Goal Acceptors are not getting enough items. + Make sure that the indicators are green for all acceptors. + buildingOutOfBounds: One or more buildings are outside of the buildable area. + Either increase the area or remove them. + autoComplete: Your puzzle autocompletes itself! Please make sure your constant + producers are not directly delivering to your goal acceptors. + difficulties: + easy: Easy + medium: Medium + hard: Hard + dlcHint: Purchased the DLC already? Make sure it is activated by right clicking + shapez.io in your library, selecting Properties > DLCs. +backendErrors: + ratelimit: You are performing your actions too frequent. Please wait a bit. + invalid-api-key: Failed to communicate with the backend, please try to + update/restart the game (Invalid Api Key). + unauthorized: Failed to communicate with the backend, please try to + update/restart the game (Unauthorized). + bad-token: Failed to communicate with the backend, please try to update/restart + the game (Bad Token). + bad-id: Invalid puzzle identifier. + not-found: The given puzzle could not be found. + bad-category: The given category could not be found. + bad-short-key: The given short key is invalid. + profane-title: Your puzzle title contains profane words. + bad-title-too-many-spaces: Your puzzle title is too short. + bad-shape-key-in-emitter: A constant producer has an invalid item. + bad-shape-key-in-goal: A goal acceptor has an invalid item. + no-emitters: Your puzzle does not contain any constant producers. + no-goals: Your puzzle does not contain any goal acceptors. + short-key-already-taken: This short key is already taken, please use another one. + can-not-report-your-own-puzzle: You can not report your own puzzle. + bad-payload: The request contains invalid data. + bad-building-placement: Your puzzle contains invalid placed buildings. + timeout: The request timed out. + too-many-likes-already: The puzzle alreay got too many likes. If you still want + to remove it, please contact support@shapez.io! + no-permission: You do not have the permission to perform this action. diff --git a/translations/base-cz.yaml b/translations/base-cz.yaml index 367c74f8..e91463ba 100644 --- a/translations/base-cz.yaml +++ b/translations/base-cz.yaml @@ -10,14 +10,14 @@ steamPage: A jako by to nestačilo, musíte také produkovat exponenciálně více, abyste uspokojili požadavky - jediná věc, která pomáhá, je škálování! Zatímco na začátku tvary pouze zpracováváte, později je musíte obarvit - těžbou a mícháním barev! Koupením hry na platformě Steam získáte přístup k plné verzi hry, ale také můžete nejdříve hrát demo verzi na shapez.io a až potom se rozhodnout! - what_others_say: What people say about shapez.io - nothernlion_comment: This game is great - I'm having a wonderful time playing, - and time has flown by. - notch_comment: Oh crap. I really should sleep, but I think I just figured out - how to make a computer in shapez.io - steam_review_comment: This game has stolen my life and I don't want it back. - Very chill factory game that won't let me stop making my lines more - efficient. + what_others_say: Co o shapez.io říkají lidé + nothernlion_comment: Tato hra je úžasná - Užívám si čas strávený hraním této + hry, jen strašně rychle utekl. + notch_comment: Sakra. Opravdu bych měl jít spát, ale myslím si, že jsem zrovna + přišel na to, jak v shapez.io vytvořit počítač. + steam_review_comment: Tato hra mi ukradla život a já ho nechci zpět. Odpočinková + factory hra, která mi nedovolí přestat dělat mé výrobní linky více + efektivní. global: loading: Načítání error: Chyba @@ -49,6 +49,7 @@ global: escape: ESC shift: SHIFT space: SPACE + loggingIn: Přihlašuji demoBanners: title: Demo verze intro: Získejte plnou verzi pro odemknutí všech funkcí a obsahu! @@ -69,6 +70,12 @@ mainMenu: savegameLevel: Úroveň <x> savegameLevelUnknown: Neznámá úroveň savegameUnnamed: Nepojmenovaný + puzzleMode: Puzzle mód + back: Zpět + puzzleDlcText: Baví vás zmenšování a optimalizace továren? Pořiďte si nyní + Puzzle DLC na Steamu pro ještě více zábavy! + puzzleDlcWishlist: Přidejte si nyní na seznam přání! + puzzleDlcViewNow: View Dlc dialogs: buttons: ok: OK @@ -82,6 +89,9 @@ dialogs: viewUpdate: Zobrazit aktualizaci showUpgrades: Zobrazit vylepšení showKeybindings: Zobrazit klávesové zkratky + retry: Opakovat + continue: Pokračovat + playOffline: Hrát offline importSavegameError: title: Chyba Importu text: "Nepovedlo se importovat vaši uloženou hru:" @@ -178,6 +188,70 @@ dialogs: title: Dostupný tutoriál desc: Pro tuto úroveň je k dispozici tutoriál, ale je dostupný pouze v angličtině. Chtěli byste se na něj podívat? + editConstantProducer: + title: Nastavte tvar + puzzleLoadFailed: + title: Načítání puzzle selhalo + desc: "Bohužel nebylo možné puzzle načíst:" + submitPuzzle: + title: Odeslat puzzle + descName: "Pojmenujte svůj puzzle:" + descIcon: "Prosím zadejte unikátní krátký klíč, který bude zobrazen jako ikona + vašeho puzzle (Ten můžete vygenerovat <link>zde</link>, nebo vyberte + jeden z níže náhodně vybraných tvarů):" + placeholderName: Název puzzlu + puzzleResizeBadBuildings: + title: Změna velikosti není možná + desc: Zónu není možné více zmenšit, protože by některé budovy byly mimo zónu. + puzzleLoadError: + title: Špatný puzzle + desc: "Načítání puzzlu selhalo:" + offlineMode: + title: Offline mód + desc: Nebylo možné kontaktovat herní servery, proto musí hra běžet v offline + módu. Ujistěte se, že máte aktivní připojení k internetu. + puzzleDownloadError: + title: Chyba stahování + desc: "Stažení puzzlu selhalo:" + puzzleSubmitError: + title: Chyba odeslání + desc: "Odeslání puzzlu selhalo:" + puzzleSubmitOk: + title: Puzzle publikováno + desc: Gratuluji! Vaše puzzle bylo publikováno a je dostupné pro ostatní hráče. + Můžete ho najít v sekci "Moje puzzly". + puzzleCreateOffline: + title: Offline mód + desc: Jelikož jste offline, nebudete moci ukládat a/nebo publikovat vaše puzzle. + Chcete přesto pokračovat? + puzzlePlayRegularRecommendation: + title: Doporučení + desc: <strong>Důrazně</strong> doporučujeme průchod základní hrou nejméně do + úrovně 12 před vstupem do puzzle DLC, jinak můžete narazit na + mechaniku hry, se kterou jste se ještě nesetkali. Chcete přesto + pokračovat? + puzzleShare: + title: Krátký klíč zkopírován + desc: Krátký klíč tohoto puzzlu (<key>) byl zkopírován do vaší schránky! Může + být vložen v puzzle menu pro přístup k danému puzzlu. + puzzleReport: + title: Nahlásit puzzle + options: + profane: Rouhavý + unsolvable: Nelze vyřešit + trolling: Trolling + puzzleReportComplete: + title: Děkujeme za vaši zpětnou vazbu! + desc: Toto puzzle bylo označeno. + puzzleReportError: + title: Nahlášení selhalo + desc: "Vaše nahlášení nemohlo být zpracováno:" + puzzleLoadShortKey: + title: Vložte krátký klíč + desc: Vložte krátký klíč pro načtení příslušného puzzlu. + puzzleDelete: + title: Smazat puzzle? + desc: Jste si jisti, že chcete smazat '<title>'? Tato akce je nevratná! ingame: keybindingsOverlay: moveMap: Posun mapy @@ -199,6 +273,7 @@ ingame: clearSelection: Zrušit výběr pipette: Kapátko switchLayers: Změnit vrstvy + clearBelts: Clear belts buildingPlacement: cycleBuildingVariants: Zmáčkněte <key> pro přepínání mezi variantami. hotkeyLabel: "Klávesová zkratka: <key>" @@ -347,7 +422,47 @@ ingame: desc: Vyvíjím to ve svém volném čase! achievements: title: Achievements - desc: Hunt them all! + desc: Získejte je všechny! + puzzleEditorSettings: + zoneTitle: Zóna + zoneWidth: Šířka + zoneHeight: Výška + trimZone: Upravit zónu + clearItems: Vymazat tvary + share: Sdílet + report: Nahlásit + clearBuildings: Clear Buildings + resetPuzzle: Reset Puzzle + puzzleEditorControls: + title: Puzzle editor + instructions: + - 1. Umístěte <strong>výrobníky</strong>, které poskytnou hráči + tvary a barvy. + - 2. Sestavte jeden či více tvarů, které chcete, aby hráč vytvořil a + doručte to do jednoho či více <strong>příjemců cílů</strong>. + - 3. Jakmile příjemce cílů dostane určitý tvar za určitý časový + úsek, <strong>uloží se jako cíl</strong>, který hráč musí později + vyprodukovat (Označeno <strong>zeleným odznakem</strong>). + - 4. Kliknutím na <strong>tlačítko zámku</strong> na určité budově + dojde k její deaktivaci. + - 5. Jakmile kliknete na ověření, vaše puzzle bude ověřeno a můžete + ho publikovat. + - 6. Během publikace budou kromě výrobníků a příjemců cílů + <strong>všechny budovy odstraněny</strong> - To je ta část, na + kterou má koneckonců každý hráč přijít sám. :) + puzzleCompletion: + title: Puzzle dokončeno! + titleLike: "Klikněte na srdíčko, pokud se vám puzzle líbilo:" + titleRating: Jak obtížný ti tento puzzle přišel? + titleRatingDesc: Vaše hodnocení nám pomůže podat vám v budoucnu lepší návrhy + continueBtn: Hrát dál + menuBtn: Menu + puzzleMetadata: + author: Autor + shortKey: Krátký klíč + rating: Úrověn obtížnosti + averageDuration: Prům. doba trvání + completionRate: Míra dokončení shopUpgrades: belt: name: Pásy, distribuce a tunely @@ -403,7 +518,7 @@ buildings: name: Rotor description: Otáčí tvary o 90 stupňů po směru hodinových ručiček. ccw: - name: Rotor (opačný) + name: Rotor (Opačný) description: Otáčí tvary o 90 stupňů proti směru hodinových ručiček. rotate180: name: Rotor (180°) @@ -546,6 +661,18 @@ buildings: 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. + constant_producer: + default: + name: Výrobník + description: Neustále vydává zadaný tvar či barvu. + goal_acceptor: + default: + name: Příjemce cílů + description: Doručte tvary příjemci cílů, abyste je nastavili jako cíl. + block: + default: + name: Blok + description: Umožňuje zablokovat políčko. storyRewards: reward_cutter_and_trash: title: Řezání tvarů @@ -941,10 +1068,14 @@ keybindings: comparator: Porovnávač item_producer: Výrobník předmětů (Sandbox) copyWireValue: "Kabely: Zkopírovat hodnotu pod kurzorem" - rotateToUp: "Rotate: Point Up" - rotateToDown: "Rotate: Point Down" - rotateToRight: "Rotate: Point Right" - rotateToLeft: "Rotate: Point Left" + rotateToUp: "Otočit: Nahoru" + rotateToDown: "Otočit: Dolů" + rotateToRight: "Otočit: Doprava" + rotateToLeft: "Otočit: Doleva" + constant_producer: Výrobník + goal_acceptor: Přijemce cílů + block: Blok + massSelectClear: Vymazat pásy about: title: O hře body: >- @@ -995,7 +1126,7 @@ tips: - 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! + pozdější expanzi! - Držení klávesy <b>SHIFT</b> umožňuje postavit více budov stejného typu. - Můžete podržet klávesu <b>ALT</b> k obrácení směru pokládaných pásů. - Efektivita je klíčová! @@ -1039,3 +1170,74 @@ tips: - Stisknutím F4 zobrazíte FPS a rychlost ticků. - Stisknutím F4 dvakrát zobrazíte souřadnici myši a kamery. - Můžete kliknout na připínáček vlevo vedle připnutého tvaru k jeho odepnutí. +puzzleMenu: + play: Hrát + edit: Upravit + title: Puzzle mód + createPuzzle: Vytvořit puzzle + loadPuzzle: Načíst + reviewPuzzle: Ověření a publikace + validatingPuzzle: Ověřování puzzlu + submittingPuzzle: Odesílání puzzlu + noPuzzles: V této sekci momentálně nejsou žádné puzzly. + categories: + levels: Úrovně + new: Nové + top-rated: Nejlépe hodnocené + mine: Moje puzzly + easy: Lehké + hard: Těžké + completed: Dokončeno + medium: Střední + official: Oficiální + trending: Populární denně + trending-weekly: Populární týdně + categories: Kategorie + difficulties: Dle obtížnosti + account: Moje puzzly + search: Vyhledávání + validation: + title: Neplatný puzzle + noProducers: Prosím umístěte výrobník! + noGoalAcceptors: Prosím umístěte příjemce cílů! + goalAcceptorNoItem: Jeden nebo více příjemců cílů ještě nemají nastavený tvar. + Doručte jim tvar, abyste jej nastavili jako cíl. + goalAcceptorRateNotMet: Jeden nebo více příjemců cílů nedostávají dostatečný + počet tvarů. Ujistěte se, že indikátory jsou zelené pro všechny + příjemce. + buildingOutOfBounds: Jedna nebo více budov je mimo zastavitelnou oblast. Buď + zvětšete plochu, nebo je odstraňte. + autoComplete: Váš puzzle automaticky dokončuje sám sebe! Ujistěte se, že + vyrobníky nedodávají své tvary přímo do přijemců cílů. + difficulties: + easy: Lehká + medium: Střední + hard: Těžká + dlcHint: Purchased the DLC already? Make sure it is activated by right clicking + shapez.io in your library, selecting Properties > DLCs. +backendErrors: + ratelimit: Provádíte své akce příliš často. Počkejte prosím. + invalid-api-key: Komunikace s back-endem se nezdařila, prosím zkuste + aktualizovat/restartovat hru (Neplatný API klíč). + unauthorized: Komunikace s back-endem se nezdařila, prosím zkuste + aktualizovat/restartovat hru (Bez autorizace). + bad-token: Komunikace s back-endem se nezdařila, prosím zkuste + aktualizovat/restartovat hru (Špatný token). + bad-id: Neplatný identifikátor puzzlu. + not-found: Daný puzzle nebyl nalezen. + bad-category: Daná kategorie nebyla nalezena. + bad-short-key: Krátký klíč je neplatný. + profane-title: Název vašeho puzzlu obsahuje rouhavá slova. + bad-title-too-many-spaces: Název vašeho puzzlu je příliš krátký. + bad-shape-key-in-emitter: Výrobník má nastaven neplatný tvar. + bad-shape-key-in-goal: Příjemce cílů má nastaven neplatný tvar. + no-emitters: Váš puzzle neobsahuje žádné výrobníky. + no-goals: Váš puzzle neobsahuje žádné příjemce cílů. + short-key-already-taken: Tento krátký klíč je již používán, zadejte prosím jiný. + can-not-report-your-own-puzzle: Nemůžete nahlásit vlastní puzzle. + bad-payload: Žádost obsahuje neplatná data. + bad-building-placement: Váš puzzle obsahuje neplatně umístěné budovy. + timeout: Žádost vypršela. + too-many-likes-already: Tento puzzle již získal příliš mnoho lajků. Pokud jej + přesto chcete odstranit, kontaktujte nás prosím na support@shapez.io! + no-permission: K provedení této akce nemáte oprávnění. diff --git a/translations/base-da.yaml b/translations/base-da.yaml index a276b19a..1e6b329f 100644 --- a/translations/base-da.yaml +++ b/translations/base-da.yaml @@ -53,6 +53,7 @@ global: escape: ESC shift: SKIFT/SHIFT space: MELLEMRUM + loggingIn: Logging in demoBanners: title: Demo Version intro: Køb spillet for at få den fulde oplevelse! @@ -72,6 +73,12 @@ mainMenu: savegameLevel: Niveau <x> savegameLevelUnknown: Ukendt Niveau savegameUnnamed: Unnamed + puzzleMode: Puzzle Mode + back: Back + puzzleDlcText: Do you enjoy compacting and optimizing factories? Get the Puzzle + DLC now on Steam for even more fun! + puzzleDlcWishlist: Wishlist now! + puzzleDlcViewNow: View Dlc dialogs: buttons: ok: OK @@ -85,6 +92,9 @@ dialogs: viewUpdate: Se Opdatering showUpgrades: Vis Opgraderinger showKeybindings: Vis Keybindings + retry: Retry + continue: Continue + playOffline: Play Offline importSavegameError: title: Import Fejl text: "Importering af gemt spil fejlede:" @@ -184,6 +194,70 @@ dialogs: title: Tutorial Available desc: There is a tutorial video available for this level, but it is only available in English. Would you like to watch it? + editConstantProducer: + title: Set Item + puzzleLoadFailed: + title: Puzzles failed to load + desc: "Unfortunately the puzzles could not be loaded:" + submitPuzzle: + title: Submit Puzzle + descName: "Give your puzzle a name:" + descIcon: "Please enter a unique short key, which will be shown as the icon of + your puzzle (You can generate them <link>here</link>, or choose one + of the randomly suggested shapes below):" + placeholderName: Puzzle Title + puzzleResizeBadBuildings: + title: Resize not possible + desc: You can't make the zone any smaller, because then some buildings would be + outside the zone. + puzzleLoadError: + title: Bad Puzzle + desc: "The puzzle failed to load:" + offlineMode: + title: Offline Mode + desc: We couldn't reach the servers, so the game has to run in offline mode. + Please make sure you have an active internet connection. + puzzleDownloadError: + title: Download Error + desc: "Failed to download the puzzle:" + puzzleSubmitError: + title: Submission Error + desc: "Failed to submit your puzzle:" + puzzleSubmitOk: + title: Puzzle Published + desc: Congratulations! Your puzzle has been published and can now be played by + others. You can now find it in the "My puzzles" section. + puzzleCreateOffline: + title: Offline Mode + desc: Since you are offline, you will not be able to save and/or publish your + puzzle. Would you still like to continue? + puzzlePlayRegularRecommendation: + title: Recommendation + desc: I <strong>strongly</strong> recommend playing the normal game to level 12 + before attempting the puzzle DLC, otherwise you may encounter + mechanics not yet introduced. Do you still want to continue? + puzzleShare: + title: Short Key Copied + desc: The short key of the puzzle (<key>) has been copied to your clipboard! It + can be entered in the puzzle menu to access the puzzle. + puzzleReport: + title: Report Puzzle + options: + profane: Profane + unsolvable: Not solvable + trolling: Trolling + puzzleReportComplete: + title: Thank you for your feedback! + desc: The puzzle has been flagged. + puzzleReportError: + title: Failed to report + desc: "Your report could not get processed:" + puzzleLoadShortKey: + title: Enter short key + desc: Enter the short key of the puzzle to load it. + puzzleDelete: + title: Delete Puzzle? + desc: Are you sure you want to delete '<title>'? This can not be undone! ingame: keybindingsOverlay: moveMap: Bevæg dig @@ -205,6 +279,7 @@ ingame: clearSelection: Ryd Selektion pipette: Pipette switchLayers: Skift Lag + clearBelts: Clear belts colors: red: Rød green: Grøn @@ -352,6 +427,46 @@ ingame: achievements: title: Achievements desc: Hunt them all! + puzzleEditorSettings: + zoneTitle: Zone + zoneWidth: Width + zoneHeight: Height + trimZone: Trim + clearItems: Clear Items + share: Share + report: Report + clearBuildings: Clear Buildings + resetPuzzle: Reset Puzzle + puzzleEditorControls: + title: Puzzle Creator + instructions: + - 1. Place <strong>Constant Producers</strong> to provide shapes and + colors to the player + - 2. Build one or more shapes you want the player to build later and + deliver it to one or more <strong>Goal Acceptors</strong> + - 3. Once a Goal Acceptor receives a shape for a certain amount of + time, it <strong>saves it as a goal</strong> that the player must + produce later (Indicated by the <strong>green badge</strong>). + - 4. Click the <strong>lock button</strong> on a building to disable + it. + - 5. Once you click review, your puzzle will be validated and you + can publish it. + - 6. Upon release, <strong>all buildings will be removed</strong> + except for the Producers and Goal Acceptors - That's the part that + the player is supposed to figure out for themselves, after all :) + puzzleCompletion: + title: Puzzle Completed! + titleLike: "Click the heart if you liked the puzzle:" + titleRating: How difficult did you find the puzzle? + titleRatingDesc: Your rating will help me to make you better suggestions in the future + continueBtn: Keep Playing + menuBtn: Menu + puzzleMetadata: + author: Author + shortKey: Short Key + rating: Difficulty score + averageDuration: Avg. Duration + completionRate: Completion rate shopUpgrades: belt: name: Bælter, Fordelere & Tuneller @@ -557,6 +672,18 @@ buildings: name: Item Producer description: Available in sandbox mode only, outputs the given signal from the wires layer on the regular layer. + constant_producer: + default: + name: Constant Producer + description: Constantly outputs a specified shape or color. + goal_acceptor: + default: + name: Goal Acceptor + description: Deliver shapes to the goal acceptor to set them as a goal. + block: + default: + name: Block + description: Allows you to block a tile. storyRewards: reward_cutter_and_trash: title: Klippe Figurer @@ -963,6 +1090,10 @@ keybindings: rotateToDown: "Rotate: Point Down" rotateToRight: "Rotate: Point Right" rotateToLeft: "Rotate: Point Left" + constant_producer: Constant Producer + goal_acceptor: Goal Acceptor + block: Block + massSelectClear: Clear belts about: title: Om dette spil body: >- @@ -1048,3 +1179,73 @@ tips: - 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. +puzzleMenu: + play: Play + edit: Edit + title: Puzzle Mode + createPuzzle: Create Puzzle + loadPuzzle: Load + reviewPuzzle: Review & Publish + validatingPuzzle: Validating Puzzle + submittingPuzzle: Submitting Puzzle + noPuzzles: There are currently no puzzles in this section. + categories: + levels: Levels + new: New + top-rated: Top Rated + mine: My Puzzles + easy: Easy + hard: Hard + completed: Completed + medium: Medium + official: Official + trending: Trending today + trending-weekly: Trending weekly + categories: Categories + difficulties: By Difficulty + account: My Puzzles + search: Search + validation: + title: Invalid Puzzle + noProducers: Please place a Constant Producer! + noGoalAcceptors: Please place a Goal Acceptor! + goalAcceptorNoItem: One or more Goal Acceptors have not yet assigned an item. + Deliver a shape to them to set a goal. + goalAcceptorRateNotMet: One or more Goal Acceptors are not getting enough items. + Make sure that the indicators are green for all acceptors. + buildingOutOfBounds: One or more buildings are outside of the buildable area. + Either increase the area or remove them. + autoComplete: Your puzzle autocompletes itself! Please make sure your constant + producers are not directly delivering to your goal acceptors. + difficulties: + easy: Easy + medium: Medium + hard: Hard + dlcHint: Purchased the DLC already? Make sure it is activated by right clicking + shapez.io in your library, selecting Properties > DLCs. +backendErrors: + ratelimit: You are performing your actions too frequent. Please wait a bit. + invalid-api-key: Failed to communicate with the backend, please try to + update/restart the game (Invalid Api Key). + unauthorized: Failed to communicate with the backend, please try to + update/restart the game (Unauthorized). + bad-token: Failed to communicate with the backend, please try to update/restart + the game (Bad Token). + bad-id: Invalid puzzle identifier. + not-found: The given puzzle could not be found. + bad-category: The given category could not be found. + bad-short-key: The given short key is invalid. + profane-title: Your puzzle title contains profane words. + bad-title-too-many-spaces: Your puzzle title is too short. + bad-shape-key-in-emitter: A constant producer has an invalid item. + bad-shape-key-in-goal: A goal acceptor has an invalid item. + no-emitters: Your puzzle does not contain any constant producers. + no-goals: Your puzzle does not contain any goal acceptors. + short-key-already-taken: This short key is already taken, please use another one. + can-not-report-your-own-puzzle: You can not report your own puzzle. + bad-payload: The request contains invalid data. + bad-building-placement: Your puzzle contains invalid placed buildings. + timeout: The request timed out. + too-many-likes-already: The puzzle alreay got too many likes. If you still want + to remove it, please contact support@shapez.io! + no-permission: You do not have the permission to perform this action. diff --git a/translations/base-de.yaml b/translations/base-de.yaml index f3478dae..d4662108 100644 --- a/translations/base-de.yaml +++ b/translations/base-de.yaml @@ -49,6 +49,7 @@ global: escape: ESC shift: UMSCH space: LEER + loggingIn: Logging in demoBanners: title: Demo-Version intro: Kauf die Vollversion für alle Features! @@ -69,6 +70,12 @@ mainMenu: savegameLevel: Level <x> savegameLevelUnknown: Unbekanntes Level savegameUnnamed: Unbenannt + puzzleMode: Puzzlemodus + back: Zurück + puzzleDlcText: Du hast Spaß daran, deine Fabriken zu optimieren und effizienter + zu machen? Hol dir das Puzzle DLC auf Steam für noch mehr Spaß! + puzzleDlcWishlist: Jetzt zur Wunschliste hinzufügen! + puzzleDlcViewNow: View Dlc dialogs: buttons: ok: OK @@ -82,6 +89,9 @@ dialogs: viewUpdate: Update anzeigen showUpgrades: Upgrades anzeigen showKeybindings: Kürzel anzeigen + retry: Erneut versuchen + continue: Fortsetzen + playOffline: Offline spielen importSavegameError: title: Importfehler text: "Fehler beim Importieren deines Speicherstands:" @@ -181,6 +191,75 @@ dialogs: title: Tutorial verfügbar desc: Für dieses Level ist ein Tutorial-Video verfügbar, allerdings nur auf Englisch. Willst du es trotzdem anschauen? + editConstantProducer: + title: Item setzen + puzzleLoadFailed: + title: Puzzle konnten nicht geladen werden + desc: "Leider konnten die Puzzle nicht geladen werden:" + submitPuzzle: + title: Puzzle veröffentlichen + descName: "Gib deinem Puzzle einen Namen:" + descIcon: "Bitte gib einen eindeutigen Kurzschlüssel ein, der als Symbol für + dein Puzzle genutzt wird (Du kannst diesen auch <link>hier</link> + generieren, oder wähle unten einen der zufällig generierten + Vorschläge):" + placeholderName: Puzzle Name + puzzleResizeBadBuildings: + title: Größenänderung nicht möglich + desc: Du kannst die Zone nicht weiter verkleinern, da ansonsten einige Gebäude + außerhalb der Zone liegen würden. + puzzleLoadError: + title: Schlechtes Puzzle + desc: "Das Puzzle konnte nicht geladen werden:" + offlineMode: + title: Offline Modus + desc: Die Server konnten nicht erreicht werden, daher läuft das Spiel im Offline + Modus. Bitte sorge dafür, dass du eine aktive Internetverbindung + hast. + puzzleDownloadError: + title: Download Fehler + desc: "Der Download des Puzzles ist fehlgeschlagen:" + puzzleSubmitError: + title: Übertragungsfehler + desc: "Das Puzzle konnte nicht übertragen werden:" + puzzleSubmitOk: + title: Puzzle veröffentlicht + desc: Herzlichen Glückwunsch! Dein Rätsel wurde veröffentlicht und kann nun von + anderen gespielt werden. Du kannst es jetzt im Bereich "Meine + Puzzle" finden. + puzzleCreateOffline: + title: Offline Modus + desc: Da du offline bist, bist du nicht in der Lage dei Puzzle zu speichern + und/oder zu veröffentlichen. Möchtest du trotzdem fortfahren? + puzzlePlayRegularRecommendation: + title: Empfehlung + desc: ch empfehle <strong>stark</strong>, das normale Spiel bis Level 12 zu + spielen, bevor du dich an das Puzzle DLC wagst, sonst stößt du + möglicherweise auf noch nicht eingeführte Mechaniken. Möchtest du + trotzdem fortfahren? + puzzleShare: + title: Kurzschlüssel kopiert + desc: Der Kurzschlüssel des Puzzles (<key>) wurde in die Zwischenablage kopiert! + Dieser kann im Puzzle Menü genutzt werden, um das Puzzle zu laden. + puzzleReport: + title: Puzzle Melden + options: + profane: Profan + unsolvable: Nicht lösbar + trolling: Trolling + puzzleReportComplete: + title: Danke für das Feedback! + desc: Das Puzzle wurde markiert. + puzzleReportError: + title: Melden fehlgeschlagen + desc: "Deine Meldung konnte nicht verarbeitet werden:" + puzzleLoadShortKey: + title: Kurzschlüssel eingeben + desc: Trage einen Kurzschlüssel ein um das Puzzle zu laden. + puzzleDelete: + title: Puzzle löschen? + desc: Bist du sicher, dass du '<title>' löschen möchtest? Dies kann nicht + rückgängig gemacht werden! ingame: keybindingsOverlay: moveMap: Bewegen @@ -202,6 +281,7 @@ ingame: clearSelection: Auswahl aufheben pipette: Pipette switchLayers: Ebenen wechseln + clearBelts: Clear belts colors: red: Rot green: Grün @@ -350,6 +430,50 @@ ingame: achievements: title: Errungenschaften desc: Hol sie dir alle! + puzzleEditorSettings: + zoneTitle: Zone + zoneWidth: Breite + zoneHeight: Höhe + trimZone: Zuschneiden + clearItems: Items löschen + share: Teilen + report: Melden + clearBuildings: Clear Buildings + resetPuzzle: Reset Puzzle + puzzleEditorControls: + title: Puzzle Editor + instructions: + - 1. Plaziere einen <strong>Item-Produzent</strong> um Shapes und + Farben für den Spieler bereitzustellen + - 2. Produziere ein oder mehrere Shapes, die der Spieler herstellen + soll und liefere dieze zu einem oder mehreren + <strong>Ziel-Akzeptoren</strong> + - 3. Sobald ein Ziel-Akzeptor ein Shape für eine gewisse Zeit + erhällt, <strong>speichert dieser es als Ziel</strong>, welches + der Spieler später herstellen muss (Angezeigt durch den + <strong>grünen Punkt</strong>). + - 4. Klicke den <strong>sperren Button</strong> um die Gebäude zu + sperren. + - 5. Sobald du auf Überprüfen gedrückt hast, wird dei Puzzel geprüft + und du kannst es veröffentlichen. + - 6. Bei der Freigabe werden <strong>alle Gebäude entfernt</strong>. + Ausgenommen sind die Produzenten und Akzeptoren - Das ist + schließlich der Teil, den die Spieler selbst herausfinden sollen + :) + puzzleCompletion: + title: Puzzle abgeschlossen! + titleLike: "Klicke auf das Herz, wenn dier das Puzzle gefallen hat:" + titleRating: Wie schwierig fandest du das Puzzle? + titleRatingDesc: Deine Bewertung wird mir helfen, in Zukunft bessere Vorschläge + zu machen + continueBtn: Weiter spielen + menuBtn: Menü + puzzleMetadata: + author: Ersteller + shortKey: Kurzschlüssel + rating: Schwierigkeitsgrad + averageDuration: Durchschnittliche Dauer + completionRate: Abschlussrate shopUpgrades: belt: name: Fließbänder, Verteiler & Tunnel @@ -566,6 +690,18 @@ buildings: name: Item-Produzent description: Nur im Sandkastenmodus verfügbar. Gibt das Signal aus der Wires-Ebene als Item aus. + constant_producer: + default: + name: Item-Produzent + description: Gibt dauerhaft ein Shape oder eine Farbe aus. + goal_acceptor: + default: + name: Ziel Akzeptor + description: Liefere ein Shape an, um dieses als Ziel festzulegen. + block: + default: + name: Sperre + description: Ermöglicht das Blockieren einer Kachel. storyRewards: reward_cutter_and_trash: title: Formen zerschneiden @@ -913,7 +1049,8 @@ settings: position, otherwise in the middle of the screen. mapResourcesScale: title: Größe der Ressourcen auf der Karte - description: Legt die Größe der Ressourcen auf der Karte (beim Herauszoomen) fest. + description: Legt die Größe der Ressourcen auf der Karte (beim Herauszoomen) + fest. keybindings: title: Tastenbelegung hint: "Tipp: Benutze STRG, UMSCH and ALT! Sie aktivieren verschiedene @@ -991,6 +1128,10 @@ keybindings: rotateToDown: "Rotieren: Nach unten zeigend" rotateToRight: "Rotieren: Nach rechts zeigend" rotateToLeft: "Rotieren: Nach links zeigend" + constant_producer: Constant Producer + goal_acceptor: Goal Acceptor + block: Block + massSelectClear: Clear belts about: title: Über dieses Spiel body: Dieses Spiel ist quelloffen (Open Source) und wurde von <a @@ -1094,3 +1235,76 @@ tips: - Drücke doppelt F4 um den Standort des Mauszeigers und der Kamera zu bestimmen. - Du kannst die angehefteten Formen am linken Rand wieder entfernen. +puzzleMenu: + play: Spielen + edit: bearbeiten + title: Puzzle Modus + createPuzzle: Puzzle erstellen + loadPuzzle: Laden + reviewPuzzle: Überprüfen & Veröffentlichen + validatingPuzzle: Puzzle wird überprüft + submittingPuzzle: Puzzle wird veröffentlicht + noPuzzles: Hier gibt es bisher noch keine Puzzles. + categories: + levels: Levels + new: Neu + top-rated: Am besten bewertet + mine: Meine Puzzles + easy: Einfach + hard: Schwierig + completed: Abgeschlossen + medium: Mittel + official: Vorgestellt + trending: Trend - Heute + trending-weekly: Trend - Woche + categories: Kategorien + difficulties: Nach Schwierigkeit + account: Meine Puzzle + search: Suche + validation: + title: Ungültiges Puzzle + noProducers: Bitte plaziere einen Item-Produzent! + noGoalAcceptors: Bitte plaziere einen Ziel-Akzeptor! + goalAcceptorNoItem: Einer oder mehrere Ziel-Akzeptoren haben noch kein + zugewiesenes Item. Liefere eine Form zu diesen, um ein Ziel zu + setzen. + goalAcceptorRateNotMet: Einer oder mehrere Ziel-Aktzeptoren bekommen nicht + genügend Items. Stelle sicher, dass alle Akzeptatorindikatoren grün + sind. + buildingOutOfBounds: Ein oder mehrere Gebäude befinden sich außerhalb des + beabauren Bereichs. Vergrößere den Bereich oder entferene die + Gebäude. + autoComplete: Dein Puzzle schließt sich selbst ab! Bitte stelle sicher, dass + deine Item-Produzent nicht direkt an deine Ziel-Akzeptoren lieferen. + difficulties: + easy: Einfach + medium: Mittel + hard: Schwer + dlcHint: Purchased the DLC already? Make sure it is activated by right clicking + shapez.io in your library, selecting Properties > DLCs. +backendErrors: + ratelimit: Du führst Aktionen zu schnell aus. Bitte warte kurz. + invalid-api-key: Kommunikation mit dem Back-End fehlgeschlagen, veruche das + Spiel neuzustarten oder zu updaten (Ungültiger Api-Schlüssel). + unauthorized: Kommunikation mit dem Back-End fehlgeschlagen, veruche das Spiel + neustarten oder zu updaten (Nicht autorisiert). + bad-token: Kommunikation mit dem Back-End fehlgeschlagen, veruche das Spiel + neustarten oder zu updaten (Ungültiger Token). + bad-id: Ungültige Puzzle Identifikation. + not-found: Das gegebene Puzzle konnte nicht gefunden werden. + bad-category: Die gegebene Kategorie konnte nicht gefunden werden. + bad-short-key: Der gegebene Kurzschlüssel ist ungültig. + profane-title: Dein Puzzletitel enthält ungültige Wörter. + bad-title-too-many-spaces: Dein Puzzletitel ist zu kurz. + bad-shape-key-in-emitter: Einem konstanten Produzenten wurde ein ungültiges Item zugewiesen. + bad-shape-key-in-goal: Einem Ziel-Akzeptor wurde ein ungültiges Item zugewiesen. + no-emitters: Dein Puzzle enthält keine konstanten Produzenten. + no-goals: Dein Puzzle enthält keine Ziel-Akzeptoren. + short-key-already-taken: Dieser Kurzschlüssel ist bereits vergeben, bitte wähle einen anderen. + can-not-report-your-own-puzzle: Du kannst nicht dein eigenes Puzzle melden. + bad-payload: Die Anfrage beinhaltet ungültige Daten. + bad-building-placement: Dein Puzzle beinhaltet Gebäude, die sich an ungültigen Stellen befinden. + timeout: Es kam zu einer Zeitüberschreitung bei der Anfrage. + too-many-likes-already: Dieses Puzzle hat schon zu viele Likes erhalten. Wenn du + es trotzdem löschen möchtest, wende dich an support@shapez.io! + no-permission: Du hast nicht die Berechtigung diese Aktion auszuführen. diff --git a/translations/base-el.yaml b/translations/base-el.yaml index 1a6a9581..0ca43bc5 100644 --- a/translations/base-el.yaml +++ b/translations/base-el.yaml @@ -53,6 +53,7 @@ global: escape: ESC shift: SHIFT space: SPACE + loggingIn: Logging in demoBanners: title: Demo Version intro: Get the standalone to unlock all features! @@ -70,9 +71,15 @@ mainMenu: savegameLevelUnknown: Άγνωστο Επίπεδο continue: Συνέχεια newGame: Καινούριο παιχνίδι - madeBy: Made by <author-link> + madeBy: Κατασκευασμένο απο <author-link> subreddit: Reddit savegameUnnamed: Unnamed + puzzleMode: Λειτουργία παζλ + back: Πίσω + puzzleDlcText: Σε αρέσει να συμπάγης και να βελτιστοποίεις εργοστάσια; Πάρε την + λειτουργεία παζλ στο Steam για ακόμη περισσότερη πλάκα! + puzzleDlcWishlist: Βαλτε το στην λίστα επιθυμιών τώρα! + puzzleDlcViewNow: View Dlc dialogs: buttons: ok: OK @@ -86,6 +93,9 @@ dialogs: viewUpdate: Προβολή ενημέρωσης showUpgrades: Εμφάνιση αναβαθμίσεων showKeybindings: Συνδυασμοί πλήκτρων + retry: Retry + continue: Continue + playOffline: Play Offline importSavegameError: title: Σφάλμα εισαγωγής text: "Αποτυχία εισαγωγής του αποθηκευμένου παιχνιδιού:" @@ -177,13 +187,13 @@ dialogs: desc: Δεν έχεις τους πόρους να επικολλήσεις αυτήν την περιοχή! Είσαι βέβαιος/η ότι θέλεις να την αποκόψεις; editSignal: - title: Set Signal - descItems: "Choose a pre-defined item:" - descShortKey: ... or enter the <strong>short key</strong> of a shape (Which you - can generate <link>here</link>) + title: Βάλε σήμα + descItems: "Διάλεξε ενα προκαθορισμένο αντικείμενο:" + descShortKey: ... ή εισάγετε ενα <strong>ενα μικρό κλειδι</strong> απο ένα σχήμα + (Που μπορείς να παράγεις <link>εδώ</link>) renameSavegame: - title: Rename Savegame - desc: You can rename your savegame here. + title: Μετανόμασε το αποθηκευμένου παιχνιδι. + desc: Μπορείς να μετανομάσεις το αποθηκευμένο σου παιχνίδι εδω. tutorialVideoAvailable: title: Tutorial Available desc: There is a tutorial video available for this level! Would you like to @@ -192,6 +202,70 @@ dialogs: title: Tutorial Available desc: There is a tutorial video available for this level, but it is only available in English. Would you like to watch it? + editConstantProducer: + title: Set Item + puzzleLoadFailed: + title: Απέτυχε να φορτώσει το παζλ. + desc: "Δυστυχώς το παζλ δεν μπορούσε να φορτωθεί:" + submitPuzzle: + title: Υπόβαλε παζλ + descName: "Δώσε όνομα στο παζλ:" + descIcon: "Παρακαλούμε εισάγετε ενα μικρό κλειδι, που θα προβληθεί εως το + εικονίδιο για το παζλ (Μπορείς να το παράγεις <link>εδώ</link>, ή + διάλεξε ενα ενα από τα παρακάτω τυχαία προτεινόμενα σχήματα):" + placeholderName: Τίτλος παζλ + puzzleResizeBadBuildings: + title: Resize not possible + desc: You can't make the zone any smaller, because then some buildings would be + outside the zone. + puzzleLoadError: + title: Κακό παζλ + desc: "Το πάζλ απέτυχε να φορτώθει:" + offlineMode: + title: Offline Mode + desc: We couldn't reach the servers, so the game has to run in offline mode. + Please make sure you have an active internet connection. + puzzleDownloadError: + title: Σφάλμα λήψης + desc: "Απέτυχε να κατεβαθεί το πάζλ:" + puzzleSubmitError: + title: Submission Error + desc: "Failed to submit your puzzle:" + puzzleSubmitOk: + title: Δημοσίευτηκε το παζλ + desc: Συγχαρητήρια! Το παζλ σας έχει δημοσιευτεί και μπορείτε τώρα να το παίξουν + οι υπολοιποι. Τώρα μπορείτε να το βρείτε στην ενότητα "Τα παζλ μου". + puzzleCreateOffline: + title: Offline Mode + desc: Since you are offline, you will not be able to save and/or publish your + puzzle. Would you still like to continue? + puzzlePlayRegularRecommendation: + title: Recommendation + desc: I <strong>strongly</strong> recommend playing the normal game to level 12 + before attempting the puzzle DLC, otherwise you may encounter + mechanics not yet introduced. Do you still want to continue? + puzzleShare: + title: Μικρό κλειδι αντιγράφηκε + desc: Το μικρο κλειδή απο το παζλ (<key>) αντιγράφηκε στο πρόχειρο! Το μπορεί να + εισαχθεί στο μενού παζλ για πρόσβαση στο παζλ. + puzzleReport: + title: Report Puzzle + options: + profane: Profane + unsolvable: Not solvable + trolling: Trolling + puzzleReportComplete: + title: Thank you for your feedback! + desc: The puzzle has been flagged. + puzzleReportError: + title: Failed to report + desc: "Your report could not get processed:" + puzzleLoadShortKey: + title: Enter short key + desc: Enter the short key of the puzzle to load it. + puzzleDelete: + title: Delete Puzzle? + desc: Are you sure you want to delete '<title>'? This can not be undone! ingame: keybindingsOverlay: moveMap: Κίνηση @@ -213,6 +287,7 @@ ingame: clearSelection: Εκκαθαρισμός επιλογής pipette: Σταγονόμετρο switchLayers: Εναλλαγή στρώματος + clearBelts: Clear belts buildingPlacement: cycleBuildingVariants: Πάτησε <key> για εναλλαγή μεταξύ παραλλαγών. hotkeyLabel: "Hotkey: <key>" @@ -363,6 +438,49 @@ ingame: achievements: title: Achievements desc: Hunt them all! + puzzleEditorSettings: + zoneTitle: Zone + zoneWidth: Width + zoneHeight: Height + trimZone: Trim + clearItems: Clear Items + share: Share + report: Report + clearBuildings: Clear Buildings + resetPuzzle: Reset Puzzle + puzzleEditorControls: + title: Κατασκευαστείς παζλ. + instructions: + - 1. Τοποθετήστε τους <strong> σταθερούς παραγωγούς </strong> για να + δώσετε σχήματα και χρώματα στον + - 2. Δημιουργήστε ένα ή περισσότερα σχήματα που θέλετε να + δημιουργήσει ο παίκτης αργότερα και παραδώστε το σε έναν ή + περισσότερους <strong> Αποδέκτες στόχων </strong> + - 3. Μόλις ένας Αποδέκτης Στόχου λάβει ένα σχήμα για ένα ορισμένο + ποσό χρόνο, <strong> το αποθηκεύει ως στόχο </strong> που πρέπει + να κάνει ο παίκτης παράγει αργότερα (Υποδεικνύεται από το <strong> + πράσινο σήμα </strong>). + - 4. Κάντε κλικ στο <strong> κουμπί κλειδώματος </strong> σε ένα + κτίριο για να το απενεργοποίησετε. + - 5. Μόλις κάνετε κλικ στην κριτική, το παζλ σας θα επικυρωθεί και + εσείς μπορεί να το δημοσιεύσει. + - 6. Μετά την κυκλοφόρηση του παζλ, <strong> όλα τα κτίρια θα + αφαιρεθούν </strong> εκτός από τους παραγωγούς και τους αποδέκτες + στόχων - Αυτό είναι το μέρος που ο παίκτης υποτίθεται ότι θα + καταλάβει μόνοι του :) + puzzleCompletion: + title: Puzzle Completed! + titleLike: "Click the heart if you liked the puzzle:" + titleRating: How difficult did you find the puzzle? + titleRatingDesc: Your rating will help me to make you better suggestions in the future + continueBtn: Keep Playing + menuBtn: Menu + puzzleMetadata: + author: Author + shortKey: Short Key + rating: Difficulty score + averageDuration: Avg. Duration + completionRate: Completion rate shopUpgrades: belt: name: Ιμάντες, Διανομείς & Σήραγγες @@ -573,6 +691,18 @@ buildings: name: Item Producer description: Available in sandbox mode only, outputs the given signal from the wires layer on the regular layer. + constant_producer: + default: + name: Constant Producer + description: Constantly outputs a specified shape or color. + goal_acceptor: + default: + name: Goal Acceptor + description: Deliver shapes to the goal acceptor to set them as a goal. + block: + default: + name: Block + description: Allows you to block a tile. storyRewards: reward_cutter_and_trash: title: Κοπή σχημάτων @@ -986,6 +1116,10 @@ keybindings: rotateToDown: "Rotate: Point Down" rotateToRight: "Rotate: Point Right" rotateToLeft: "Rotate: Point Left" + constant_producer: Constant Producer + goal_acceptor: Goal Acceptor + block: Block + massSelectClear: Clear belts about: title: Σχετικά με αυτό το παιχνίδι body: >- @@ -1078,3 +1212,73 @@ tips: - 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. +puzzleMenu: + play: Play + edit: Edit + title: Puzzle Mode + createPuzzle: Create Puzzle + loadPuzzle: Load + reviewPuzzle: Review & Publish + validatingPuzzle: Validating Puzzle + submittingPuzzle: Submitting Puzzle + noPuzzles: There are currently no puzzles in this section. + categories: + levels: Levels + new: New + top-rated: Top Rated + mine: My Puzzles + easy: Easy + hard: Hard + completed: Completed + medium: Medium + official: Official + trending: Trending today + trending-weekly: Trending weekly + categories: Categories + difficulties: By Difficulty + account: My Puzzles + search: Search + validation: + title: Invalid Puzzle + noProducers: Please place a Constant Producer! + noGoalAcceptors: Please place a Goal Acceptor! + goalAcceptorNoItem: One or more Goal Acceptors have not yet assigned an item. + Deliver a shape to them to set a goal. + goalAcceptorRateNotMet: One or more Goal Acceptors are not getting enough items. + Make sure that the indicators are green for all acceptors. + buildingOutOfBounds: One or more buildings are outside of the buildable area. + Either increase the area or remove them. + autoComplete: Your puzzle autocompletes itself! Please make sure your constant + producers are not directly delivering to your goal acceptors. + difficulties: + easy: Easy + medium: Medium + hard: Hard + dlcHint: Purchased the DLC already? Make sure it is activated by right clicking + shapez.io in your library, selecting Properties > DLCs. +backendErrors: + ratelimit: You are performing your actions too frequent. Please wait a bit. + invalid-api-key: Failed to communicate with the backend, please try to + update/restart the game (Invalid Api Key). + unauthorized: Failed to communicate with the backend, please try to + update/restart the game (Unauthorized). + bad-token: Failed to communicate with the backend, please try to update/restart + the game (Bad Token). + bad-id: Invalid puzzle identifier. + not-found: The given puzzle could not be found. + bad-category: The given category could not be found. + bad-short-key: The given short key is invalid. + profane-title: Your puzzle title contains profane words. + bad-title-too-many-spaces: Your puzzle title is too short. + bad-shape-key-in-emitter: A constant producer has an invalid item. + bad-shape-key-in-goal: A goal acceptor has an invalid item. + no-emitters: Your puzzle does not contain any constant producers. + no-goals: Your puzzle does not contain any goal acceptors. + short-key-already-taken: This short key is already taken, please use another one. + can-not-report-your-own-puzzle: You can not report your own puzzle. + bad-payload: The request contains invalid data. + bad-building-placement: Your puzzle contains invalid placed buildings. + timeout: The request timed out. + too-many-likes-already: The puzzle alreay got too many likes. If you still want + to remove it, please contact support@shapez.io! + no-permission: You do not have the permission to perform this action. diff --git a/translations/base-en.yaml b/translations/base-en.yaml index e3842cdb..e5259dd6 100644 --- a/translations/base-en.yaml +++ b/translations/base-en.yaml @@ -48,6 +48,7 @@ steamPage: global: loading: Loading error: Error + loggingIn: Logging in # How big numbers are rendered, e.g. "10,000" thousandsDivider: "," @@ -116,6 +117,61 @@ mainMenu: savegameLevel: Level <x> savegameLevelUnknown: Unknown Level savegameUnnamed: Unnamed + puzzleMode: Puzzle Mode + back: Back + + puzzleDlcText: >- + Do you enjoy compacting and optimizing factories? + Get the Puzzle DLC now on Steam for even more fun! + puzzleDlcWishlist: Wishlist now! + puzzleDlcViewNow: View Dlc + +puzzleMenu: + play: Play + edit: Edit + title: Puzzle Mode + createPuzzle: Create Puzzle + loadPuzzle: Load + reviewPuzzle: Review & Publish + validatingPuzzle: Validating Puzzle + submittingPuzzle: Submitting Puzzle + noPuzzles: There are currently no puzzles in this section. + dlcHint: Purchased the DLC already? Make sure it is activated by right clicking shapez.io in your library, selecting Properties > DLCs. + + categories: + levels: Levels + new: New + top-rated: Top Rated + mine: Created + easy: Easy + medium: Medium + hard: Hard + completed: Completed + official: Tutorial + trending: Trending today + trending-weekly: Trending weekly + categories: Categories + difficulties: By Difficulty + account: My Puzzles + search: Search + + difficulties: + easy: Easy + medium: Medium + hard: Hard + + validation: + title: Invalid Puzzle + noProducers: Please place a Constant Producer! + noGoalAcceptors: Please place a Goal Acceptor! + goalAcceptorNoItem: >- + One or more Goal Acceptors have not yet assigned an item. Deliver a shape to them to set a goal. + goalAcceptorRateNotMet: >- + One or more Goal Acceptors are not getting enough items. Make sure that the indicators are green for all acceptors. + buildingOutOfBounds: >- + One or more buildings are outside of the buildable area. Either increase the area or remove them. + autoComplete: >- + Your puzzle autocompletes itself! Please make sure your constant producers are not directly delivering to your goal acceptors. dialogs: buttons: @@ -130,6 +186,9 @@ dialogs: viewUpdate: View Update showUpgrades: Show Upgrades showKeybindings: Show Keybindings + retry: Retry + continue: Continue + playOffline: Play Offline importSavegameError: title: Import Error @@ -234,6 +293,9 @@ dialogs: Choose a pre-defined item: descShortKey: ... or enter the <strong>short key</strong> of a shape (Which you can generate <link>here</link>) + editConstantProducer: + title: Set Item + markerDemoLimit: desc: You can only create two custom markers in the demo. Get the standalone for unlimited markers! @@ -256,6 +318,90 @@ dialogs: resetAchievements: title: Reset Achievements description: Are you sure you want to reset all your achievements? + puzzleLoadFailed: + title: Puzzles failed to load + desc: >- + Unfortunately the puzzles could not be loaded: + + submitPuzzle: + title: Submit Puzzle + descName: >- + Give your puzzle a name: + descIcon: >- + Please enter a unique short key, which will be shown as the icon of your puzzle (You can generate them <link>here</link>, or choose one of the randomly suggested shapes below): + + placeholderName: Puzzle Title + + puzzleResizeBadBuildings: + title: Resize not possible + desc: You can't make the zone any smaller, because then some buildings would be outside the zone. + + puzzleLoadError: + title: Bad Puzzle + desc: >- + The puzzle failed to load: + + offlineMode: + title: Offline Mode + desc: >- + We couldn't reach the servers, so the game has to run in offline mode. Please make sure you have an active internet connection. + + puzzleDownloadError: + title: Download Error + desc: >- + Failed to download the puzzle: + + puzzleSubmitError: + title: Submission Error + desc: >- + Failed to submit your puzzle: + + puzzleSubmitOk: + title: Puzzle Published + desc: >- + Congratulations! Your puzzle has been published and can now be played by others. You can now find it in the "My puzzles" section. + + puzzleCreateOffline: + title: Offline Mode + desc: >- + Since you are offline, you will not be able to save and/or publish your puzzle. Would you still like to continue? + + puzzlePlayRegularRecommendation: + title: Recommendation + desc: >- + I <strong>strongly</strong> recommend playing the normal game to level 12 before attempting the puzzle DLC, otherwise you may encounter mechanics not yet introduced. Do you still want to continue? + + puzzleShare: + title: Short Key Copied + desc: >- + The short key of the puzzle (<key>) has been copied to your clipboard! It can be entered in the puzzle menu to access the puzzle. + + puzzleReport: + title: Report Puzzle + options: + profane: Profane + unsolvable: Not solvable + trolling: Trolling + + puzzleReportComplete: + title: Thank you for your feedback! + desc: >- + The puzzle has been flagged. + + puzzleReportError: + title: Failed to report + desc: >- + Your report could not get processed: + + puzzleLoadShortKey: + title: Enter short key + desc: >- + Enter the short key of the puzzle to load it. + + puzzleDelete: + title: Delete Puzzle? + desc: >- + Are you sure you want to delete '<title>'? This can not be undone! ingame: # This is shown in the top left corner and displays useful keybindings in @@ -277,6 +423,7 @@ ingame: plannerSwitchSide: Flip planner side cutSelection: Cut copySelection: Copy + clearBelts: Clear belts clearSelection: Clear selection pipette: Pipette switchLayers: Switch layers @@ -486,6 +633,46 @@ ingame: title: Support me desc: I develop the game in my spare time! + # puzzle mode + puzzleEditorSettings: + zoneTitle: Zone + zoneWidth: Width + zoneHeight: Height + trimZone: Trim + clearItems: Clear Items + clearBuildings: Clear Buildings + resetPuzzle: Reset Puzzle + share: Share + report: Report + + puzzleEditorControls: + title: Puzzle Creator + instructions: + - 1. Place <strong>Constant Producers</strong> to provide shapes and colors to the player + - 2. Build one or more shapes you want the player to build later and deliver it to one or more <strong>Goal Acceptors</strong> + - 3. Once a Goal Acceptor receives a shape for a certain amount of time, it <strong>saves it as a goal</strong> that the player must produce later (Indicated by the <strong>green badge</strong>). + - 4. Click the <strong>lock button</strong> on a building to disable it. + - 5. Once you click review, your puzzle will be validated and you can publish it. + - 6. Upon release, <strong>all buildings will be removed</strong> except for the Producers and Goal Acceptors - That's the part that the player is supposed to figure out for themselves, after all :) + + puzzleCompletion: + title: Puzzle Completed! + + titleLike: >- + Click the heart if you liked the puzzle: + titleRating: How difficult did you find the puzzle? + titleRatingDesc: Your rating will help me to make you better suggestions in the future + + continueBtn: Keep Playing + menuBtn: Menu + + puzzleMetadata: + author: Author + shortKey: Short Key + rating: Difficulty score + averageDuration: Avg. Duration + completionRate: Completion rate + # All shop upgrades shopUpgrades: belt: @@ -854,6 +1041,21 @@ buildings: name: Item Producer description: Available in sandbox mode only, outputs the given signal from the wires layer on the regular layer. + constant_producer: + default: + name: &constant_producer Constant Producer + description: Constantly outputs a specified shape or color. + + goal_acceptor: + default: + name: &goal_acceptor Goal Acceptor + description: Deliver shapes to the goal acceptor to set them as a goal. + + block: + default: + name: &block Block + description: Allows you to block a tile. + storyRewards: # Those are the rewards gained from completing the store reward_cutter_and_trash: @@ -935,7 +1137,7 @@ storyRewards: title: Storage desc: >- You have unlocked the <strong>storage</strong> building - It allows you to store items up to a given capacity!<br><br> - It priorities the left output, so you can also use it as an <strong>overflow gate</strong>! + It prioritises the left output, so you can also use it as an <strong>overflow gate</strong>! reward_blueprints: title: Blueprints @@ -1282,6 +1484,9 @@ keybindings: analyzer: *analyzer comparator: *comparator item_producer: Item Producer (Sandbox) + constant_producer: *constant_producer + goal_acceptor: *goal_acceptor + block: *block # --- pipette: Pipette @@ -1305,6 +1510,7 @@ keybindings: massSelectSelectMultiple: Select multiple areas massSelectCopy: Copy area massSelectCut: Cut area + massSelectClear: Clear belts placementDisableAutoOrientation: Disable automatic orientation placeMultiple: Stay in placement mode @@ -1336,6 +1542,29 @@ demo: settingNotAvailable: Not available in the demo. +backendErrors: + ratelimit: You are performing your actions too frequent. Please wait a bit. + invalid-api-key: Failed to communicate with the backend, please try to update/restart the game (Invalid Api Key). + unauthorized: Failed to communicate with the backend, please try to update/restart the game (Unauthorized). + bad-token: Failed to communicate with the backend, please try to update/restart the game (Bad Token). + bad-id: Invalid puzzle identifier. + not-found: The given puzzle could not be found. + bad-category: The given category could not be found. + bad-short-key: The given short key is invalid. + profane-title: Your puzzle title contains profane words. + bad-title-too-many-spaces: Your puzzle title is too short. + bad-shape-key-in-emitter: A constant producer has an invalid item. + bad-shape-key-in-goal: A goal acceptor has an invalid item. + no-emitters: Your puzzle does not contain any constant producers. + no-goals: Your puzzle does not contain any goal acceptors. + short-key-already-taken: This short key is already taken, please use another one. + can-not-report-your-own-puzzle: You can not report your own puzzle. + bad-payload: The request contains invalid data. + bad-building-placement: Your puzzle contains invalid placed buildings. + timeout: The request timed out. + too-many-likes-already: The puzzle alreay got too many likes. If you still want to remove it, please contact support@shapez.io! + no-permission: You do not have the permission to perform this action. + tips: - The hub will accept any input, not just the current shape! - Make sure your factories are modular - it will pay out! diff --git a/translations/base-es.yaml b/translations/base-es.yaml index 9e5420aa..23eccceb 100644 --- a/translations/base-es.yaml +++ b/translations/base-es.yaml @@ -14,19 +14,19 @@ steamPage: Mientras que sólo procesas formas al principio, tienes que colorearlas después - ¡para ello tienes que extraer y mezclar colores! 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! - what_others_say: What people say about shapez.io - nothernlion_comment: This game is great - I'm having a wonderful time playing, - and time has flown by. - notch_comment: Oh crap. I really should sleep, but I think I just figured out - how to make a computer in shapez.io - steam_review_comment: This game has stolen my life and I don't want it back. - Very chill factory game that won't let me stop making my lines more - efficient. + what_others_say: Lo que otras personas dicen sobre shapez.io + nothernlion_comment: Este juego es estupendo - Estoy teniendo un tiempo + maravolloso jugano, y el tiempo ha pasado volando. + notch_comment: Miércoles. Verdaderamente debería dormir, pero creo que acabo de + descubrir como hacer un ordenador en shapez.io + steam_review_comment: Este juego ha robado mi vida y no la quiero de vuelta. Muy + relajante juego de fábrica que no me dejará hacer mis lineas más + eficientes. global: loading: Cargando error: Error - thousandsDivider: . - decimalSeparator: "," + thousandsDivider: "," + decimalSeparator: . suffix: thousands: k millions: M @@ -53,6 +53,7 @@ global: escape: ESC shift: SHIFT space: ESPACIO + loggingIn: Iniciando sesión demoBanners: title: Versión de prueba intro: ¡Obtén el juego completo para desbloquear todas las características! @@ -72,6 +73,12 @@ mainMenu: savegameLevel: Nivel <x> savegameLevelUnknown: Nivel desconocido savegameUnnamed: Sin nombre + puzzleMode: Modo Puzle + back: Atrás + puzzleDlcText: ¿Disfrutas compactando y optimizando fábricas? ¡Consigue ahora el + DLC de Puzles en Steam para aún más diversión! + puzzleDlcWishlist: ¡Añádelo ahora a tu lista de deseos! + puzzleDlcViewNow: View Dlc dialogs: buttons: ok: OK @@ -85,6 +92,9 @@ dialogs: viewUpdate: Ver actualización showUpgrades: Ver mejoras showKeybindings: Ver atajos de teclado + retry: Reintentar + continue: Continuar + playOffline: Jugar Offline importSavegameError: title: Error de importación text: "Fallo al importar tu partida guardada:" @@ -96,7 +106,7 @@ dialogs: text: "No se ha podido cargar la partida guardada:" confirmSavegameDelete: title: Confirmar borrado - text: ¿Estás seguro de querér borrar el siguiente guardado?<br><br> + text: ¿Estás seguro de que quieres borrar el siguiente guardado?<br><br> '<savegameName>' que está en el nivel <savegameLevel><br><br> ¡Esto no se puede deshacer! savegameDeletionError: @@ -174,7 +184,7 @@ dialogs: crashear tu juego! editSignal: title: Establecer señal - descItems: "Elige un item pre-definido:" + descItems: "Elige un item predefinido:" descShortKey: ... o escribe la <strong>clave</strong> de una forma (La cual puedes generar <link>aquí</link>) renameSavegame: @@ -187,6 +197,72 @@ dialogs: title: Tutorial Disponible desc: Hay un video tutorial disponible para este nivel, pero solo está disponible en inglés ¿Te gustaría verlo? + editConstantProducer: + title: Set Item + puzzleLoadFailed: + title: Fallo al cargar los Puzles + desc: Desafortunadamente, no se pudieron cargar los puzles. + submitPuzzle: + title: Enviar Puzzle + descName: "Nombra tu puzle:" + descIcon: "Por favor ingresa una clave única, que será el icono de tu puzle + (Puedes generarlas <link>aquí</link>, o escoger una de las formas + sugeridas de forma aleatoria, aquí abajo):" + placeholderName: Título del Puzle + puzzleResizeBadBuildings: + title: No es posible cambiar el tamaño + desc: No puedes hacer el área más pequeña, puesto que algunos edificios estarían + fuera de esta. + puzzleLoadError: + title: Fallo al cargar el puzle + desc: "No se pudo cargar el puzle:" + offlineMode: + title: Modo sin conexión + desc: No pudimos conectar con los servidores, y por ello el juego debe funcionar + en el modo sin conexión. Por favor asegúrate de que tu conexión a + internet funciona correctamente. + puzzleDownloadError: + title: Fallo al descargar + desc: "Fallo al descargar el puzle:" + puzzleSubmitError: + title: Error al enviar + desc: "No pudimos enviar tu puzle:" + puzzleSubmitOk: + title: Puzle Publicado + desc: ¡Enhorabuena! Tu puzle ha sido publicado y ahora pueden jugarlo otros. + Puedes encontrarlo en la sección "Mis puzles". + puzzleCreateOffline: + title: Modo sin conexión + desc: Puesto que estás sin conexión, no podrás guardar y/o publicar tu puzle. + ¿Quieres continuar igualmente? + puzzlePlayRegularRecommendation: + title: Recomendación + desc: Te recomiendo <strong>fuertemente</strong> jugar el juego normal hasta el + nivel 12 antes de intentar el DLC de puzles, de otra manera puede + que te encuentres con mecánicas que aún no hemos introducido. + ¿Quieres continuar igualmente? + puzzleShare: + title: Clave Copiada + desc: Hemos copiado la clave de tu puzle (<key>) a tu portapapeles! Puedes + ponerlo en el menú de puzles para acceder al puzle. + puzzleReport: + title: Reportar Puzle + options: + profane: Lenguaje soez + unsolvable: Imposible de resolver + trolling: Troll + puzzleReportComplete: + title: ¡Gracias por tu aporte! + desc: El puzle ha sido marcado como abuso. + puzzleReportError: + title: No se pudo reportar + desc: "No pudimos procesar tu informe:" + puzzleLoadShortKey: + title: Introducir clave + desc: Introduce la clave del puzle para cargarlo. + puzzleDelete: + title: Delete Puzzle? + desc: Are you sure you want to delete '<title>'? This can not be undone! ingame: keybindingsOverlay: moveMap: Mover @@ -196,7 +272,7 @@ ingame: placeMultiple: Colocar varios reverseOrientation: Invertir la orientación disableAutoOrientation: Desactivar la autoorientación - toggleHud: Habilitar el HUD + toggleHud: Habilitar la interfaz placeBuilding: Colocar edificio createMarker: Crear marcador delete: Destruir @@ -208,6 +284,7 @@ ingame: clearSelection: Limpiar selección pipette: Cuentagotas switchLayers: Cambiar capas + clearBelts: Clear belts colors: red: Rojo green: Verde @@ -217,7 +294,7 @@ ingame: cyan: Cian white: Blanco black: Negro - uncolored: Gris + uncolored: Incoloro buildingPlacement: cycleBuildingVariants: Pulsa <key> para rotar por las distintas variantes. hotkeyLabel: "Tecla: <key>" @@ -248,18 +325,18 @@ ingame: dataSources: stored: title: Almacenado - description: Muestra la cantidad de figuras guardadas en tu HUB. + description: Muestra la cantidad de figuras guardadas en tu Centro. produced: title: Producido description: Muestra todas las figuras que tu fábrica al completo produce, incluyendo productos intermedios. delivered: title: Entregados - description: Muestra las figuras que son entregadas a tu HUB. + description: Muestra las figuras que son entregadas a tu Centro. noShapesProduced: Todavía no se han producido figuras. shapesDisplayUnits: second: <shapes> / s - minute: <shapes> / m + minute: <shapes> / min hour: <shapes> / h settingsMenu: playtime: Tiempo de juego @@ -273,7 +350,7 @@ ingame: cost: Coste waypoints: waypoints: Marcadores - hub: HUB + hub: Centro description: Click izquierdo sobre un marcador para ir ahí, click derecho para borrarlo. <br><br> Pulsa <keybinding> para crear un marcador de la vista actual o <strong>click derecho</strong> para crear un marcador @@ -289,8 +366,9 @@ ingame: 1_1_extractor: ¡Coloca un <strong>extractor</strong> encima de un <strong>círculo</strong> para extraerlo! 1_2_conveyor: "¡Conecta el extractor con una <strong>cinta - transportadora</strong> a tu HUB!<br><br> Pista: ¡<strong>Pulsa - y arrastra</strong> la cinta transportadora con el ratón!" + transportadora</strong> a tu Centro!<br><br> Pista: + ¡<strong>Pulsa y arrastra</strong> la cinta transportadora con + el ratón!" 1_3_expand: '¡Esto <strong>NO</strong> es un "juego de esperar"! Construye más extractores y cintas transportadoras para completar el objetivo más rápido.<br><br> Pista: Mantén pulsado <strong>SHIFT</strong> @@ -307,21 +385,21 @@ ingame: para acelerar este lento proceso!<br><br> PD: Usa las teclas <strong>0-9 </strong> para acceder a los edificios más rápido!" 3_1_rectangles: "¡Ahora consigamos unos rectangulos! <strong>construye 4 - extractores</strong> y conectalos a el HUB.<br><br> PD: Manten - apretado <strong>SHIFT</strong> mientrás pones cintas + extractores</strong> y conectalos a el Centro.<br><br> PD: + Manten apretado <strong>SHIFT</strong> mientrás pones cintas transportadoras para activar el planeador de cintas!" - 21_1_place_quad_painter: ¡Pon el <strong>pintaor cuadruple</strong> y consigue - unos <strong>circulos</strong>, el color <strong>blanco</strong> + 21_1_place_quad_painter: ¡Pon el <strong>pintador cuádruple</strong> y consigue + unos <strong>círculos</strong>, el color <strong>blanco</strong> y el color <strong>rojo</strong>! - 21_2_switch_to_wires: ¡Cambia a la capa de cables apretando la técla + 21_2_switch_to_wires: ¡Cambia a la capa de cables apretando la tecla <strong>E</strong>!<br><br> Luego <strong>conecta las cuatro - entradas</strong> de el pintador con cables! + entradas</strong> del pintador con cables! 21_3_place_button: ¡Genial! ¡Ahora pon un <strong>Interruptor</strong> y - conectalo con cables! + conéctalo con cables! 21_4_press_button: "Presiona el interruptor para hacer que <strong>emita una - señal verdadera</strong> lo cual activa el piintador.<br><br> - PD: ¡No necesitas conectar todas las entradas! Intenta - conectando solo dos." + señal</strong> lo cual activa el pintador.<br><br> PD: ¡No + necesitas conectar todas las entradas! Intenta conectando sólo + dos." connectedMiners: one_miner: 1 Minero n_miners: <amount> Mineros @@ -356,8 +434,51 @@ ingame: title: Apoyame desc: ¡Desarrollo este juego en mi tiempo libre! achievements: - title: Achievements - desc: Hunt them all! + title: Logros + desc: Atrapalos a todos! + puzzleEditorSettings: + zoneTitle: Área + zoneWidth: Anchura + zoneHeight: Altura + trimZone: Área de recorte + clearItems: Eliminar todos los elementos + share: Compartir + report: Reportar + clearBuildings: Clear Buildings + resetPuzzle: Reset Puzzle + puzzleEditorControls: + title: Editor de Puzles + instructions: + - 1. Pon <strong>Productores Constantes</strong> para proveer al + jugador de formas y colores. + - 2. Construye una o más formas que quieres que el jugador construya + más tarde y llévalo hacia uno o más <strong>Aceptadores de + Objetivos</strong>. + - 3. Cuando un Aceptador de Objetivos recibe una forma por cierto + tiempo, <strong>la guarda como un objetivo</strong> que el jugador + debe producir más tarde (Lo sabrás por el <strong>indicador + verde</strong>). + - 4. Haz clic en el <strong>candado</strong> de un edificio para + desactivarlo. + - 5. Una vez hagas clic en "revisar", tu puzle será validado y + podrás publicarlo. + - 6. Una vez publicado, <strong>todos los edificios serán + eliminados</strong> excepto los Productores and Aceptadores de + Objetivo - Esa es la parte que el jugador debe averiguar por sí + mismo, después de todo :) + puzzleCompletion: + title: Puzle Completado! + titleLike: "Haz click en el corazón si te gustó el puzle:" + titleRating: ¿Cuánta dificultad tuviste al resolver el puzle? + titleRatingDesc: Tu puntuación me ayudará a hacerte mejores sugerencias en el futuro + continueBtn: Continuar Jugando + menuBtn: Menú + puzzleMetadata: + author: Autor + shortKey: Clave + rating: Puntuación de dificultad + averageDuration: Duración media + completionRate: Índice de finalización shopUpgrades: belt: name: Cintas transportadoras, Distribuidores y Túneles @@ -375,8 +496,8 @@ buildings: hub: deliver: Entregar toUnlock: para desbloquear - levelShortcut: LVL - endOfDemo: End of Demo + levelShortcut: Nivel + endOfDemo: Fin de la demo belt: default: name: Cinta Transportadora @@ -425,7 +546,7 @@ buildings: name: Rotador (Inverso) description: Rota las figuras en sentido antihorario 90 grados. rotate180: - name: Rotador (180) + name: Rotador (180º) description: Rota formas en 180 grados. stacker: default: @@ -460,7 +581,7 @@ buildings: description: Acepta formas desde todos los lados y las destruye. Para siempre. balancer: default: - name: Balanceador + name: Equlibrador description: Multifuncional - Distribuye igualmente todas las entradas en las salidas. merger: @@ -483,11 +604,11 @@ buildings: desbordamiento. wire_tunnel: default: - name: Cruze de cables + name: Cruce de cables description: Permite que dos cables se cruzen sin conectarse. constant_signal: default: - name: Señal costante + name: Señal constante description: Emite una señal constante, que puede ser una forma, color o valor booleano (1 / 0). lever: @@ -572,8 +693,21 @@ buildings: item_producer: default: name: Productor de items - description: Solo disponible en modo libre, envía la señal recivida de la capa + description: Solo disponible en modo libre, envía la señal recibida de la capa de cables en la capa regular. + constant_producer: + default: + name: Productor de una sola pieza + description: Da constantemente la figura o el color especificados. + goal_acceptor: + default: + name: Aceptador de objetivos + description: Tranporta figuras al aceptador de objetivos para ponerlas como + objetivo. + block: + default: + name: Bloque + description: Permite bloquear una celda. storyRewards: reward_cutter_and_trash: title: Cortador de figuras @@ -646,15 +780,15 @@ storyRewards: como una <strong>puerta de desbordamiento</strong>! reward_freeplay: title: Juego libre - desc: ¡Lo hiciste! Haz desbloqueado el <strong>modo de juego libre</strong>! + desc: ¡Lo hiciste! Has desbloqueado el <strong>modo de juego libre</strong>! ¡Esto significa que las formas ahora son - <strong>aleatoriamente</strong> generadas!<br><br> Debído a que - desde ahora de adelante el HUB pedrirá una cantidad especifica de - formas <strong>por segundo</strong> ¡Te recomiendo encarecidamente - que construyas una maquina que automáticamente envíe la forma - pedida!<br><br> El HUB emite la forma pedida en la capa de cables, - así que todo lo que tienes que hacer es analizarla y automaticamente - configurar tu fabrica basada en ello. + <strong>aleatoriamente</strong> generadas!<br><br> Debido a que + desde ahora de adelante el Centro pedirá una cantidad específica de + formas <strong>por segundo</strong>, ¡Te recomiendo encarecidamente + que construyas una máquina que automáticamente envíe la forma + pedida!<br><br> El Centro emite la forma pedida en la capa de + cables, así que todo lo que tienes que hacer es analizarla y + automáticamente configurar tu fábrica basada en ello. reward_blueprints: title: Planos desc: ¡Ahora puedes <strong>copiar y pegar</strong> partes de tu fábrica! @@ -719,9 +853,9 @@ storyRewards: ¡Ahora puedes simular un cortador, rotador, apilador y más dentro de la capa de cables! Con esto ahora tienes tres opciones para continuar el juego:<br><br> - Construir una <strong>maquina - automatizada</strong> para crear cualquier forma que te pida el HUB - (¡Te recomiendo que lo intentes!).<br><br> - Construir algo genial - con los cables.<br><br> - Continuar jugando de la manera + automatizada</strong> para crear cualquier forma que te pida el + Centro (¡Te recomiendo que lo intentes!).<br><br> - Construir algo + genial con los cables.<br><br> - Continuar jugando de la manera regular.<br><br> ¡Cualquiera que eligas, recuerda divertirte! reward_wires_painter_and_levers: title: Cables y pintor cuádruple @@ -876,8 +1010,8 @@ settings: description: Deshabilitar la grilla puede ayudar con el rendimiento. ¡También hace que el juego se vea más limpio! clearCursorOnDeleteWhilePlacing: - title: Limpiar el cursos al apretar click derecho - description: Activado por defecto, Limpia el cursor al hacer click derecho + title: Limpiar el cursor al apretar click derecho + description: Activado por defecto, limpia el cursor al hacer click derecho mientras tengas un un edificio seleccionado. Si se deshabilita, puedes eliminar edificios al hacer click derecho mientras pones un edificio. @@ -890,14 +1024,14 @@ settings: description: Este juego está dividido en chunks de 16x16 cuadrados, si esta opción es habilitada los bordes de cada chunk serán mostrados. pickMinerOnPatch: - title: Elegír el minero en la veta de recursos - description: Activado pir defecto, selecciona el minero si usas el cuentagotas + title: Elegir el minero en la veta de recursos + description: Activado por defecto, selecciona el minero si usas el cuentagotas sobre una veta de recursos. simplifiedBelts: title: Cintas trasportadoras simplificadas (Feo) description: No rederiza los items en las cintas trasportadoras exceptuando al pasar el cursor sobre la cinta para mejorar el rendimiento. No - recomiendo jugar con esta opcion activada a menos que necesites + recomiendo jugar con esta opción activada a menos que necesites fuertemente mejorar el rendimiento. enableMousePan: title: Habilitar movimiento con mouse @@ -910,8 +1044,8 @@ settings: diferencia de hacer zoom en el centro de la pantalla. mapResourcesScale: title: Tamaño de recursos en el mapa - description: Controla el tamaño de los recursos en la vista de aerea del mapa - (Al hacer zoom minimo). + description: Controla el tamaño de los recursos en la vista de aérea del mapa + (Al hacer zoom mínimo). rangeSliderPercentage: <amount> % keybindings: title: Atajos de teclado @@ -941,7 +1075,7 @@ keybindings: menuOpenShop: Mejoras menuOpenStats: Estadísticas menuClose: Cerrar menú - toggleHud: Activar HUD + toggleHud: Activar Interfaz toggleFPSInfo: Activar FPS e información de depurado switchLayers: Cambiar capas exportScreenshot: Exportar la base completa como imagen @@ -963,7 +1097,7 @@ keybindings: pasteLastBlueprint: Pegar último plano cycleBuildings: Ciclar edificios lockBeltDirection: Activar planificador de cintas transportadoras - switchDirectionLockSide: "Planner: Cambiar sentido" + switchDirectionLockSide: "Planificador: Cambiar sentido" massSelectStart: Mantén pulsado y arrastra para empezar massSelectSelectMultiple: Seleccionar múltiples áreas massSelectCopy: Copiar área @@ -986,10 +1120,14 @@ keybindings: comparator: Comparador item_producer: Productor de items (Sandbox) copyWireValue: "Cables: Copiar valor bajo el cursos" - rotateToUp: "Rotate: Point Up" - rotateToDown: "Rotate: Point Down" - rotateToRight: "Rotate: Point Right" - rotateToLeft: "Rotate: Point Left" + rotateToUp: "Rotar: Apuntar hacia arriba" + rotateToDown: "Rotar: Apuntar hacia abajo" + rotateToRight: "Rotar: Apuntar hacia la derecha" + rotateToLeft: "Rotar: Apuntar hacia la izquierda" + constant_producer: Constant Producer + goal_acceptor: Goal Acceptor + block: Block + massSelectClear: Clear belts about: title: Sobre el juego body: >- @@ -1015,9 +1153,9 @@ demo: exportingBase: Exportando la base completa como imagen settingNotAvailable: No disponible en la versión de prueba. tips: - - El HUB acepta entradas de cualquier tipo ¡No solo la forma actual! + - El Centro acepta entradas de cualquier tipo ¡No solo la forma actual! - Trata de que tus fábricas sean modulares - ¡Te servirá en el futuro! - - No construyas muy cerca del HUB ¡O se volverá todo un caos! + - No construyas muy cerca del Centro ¡O se volverá todo un caos! - Si apilar las formas no funciona, intenta intercambiar las entradas. - Puedes activar la dirección del planeador de cintas apretando <b>R</b>. - Mantener apretado <b>CTRL</b> te permite poner cintas sin auto-orientación. @@ -1047,7 +1185,7 @@ tips: - Puedes apretar <b>ALT</b> para invertir la dirección a la que van las cintas. - ¡La eficiencia es la clave! - - Mientras más lejos del HUB estés más complejas serán las formas que te + - Mientras más lejos del Centro estés más complejas serán las formas que te encuentres. - Las máquinas tienen una velocidad limitada, divídelas para una máxima eficiencia. @@ -1071,7 +1209,7 @@ tips: - Echa un vistazo más de cerca al mezclador de colores, y tus preguntas serán respondidas. - Usa <b>CTRL</b> + Click izquierdo para seleccionar un área. - - Construir demasiado cerca del HUB puede interponerse en el camino de + - Construir demasiado cerca del Centro puede interponerse en el camino de proyectos a futuro. - El icono del alfiler junto a cada forma de la lista de mejoras lo fija a la pantalla. @@ -1084,10 +1222,84 @@ tips: - ¡Este juego tiene un montón de atajos útiles! Asegúrate de revisar la página de ajustes. - Este juego tiene muchos ajustes, ¡asegúrate de revisarlos! - - ¡El marcador de tu HUB tiene una pequeña brújula para indicar su dirección! + - ¡El marcador de tu Centro tiene una pequeña brújula para indicar su + dirección! - Para despejar las cintas transportadoras, corta el área y luego pégala en el mismo lugar. - Presiona F4 para mostrar tu FPS y Tick Rate. - Presiona F4 dos veces para mostrar las coordenadas de tu ratón y de la cámara. - Puedes hacer clic en una forma fijada en el lado izquierdo para desfijarla. +puzzleMenu: + play: Jugar + edit: Editar + title: Puzles + createPuzzle: Crear Puzle + loadPuzzle: Cargar + reviewPuzzle: Revisar y Publicar + validatingPuzzle: Validando Puzle + submittingPuzzle: Enviando Puzzle + noPuzzles: Ahora mismo no hay puzles en esta sección. + categories: + levels: Niveles + new: Nuevos + top-rated: Los mejor valorados + mine: Mis Puzles + easy: Fáciles + hard: Difíciles + completed: Completados + medium: Medium + official: Official + trending: Trending today + trending-weekly: Trending weekly + categories: Categories + difficulties: By Difficulty + account: My Puzzles + search: Search + validation: + title: Puzle no válido + noProducers: Por favor, ¡pon un Productor de una sola pieza! + noGoalAcceptors: Por favor , ¡pon un Aceptador de objetivos! + goalAcceptorNoItem: Uno o más aceptadores de objetivos no tienen asignado un + elemento. Transporta una forma hacia ellos para poner un objetivo. + goalAcceptorRateNotMet: Uno o más aceptadores de objetivos no están recibiendo + suficientes elementos. Asegúrate de que los indicadores están verdes + para todos los aceptadores. + buildingOutOfBounds: Uno o más edificios están fuera del área en la que puedes + construir. Aumenta el área o quítalos. + autoComplete: ¡Tu puzle se completa solo! Asegúrate de que tus productores de un + solo elemento no están conectados directamente a tus aceptadores de + objetivos. + difficulties: + easy: Easy + medium: Medium + hard: Hard + dlcHint: Purchased the DLC already? Make sure it is activated by right clicking + shapez.io in your library, selecting Properties > DLCs. +backendErrors: + ratelimit: Estás haciendo tus acciones con demasiada frecuencia. Por favor, + espera un poco. + invalid-api-key: No pudimos conectar con el servidor, por favor intenta + actualizar/reiniciar el juego (Key de API Inválida). + unauthorized: No pudimos conectar con el servidor, por favor intenta + actualizar/reiniciar el juego (Sin Autorización). + bad-token: No pudimos conectar con el servidor, por favor intenta + actualizar/reiniciar el juego (Mal Token). + bad-id: El identificador del puzle no es válido. + not-found: No pudimos encontrar ese puzle. + bad-category: No pudimos encontar esa categoría. + bad-short-key: La clave que nos diste no es válida. + profane-title: El título de tu puzle contiene lenguaje soez. + bad-title-too-many-spaces: El título de tu puzle es demasiado breve. + bad-shape-key-in-emitter: Un productor de un solo elemento tiene un elemento no válido. + bad-shape-key-in-goal: Un aceptador de objetivos tiene un elemento no válido. + no-emitters: Tu puzle no contiene ningún productor de un solo item. + no-goals: Tu puzle no contiene ningún aceptador de objetivos. + short-key-already-taken: Esta clave ya está siendo usada, por favor usa otra. + can-not-report-your-own-puzzle: No pudes reportar tu propio puzle. + bad-payload: La petición contiene datos no válidos. + bad-building-placement: Tu puzle contiene edificios en posiciones no válidas. + timeout: El tiempo para la solicitud ha expirado. + too-many-likes-already: The puzzle alreay got too many likes. If you still want + to remove it, please contact support@shapez.io! + no-permission: You do not have the permission to perform this action. diff --git a/translations/base-fi.yaml b/translations/base-fi.yaml index 737b0fd6..a11cee94 100644 --- a/translations/base-fi.yaml +++ b/translations/base-fi.yaml @@ -53,6 +53,7 @@ global: escape: ESC shift: SHIFT space: VÄLILYÖNTI + loggingIn: Logging in demoBanners: title: Demoversio intro: Hanki pelin täysversio avataksesi kaikki ominaisuudet! @@ -72,6 +73,12 @@ mainMenu: savegameLevel: Taso <x> savegameLevelUnknown: Tuntematon taso savegameUnnamed: Nimetön + puzzleMode: Puzzle Mode + back: Back + puzzleDlcText: Do you enjoy compacting and optimizing factories? Get the Puzzle + DLC now on Steam for even more fun! + puzzleDlcWishlist: Wishlist now! + puzzleDlcViewNow: View Dlc dialogs: buttons: ok: OK @@ -85,6 +92,9 @@ dialogs: viewUpdate: Näytä päivitys showUpgrades: Näytä päivitykset showKeybindings: Näytä pikanäppäimet + retry: Retry + continue: Continue + playOffline: Play Offline importSavegameError: title: Tuontivirhe text: "Tallennuksen tuonti epäonnistui:" @@ -180,6 +190,70 @@ dialogs: tutorialVideoAvailableForeignLanguage: title: Ohjevideo saatavilla desc: Tästä tasosta on saatavilla ohjevideo! Haluaisitko katsoa sen? + editConstantProducer: + title: Set Item + puzzleLoadFailed: + title: Puzzles failed to load + desc: "Unfortunately the puzzles could not be loaded:" + submitPuzzle: + title: Submit Puzzle + descName: "Give your puzzle a name:" + descIcon: "Please enter a unique short key, which will be shown as the icon of + your puzzle (You can generate them <link>here</link>, or choose one + of the randomly suggested shapes below):" + placeholderName: Puzzle Title + puzzleResizeBadBuildings: + title: Resize not possible + desc: You can't make the zone any smaller, because then some buildings would be + outside the zone. + puzzleLoadError: + title: Bad Puzzle + desc: "The puzzle failed to load:" + offlineMode: + title: Offline Mode + desc: We couldn't reach the servers, so the game has to run in offline mode. + Please make sure you have an active internet connection. + puzzleDownloadError: + title: Download Error + desc: "Failed to download the puzzle:" + puzzleSubmitError: + title: Submission Error + desc: "Failed to submit your puzzle:" + puzzleSubmitOk: + title: Puzzle Published + desc: Congratulations! Your puzzle has been published and can now be played by + others. You can now find it in the "My puzzles" section. + puzzleCreateOffline: + title: Offline Mode + desc: Since you are offline, you will not be able to save and/or publish your + puzzle. Would you still like to continue? + puzzlePlayRegularRecommendation: + title: Recommendation + desc: I <strong>strongly</strong> recommend playing the normal game to level 12 + before attempting the puzzle DLC, otherwise you may encounter + mechanics not yet introduced. Do you still want to continue? + puzzleShare: + title: Short Key Copied + desc: The short key of the puzzle (<key>) has been copied to your clipboard! It + can be entered in the puzzle menu to access the puzzle. + puzzleReport: + title: Report Puzzle + options: + profane: Profane + unsolvable: Not solvable + trolling: Trolling + puzzleReportComplete: + title: Thank you for your feedback! + desc: The puzzle has been flagged. + puzzleReportError: + title: Failed to report + desc: "Your report could not get processed:" + puzzleLoadShortKey: + title: Enter short key + desc: Enter the short key of the puzzle to load it. + puzzleDelete: + title: Delete Puzzle? + desc: Are you sure you want to delete '<title>'? This can not be undone! ingame: keybindingsOverlay: moveMap: Liiku @@ -201,6 +275,7 @@ ingame: clearSelection: Tyhjennä valinta pipette: Pipetti switchLayers: Vaihda tasoa + clearBelts: Clear belts colors: red: Punainen green: Vihreä @@ -349,6 +424,46 @@ ingame: achievements: title: Saavutukset desc: Metsästä kaikki! + puzzleEditorSettings: + zoneTitle: Zone + zoneWidth: Width + zoneHeight: Height + trimZone: Trim + clearItems: Clear Items + share: Share + report: Report + clearBuildings: Clear Buildings + resetPuzzle: Reset Puzzle + puzzleEditorControls: + title: Puzzle Creator + instructions: + - 1. Place <strong>Constant Producers</strong> to provide shapes and + colors to the player + - 2. Build one or more shapes you want the player to build later and + deliver it to one or more <strong>Goal Acceptors</strong> + - 3. Once a Goal Acceptor receives a shape for a certain amount of + time, it <strong>saves it as a goal</strong> that the player must + produce later (Indicated by the <strong>green badge</strong>). + - 4. Click the <strong>lock button</strong> on a building to disable + it. + - 5. Once you click review, your puzzle will be validated and you + can publish it. + - 6. Upon release, <strong>all buildings will be removed</strong> + except for the Producers and Goal Acceptors - That's the part that + the player is supposed to figure out for themselves, after all :) + puzzleCompletion: + title: Puzzle Completed! + titleLike: "Click the heart if you liked the puzzle:" + titleRating: How difficult did you find the puzzle? + titleRatingDesc: Your rating will help me to make you better suggestions in the future + continueBtn: Keep Playing + menuBtn: Menu + puzzleMetadata: + author: Author + shortKey: Short Key + rating: Difficulty score + averageDuration: Avg. Duration + completionRate: Completion rate shopUpgrades: belt: name: Kuljettimet, jakelijat & tunnelit @@ -559,15 +674,26 @@ buildings: name: Signaaligeneraattori description: Saatavilla vain hiekkalaatikkotilassa. Lähettää johtotasolla annetun signaalin normaaliin tasoon. + constant_producer: + default: + name: Constant Producer + description: Constantly outputs a specified shape or color. + goal_acceptor: + default: + name: Goal Acceptor + description: Deliver shapes to the goal acceptor to set them as a goal. + block: + default: + name: Block + description: Allows you to block a tile. storyRewards: reward_cutter_and_trash: title: Muotojen Leikkaus - desc: Avasit <strong>Leikkurin</strong>, joka leikkaa muotoja - ylhäältä alas <strong>muodon suunnasta - riippumatta</strong>!<br><br>muista hankkiutua eroon jätteestä, tai - muuten <strong>se tukkii ja pysäyttää leikkurin</strong> - Siksi - olen antanut sinulle <strong>roskiksen</strong>, joka tuhoaa - kaiken sinne laitetun! + desc: Avasit <strong>Leikkurin</strong>, joka leikkaa muotoja ylhäältä alas + <strong>muodon suunnasta riippumatta</strong>!<br><br>muista + hankkiutua eroon jätteestä, tai muuten <strong>se tukkii ja + pysäyttää leikkurin</strong> - Siksi olen antanut sinulle + <strong>roskiksen</strong>, joka tuhoaa kaiken sinne laitetun! reward_rotater: title: Kääntö desc: Avasit <strong>Kääntäjän</strong>! Se kääntää muotoja myötäpäivään 90 @@ -629,11 +755,11 @@ storyRewards: reward_freeplay: title: Vapaapeli desc: Onnistuit! Avasit juuri <strong>vapaapelimuodon</strong>! Muodot luodaan - nyt <strong>satunnaisesti</strong><br><br> Koska keskusrakennus vaatii - tietyn <strong>kuljetuskapasiteetin</strong> tästä eteenpäin, suosittelen - lämpimästi rakentamaan koneen, joka tuottaa vaaditun muodon - automaattisesti!<br><br> Keskusrakennus lähettää pyydetyn muodon - johtotasolle, joten sinun ei tarvitse kuin analysoida se ja + nyt <strong>satunnaisesti</strong><br><br> Koska keskusrakennus + vaatii tietyn <strong>kuljetuskapasiteetin</strong> tästä eteenpäin, + suosittelen lämpimästi rakentamaan koneen, joka tuottaa vaaditun + muodon automaattisesti!<br><br> Keskusrakennus lähettää pyydetyn + muodon johtotasolle, joten sinun ei tarvitse kuin analysoida se ja konfiguroida tehtaasi sen perusteella. reward_blueprints: title: Piirustukset @@ -956,12 +1082,15 @@ keybindings: rotateToDown: "Käännä: osoittaa alas" rotateToRight: "Käännä: osoittaa oikealle" rotateToLeft: "Käännä: osoittaa vasemmalle" + constant_producer: Constant Producer + goal_acceptor: Goal Acceptor + block: Block + massSelectClear: Clear belts about: title: Tietoja tästä pelistä body: >- - Tämä peli on avointa lähdekoodia ja <a - href="https://github.com/tobspr" target="_blank">Tobias Springer</a>in - (siis minun) kehittämäni.<br><br> + Tämä peli on avointa lähdekoodia ja <a href="https://github.com/tobspr" + target="_blank">Tobias Springer</a>in (siis minun) kehittämäni.<br><br> Jos haluat avustaa, käy katsomassa <a href="<githublink>" target="_blank">shapez.io GitHubissa</a>.<br><br> @@ -1043,3 +1172,73 @@ tips: - Paina F4 nähdäksesi FPS laskurin ja virkistystaajuuden. - Press F4 kahdesti nähdäksesi hiiren ja kameran ruudun. - Klikkaa kiinnitetyn muodon vasemmalta puolelta irrottaaksesi sen. +puzzleMenu: + play: Play + edit: Edit + title: Puzzle Mode + createPuzzle: Create Puzzle + loadPuzzle: Load + reviewPuzzle: Review & Publish + validatingPuzzle: Validating Puzzle + submittingPuzzle: Submitting Puzzle + noPuzzles: There are currently no puzzles in this section. + categories: + levels: Levels + new: New + top-rated: Top Rated + mine: My Puzzles + easy: Easy + hard: Hard + completed: Completed + medium: Medium + official: Official + trending: Trending today + trending-weekly: Trending weekly + categories: Categories + difficulties: By Difficulty + account: My Puzzles + search: Search + validation: + title: Invalid Puzzle + noProducers: Please place a Constant Producer! + noGoalAcceptors: Please place a Goal Acceptor! + goalAcceptorNoItem: One or more Goal Acceptors have not yet assigned an item. + Deliver a shape to them to set a goal. + goalAcceptorRateNotMet: One or more Goal Acceptors are not getting enough items. + Make sure that the indicators are green for all acceptors. + buildingOutOfBounds: One or more buildings are outside of the buildable area. + Either increase the area or remove them. + autoComplete: Your puzzle autocompletes itself! Please make sure your constant + producers are not directly delivering to your goal acceptors. + difficulties: + easy: Easy + medium: Medium + hard: Hard + dlcHint: Purchased the DLC already? Make sure it is activated by right clicking + shapez.io in your library, selecting Properties > DLCs. +backendErrors: + ratelimit: You are performing your actions too frequent. Please wait a bit. + invalid-api-key: Failed to communicate with the backend, please try to + update/restart the game (Invalid Api Key). + unauthorized: Failed to communicate with the backend, please try to + update/restart the game (Unauthorized). + bad-token: Failed to communicate with the backend, please try to update/restart + the game (Bad Token). + bad-id: Invalid puzzle identifier. + not-found: The given puzzle could not be found. + bad-category: The given category could not be found. + bad-short-key: The given short key is invalid. + profane-title: Your puzzle title contains profane words. + bad-title-too-many-spaces: Your puzzle title is too short. + bad-shape-key-in-emitter: A constant producer has an invalid item. + bad-shape-key-in-goal: A goal acceptor has an invalid item. + no-emitters: Your puzzle does not contain any constant producers. + no-goals: Your puzzle does not contain any goal acceptors. + short-key-already-taken: This short key is already taken, please use another one. + can-not-report-your-own-puzzle: You can not report your own puzzle. + bad-payload: The request contains invalid data. + bad-building-placement: Your puzzle contains invalid placed buildings. + timeout: The request timed out. + too-many-likes-already: The puzzle alreay got too many likes. If you still want + to remove it, please contact support@shapez.io! + no-permission: You do not have the permission to perform this action. diff --git a/translations/base-fr.yaml b/translations/base-fr.yaml index 34749836..c90b41bf 100644 --- a/translations/base-fr.yaml +++ b/translations/base-fr.yaml @@ -11,11 +11,11 @@ steamPage: Et en plus, vous devrez aussi produire de plus en plus pour satisfaire la demande. La seule solution est de construire en plus grand ! Au début vous ne ferez que découper les formes, mais plus tard vous devrez les peindre — et pour ça vous devrez extraire et mélanger des couleurs ! En achetant le jeu sur Steam, vous aurez accès à la version complète, mais vous pouvez aussi jouer à une démo sur shapez.io et vous décider ensuite ! - what_others_say: What people say about shapez.io + what_others_say: Ce que les gens pensent de shapez.io nothernlion_comment: This game is great - I'm having a wonderful time playing, and time has flown by. - notch_comment: Oh crap. I really should sleep, but I think I just figured out - how to make a computer in shapez.io + notch_comment: Mince ! Je devrais vraiment me coucher, mais je crois que j'ai + trouvé comment faire un ordinateur dans shapez.io steam_review_comment: This game has stolen my life and I don't want it back. Very chill factory game that won't let me stop making my lines more efficient. @@ -50,6 +50,7 @@ global: escape: ESC shift: MAJ space: ESPACE + loggingIn: Logging in demoBanners: title: Version de démo intro: Achetez la version complète pour débloquer toutes les fonctionnalités ! @@ -70,6 +71,12 @@ mainMenu: savegameLevel: Niveau <x> savegameLevelUnknown: Niveau inconnu savegameUnnamed: Sans titre + puzzleMode: Puzzle Mode + back: Back + puzzleDlcText: Vous aimez compacter et optimiser vos usines ? Achetez le DLC sur + Steam dés maintenant pour encore plus d'amusement ! + puzzleDlcWishlist: Wishlist now! + puzzleDlcViewNow: View Dlc dialogs: buttons: ok: OK @@ -83,6 +90,9 @@ dialogs: viewUpdate: Voir les mises à jour showUpgrades: Montrer les améliorations showKeybindings: Montrer les raccourcis + retry: Réesayer + continue: Continuer + playOffline: Jouer Hors-ligne importSavegameError: title: Erreur d’importation text: "Impossible d’importer votre sauvegarde :" @@ -183,6 +193,70 @@ dialogs: title: Tutoriel disponible desc: Il y a un tutoriel vidéo pour ce niveau, mais il n’est disponible qu’en anglais. Voulez-vous le regarder ? + editConstantProducer: + title: Définir l'objet + puzzleLoadFailed: + title: Le chargement du Puzzle à échoué + desc: "Malheuresement, le puzzle n'a pas pu être chargé :" + submitPuzzle: + title: Envoyer le Puzzle + descName: "Donnez un nom à votre puzzle :" + descIcon: "Please enter a unique short key, which will be shown as the icon of + your puzzle (You can generate them <link>here</link>, or choose one + of the randomly suggested shapes below):" + placeholderName: Puzzle Title + puzzleResizeBadBuildings: + title: Resize not possible + desc: You can't make the zone any smaller, because then some buildings would be + outside the zone. + puzzleLoadError: + title: Mauvais Puzzle + desc: "Le chargement du puzzle a échoué :" + offlineMode: + title: Mode hors-ligne + desc: We couldn't reach the servers, so the game has to run in offline mode. + Please make sure you have an active internet connection. + puzzleDownloadError: + title: Erreur de téléchargment + desc: "Le téléchargement à échoué :" + puzzleSubmitError: + title: Erreur d'envoi + desc: "L'envoi à échoué :" + puzzleSubmitOk: + title: Puzzle envoyé + desc: Félicitation ! Votre puzzle à été envoyé et peut maintenant être joué. + Vous pouvez maintenant le retrouver dans la section "Mes Puzzles". + puzzleCreateOffline: + title: Mode Hors-ligne + desc: Since you are offline, you will not be able to save and/or publish your + puzzle. Would you still like to continue? + puzzlePlayRegularRecommendation: + title: Recommendation + desc: I <strong>strongly</strong> recommend playing the normal game to level 12 + before attempting the puzzle DLC, otherwise you may encounter + mechanics not yet introduced. Do you still want to continue? + puzzleShare: + title: Short Key Copied + desc: The short key of the puzzle (<key>) has been copied to your clipboard! It + can be entered in the puzzle menu to access the puzzle. + puzzleReport: + title: Report Puzzle + options: + profane: Profane + unsolvable: Not solvable + trolling: Trolling + puzzleReportComplete: + title: Merci pour votre retour ! + desc: Le puzzle a été marqué. + puzzleReportError: + title: Failed to report + desc: "Your report could not get processed:" + puzzleLoadShortKey: + title: Enter short key + desc: Enter the short key of the puzzle to load it. + puzzleDelete: + title: Delete Puzzle? + desc: Are you sure you want to delete '<title>'? This can not be undone! ingame: keybindingsOverlay: moveMap: Déplacer @@ -204,6 +278,7 @@ ingame: clearSelection: Effacer la sélection pipette: Pipette switchLayers: Changer de calque + clearBelts: Supprimer les rails colors: red: Rouge green: Vert @@ -357,6 +432,46 @@ ingame: achievements: title: Achievements desc: Hunt them all! + puzzleEditorSettings: + zoneTitle: Zone + zoneWidth: Width + zoneHeight: Height + trimZone: Trim + clearItems: Clear Items + share: Share + report: Report + clearBuildings: Clear Buildings + resetPuzzle: Reset Puzzle + puzzleEditorControls: + title: Puzzle Creator + instructions: + - 1. Place <strong>Constant Producers</strong> to provide shapes and + colors to the player + - 2. Build one or more shapes you want the player to build later and + deliver it to one or more <strong>Goal Acceptors</strong> + - 3. Once a Goal Acceptor receives a shape for a certain amount of + time, it <strong>saves it as a goal</strong> that the player must + produce later (Indicated by the <strong>green badge</strong>). + - 4. Click the <strong>lock button</strong> on a building to disable + it. + - 5. Once you click review, your puzzle will be validated and you + can publish it. + - 6. Upon release, <strong>all buildings will be removed</strong> + except for the Producers and Goal Acceptors - That's the part that + the player is supposed to figure out for themselves, after all :) + puzzleCompletion: + title: Puzzle Completed! + titleLike: "Click the heart if you liked the puzzle:" + titleRating: How difficult did you find the puzzle? + titleRatingDesc: Your rating will help me to make you better suggestions in the future + continueBtn: Keep Playing + menuBtn: Menu + puzzleMetadata: + author: Author + shortKey: Short Key + rating: Difficulty score + averageDuration: Avg. Duration + completionRate: Completion rate shopUpgrades: belt: name: Convoyeurs, distributeurs et tunnels @@ -574,6 +689,18 @@ buildings: name: Générateur d’objet description: Seulement disponible en mode bac à sable. Renvoie le signal du calque de câblage sur le calque normal. + constant_producer: + default: + name: Constant Producer + description: Constantly outputs a specified shape or color. + goal_acceptor: + default: + name: Goal Acceptor + description: Deliver shapes to the goal acceptor to set them as a goal. + block: + default: + name: Block + description: Allows you to block a tile. storyRewards: reward_cutter_and_trash: title: Découpage de formes @@ -1000,6 +1127,10 @@ keybindings: rotateToDown: "Rotate: Point Down" rotateToRight: "Rotate: Point Right" rotateToLeft: "Rotate: Point Left" + constant_producer: Constant Producer + goal_acceptor: Goal Acceptor + block: Block + massSelectClear: Clear belts about: title: À propos de ce jeu body: >- @@ -1101,3 +1232,76 @@ tips: - Appuyez sur F4 pour voir vos IPS et votre fréquence de rafraîchissement. - Appuyez deux fois sur F4 pour voir les coordonnées. - Cliquez sur une forme épinglée à gauche pour l’enlever. +puzzleMenu: + play: Jouer + edit: Éditer + title: Mode Puzzle + createPuzzle: Créer un Puzzle + loadPuzzle: charger + reviewPuzzle: Revoir & Publier + validatingPuzzle: Validation du Puzzle + submittingPuzzle: Publication du Puzzle + noPuzzles: Il n'y a actuellement aucun puzzle dans cette section. + categories: + levels: Niveaux + new: Nouveau + top-rated: Les-mieux notés + mine: Mes puzzles + easy: Facile + hard: Difficile + completed: Complété + medium: Medium + official: Official + trending: Trending today + trending-weekly: Trending weekly + categories: Categories + difficulties: By Difficulty + account: My Puzzles + search: Search + validation: + title: Puzzle invalide + noProducers: Veuillez placer un producteur constant ! + noGoalAcceptors: Veuillez placer un accepteur d'objectif ! + goalAcceptorNoItem: Un ou plusieurs accepteurs d'objectif n'ont pas encore + attribué d'élément. Donnez-leur une forme pour fixer un objectif. + goalAcceptorRateNotMet: Un ou plusieurs accepteurs d'objectifs n'obtiennent pas + assez d'articles. Assurez-vous que les indicateurs sont verts pour + tous les accepteurs. + buildingOutOfBounds: Un ou plusieurs bâtiments se trouvent en dehors de la zone + constructible. Augmentez la surface ou supprimez-les. + autoComplete: Votre puzzle se complète automatiquement ! Veuillez vous assurer + que vos producteurs constants ne livrent pas directement à vos + accepteurs d'objectifs. + difficulties: + easy: Easy + medium: Medium + hard: Hard + dlcHint: Purchased the DLC already? Make sure it is activated by right clicking + shapez.io in your library, selecting Properties > DLCs. +backendErrors: + ratelimit: Vous effectuez vos actions trop fréquemment. Veuillez attendre un peu + s'il vous plait. + invalid-api-key: Échec de la communication avec le backend, veuillez essayer de + mettre à jour/redémarrer le jeu (clé Api invalide). + unauthorized: Échec de la communication avec le backend, veuillez essayer de + mettre à jour/redémarrer le jeu (non autorisé). + bad-token: Échec de la communication avec le backend, veuillez essayer de mettre + à jour/redémarrer le jeu (Mauvais jeton). + bad-id: Identifiant de puzzle non valide. + not-found: Le puzzle donné n'a pas pu être trouvé. + bad-category: La catégorie donnée n'a pas pu être trouvée. + bad-short-key: La clé courte donnée n'est pas valide. + profane-title: Le titre de votre puzzle contient des mots interdits. + bad-title-too-many-spaces: Le titre de votre puzzle est trop court. + bad-shape-key-in-emitter: Un producteur constant a un élément invalide. + bad-shape-key-in-goal: Un accepteur de but a un élément invalide. + no-emitters: Votre puzzle ne contient aucun producteur constant. + no-goals: Votre puzzle ne contient aucun accepteur de but. + short-key-already-taken: Cette clé courte est déjà prise, veuillez en utiliser une autre. + can-not-report-your-own-puzzle: Vous ne pouvez pas signaler votre propre puzzle. + bad-payload: La demande contient des données invalides. + bad-building-placement: Votre puzzle contient des bâtiments placés non valides. + timeout: La demande a expiré. + too-many-likes-already: The puzzle alreay got too many likes. If you still want + to remove it, please contact support@shapez.io! + no-permission: You do not have the permission to perform this action. diff --git a/translations/base-he.yaml b/translations/base-he.yaml new file mode 100644 index 00000000..9be04af5 --- /dev/null +++ b/translations/base-he.yaml @@ -0,0 +1,1187 @@ +steamPage: + shortText: shapez.io הוא משחק בנוגע לבניית מפעלים אוטמטים ליצירה של צורת מסובכות + יותר ויותר במפה אין סופית. + discordLinkShort: דיסקורד רשמי + intro: >- + אתה אוהב משחקי אוטומציה? אתה במקום הנכון! + + shapez.io הוא משחק שלווה שבו אתה בונה מפעל בשביל ליצור צורות גאומטריות אוטומטית. ככל שמתקדמים השלבים, הצורות נהיות יותר ויותר מסובכות, ואתה צריך להפתח על המפה האין סופית. + + ואם זה לא היה מספיק, אתה צריך ליצור יותר ויותר צורות בשביל לספק את הדרישה - הדבר היחיד שיכול לעזור זה להגדיל את המפעל! בזמן שבהתחלה אתה רק צריך לערוך צורות, בהמשך אתה צריך לצבוע אותם בעזרת צבעים שאתה מערבב. + + קניית המשחק בsteam תתן לך גישה למשחק המלא, אבל אתה יכול לשחק משחק דמו בhttps://shapez.io/ ולהחליט אחר כך. + what_others_say: What people say about shapez.io + nothernlion_comment: This game is great - I'm having a wonderful time playing, + and time has flown by. + notch_comment: Oh crap. I really should sleep, but I think I just figured out + how to make a computer in shapez.io + steam_review_comment: This game has stolen my life and I don't want it back. + Very chill factory game that won't let me stop making my lines more + efficient. +global: + loading: טוען + error: שגיאה + thousandsDivider: "," + decimalSeparator: . + suffix: + thousands: k + millions: M + billions: B + trillions: T + infinite: ∞ + time: + oneSecondAgo: לפני שנייה אחת + xSecondsAgo: לפני <x> שניות + oneMinuteAgo: לפני דקה אחת + xMinutesAgo: לפני <x> דקות + oneHourAgo: לפני שעה אחת + xHoursAgo: לפני <x> שעות + oneDayAgo: לפני יום אחד + xDaysAgo: לפני <x> ימים + secondsShort: <seconds>s + minutesAndSecondsShort: <minutes>m <seconds>s + hoursAndMinutesShort: <hours>h <minutes>m + xMinutes: <x> דקות + keys: + tab: TAB + control: CTRL + alt: ALT + escape: ESC + shift: SHIFT + space: SPACE + loggingIn: Logging in +demoBanners: + title: גרסאת דמו + intro: תשיג את המשחק המלא כדי לפתוח את כל הפיצ'רים +mainMenu: + play: שחק + continue: המשך + newGame: משחק חדש + changelog: עדכונים + subreddit: רדיט + importSavegame: יבוא + openSourceHint: המשחק הזה הוא עם קוד פתוח! + discordLink: שרת הדסקורד הרשמי + helpTranslate: תעזור לתרגם! + madeBy: <author-link> :יוצר המשחק + browserWarning: מצטערים, אבל המשחק רק באיטיות על הדפדפן שלך! תשיג את הגרסא + להורדה או שתוריד גוגל לחוויה המלאה. + savegameLevel: שלב <x> + savegameLevelUnknown: שלב לא ידוע + savegameUnnamed: ללא שם + puzzleMode: Puzzle Mode + back: Back + puzzleDlcText: Do you enjoy compacting and optimizing factories? Get the Puzzle + DLC now on Steam for even more fun! + puzzleDlcWishlist: Wishlist now! + puzzleDlcViewNow: View Dlc +dialogs: + buttons: + ok: אישור + delete: מחיקה + cancel: ביטול + later: מאוחר יותר + restart: פתיחה מחדש + reset: אפס + getStandalone: השג את הגרסא להורדה + deleteGame: אני יודע מה אני עושה + viewUpdate: צפה בעדכון + showUpgrades: הצג שדרוגים + showKeybindings: הצג מקשים + retry: Retry + continue: Continue + playOffline: Play Offline + importSavegameError: + title: שגיאה ביבוא + text: "לא הצליח ליבא את השמירה שלך:" + importSavegameSuccess: + title: המשחק יובא + text: ייבא את המשחק שלך בהצלחה. + gameLoadFailure: + title: השמיררה שבורה + text: לא הצליח לטעון את השמירה. + confirmSavegameDelete: + title: אימות המחיקה + text: אתה בטוח שאתה רוצה למחוק את השמירה הבאה?<br><br> '<savegameName>' בשלב + <savegameLevel><br><br> פעולה זו אינה הפיכה! + savegameDeletionError: + title: נכשל למחוק + text: "נכשל למחוק רת השמיקה:" + restartRequired: + title: איתחול נדרש + text: אתה צריך לסגור ולפתוח את המשחק כדי לישם הגדרה זו. + editKeybinding: + title: שינוי מקשים + desc: תלחץ על מקש או על כפתור בעכבר שאתה רוצה להשתמש, או escape בשביל לבטל. + resetKeybindingsConfirmation: + title: אפס את המקשים + desc: זה יחזיר את כל המקשים להגדרה המקורית. בבקשה תאמת. + keybindingsResetOk: + title: אפס את המקשים + desc: זה יחזיר את כל המקשים להגדרה המקורית! + featureRestriction: + title: גראת דמו + desc: ניסית להשתמש בפיצ'ר (<feature>) שהוא לא זמין בדמו. תשיג את הגרסה להורדה + בשביל החוויה המלאה! + oneSavegameLimit: + title: הגבלת שמירה + desc: אתה יכול לשמור רק שמירה אחת בכל זמן נתון בגרסת הדמו. אתה יכול למחוק את + האחד הנוכחי או להשיג את הגרסה להורדה! + updateSummary: + title: עדכון חדש! + desc: "כאן כל השינויים מאז ששיחקת לאחרונה:" + upgradesIntroduction: + title: נפתח השדרוגים + desc: כל הצורות שייצרת יכולים עכשיו לשמש אותך בשביל לפתוח שדרוגים - <strong>אל + תהרוס את המפעלים הישנים שלך!</strong> אתה יכול לגשת לשדרוגים בפינה + העליונה ימנית של המסך. + massDeleteConfirm: + title: אישור מחיקה + desc: אתה מוחק הרבה מבנים (<count> בשביל להיות מדוייק)! אתה בטוח שאתה רוצה לעשות + את זה? + massCutConfirm: + title: אישור הזזה + desc: אתה מזיז הרבה מבנים (<count> בשביל להיות מדוייק)! אתה בטוח שאתה רוצה לעשות + את זה? + massCutInsufficientConfirm: + title: איזור הזזה + desc: אתה לא תוכל להדביק את האיזור הזה, אתה בטוח שאתה רוצה לחתוך אותו? + blueprintsNotUnlocked: + title: עדיין לא נפתח + desc: תסיים את שלב 12 בשביל לפתוח תבניות + keybindingsIntroduction: + title: מקשים שימושיים + desc: "במשחק הזה ישנם המון מקשים בשביל להקל כל בניית מפעלים גדולים. הנה כמה מהם, + אבל כדאי לך <strong>להסתכל על הגדרות המקשים</strong>!<br><br> <code + class='keybinding'>CTRL</code> + לגרור את העכבר: לבחוק שטח.<br> + <code class='keybinding'>SHIFT</code>: החזק בשביל לבנות כמה מאותו + מבנה.<br> <code class='keybinding'>ALT</code>: הפוך כיוון של + המסוע.<br>" + createMarker: + title: סימון חדש + titleEdit: עריכת סימון + desc: תן לזה שם, אתה יכול גם לרשום <strong>קוד קצר</strong> של צורה (שאתה יכול + לייצר <link>כאן</link>) + editSignal: + title: קבע ערך + descItems: "תבחר מההבאים:" + descShortKey: ... או שתשתמש ב <strong>קוד קצר</strong> של צורה (שאתה יכול לייצר + <link>כאן</link>) + markerDemoLimit: + desc: אתה יכול ליצור רק שני סימונים בגרסאת הדמו. תשיג את הגרסה להורדה בשביל כמול + לא מוגבלת של סימונים! + exportScreenshotWarning: + title: יצוא צילום מסך + desc: אתה ביקשת לייצא את המפעל שלך כצילום מסך. בבקשה תהיה מודע שזה יכול להיות די + איטי למפעלים גדולים ויכול להיות שזה יקריס לך את המשחק! + renameSavegame: + title: שנה שם לשמירה + desc: אתה יכול לשנות את שם השמירה כאן. + tutorialVideoAvailable: + title: הדרכה זמינה + desc: יש סרטון הדרכה זמין לשלב הזה! תרצה לצפות בזה? + tutorialVideoAvailableForeignLanguage: + title: הדרכה זמינה + desc: יש סרטון הדרכה לשלב הזה אבל הוא באנגלית. תרצה לצפות בו? + editConstantProducer: + title: Set Item + puzzleLoadFailed: + title: Puzzles failed to load + desc: "Unfortunately the puzzles could not be loaded:" + submitPuzzle: + title: Submit Puzzle + descName: "Give your puzzle a name:" + descIcon: "Please enter a unique short key, which will be shown as the icon of + your puzzle (You can generate them <link>here</link>, or choose one + of the randomly suggested shapes below):" + placeholderName: Puzzle Title + puzzleResizeBadBuildings: + title: Resize not possible + desc: You can't make the zone any smaller, because then some buildings would be + outside the zone. + puzzleLoadError: + title: Bad Puzzle + desc: "The puzzle failed to load:" + offlineMode: + title: Offline Mode + desc: We couldn't reach the servers, so the game has to run in offline mode. + Please make sure you have an active internet connection. + puzzleDownloadError: + title: Download Error + desc: "Failed to download the puzzle:" + puzzleSubmitError: + title: Submission Error + desc: "Failed to submit your puzzle:" + puzzleSubmitOk: + title: Puzzle Published + desc: Congratulations! Your puzzle has been published and can now be played by + others. You can now find it in the "My puzzles" section. + puzzleCreateOffline: + title: Offline Mode + desc: Since you are offline, you will not be able to save and/or publish your + puzzle. Would you still like to continue? + puzzlePlayRegularRecommendation: + title: Recommendation + desc: I <strong>strongly</strong> recommend playing the normal game to level 12 + before attempting the puzzle DLC, otherwise you may encounter + mechanics not yet introduced. Do you still want to continue? + puzzleShare: + title: Short Key Copied + desc: The short key of the puzzle (<key>) has been copied to your clipboard! It + can be entered in the puzzle menu to access the puzzle. + puzzleReport: + title: Report Puzzle + options: + profane: Profane + unsolvable: Not solvable + trolling: Trolling + puzzleReportComplete: + title: Thank you for your feedback! + desc: The puzzle has been flagged. + puzzleReportError: + title: Failed to report + desc: "Your report could not get processed:" + puzzleLoadShortKey: + title: Enter short key + desc: Enter the short key of the puzzle to load it. + puzzleDelete: + title: Delete Puzzle? + desc: Are you sure you want to delete '<title>'? This can not be undone! +ingame: + keybindingsOverlay: + moveMap: הזזה + selectBuildings: בחירת שטח + stopPlacement: הפסק השמה + rotateBuilding: סיבוב מבנה + placeMultiple: מקם כמה + reverseOrientation: כיוון הפוך + disableAutoOrientation: ביטול כיוון אוטומטי + toggleHud: הסתר/הצג תפריטים + placeBuilding: שים מבנה + createMarker: שים סימון + delete: מחיקה + pasteLastBlueprint: הדבקת התבנית האחרונה + lockBeltDirection: הנחת מסוע מהיר + plannerSwitchSide: הפוך צד המסוע + cutSelection: חיתוח + copySelection: העתקה + clearSelection: ביטול הבחירה + pipette: דגימה + switchLayers: החלפת שכבה + clearBelts: Clear belts + colors: + red: אדום + green: ירוק + blue: כחול + yellow: צהוב + purple: ורוד + cyan: תכלת + white: לבן + black: שחור + uncolored: אפור + buildingPlacement: + cycleBuildingVariants: לחץ <key> בשביל להחליף סוג. + hotkeyLabel: "מקש: <key>" + infoTexts: + speed: מהירות + range: אורך + storage: אחסון + oneItemPerSecond: חפץ 1 / second + itemsPerSecond: <x> חפצים / s + itemsPerSecondDouble: (x2) + tiles: <x> משבצות + levelCompleteNotification: + levelTitle: שלב <level> + completed: הסתיים + unlockText: נפתח <reward>! + buttonNextLevel: שלב הבא + notifications: + newUpgrade: עדכון חדש זמין! + gameSaved: המשחק שלך נשמר. + freeplayLevelComplete: שלב <level> הסתיים! + shop: + title: שדרוגים + buttonUnlock: שדרג + tier: רמה <x> + maximumLevel: רמה מקסימלית (מהירות x<currentMult>) + statistics: + title: סטטיסטיקה + dataSources: + stored: + title: מאוכסן + description: .כל הצורות שמאוכסנות בהאב + produced: + title: מיוצר + description: .כל הצורות שנוצרות בתוך המפעל שלך, כולל צורות בשביל צורות אחרות + delivered: + title: מגיע + description: .צורות שמגיעות להאב ברגע זה + noShapesProduced: .עדיין לא נוצרו צורות + shapesDisplayUnits: + second: <shapes> / s + minute: <shapes> / m + hour: <shapes> / h + settingsMenu: + playtime: זמן משחק + buildingsPlaced: מבנים + beltsPlaced: מסועים + tutorialHints: + title: צריך עזרה? + showHint: הצג רמז + hideHint: סגור + blueprintPlacer: + cost: מחיר + waypoints: + waypoints: סימונים + hub: האב + description: מקש שמאלי על סימון בשביל להגיע אליו, מקש ימני בשביל למחוק + אותו.<br><br>לחץ <keybinding> בשביל ליצור סימון במרכז המסך, או + <strong>מקש-ימני</strong> בשביל ליצור סימון במקום של העכבר. + creationSuccessNotification: הסימון נוצר. + shapeViewer: + title: שכבות + empty: ריק + copyKey: העתק קוד + interactiveTutorial: + title: מדריך + hints: + 1_1_extractor: שים <strong>חוצב</strong> מעל <strong>צורת עיגול</strong> בשביל + להשיג אותה! + 1_2_conveyor: "חבר את החוצב עם <strong>מסועים</strong> להאב שלך!<br><br>טיפ: + <strong>לחץ וגרור</strong> את המסוע עם העכבר שלך!" + 1_3_expand: "זה <strong>לא</strong> משחק שמחכים בו הרבה! בנה עוד חוצבים ומסועים + בשביל לסיים את המשימה מהר יותר.<br><br>טיפ: החזק + <strong>SHIFT</strong> בשביל לשים כמה חוצבים, והשתמש + ב<strong>R</strong> בשביל לסובב אותם." + 2_1_place_cutter: "עכשיו שים <strong>חותך</strong> בשביל לחתוך את העיגולים לשני + חצאים!<br><br> נ.ב: החותך תמיד חותך <strong>אנכית</strong> לא + משנה הכיוון שלו." + 2_2_place_trash: החותך יכול <strong>להסתם ולהתקע</strong>!<br><br> השתמש + ב<strong>פח</strong> בשביל להפתר מהשארית הלא שימושית (בינתיים). + 2_3_more_cutters: "עבודה טובה! עכשיו שים <strong>עוד 2 חותכים</strong> בשביל + להאיץ את התהליך!<br><br> נ.ב: השתמש <strong>במקשים 0-9</strong> + בשביל לבחור מבנים מהר יותר!" + 3_1_rectangles: "עכשיו בוא נחצוב כמה מלבנים! <strong>בנה 4 חוצבים</strong> וחבר + אותם עם מסועים להאב.<br><br> נ.ב: החזק <strong>SHIFT</strong> + בזמן שאתה גורר מסוע בשביל לבנות מסועים מהר יותר!" + 21_1_place_quad_painter: שים את ה<strong>צובע המרובע</strong> והשיג כמה + <strong>עיגולים</strong>, צבע <strong>לבן</strong> וצבע + <strong>אדום</strong>! + 21_2_switch_to_wires: החלף לשכבת הכבלים ע"י לחיצה על <strong>E</strong>!<br><br> + לאחר מכן <strong>חבר את כל ארבעת הכניסות</strong> של הצובע + לכבלים! + 21_3_place_button: נהדר! עכשיו שים <strong>מפסק</strong> ותחבק אותו לכבלים! + 21_4_press_button: "חלץ על המפסק כדי שהוא<strong>יפיק אות חיובי</strong> וזה + יפעיל את הצובע המרובע.<br><br> נ.ב: אתה לא צריך לחבר את כל + הכניסות! נסה לחבר רק 2." + connectedMiners: + one_miner: חוצב 1 + n_miners: <amount> חוצבים + limited_items: מוגבל ל<max_throughput> + watermark: + title: גרסת דמו + desc: לחץ פה בשביל לראות את היתרונות של הגרסה להורדה! + get_on_steam: Steamהשג ב + standaloneAdvantages: + title: "!השג את הגרסה המלאה" + no_thanks: "!לא, תודה" + points: + levels: + title: 12 שלבים חדשים + desc: '!סה"כ 26 שלבים' + buildings: + title: 18 מבנים חדשים + desc: "!אפשרות להפוך את המפעל לאוטומטי לגמרי" + achievements: + title: הישגים + desc: "!השג את כולם" + upgrades: + title: רמות לשדרוגים ∞ + desc: "!לגרסת הדמו הזאת יש רק 5" + markers: + title: סימונים ∞ + desc: "!אף פעם לא תלך לאיבוד במפעל שלך" + wires: + title: כבלים + desc: "!מימד חדש לגמרי" + darkmode: + title: תצוגה כהה + desc: "!תפסיק להכאיב לעיניים שלך" + support: + title: תמוך בי + desc: "!אני יצרתי רת המשחק הזה בזמני החופשי" + puzzleEditorSettings: + zoneTitle: Zone + zoneWidth: Width + zoneHeight: Height + trimZone: Trim + clearItems: Clear Items + share: Share + report: Report + clearBuildings: Clear Buildings + resetPuzzle: Reset Puzzle + puzzleEditorControls: + title: Puzzle Creator + instructions: + - 1. Place <strong>Constant Producers</strong> to provide shapes and + colors to the player + - 2. Build one or more shapes you want the player to build later and + deliver it to one or more <strong>Goal Acceptors</strong> + - 3. Once a Goal Acceptor receives a shape for a certain amount of + time, it <strong>saves it as a goal</strong> that the player must + produce later (Indicated by the <strong>green badge</strong>). + - 4. Click the <strong>lock button</strong> on a building to disable + it. + - 5. Once you click review, your puzzle will be validated and you + can publish it. + - 6. Upon release, <strong>all buildings will be removed</strong> + except for the Producers and Goal Acceptors - That's the part that + the player is supposed to figure out for themselves, after all :) + puzzleCompletion: + title: Puzzle Completed! + titleLike: "Click the heart if you liked the puzzle:" + titleRating: How difficult did you find the puzzle? + titleRatingDesc: Your rating will help me to make you better suggestions in the future + continueBtn: Keep Playing + menuBtn: Menu + puzzleMetadata: + author: Author + shortKey: Short Key + rating: Difficulty score + averageDuration: Avg. Duration + completionRate: Completion rate +shopUpgrades: + belt: + name: מסועים, מסדרים & מנהרות + description: מהירות x<currentMult> → x<newMult> + miner: + name: חציבה + description: מהירות x<currentMult> → x<newMult> + processors: + name: חיתוך, סיבוב & חיבור + description: מהירות x<currentMult> → x<newMult> + painting: + name: ערבוב & צביעה + description: מהירות x<currentMult> → x<newMult> +buildings: + hub: + deliver: ספק + toUnlock: בשביל + levelShortcut: שלב + endOfDemo: סוף הדמו + belt: + default: + name: מסוע + description: מעביר חפצים, לחץ וגרור בשביל לשים כמה. + miner: + default: + name: חוצב + description: שים מעל צורה או צבע בשביל להשיג אותם. + chainable: + name: חוצב (מתחבר) + description: שים מעל צורה או צבע בשביל להשיג אותם. יכול להתחבר. + underground_belt: + default: + name: מנהרה + description: מאפשר לך להעביר חפצים מתחת למבנים ומסועים. + tier2: + name: מנהרה רמה II + description: מאפשר לך להעביר חפצים מתחת למבנים ומסועים. + balancer: + default: + name: מאזן + description: רב שימישי- מחלק את כל הדברים שנכנסים באופן שווה לכל היציאות. + merger: + name: ממזג (קומפקטי) + description: מחבר שני מסועים לאחד. + merger-inverse: + name: ממזג (קומפקטי) + description: מחבר שני מסועים לאחד. + splitter: + name: מפצל (קומפקטי) + description: מפצל מסוע אחד לשניים. + splitter-inverse: + name: מפצל (קומפקטי) + description: מפצל מסוע אחד לשניים. + cutter: + default: + name: חותך + description: חותך את הצורות אנכית ומוציא את שני החלקים. <strong>אם אתה משתמש רק + בחלק אחד, תוודא שאתה הורס את החלק השני או שיווצר סתימה!</strong> + quad: + name: חותך (מרובע) + description: חותך את הצורות לארבעה חלקים. <strong>אם אתה משתמש רק בחלק אחד, + תוודא שאתה הורס את השאר החלקים או שיווצר סתימה!</strong> + rotater: + default: + name: מסובב + description: מסובב חלקים עם כיוון השעון ב90 מעלות. + ccw: + name: מסובב (נגד כיוון השעון) + description: מסובב חלקים נגד כיוון השעון ב90 מעלות. + rotate180: + name: מסובב (180°) + description: מסובב חלקים ב180 מעלות. + stacker: + default: + name: מחבר + description: מחבר את מה שנכנס, באותה שכבה אם אפשרי, אחרת הימני מוערם מעל השמאלי. + mixer: + default: + name: מערבב + description: מערבב את שני הצבעים לפי צבעי האור. + painter: + default: + name: צובע + description: צובע את הצורות מנכנסות הצד השמאלי בצבע שנכנס מלמעלה. + mirrored: + name: צובע + description: צובע את הצורות מנכנסות הצד השמאלי בצבע שנכנס מלמטה. + double: + name: צובע (כפול) + description: צובע את הצורות מנכנסות הצד השמאלי בצבע שנכנס מלמעלה. + quad: + name: צובע (מרובע) + description: מאפשר לצבוע כל רבע בצורה בנפרד. רק מקומות עם <strong>ערך + חיובי</strong> בשכבת הכבלים יצבעו! + trash: + default: + name: פח + description: הורס כל מה שנכנס אליו לנצח. + storage: + default: + name: אחסון + description: מאחסן חפצים שנשארו עד גבול מסויים. הוא מוציא בעדיפות לצד השמאלי, אז + אתה יכול להשתמש בצד הימני כשער הצפה. + wire: + default: + name: כבל + description: מעביר מידע, שהוא יכול להיות צורה, צבע, או בולאיני (0 או 1). כבלים + בצבים שונים לא יתחברו אחד לשני. + second: + name: כבל + description: מעביר מידע, שהוא יכול להיות צורה, צבע, או בולאיני (0 או 1). כבלים + בצבים שונים לא יתחברו אחד לשני. + wire_tunnel: + default: + name: הצלבת כבלים + description: מאפשר שני כבלים לחצות אחד את השני בלי להתחבר. + constant_signal: + default: + name: אות קבוע + description: מיצר אות קבוע, שהוא יכול להיות צורה, צבע, או בולאיני (0 או 1). + lever: + default: + name: מפסק + description: ניתן להפעיל או להפסיק אותו בשביל ליצור אות בולאיני (0 או 1) בשכבת + הכבלים, וכך יכול לשלוט על פעולה של מבנים כמו לדוגמה מסנן. + logic_gate: + default: + name: שער לוגי גם + description: פולט את הבוליאני "1" אם שני הכניסות הם ערך חיובי (ערך חיובי הוא כל + צורה, צבע או הבוליאני "1"). + not: + name: שער לוגי לא + description: פולט את הבוליאני "1" אם הכניסה הוא אינו ערך חיובי (ערך חיובי הוא כל + צורה, צבע או הבוליאני "1"). + xor: + name: שער לוגי קסור + description: Emits a boolean "1" if one of the inputs is truthy, but not both. + (Truthy means shape, color or boolean "1") + or: + name: שער לוגי או + description: פולט את הבוליאני "1" אם אחד מהכניסות הוא ערך חיובי (ערך חיובי הוא + כל צורה, צבע או הבוליאני "1"). + transistor: + default: + name: טרנזיסטור + description: מעביר את האינפורמציה מלמטה ללמעלה אם הכניסה מהצד היא ערך + חיובי (ערך חיובי הוא כל צורה, צבע או הבוליאני "1"). + mirrored: + name: טרנזיסטור + description: מעביר את האינפורמציה מלמטה ללמעלה אם הכניסה מהצד היא ערך + חיובי (ערך חיובי הוא כל צורה, צבע או הבוליאני "1"). + filter: + default: + name: מסנן + description: חבר לאות בשביל לסנן שרק משהו אחד יצא מלמעלה והשאר מהצד. יכול גם + לקבל ערכים בוליאנים (0 או 1). + display: + default: + name: מסך + description: חבר אות בשביל להציג אותו על המסך, האות יכול להיות צורה, צבע או + בוליאני. + 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: זמין רק במצב ארגז חול, מוצא את האות מהכבלים לשכבה הרגילה. + constant_producer: + default: + name: Constant Producer + description: Constantly outputs a specified shape or color. + goal_acceptor: + default: + name: Goal Acceptor + description: Deliver shapes to the goal acceptor to set them as a goal. + block: + default: + name: Block + description: Allows you to block a tile. +storyRewards: + reward_cutter_and_trash: + title: חיתוך צורות + desc: אתה בדיוק קיבלת גישה ל<strong>חותך</strong>, שחותך צורות לחצי אנכית + <strong>לא משנה איזה כיוון הוא</strong>!<br><br>וודא שאתה נפתר + מהשאריות, אחרת <strong>הוא יסתם ויתקע</strong> - בשביל המטרה הזאת + אני הבאתי לך את ה<strong>פח</strong>, שהורס כל מה שאתה מכניס אליו! + reward_rotater: + title: סיבוב + desc: קיבלת גישה ל<strong>מסובב</strong> ! הוא מסובב צורות עם כיוון השעון ב90 + מעלות. + reward_painter: + title: צביעה + desc: "קיבלת גישה ל<strong>צובע</strong> - תשיג צבעים (בדיוק כמו שאתה משיג + צורות) ותחבר אותם עם הצורות בעזרת הצובע בשביל לצבוע + אותם!<br><br>נ.ב: אם אתה עיוור צבעים, יש <strong>מצב עיוור + צבעים</strong> בהגדרות!" + reward_mixer: + title: ערבוב צבעים + desc: קיבלת גישה ל<strong>מערבב</strong> - הוא מערבב שני צבעים לפי <strong>צבעי + האור</strong>! + reward_stacker: + title: חיבור + desc: עכשיו אתה יכול לחבר צורות עם ה<strong>מחבר</strong>! שני הצורות שנכנסות + מתחברות. אם הם יכולים להיות אחד ליד השני הם <strong>יודבקו</strong>, + אחרת, הימני <strong>יודבק מעל</strong> השמאלי! + reward_balancer: + title: מאזן + desc: קיבלת גישה ל<strong>מאזן</strong> הרב שימושי - הוא יכול לשמש בשביל לבנות + מפעלים גדולים יותר ע"י <strong>פיצול וחיבור חפצים</strong> על מספר + מסועים! + reward_tunnel: + title: מנהרה + desc: קיבלת גישה ל<strong>מנהרה</strong> - אתה יכול עכשיו להעביר חפצים מתחת + למסועים ומבנים עם זה! + reward_rotater_ccw: + title: סיבוב נגד כיוון השעון + desc: קיבלת גישה לצורה נוספת של <strong>מסובב</strong> - זה מאפשר לך לסובב צורות + נגד כיוון השעון! בשביל לבנות את זה, תבחר מסובב, ו<strong>תלחץ 'T' + בשיל לחליף בין הסוגים שלו</strong>! + reward_miner_chainable: + title: חוצב מתחבר + desc: "קיבלת גישה ל <strong>חוצב מתחבר</strong>! הוא יכול <strong>להעביר את + החומרים קדימה</strong> לחוצבים האחרים, אז אתה יכול לחצוב חומרים יותר + ביעילות!<br><br> נ.ב: החוצבים הישנים הוחלפו עכשיו ברצועת הכלים שלך!" + reward_underground_belt_tier_2: + title: מהנרות רמה II + desc: קיבלת גישה לצורה נוספת של <strong>מנהרות</strong> - יש לו <strong>יותר + אורך</strong>, ואתה יכול גם לערבב ולהתאים את המנהרות האלו עכשיו! + reward_merger: + title: ממזג קומפקטי + desc: קיבלת גישה ל<strong>ממזג</strong>, צורה של <strong>מאזן</strong> - הוא + יכול למזג שני מסועים למסוע אחד! + reward_splitter: + title: מפצל קומפקטי + desc: קיבלת גישה ל<strong>מפצל</strong>, צורה של <strong>מאזן</strong> - הוא + יכול לפצל מסוע אחד לשני מסועים! + reward_belt_reader: + title: Belt reader + desc: קיבלת גישה ל<strong>מסוע מדידה</strong>! זה מאפשר לך למדוד כמה חפצים + עוברים במסוע.<br><br>וחכה עד שתפתח את הכבלים - אז זה יהיה ממש + שימושי! + reward_cutter_quad: + title: חותך מרובע + desc: קיבלת גישה לצורה נוספת של <strong>חותך</strong> - זה מאפשר לך לחתוך צורה + לארבעה ל<strong>ארבעה חלקים</strong> במקום רק שניים! + reward_painter_double: + title: צובע כפול + desc: קיבלת גישה לצורה נוספת של <strong>צובע</strong> - זה עובד דומה לצובע רגיל, + אבל מאפשר לך לצבוע <strong>שני צורות בבת אחת</strong>, משתמש בצבע + אחד במקום שניים! + reward_storage: + title: אחסון + desc: קיבלת גישה ל<strong>אחסון</strong> - זה מאפשר לך לאחסן חפצים עד כמות + מסויימת!<br><br> הוא מוציא בעדיפות לצד השמאלי, אז אתה יכול להשתמש + בצד הימני כ<strong>שער הצפה</strong>! + reward_blueprints: + title: תבניות + desc: אתה יכול עכשיו <strong>להעתיק ולהדביק</strong> חלקים מהמפעל שלך! תבחר שטח + (החזק CTRL, וגרור עם העכבר), ואז לחץ 'C' בשביל להעתיק את + זה.<br><br>הדבקה זה <strong>לא חינם</strong>, אתה צריך ליצר + <strong>צורת תבנית</strong> בשביל לקנות את זה! (אלו שבדיוק יצרת). + reward_rotater_180: + title: מסובב (180°) + desc: קיבלת גישה <strong>מסובב</strong> 180 מעלות! - זה מאפשר לך לסובב צורה ב + 180 מעולות (הפתעה! :D) + reward_wires_painter_and_levers: + title: כבלים & צובע מרובע + desc: "קיבלת גישה ל<strong>שכבת הכבלים</strong>: זה שכבה נפרדת מעל השכבה הרגילה + שמאפשרת לך הרבה מכניקות חדשות!<br><br> בשביל להתחיל, אני נתתי לך + גישה ל<strong>צובע המרובע</strong> - חבר את הכבלים למקומות שאתה רוצה + לצבוע בשכבת הכבלים!<br><br> בשביל להחליף לשכבת הכבלים, לחץ + <strong>E</strong>. <br><br> נ.ב: <strong>תפעיל רמזים</strong> + בהגדרות כדי להפעיל את המדריך של הכבלים!" + reward_filter: + title: מסנן + desc: קיבלת גישה ל<strong>מסנן</strong>! זה יאפשר לך לסנן חפצים לפי האותות משכבת + הכבלים.<br><br> אתה יכול גם לתת לו אות בולאיני (1 או 0) בשביל להפעיל + או לכבות אותו לגמרי. + reward_display: + title: מסך + desc: "קיבלת גישה ל<strong>מסך</strong> - חבר אות בשכבת הכבלים בשביל להציג + אותו!<br><br> נ.ב: שמת לב שמסועים ואחסון מוציאים את החפץ האחרון שהיה + בהם? נסה להציג את זה על מסך!" + reward_constant_signal: + title: אות קבוע + desc: קיבלת גישה ל<strong>אות קבוע</strong> לשכבת הכבלים! זה שימושי בשביל לחבר + אותו ל<strong>מסננים</strong> לדוגמה.<br><br> האות הקבוע יכול ליצור + <strong>צורה</strong>, <strong>צבע</strong> או + <strong>בוליאני</strong> (1 או 0). + reward_logic_gates: + title: שערים לוגים + desc: "קיבלת גישה ל<strong>שערים לוגים</strong>! אתה לא צריך להתרגש מזה, אבל זה + די מגניב!<br><br> עם שערים לוגים אתה יכול לחשב את הפעולות: וגם, או, + קסור ולא.<br><br> כבונוס אני גם הבאתי לך גם את + ה<strong>טרנזיסטור</strong>!" + reward_virtual_processing: + title: עיבוד וירטואלי + desc: אני הבאתי לך המון מבנים שמאפשרים לך <strong>לדמות עיבוד שלך + צורות</strong>!<br><br> אתה יכול לדמות בשכבת הכבלים חיתוך, סיבוב, + חיבור ועוד! עם זה יש לך עכשיו שלושה אפשרויות להמשיך את + המשחק:<br><br> - לבנות <strong>מכונה אוטומטית</strong> שתייצר כל + צורה אפשרית שההאב מבקש (אני ממליץ לנסות את זה!).<br><br> - לבנות + משהו מגניב עם הכבלים.<br><br> - להמשיך לשחק רגיל.<br><br> עם כל מה + שתבחר, תזכור להנות! + no_reward: + title: שלב הבא + desc: "השלב הזה לא הביא לך פרסים, אבל ההבא יביא! <br><br> נ.ב: עדיף לא להרוס את + המפעלים הקיימים שלך - אתה תצטרך את <strong>כל</strong> הצורות האלו + מאוחר יותר בשביל <strong>לפתוח שדרוגים</strong>!" + no_reward_freeplay: + title: שלב הבא + desc: כל הכבוד! + reward_freeplay: + title: משחק חופשי + desc: You did it! You unlocked the <strong>free-play mode</strong>! This means + that shapes are now <strong>randomly</strong> generated!<br><br> + Since the hub will require a <strong>throughput</strong> from now + on, I highly recommend to build a machine which automatically + delivers the requested shape!<br><br> 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. + reward_demo_end: + title: סוף הדמו + desc: הגעת לסוף גרסת הדמו +settings: + title: הגדרות + categories: + general: כללי + userInterface: תפריטים + advanced: מתקדם + performance: ביצועים + versionBadges: + dev: Development + staging: Staging + prod: Production + buildDate: Built <at-date> + rangeSliderPercentage: <amount> % + labels: + uiScale: + title: גודל תפריטים + description: שנה את הגודל של התפריטים + scales: + super_small: זעיר + small: קטן + regular: רגיל + large: גדול + huge: עצום + autosaveInterval: + title: זמן בין שמירות אוטומטיות + description: .משפיע על תדירות השמירה האוטומטית. אתה יכול גם לבטל את זה לגמרי + intervals: + one_minute: דקה + two_minutes: שתי דקות + five_minutes: חמש דקות + ten_minutes: עשר דקות + twenty_minutes: עשרים דקות + disabled: אף פעם + scrollWheelSensitivity: + title: רגישות זום + description: משפיע על כמה רגיש הזום (גם הגלגלת וגם משטח הנגיעה) + sensitivity: + super_slow: ממש לאט + slow: לאט + regular: רגיל + fast: מהר + super_fast: ממש מהר + movementSpeed: + title: מהירות תנועה + description: משנה כמה מהר התצוגה זזה כשמשתמשים במקלדת או שמים את העכבר בגבולות + המסך. + speeds: + super_slow: ממש לאט + slow: לאט + regular: רגיל + fast: מהר + super_fast: קצת מהר + extremely_fast: ממש מהר + language: + title: שפה + description: '!שנה את השפה. כל השפות נוצרו ע"י משתמשים ולכן אולי לא יהיו מלאים' + enableColorBlindHelper: + title: מצב עיוורי צבעים + description: .הפעלת כלים שונים שמאפשרים לך לשחק אם אתה עיוור צבעים + fullscreen: + title: מסך מלא + description: .זה מומלץ לשחק את המשחק במסך מלא השהיל החוויה המיטבית. זמין רק + בגרסה להורדה + soundsMuted: + title: השתקת צלילים + description: .אם מופעל, משתיק את כל הצלילים + musicMuted: + title: השתקת מוזיקה + description: .אם מופעל, משתיק את המוזיקה + soundVolume: + title: ווליום + description: .משפיע על הווליום של הצלילים + musicVolume: + title: מוזיקה + description: .משפיע על הווליום של המוזיקה + theme: + title: תצוגה + description: .שנה תצוגה לכהה או בהירה + themes: + dark: כהה + light: בהיר + refreshRate: + title: Tick Rate + description: .ים יהיו בכל שנייהtick זה משפיע כמה + alwaysMultiplace: + title: השמה מרובה + description: .באופן קבוע SHIFT אם מופעל, כל המבנים ישארו נבחרים גם אחרי ששמים + אותם עד שאתה מבטל את זה. זה זהה ללחיצת + offerHints: + title: רמזים ומדריכים + description: .האם להציג רמזים ומדריכים. בנוסף מסתיר חלק מהתפריטים עד לשלב מסויים + בשביל להקל על הכניסה למשחק + enableTunnelSmartplace: + title: מנהרות חכמות + description: .מתי שמופעל, מקום מנהרות ימחק באופן אוטומטי מסועים לא שימושיים. זה + גם ימחק מנהרות מיותרות + vignette: + title: הצללה + description: .מפעיל הצללה, שמכהה את הקצבות של המסך ועושה שיהיה יותר קל לקרוא את + הטקסט + rotationByBuilding: + title: סיבוב ע"י סוג מבנה + description: .כל מבנה זוכר את הכיוון ששמת אותו פעם אחרונה. זה יכול להיות נוח אם + אתה מחליף בין מבנים שונים הרבה פעמים + compactBuildingInfo: + title: אינפורמצית מבנים קומפקטית + description: .מקצר את קופסת האינפורמציה של המבנים שיציג רק את המהירות שלו. אחרת + הסבר ותמונה יופיעו + disableCutDeleteWarnings: + title: ביטול אזהרה בעת חיתוך ומחיקה + description: .ביטול ההזהרה שמופיעה מתי שחותכים/מוחקים יותר מ1000 מבנים + lowQualityMapResources: + title: איכות ירודה של מאגרים + description: .מפשט את הרנדור של המשאבים על המפה מתי שמסתכלים מקרוב בשביל לשפר את + הביצועים !זה אפילו נראה נקי יותר, אז תנסה את זה + disableTileGrid: + title: הסתר את רשת + description: "!הסתרת הרשת יכולה לשפר את הביצועים. זה גם יכול לגרום למשחק להראות + נקי יותר" + clearCursorOnDeleteWhilePlacing: + title: נקה את הסמן בלחיצה על מקש ימני + description: .מופעל בברירת המחדל, מנקה את הסמן מתי שאתה לוחץ מקש ימני. אם זה + כבוי אתה יכול למחוק מבנים עם מקש ימני מתי שאתה מחזיק מבנה + lowQualityTextures: + title: טקסטורה באכות ירודה (מכוער) + description: "!משתמש בטקסטורה באיכות ירודה בשביל לשפר את הביצועים. זה גורם למשחק + להראות ממש מכוער" + displayChunkBorders: + title: מציג גבולות צ'נקים + description: .המשחק מחולק לצ'נקים של 16 על 16 משבצות, אם ההגדרה הזאת מופעלת, + הגבולות של הצ'נקים יופיעו + pickMinerOnPatch: + title: בחר חוצב על מאגר + description: .מופעל בברירת המחדל, בוחר את החוצב אם אתה דוגם כשהעכבר מעל מאגר + simplifiedBelts: + title: מסועים פשוטים (מכוער) + description: .לא מציג חומרים על מסועים חוץ ממתי שמעבירים את הסמן מעליהם בשביל + לשפר ביצועים. אני לא ממליץ לשחק עם ההגדרה הזאת ממש צריך את + הביצועים האלו + enableMousePan: + title: תנועה בנגיעה בקצה המסך + description: .מאפשר לזוז ע"י הצמדת העכבר לקצה המסך. המהירות נקבעת ע"י המהירות של + התנועה עם המקשים + zoomToCursor: + title: הגדלה לכיוון הסמן + description: .אם מופעל, הזום יהיה לכיוון הסמן של העכבר, אחרת למרכז המסך + mapResourcesScale: + title: גודל המשאבים במפה + description: .שולט על הגודל של הצורות על המפה (בתצוגה המוקטנת) +keybindings: + title: מקשים + hint: ".הם מאפשרים הרבה אפשרויות השמה !ALTו SHIFT ,CTRLטיפ: השתמש ב" + resetKeybindings: אפס + categoryLabels: + general: תוכנה + ingame: משחק + navigation: תנועה + placement: השמה + massSelect: בחירה + buildings: קיצורים למבנים + placementModifiers: תוספי השמה + mappings: + confirm: אישור + back: חזרה + mapMoveUp: לזוז למעלה + mapMoveRight: לזוז ימינה + mapMoveDown: לזוז למטה + mapMoveLeft: לזוז שמאלה + mapMoveFaster: לזוז מהר + centerMap: לזוז למרכז + mapZoomIn: הגדלת תצוגה + mapZoomOut: הקטנת תצוגה + createMarker: צור סימון + menuOpenShop: שדרוגים + menuOpenStats: סטטיסטיקה + menuClose: סגירת תפריט + toggleHud: הצג/הסתר תפריטים + toggleFPSInfo: ואיפורמציה לפתרון באגים FPS הצג/הסתר + switchLayers: החלף שכבות + exportScreenshot: צור צילום מסך של כל המפעל + belt: מסוע + balancer: מאזן + underground_belt: מנהרה + miner: חוצב + cutter: חותך + rotater: מסובב + stacker: מחבר + mixer: מערבב + painter: צובע + trash: פח + storage: אחסון + wire: כבל + constant_signal: אות קבוע + logic_gate: שער לוגי + lever: מפסק + filter: ממיין + wire_tunnel: הצלבת כבלים + display: מסך + reader: מסוע מדידה + virtual_processor: מעבד וירטואלי + transistor: טרנזיסטור + analyzer: מנתח צורות + comparator: משווה + item_producer: מייצר חפצים (ארגז חול) + pipette: דגימה + rotateWhilePlacing: סיבוב + rotateInverseModifier: "Modifier: סיבוב נגד כיוון השעון במקום" + rotateToUp: "לסובב: למעלה" + rotateToDown: "לסובב: למטה" + rotateToRight: "לסובב: ימינה" + rotateToLeft: "לסובב: שמאלה" + cycleBuildingVariants: החלפת סוגים של מבנה + confirmMassDelete: מחיקת שטח + pasteLastBlueprint: הדבקת התבנית האחרונה + cycleBuildings: החלפת מבנה + lockBeltDirection: הפעלת בנייה מהירה של מסועים + switchDirectionLockSide: "בנייה מהירה של מסועים: החלפת צד" + copyWireValue: "כבלים: העתקת ערך מתחת לעכבר" + massSelectStart: החזק וגרור כדי להתחיל + massSelectSelectMultiple: בחר כמה איזורים + massSelectCopy: העתקת איזור + massSelectCut: חיתוך איזור + placementDisableAutoOrientation: ביטול כיוון אוטומטי + placeMultiple: השאר במצב השמה + placeInverse: הפוך כיוון מסוע + constant_producer: Constant Producer + goal_acceptor: Goal Acceptor + block: Block + massSelectClear: Clear belts +about: + title: בנוגע למשחק הזה + body: >- + .(זה אני) <a href="https://github.com/tobspr" target="_blank">Tobias + Springer</a> המשחק הזה הוא עם קוד פתוח ויוצר ע"י <br><br> + + .<a href="<githublink>" target="_blank">shapez.io on GitHub</a>-אם אתה רוצה לתרום, כנס ל<br><br> + + המשחק הזה לא היה אפשרי בלי קהילת הדיסקורד המופלאה מסביב למשחקים שלי - באמת כדאי לך להצטרף ל<a href="<discordlink>" target="_blank">שרת הדיסקורד</a>!<br><br> + + .הוא מדהים - <a href="https://soundcloud.com/pettersumelius" target="_blank">Peppsen</a> הסאונד נוצר ע"י<br><br> + + .המשחק הזה לא היה קיים Factorio אם לא היינו משחקים - <a href="https://github.com/niklas-dahl" target="_blank">Niklas</a> לבסוף, תודה ענקית לחבר הכי טוב שלי +changelog: + title: עדכונים +demo: + features: + restoringGames: שחזור משחקים שמורים + importingGames: יבוא משחקים שמורים + oneGameLimit: מוגבל לשמירה אחת + customizeKeybindings: מקשים בהתאמה אישית + exportingBase: יצוא כל המפעל כתמונה + settingNotAvailable: .הגדרה לא זמינה בדמו +tips: + - "!ההאב יקבל כל צורה, לא רק את הצורה הנדרשת עכשיו" + - "!וודא שהמפעלים שלך מחולקים - זה יהיה לך יותר קל" + - "!אל תבנה יותר מדיי קרוב להאב, זה יהפוך לבלגן" + - .אם החיבור לא עובד, תנסה להחליף את מה נכנס לאיזה צד + - .<b>R</b> אתה יכול לשנות את הכיוון של בניית המסועים המהירה ע"י ללחוץ על + - .תאפשר לבנות הרבה מסילות באותו כיוון <b>CTRL</b> החזקת + - .היחס של המהירויות של המבנים ישאר זהה אם השדרוגים הם באותו רמה + - .לשים חפצים במסוע אחת מאשר כמה יהיה יותר יעיל במקום + - "!אתה תשיג עוד צורות של מבנים בהמשך המשחק" + - .בשביל להחליף בין צורות שונות של מבנים <b>T</b>אתה יכול להשתמש ב + - "!סימטריה זה המפתח" + - .אתה יכול לשים סוגים שונים של מנהרות על אותו קו לסרוגין + - "!נסה לבנות מפעלים קומפקטים, זה ישתלם" + - <b>T</b> לצובע יש סוג הפוך שאתה יכול לבחור ע"י + - .אם יש לך את היחס הנכון בין כמות המבנים, יהיה לך את האיכות המיטבית + - .ברמה המקסימלית, 5 חוצבים ימלאו מסוע אחד + - "!אל תשכח בקשר למנהרות" + - .אתה לא צריך לחלק חפצים באופן שווה בשביל יעילות המיטבית + - .תפעיל את מצב בנייה מהירה של מסועים, שתתן לך לשים מסועים ארוכים בקלות + <b>SHIFT</b> החזקת + - .חותכים תמיד חותכים לגובה, לא משנה מה הכיוון שלהם + - .ערבב את כל שלושת הצבעים הבסיסיים בשביל להשיג את הצבע לבן + - .לאכסון יש העדפה להוציא רק מהצד השמאלי + - "!הכן לך עיצוב של מבנה שתוכל להשתמש בו כשתצטרך - זה שווה את זה" + - .תתן לך לבנות כמה מבנים באותו זמן <b>SHIFT</b> החזקת + - .בשביל להפוך את הכיוון של המסועים שאתה שם <b>ALT</b> אתה יכול להחזיק + - "!יעילות זה המפתח" + - .מאגרי צורות שיותר רחוקות מההאב הן יותר מסובכות + - .למכונות יש מהירות מוגבלת, חלק את החומרים בשביל היעילות המיטבית + - .התשמש במאזנים בשביל למקסם את היעילות + - .כיוון זה לא חשוב. נסה לא לחצות מסועים יותא מידי + - "!תכנן מראש, אחרת יהיה לך בלאגן" + - .אל תמחק את המפעלים הישנים שלך! אתה תצתרך אותם בשביל לפתוח שדרוגים + - "!נסה לפתור את השלבים 20 ו26 בעצמך לפני שאתה מחפש פתרונות" + - .אל תסבך דברים, נסה להשאר פשוט ותגיע רחוק + - .אתה כנראה תצטרך להשתמש במפעלים שלך שוב בהמשך המשחק. תבנה את המפעלים שלך + בצורה שתוכל להשתמש בהם שוב בהמשך + - .לפעמים אתה יכול למצוא צורה שאתה צריך במפה בלי להתחיל לבנות אותה עם חותכים + ומחברים + - .צורת תחנת רוח שלמה לעולם לא תופיע בטבעיות במפה + - .צבע את הצורות לפני שאתה חותך אותם בשביל האיכות המיטבית + - .אם תפצל את המפעל שלך, לא יהיה לך חסר מקום + - .הכן תבניות נפרדות למפעל שלך. הם יהיו חשובים בשביל החלקים של המפעל שלך + - .הסתכל על המערבב צבעים מקרוב יותר והשאלות שלך יפתרו + - .גרירה בשביל לבחור שטח + <b>CTRL</b>השתמש ב + - .בנייה קרובה מידי להאב יכולה להפריע בדרך של פרוייקטים מאוחרים יותר + - .סימן הסיכה ליד כל צורה בשדרוגים תצמיד אותה לצד שמאל של המסך + - .ערבב את כל שלושת הצבעים הבסיסיים בשביל להשיג את הצבע לבן + - "!יש לך מפה אין סופית. אל תשים את המפעל שלך רק צמוד להאב, תרחיב אותו" + - .זה המשחק האהוב עליי !Factorio נסה גם את את + - .החותך המרובע חותך עם כיוון השעון, מתחיל מלמעלה ימין. + - "!אתה יכול להוריד את השמירות שלך בתפריט הראשי!" + - .למשחק הזה יש הרבה מקשים שימושיים! חפש אותם בהגדרות. + - "!למשחק הזה יש הרבה הגדרות, חפש אותם" + - "!לסימון של ההאב שלך יש חץ שמסמן באיזה כיוון הוא" + - .בשביל לנקות מסוע, חתוך את האיזור ואז תדביק באותו מקום + - .לחץ F4 בשביל להציג את הFPS ואת הTickRate + - .לחץ F4 פעמיים בשביל להציג את המשבצת שהעכבר והמצלמה בהם + - .אתה יכול ללחוץ על צורה מוצמדת בצד שמאל בשביל לבטל את ההצמדה +puzzleMenu: + play: Play + edit: Edit + title: Puzzle Mode + createPuzzle: Create Puzzle + loadPuzzle: Load + reviewPuzzle: Review & Publish + validatingPuzzle: Validating Puzzle + submittingPuzzle: Submitting Puzzle + noPuzzles: There are currently no puzzles in this section. + categories: + levels: Levels + new: New + top-rated: Top Rated + mine: My Puzzles + easy: Easy + hard: Hard + completed: Completed + medium: Medium + official: Official + trending: Trending today + trending-weekly: Trending weekly + categories: Categories + difficulties: By Difficulty + account: My Puzzles + search: Search + validation: + title: Invalid Puzzle + noProducers: Please place a Constant Producer! + noGoalAcceptors: Please place a Goal Acceptor! + goalAcceptorNoItem: One or more Goal Acceptors have not yet assigned an item. + Deliver a shape to them to set a goal. + goalAcceptorRateNotMet: One or more Goal Acceptors are not getting enough items. + Make sure that the indicators are green for all acceptors. + buildingOutOfBounds: One or more buildings are outside of the buildable area. + Either increase the area or remove them. + autoComplete: Your puzzle autocompletes itself! Please make sure your constant + producers are not directly delivering to your goal acceptors. + difficulties: + easy: Easy + medium: Medium + hard: Hard + dlcHint: Purchased the DLC already? Make sure it is activated by right clicking + shapez.io in your library, selecting Properties > DLCs. +backendErrors: + ratelimit: You are performing your actions too frequent. Please wait a bit. + invalid-api-key: Failed to communicate with the backend, please try to + update/restart the game (Invalid Api Key). + unauthorized: Failed to communicate with the backend, please try to + update/restart the game (Unauthorized). + bad-token: Failed to communicate with the backend, please try to update/restart + the game (Bad Token). + bad-id: Invalid puzzle identifier. + not-found: The given puzzle could not be found. + bad-category: The given category could not be found. + bad-short-key: The given short key is invalid. + profane-title: Your puzzle title contains profane words. + bad-title-too-many-spaces: Your puzzle title is too short. + bad-shape-key-in-emitter: A constant producer has an invalid item. + bad-shape-key-in-goal: A goal acceptor has an invalid item. + no-emitters: Your puzzle does not contain any constant producers. + no-goals: Your puzzle does not contain any goal acceptors. + short-key-already-taken: This short key is already taken, please use another one. + can-not-report-your-own-puzzle: You can not report your own puzzle. + bad-payload: The request contains invalid data. + bad-building-placement: Your puzzle contains invalid placed buildings. + timeout: The request timed out. + too-many-likes-already: The puzzle alreay got too many likes. If you still want + to remove it, please contact support@shapez.io! + no-permission: You do not have the permission to perform this action. diff --git a/translations/base-hr.yaml b/translations/base-hr.yaml index 38cdd6ee..6d58d9f2 100644 --- a/translations/base-hr.yaml +++ b/translations/base-hr.yaml @@ -52,6 +52,7 @@ global: escape: ESC shift: SHIFT space: SPACE + loggingIn: Logging in demoBanners: title: Demo Verzija intro: Nabavi samostalnu igru kako bi otključao sve značajke! @@ -71,6 +72,12 @@ mainMenu: savegameLevel: Nivo <x> savegameLevelUnknown: Nepoznati Nivo savegameUnnamed: Unnamed + puzzleMode: Puzzle Mode + back: Back + puzzleDlcText: Do you enjoy compacting and optimizing factories? Get the Puzzle + DLC now on Steam for even more fun! + puzzleDlcWishlist: Wishlist now! + puzzleDlcViewNow: View Dlc dialogs: buttons: ok: OK @@ -84,6 +91,9 @@ dialogs: viewUpdate: Pogledaj ažuriranje showUpgrades: Pokaži Nadogradnje showKeybindings: Pokaži tipke + retry: Retry + continue: Continue + playOffline: Play Offline importSavegameError: title: Greška prilikom uvoza text: "Neuspješan uvoz spremljene igre:" @@ -181,6 +191,70 @@ dialogs: title: Tutorial Available desc: There is a tutorial video available for this level, but it is only available in English. Would you like to watch it? + editConstantProducer: + title: Set Item + puzzleLoadFailed: + title: Puzzles failed to load + desc: "Unfortunately the puzzles could not be loaded:" + submitPuzzle: + title: Submit Puzzle + descName: "Give your puzzle a name:" + descIcon: "Please enter a unique short key, which will be shown as the icon of + your puzzle (You can generate them <link>here</link>, or choose one + of the randomly suggested shapes below):" + placeholderName: Puzzle Title + puzzleResizeBadBuildings: + title: Resize not possible + desc: You can't make the zone any smaller, because then some buildings would be + outside the zone. + puzzleLoadError: + title: Bad Puzzle + desc: "The puzzle failed to load:" + offlineMode: + title: Offline Mode + desc: We couldn't reach the servers, so the game has to run in offline mode. + Please make sure you have an active internet connection. + puzzleDownloadError: + title: Download Error + desc: "Failed to download the puzzle:" + puzzleSubmitError: + title: Submission Error + desc: "Failed to submit your puzzle:" + puzzleSubmitOk: + title: Puzzle Published + desc: Congratulations! Your puzzle has been published and can now be played by + others. You can now find it in the "My puzzles" section. + puzzleCreateOffline: + title: Offline Mode + desc: Since you are offline, you will not be able to save and/or publish your + puzzle. Would you still like to continue? + puzzlePlayRegularRecommendation: + title: Recommendation + desc: I <strong>strongly</strong> recommend playing the normal game to level 12 + before attempting the puzzle DLC, otherwise you may encounter + mechanics not yet introduced. Do you still want to continue? + puzzleShare: + title: Short Key Copied + desc: The short key of the puzzle (<key>) has been copied to your clipboard! It + can be entered in the puzzle menu to access the puzzle. + puzzleReport: + title: Report Puzzle + options: + profane: Profane + unsolvable: Not solvable + trolling: Trolling + puzzleReportComplete: + title: Thank you for your feedback! + desc: The puzzle has been flagged. + puzzleReportError: + title: Failed to report + desc: "Your report could not get processed:" + puzzleLoadShortKey: + title: Enter short key + desc: Enter the short key of the puzzle to load it. + puzzleDelete: + title: Delete Puzzle? + desc: Are you sure you want to delete '<title>'? This can not be undone! ingame: keybindingsOverlay: moveMap: Kretanje @@ -202,6 +276,7 @@ ingame: clearSelection: Očisti odabir pipette: Pipeta switchLayers: Promijeni sloj + clearBelts: Clear belts colors: red: Crvena green: Zelena @@ -349,6 +424,46 @@ ingame: achievements: title: Achievements desc: Hunt them all! + puzzleEditorSettings: + zoneTitle: Zone + zoneWidth: Width + zoneHeight: Height + trimZone: Trim + clearItems: Clear Items + share: Share + report: Report + clearBuildings: Clear Buildings + resetPuzzle: Reset Puzzle + puzzleEditorControls: + title: Puzzle Creator + instructions: + - 1. Place <strong>Constant Producers</strong> to provide shapes and + colors to the player + - 2. Build one or more shapes you want the player to build later and + deliver it to one or more <strong>Goal Acceptors</strong> + - 3. Once a Goal Acceptor receives a shape for a certain amount of + time, it <strong>saves it as a goal</strong> that the player must + produce later (Indicated by the <strong>green badge</strong>). + - 4. Click the <strong>lock button</strong> on a building to disable + it. + - 5. Once you click review, your puzzle will be validated and you + can publish it. + - 6. Upon release, <strong>all buildings will be removed</strong> + except for the Producers and Goal Acceptors - That's the part that + the player is supposed to figure out for themselves, after all :) + puzzleCompletion: + title: Puzzle Completed! + titleLike: "Click the heart if you liked the puzzle:" + titleRating: How difficult did you find the puzzle? + titleRatingDesc: Your rating will help me to make you better suggestions in the future + continueBtn: Keep Playing + menuBtn: Menu + puzzleMetadata: + author: Author + shortKey: Short Key + rating: Difficulty score + averageDuration: Avg. Duration + completionRate: Completion rate shopUpgrades: belt: name: Trake, Distributer i Tuneli @@ -553,6 +668,18 @@ buildings: name: Item Producer description: Available in sandbox mode only, outputs the given signal from the wires layer on the regular layer. + constant_producer: + default: + name: Constant Producer + description: Constantly outputs a specified shape or color. + goal_acceptor: + default: + name: Goal Acceptor + description: Deliver shapes to the goal acceptor to set them as a goal. + block: + default: + name: Block + description: Allows you to block a tile. storyRewards: reward_cutter_and_trash: title: Rezanje Oblika @@ -949,6 +1076,10 @@ keybindings: rotateToDown: "Rotate: Point Down" rotateToRight: "Rotate: Point Right" rotateToLeft: "Rotate: Point Left" + constant_producer: Constant Producer + goal_acceptor: Goal Acceptor + block: Block + massSelectClear: Clear belts about: title: O Igri body: >- @@ -1034,3 +1165,73 @@ tips: - 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. +puzzleMenu: + play: Play + edit: Edit + title: Puzzle Mode + createPuzzle: Create Puzzle + loadPuzzle: Load + reviewPuzzle: Review & Publish + validatingPuzzle: Validating Puzzle + submittingPuzzle: Submitting Puzzle + noPuzzles: There are currently no puzzles in this section. + categories: + levels: Levels + new: New + top-rated: Top Rated + mine: My Puzzles + easy: Easy + hard: Hard + completed: Completed + medium: Medium + official: Official + trending: Trending today + trending-weekly: Trending weekly + categories: Categories + difficulties: By Difficulty + account: My Puzzles + search: Search + validation: + title: Invalid Puzzle + noProducers: Please place a Constant Producer! + noGoalAcceptors: Please place a Goal Acceptor! + goalAcceptorNoItem: One or more Goal Acceptors have not yet assigned an item. + Deliver a shape to them to set a goal. + goalAcceptorRateNotMet: One or more Goal Acceptors are not getting enough items. + Make sure that the indicators are green for all acceptors. + buildingOutOfBounds: One or more buildings are outside of the buildable area. + Either increase the area or remove them. + autoComplete: Your puzzle autocompletes itself! Please make sure your constant + producers are not directly delivering to your goal acceptors. + difficulties: + easy: Easy + medium: Medium + hard: Hard + dlcHint: Purchased the DLC already? Make sure it is activated by right clicking + shapez.io in your library, selecting Properties > DLCs. +backendErrors: + ratelimit: You are performing your actions too frequent. Please wait a bit. + invalid-api-key: Failed to communicate with the backend, please try to + update/restart the game (Invalid Api Key). + unauthorized: Failed to communicate with the backend, please try to + update/restart the game (Unauthorized). + bad-token: Failed to communicate with the backend, please try to update/restart + the game (Bad Token). + bad-id: Invalid puzzle identifier. + not-found: The given puzzle could not be found. + bad-category: The given category could not be found. + bad-short-key: The given short key is invalid. + profane-title: Your puzzle title contains profane words. + bad-title-too-many-spaces: Your puzzle title is too short. + bad-shape-key-in-emitter: A constant producer has an invalid item. + bad-shape-key-in-goal: A goal acceptor has an invalid item. + no-emitters: Your puzzle does not contain any constant producers. + no-goals: Your puzzle does not contain any goal acceptors. + short-key-already-taken: This short key is already taken, please use another one. + can-not-report-your-own-puzzle: You can not report your own puzzle. + bad-payload: The request contains invalid data. + bad-building-placement: Your puzzle contains invalid placed buildings. + timeout: The request timed out. + too-many-likes-already: The puzzle alreay got too many likes. If you still want + to remove it, please contact support@shapez.io! + no-permission: You do not have the permission to perform this action. diff --git a/translations/base-hu.yaml b/translations/base-hu.yaml index b4081104..b20a52fa 100644 --- a/translations/base-hu.yaml +++ b/translations/base-hu.yaml @@ -11,14 +11,14 @@ steamPage: És ha ez nem lenne elég, exponenciálisan többet kell termelned az igények kielégítése érdekében - az egyetlen dolog, ami segít, az a termelés mennyisége! Az alakzatokat a játék elején csak feldolgoznod kell, később azonban színezned is kell őket - ehhez bányászni és keverni kell a színeket! A játék Steamen történő megvásárlása hozzáférést biztosít a teljes verzióhoz, de kipróbálhatod a játékot a shapez.io oldalon, és később dönthetsz! - what_others_say: What people say about shapez.io - nothernlion_comment: This game is great - I'm having a wonderful time playing, - and time has flown by. - notch_comment: Oh crap. I really should sleep, but I think I just figured out - how to make a computer in shapez.io - steam_review_comment: This game has stolen my life and I don't want it back. - Very chill factory game that won't let me stop making my lines more - efficient. + what_others_say: Mit mondanak mások a shapez.io-ról + nothernlion_comment: Ez a játék nagyszerű - Csodás élmény vele játszani, az idő + meg csak repül. + notch_comment: Basszus... aludnom kéne, de épp most jöttem rá, hogyan tudok + számítógépet építeni a shapez.io-ban! + steam_review_comment: Ez a játék ellopta az életemet, de nem kérem vissza! + Nagyon nyugis gyárépítős játék, amiben nem győzöm a futószalagjaimat + optimalizálni. global: loading: Betöltés error: Hiba @@ -50,6 +50,7 @@ global: escape: ESC shift: SHIFT space: SZÓKÖZ + loggingIn: Bejelentkezés demoBanners: title: Demó verzió intro: Vásárold meg az Önálló Verziót a teljes játékélményért! @@ -69,6 +70,12 @@ mainMenu: savegameLevel: <x>. szint savegameLevelUnknown: Ismeretlen szint savegameUnnamed: Névtelen + puzzleMode: Fejtörő Mód + back: Vissza + puzzleDlcText: Szereted optimalizálni a gyáraid méretét és hatákonyságát? + Szerezd meg a Puzzle DLC-t a Steamen most! + puzzleDlcWishlist: Kívánságlistára vele! + puzzleDlcViewNow: View Dlc dialogs: buttons: ok: OK @@ -82,6 +89,9 @@ dialogs: viewUpdate: Frissítés Megtekintése showUpgrades: Fejlesztések showKeybindings: Irányítás + retry: Újra + continue: Folytatás + playOffline: Offline Játék importSavegameError: title: Importálás Hiba text: "Nem sikerült importálni a mentésedet:" @@ -182,6 +192,71 @@ dialogs: title: Oktatás Elérhető desc: Elérhető egy oktatóvideó ehhez a szinthez, de csak angol nyelven. Szeretnéd megnézni? + editConstantProducer: + title: Elem beállítása + puzzleLoadFailed: + title: Fejtörő betöltése sikertelen + desc: "Sajnos a fejtörőt nem sikerült betölteni:" + submitPuzzle: + title: Fejtörő Beküldése + descName: "Adj nevet a fejtörődnek:" + descIcon: "Írj be egy egyedi gyorskódot, ami a fejtörőd ikonja lesz + (<link>itt</link> tudod legenerálni, vagy válassz egyet az alábbi + random generált alakzatok közül):" + placeholderName: Fejtörő Neve + puzzleResizeBadBuildings: + title: Átméretezés nem lehetséges + desc: Nem tudod tovább csökkenteni a zóna méretét, mert bizonyos épületek + kilógnának a zónából. + puzzleLoadError: + title: Hibás Fejtörő + desc: "A fejtörőt nem sikerült betölteni:" + offlineMode: + title: Offline Mód + desc: Nem tudjuk elérni a szervereket, így a játék Offline módban fut. Kérlek + győződj meg róla, hogy megfelelő az internetkapcsolatod. + puzzleDownloadError: + title: Letöltési Hiba + desc: "Nem sikerült letölteni a fejtörőt:" + puzzleSubmitError: + title: Beküldési Hiba + desc: "Nem sikerült beküldeni a fejtörőt:" + puzzleSubmitOk: + title: Fejtörő Közzétéve + desc: Gratulálunk! A fejtörődet közzétettük, így mások által is játszhatóvá + vált. A fejtörőidet a "Fejtörőim" ablakban találod. + puzzleCreateOffline: + title: Offline Mód + desc: Offline módban nem lehet elmenteni és közzétenni a fejtörődet. Szeretnéd + így is folytatni? + puzzlePlayRegularRecommendation: + title: Javaslat + desc: A Puzzle DLC előtt <strong>erősen</strong> ajánlott az alapjátékot + legalább a 12-dik Szintig kijátszani. Ellenekző esetben olyan + mechanikákkal találkozhatsz, amelyeket még nem ismersz. Szeretnéd + így is folytatni? + puzzleShare: + title: Gyorskód Másolva a Vágólapra + desc: A fejtörő gyorskódját (<key>) kimásoltad a vágólapra! A Fejtörők menüben + beillesztve betöltheted vele a fejtörőt. + puzzleReport: + title: Fejtörő Jelentése + options: + profane: Durva + unsolvable: Nem megoldható + trolling: Trollkodás + puzzleReportComplete: + title: Köszönjük a visszajelzésedet! + desc: A fejtörőt sikeresen jelentetted. + puzzleReportError: + title: Nem sikerült jelenteni + desc: "A jelentésedet nem tudtuk feldolgozni:" + puzzleLoadShortKey: + title: Gyorskód Beillesztése + desc: Illeszd be a gyorskódot, hogy betöltsd a Fejtörőt. + puzzleDelete: + title: Delete Puzzle? + desc: Are you sure you want to delete '<title>'? This can not be undone! ingame: keybindingsOverlay: moveMap: Mozgatás @@ -203,6 +278,7 @@ ingame: clearSelection: Kijelölés megszüntetése pipette: Pipetta switchLayers: Réteg váltás + clearBelts: Futószalagok Kiürítése colors: red: Piros green: Zöld @@ -350,8 +426,50 @@ ingame: title: Támogass desc: A játékot továbbfejlesztem szabadidőmben achievements: - title: Achievements - desc: Hunt them all! + title: Steam Achievementek + desc: Szerezd meg mindet! + puzzleEditorSettings: + zoneTitle: Zóna + zoneWidth: Szélesség + zoneHeight: Magasság + trimZone: Üres szegélyek levásága + clearItems: Elemek eltávolítása + share: Megosztás + report: Jelentés + clearBuildings: Clear Buildings + resetPuzzle: Reset Puzzle + puzzleEditorControls: + title: Fejtörő Készítő + instructions: + - 1. Helyezz le <strong>Termelőket</strong>, amelyek alakzatokat és + színeket generálnak a játékosoknak. + - 2. Készíts el egy vagy több alakzatot, amit szeretnél, hogy a + játékos később legyártson, és szállítsd el egy vagy több + <strong>Elfogadóba</strong>. + - 3. Amint az Elfogadóba folyamatosan érkeznek az alakzatok, + <strong>elmenti, mint célt</strong>, amit a játékosnak később + teljesítenie kell (Ezt a <strong>zöld jelölő</strong> mutatja). + - 4. Kattints a <strong>Lezárás gombra</strong> egy épületen, hogy + felfüggeszd azt. + - 5. A fejtörőd beküldésekor átnézzük azt, majd lehetőséged lesz + közzétenni. + - 6. A fejtörő kiadásakor, <strong>minden épület törlődik</strong>, + kivéve a Termelők és az Elfogadók - a többit ugyebár a játékosnak + kell majd kitalálnia :) + puzzleCompletion: + title: Fejtörő Teljesítve! + titleLike: "Kattins a ♥ gombra, ha tetszett a fejtörő:" + titleRating: Mennyire találtad nehéznek a fejtörőt? + titleRatingDesc: Az értékelésed lehetővé teszi, hogy okosabb javaslatokat kapj a + jövőben + continueBtn: Játék Folytatása + menuBtn: Menü + puzzleMetadata: + author: Szerző + shortKey: Gyorskód + rating: Nehézség + averageDuration: Átlagos Időtartam + completionRate: Teljesítési Arány shopUpgrades: belt: name: Futószalagok, Elosztók & Alagutak @@ -561,6 +679,19 @@ buildings: name: Létrehozó description: Csak Homokozó módban elérhető. Létrehozza a Vezeték rétegen beállított jelet a normál rétegen. + constant_producer: + default: + name: Termelő + description: Folyamatosan termeli a beállított alakzatot vagy színt. + goal_acceptor: + default: + name: Elfogadó + description: Szállíts alakzatoakt az Elfogadóba, hogy beállítsd egy Fejtörő + céljaként. + block: + default: + name: Blokkolás + description: Lehetővé teszi, hogy leblokkolj egy csempét. storyRewards: reward_cutter_and_trash: title: Alakzatok Vágása @@ -968,10 +1099,14 @@ keybindings: placementDisableAutoOrientation: Automatikus irány kikapcsolása placeMultiple: Több lehelyezése placeInverse: Futószalag irányának megfordítása - rotateToUp: "Rotate: Point Up" - rotateToDown: "Rotate: Point Down" - rotateToRight: "Rotate: Point Right" - rotateToLeft: "Rotate: Point Left" + rotateToUp: "Forgatás: Felfelé" + rotateToDown: "Forgatás: Lefelé" + rotateToRight: "Forgatás: Jobbra" + rotateToLeft: "Forgatás: Balra" + constant_producer: Termelő + goal_acceptor: Elfogadó + block: Blokkolás + massSelectClear: Futószalagok Kiürítése about: title: A Játékról body: >- @@ -1007,7 +1142,7 @@ tips: - Serial execution is more efficient than parallel. - A szimmetria kulcsfontosságú! - You can use <b>T</b> to switch between different variants. - - Symmetry is key! + - A szimmetria kulcsfontosságú! - Az épületek megfelelő arányban való építésével maximalizálható a hatékonyság. - A legmagasabb szinten 5 Bánya teljesen megtölt egy Futószalagot. @@ -1043,7 +1178,7 @@ tips: - A Fejlesztések lapon a gombostű ikon megnyomásával kitűzheted a képernyőre az aktuális alakzatot. - Keverd össze mind a három alapszínt, hogy Fehéret csinálj! - - A pálya végtelen méretű; ne nyomorgasd sösze a gyáraidat, terjeszkedj! + - A pálya végtelen méretű; ne nyomorgasd össze a gyáraidat, terjeszkedj! - Próbáld ki a Factorio-t is! Az a kedvenc játékom. - A Negyedelő az alakzat jobb felső negyedétől kezd vágni, az óramutató járásával megegyező irányban! @@ -1062,6 +1197,76 @@ tips: - This game has a lot of settings, be sure to check them out! - Your hub marker has a small compass that shows which direction it is in! - To clear belts, cut the area and then paste it at the same location. - - Press F4 to show your FPS and Tick Rate. + - You can click a pinned shape on the left side to unpin it. - 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. +puzzleMenu: + play: Játék + edit: Szerkesztés + title: Fejtörő Mód + createPuzzle: Új Fejtörő + loadPuzzle: Betöltés + reviewPuzzle: Beküldés & Publikálás + validatingPuzzle: Fejtörő Validálása + submittingPuzzle: Fejtörő Beküldése + noPuzzles: Jelenleg nincs fejtörő ebben a szekcióban. + categories: + levels: Szintek + new: Új + top-rated: Legjobbra Értékelt + mine: Az Én Fejtörőim + easy: Könnyű + hard: Nehéz + completed: Teljesítve + medium: Medium + official: Official + trending: Trending today + trending-weekly: Trending weekly + categories: Categories + difficulties: By Difficulty + account: My Puzzles + search: Search + validation: + title: Hibás Fejtörő + noProducers: Helyezz le egy Termelőt! + noGoalAcceptors: Helyezz le egy Elfogadót! + goalAcceptorNoItem: Egy vagy több Elfogadónál nincs beállítva célként alakzat. + Szállíts le egy alazkatot a cél beállításához. + goalAcceptorRateNotMet: Egy vagy több Elfogadó nem kap elegendő alakzatot. + Győződj meg róla, hogy a jelölő minden Elfogadónál zölden világít. + buildingOutOfBounds: Egy vagy több épület kívül esik a beépíthető területen. + Növeld meg a terület méretét, vagy távolíts el épületeket. + autoComplete: A fejtörő automatikusan megoldja magát! Győződj meg róla, hogy a + Termelők nem közvetlenül az Elfogadóba termelnek. + difficulties: + easy: Easy + medium: Medium + hard: Hard + dlcHint: Purchased the DLC already? Make sure it is activated by right clicking + shapez.io in your library, selecting Properties > DLCs. +backendErrors: + ratelimit: Túl gyorsan csinálsz dolgokat. Kérlek, várj egy kicsit. + invalid-api-key: Valami nem oké a játékkal. Próbáld meg frissíteni vagy + újraindítani. (HIBA - Invalid Api Key). + unauthorized: Valami nem oké a játékkal. Próbáld meg frissíteni vagy + újraindítani. (HIBA - Unauthorized). + bad-token: Valami nem oké a játékkal. Próbáld meg frissíteni vagy újraindítani. + (HIBA - Bad Token). + bad-id: Helytelen Fejtörő azonosító. + not-found: A megadott fejtörőt nem találjuk. + bad-category: A megadott kategóriát nem találjuk. + bad-short-key: A megadott gyorskód helytelen. + profane-title: A fejtörő címe csúnya szavakat tartalmaz. + bad-title-too-many-spaces: A fejtörő címe túl rövid. + bad-shape-key-in-emitter: Egy Termelőnek helytelen alakzat van beállítva. + bad-shape-key-in-goal: Egy Elfogadónak helytelen alakzat van beállítva. + no-emitters: A fejtörődben nem szerepel Termelő. + no-goals: A fejtörődben nem szerepel Elfogadó. + short-key-already-taken: Ez a gyorskód már foglalt, kérlek válassz másikat. + can-not-report-your-own-puzzle: Nem jelentheted a saját fejtörődet. + bad-payload: A kérés helytelen adatot tartalmaz. + bad-building-placement: A fejtörőd helytelenül lehelyezett épületeket tartalmaz. + timeout: A kérés időtúllépésbe került. + too-many-likes-already: The puzzle alreay got too many likes. If you still want + to remove it, please contact support@shapez.io! + no-permission: You do not have the permission to perform this action. diff --git a/translations/base-ind.yaml b/translations/base-ind.yaml index 761caf46..46a0db87 100644 --- a/translations/base-ind.yaml +++ b/translations/base-ind.yaml @@ -17,8 +17,8 @@ steamPage: notch_comment: Oh sial. Saya benar-benar harus tidur, namun sepertinya saya baru menemukan bagaimana cara membuat komputer di shapez.io steam_review_comment: Game ini telah mencuri hidup saya dan saya tidak - menginginkannya kembali. Game pembuatan pabrik yang sangat santai yang tidak - akan membiarkan saya berhenti membuat pabrik saya lebih efisien. + menginginkannya kembali. Game pembuatan pabrik yang sangat santai yang + tidak akan membiarkan saya berhenti membuat pabrik saya lebih efisien. global: loading: Memuat error: Terjadi kesalahan @@ -50,6 +50,7 @@ global: escape: ESC shift: SHIFT space: SPACE + loggingIn: Logging in demoBanners: title: Versi Demo intro: Dapatkan versi lengkap untuk membuka semua fitur! @@ -69,6 +70,12 @@ mainMenu: savegameLevel: Level <x> savegameLevelUnknown: Level tidak diketahui savegameUnnamed: Tidak Dinamai + puzzleMode: Puzzle Mode + back: Back + puzzleDlcText: Do you enjoy compacting and optimizing factories? Get the Puzzle + DLC now on Steam for even more fun! + puzzleDlcWishlist: Wishlist now! + puzzleDlcViewNow: View Dlc dialogs: buttons: ok: OK @@ -82,6 +89,9 @@ dialogs: viewUpdate: Tampilkan Update showUpgrades: Tunjukkan Tingkatan showKeybindings: Tunjukkan Tombol Pintas + retry: Retry + continue: Continue + playOffline: Play Offline importSavegameError: title: Kesalahan pada Impor text: "Gagal memasukkan data simpanan kamu:" @@ -185,6 +195,70 @@ dialogs: title: Tutorial Tersedia desc: Ada video tutorial yang tersedia untuk level ini, tetapi hanya dalam Bahasa Inggris. Apakah kamu ingin menontonnya? + editConstantProducer: + title: Set Item + puzzleLoadFailed: + title: Puzzles failed to load + desc: "Unfortunately the puzzles could not be loaded:" + submitPuzzle: + title: Submit Puzzle + descName: "Give your puzzle a name:" + descIcon: "Please enter a unique short key, which will be shown as the icon of + your puzzle (You can generate them <link>here</link>, or choose one + of the randomly suggested shapes below):" + placeholderName: Puzzle Title + puzzleResizeBadBuildings: + title: Resize not possible + desc: You can't make the zone any smaller, because then some buildings would be + outside the zone. + puzzleLoadError: + title: Bad Puzzle + desc: "The puzzle failed to load:" + offlineMode: + title: Offline Mode + desc: We couldn't reach the servers, so the game has to run in offline mode. + Please make sure you have an active internet connection. + puzzleDownloadError: + title: Download Error + desc: "Failed to download the puzzle:" + puzzleSubmitError: + title: Submission Error + desc: "Failed to submit your puzzle:" + puzzleSubmitOk: + title: Puzzle Published + desc: Congratulations! Your puzzle has been published and can now be played by + others. You can now find it in the "My puzzles" section. + puzzleCreateOffline: + title: Offline Mode + desc: Since you are offline, you will not be able to save and/or publish your + puzzle. Would you still like to continue? + puzzlePlayRegularRecommendation: + title: Recommendation + desc: I <strong>strongly</strong> recommend playing the normal game to level 12 + before attempting the puzzle DLC, otherwise you may encounter + mechanics not yet introduced. Do you still want to continue? + puzzleShare: + title: Short Key Copied + desc: The short key of the puzzle (<key>) has been copied to your clipboard! It + can be entered in the puzzle menu to access the puzzle. + puzzleReport: + title: Report Puzzle + options: + profane: Profane + unsolvable: Not solvable + trolling: Trolling + puzzleReportComplete: + title: Thank you for your feedback! + desc: The puzzle has been flagged. + puzzleReportError: + title: Failed to report + desc: "Your report could not get processed:" + puzzleLoadShortKey: + title: Enter short key + desc: Enter the short key of the puzzle to load it. + puzzleDelete: + title: Delete Puzzle? + desc: Are you sure you want to delete '<title>'? This can not be undone! ingame: keybindingsOverlay: moveMap: Geser @@ -206,6 +280,7 @@ ingame: clearSelection: Hapus pilihan pipette: Pipet switchLayers: Ganti lapisan + clearBelts: Clear belts colors: red: Merah green: Hijau @@ -358,6 +433,46 @@ ingame: achievements: title: Achievements desc: Hunt them all! + puzzleEditorSettings: + zoneTitle: Zone + zoneWidth: Width + zoneHeight: Height + trimZone: Trim + clearItems: Clear Items + share: Share + report: Report + clearBuildings: Clear Buildings + resetPuzzle: Reset Puzzle + puzzleEditorControls: + title: Puzzle Creator + instructions: + - 1. Place <strong>Constant Producers</strong> to provide shapes and + colors to the player + - 2. Build one or more shapes you want the player to build later and + deliver it to one or more <strong>Goal Acceptors</strong> + - 3. Once a Goal Acceptor receives a shape for a certain amount of + time, it <strong>saves it as a goal</strong> that the player must + produce later (Indicated by the <strong>green badge</strong>). + - 4. Click the <strong>lock button</strong> on a building to disable + it. + - 5. Once you click review, your puzzle will be validated and you + can publish it. + - 6. Upon release, <strong>all buildings will be removed</strong> + except for the Producers and Goal Acceptors - That's the part that + the player is supposed to figure out for themselves, after all :) + puzzleCompletion: + title: Puzzle Completed! + titleLike: "Click the heart if you liked the puzzle:" + titleRating: How difficult did you find the puzzle? + titleRatingDesc: Your rating will help me to make you better suggestions in the future + continueBtn: Keep Playing + menuBtn: Menu + puzzleMetadata: + author: Author + shortKey: Short Key + rating: Difficulty score + averageDuration: Avg. Duration + completionRate: Completion rate shopUpgrades: belt: name: Sabuk konveyor, Pembagi Arus & Terowongan @@ -577,6 +692,18 @@ buildings: name: Pembuat Bentuk description: Hanya tersedia di dalam mode sandbox, mengeluarkan sinyal yang diberikan dari lapisan kabel ke lapisan biasa. + constant_producer: + default: + name: Constant Producer + description: Constantly outputs a specified shape or color. + goal_acceptor: + default: + name: Goal Acceptor + description: Deliver shapes to the goal acceptor to set them as a goal. + block: + default: + name: Block + description: Allows you to block a tile. storyRewards: reward_cutter_and_trash: title: Memotong Bentuk @@ -1008,6 +1135,10 @@ keybindings: rotateToDown: "Rotate: Point Down" rotateToRight: "Rotate: Point Right" rotateToLeft: "Rotate: Point Left" + constant_producer: Constant Producer + goal_acceptor: Goal Acceptor + block: Block + massSelectClear: Clear belts about: title: Tentang permainan ini body: >- @@ -1115,3 +1246,73 @@ tips: - Tekan F4 dua kali untuk menunjukkan ubin mouse dan kameramu. - Kamu bisa mengklik bentuk yang di-pin di sebelah kiri untuk tidak mem-pinnya lagi. +puzzleMenu: + play: Play + edit: Edit + title: Puzzle Mode + createPuzzle: Create Puzzle + loadPuzzle: Load + reviewPuzzle: Review & Publish + validatingPuzzle: Validating Puzzle + submittingPuzzle: Submitting Puzzle + noPuzzles: There are currently no puzzles in this section. + categories: + levels: Levels + new: New + top-rated: Top Rated + mine: My Puzzles + easy: Easy + hard: Hard + completed: Completed + medium: Medium + official: Official + trending: Trending today + trending-weekly: Trending weekly + categories: Categories + difficulties: By Difficulty + account: My Puzzles + search: Search + validation: + title: Invalid Puzzle + noProducers: Please place a Constant Producer! + noGoalAcceptors: Please place a Goal Acceptor! + goalAcceptorNoItem: One or more Goal Acceptors have not yet assigned an item. + Deliver a shape to them to set a goal. + goalAcceptorRateNotMet: One or more Goal Acceptors are not getting enough items. + Make sure that the indicators are green for all acceptors. + buildingOutOfBounds: One or more buildings are outside of the buildable area. + Either increase the area or remove them. + autoComplete: Your puzzle autocompletes itself! Please make sure your constant + producers are not directly delivering to your goal acceptors. + difficulties: + easy: Easy + medium: Medium + hard: Hard + dlcHint: Purchased the DLC already? Make sure it is activated by right clicking + shapez.io in your library, selecting Properties > DLCs. +backendErrors: + ratelimit: You are performing your actions too frequent. Please wait a bit. + invalid-api-key: Failed to communicate with the backend, please try to + update/restart the game (Invalid Api Key). + unauthorized: Failed to communicate with the backend, please try to + update/restart the game (Unauthorized). + bad-token: Failed to communicate with the backend, please try to update/restart + the game (Bad Token). + bad-id: Invalid puzzle identifier. + not-found: The given puzzle could not be found. + bad-category: The given category could not be found. + bad-short-key: The given short key is invalid. + profane-title: Your puzzle title contains profane words. + bad-title-too-many-spaces: Your puzzle title is too short. + bad-shape-key-in-emitter: A constant producer has an invalid item. + bad-shape-key-in-goal: A goal acceptor has an invalid item. + no-emitters: Your puzzle does not contain any constant producers. + no-goals: Your puzzle does not contain any goal acceptors. + short-key-already-taken: This short key is already taken, please use another one. + can-not-report-your-own-puzzle: You can not report your own puzzle. + bad-payload: The request contains invalid data. + bad-building-placement: Your puzzle contains invalid placed buildings. + timeout: The request timed out. + too-many-likes-already: The puzzle alreay got too many likes. If you still want + to remove it, please contact support@shapez.io! + no-permission: You do not have the permission to perform this action. diff --git a/translations/base-it.yaml b/translations/base-it.yaml index fd6928b9..aa1b898e 100644 --- a/translations/base-it.yaml +++ b/translations/base-it.yaml @@ -14,7 +14,7 @@ steamPage: All'inizio lavorerai solo con le forme, ma in seguito dovrai colorarle; a questo scopo dovrai estrarre e mescolare i colori! Comprare il gioco su Steam ti garantirà l'accesso alla versone completa, ma puoi anche giocare una demo su shapez.io e decidere in seguito! - what_others_say: What people say about shapez.io + what_others_say: "Hanno detto di shapez.io:" nothernlion_comment: This game is great - I'm having a wonderful time playing, and time has flown by. notch_comment: Oh crap. I really should sleep, but I think I just figured out @@ -53,6 +53,7 @@ global: escape: ESC shift: MAIUSC space: SPAZIO + loggingIn: Logging in demoBanners: title: Versione Demo intro: Ottieni la versione completa per sbloccare tutte le funzioni! @@ -72,6 +73,12 @@ mainMenu: madeBy: Creato da <author-link> subreddit: Reddit savegameUnnamed: Senza nome + puzzleMode: Modalità puzzle + back: Back + puzzleDlcText: Ti piace miniaturizzare e ottimizzare le tue fabbriche? Ottini il + Puzzle DLC ora su steam per un divertimento ancora maggiore! + puzzleDlcWishlist: Aggiungi alla lista dei desideri ora! + puzzleDlcViewNow: View Dlc dialogs: buttons: ok: OK @@ -85,6 +92,9 @@ dialogs: viewUpdate: Mostra aggiornamento showUpgrades: Mostra miglioramenti showKeybindings: Mostra scorciatoie + retry: Riprova + continue: Continua + playOffline: Gioca offline importSavegameError: title: Errore di importazione text: "Impossibile caricare il salvataggio:" @@ -189,6 +199,73 @@ dialogs: title: Tutorial Disponibile desc: C'è un video tutorial per questo livello, ma è disponibile solo in Inglese. Vorresti dargli un'occhiata? + editConstantProducer: + title: Imposta oggetto + puzzleLoadFailed: + title: Impossibile caricare i puzzle + desc: "Sfortunatamente non è stato possibile caricare i puzzle:" + submitPuzzle: + title: Pubblica il puzzle + descName: "Dai un nome al tuo puzzle:" + descIcon: "Per favore inserisci un codice per la forma identificativa, che sarà + mostrata come icona del tuo puzzle (Pui generarla <link>qui</link>, + oppure sceglierne una tra quelle casuali qui sotto):" + placeholderName: Nome puzzle + puzzleResizeBadBuildings: + title: Impossibile ridimensionare + desc: Non è possibile ridurre la zona ulteriormente, dato che alcuni edifici + sarebbero fuori dalla zona. + puzzleLoadError: + title: Caricamento fallito + desc: "Impossibile caricare il puzzle:" + offlineMode: + title: Modalità offline + desc: Non siamo risciti a contattare i server, quindi il gioco è in modalità + offline. Per favore assicurati di avere una connessione internet + attiva. + puzzleDownloadError: + title: Errore di download + desc: "Il download del puzzle è fallito:" + puzzleSubmitError: + title: Errore di pubblicazione + desc: "La pubblicazione del puzzle è fallita:" + puzzleSubmitOk: + title: Puzzle pubblicato + desc: Congratulazioni! Il tuo puzzle è stato pubblicato e ora può essere giocato + da altri. Puoi trovarlo nella sezione "I miei puzzle". + puzzleCreateOffline: + title: Modalità offline + desc: Dato che sei offline, non potrai salvare e/o pubblicare il tuo puzzle. Sei + sicuro di voler contnuare? + puzzlePlayRegularRecommendation: + title: Raccomandazione + desc: Ti raccomando <strong>fortemente</strong> di giocare nella modalità + normale fino al livello 12 prima di cimentarti nel puzzle DLC, + altrimenti potresti incontrare meccaniche non ancora introdotte. Sei + sicuro di voler continuare? + puzzleShare: + title: Codice copiato + desc: Il codice del puzzle (<key>) è stato copiato negli appunti! Può essere + inserito nel menù dei puzzle per accedere al puzzle. + puzzleReport: + title: Segnala puzzle + options: + profane: Volgare + unsolvable: Senza soluzione + trolling: Troll + puzzleReportComplete: + title: Grazie per il tuo feedback! + desc: Il puzzle è stato segnalato. + puzzleReportError: + title: Segnalazione fallita + desc: "Non è stato possibile elaborare la tua segnalazione:" + puzzleLoadShortKey: + title: Inserisci codice + desc: Inserisci il codice del puzzle per caricarlo. + puzzleDelete: + title: Cancellare il puzzle? + desc: Sei sicuro di voler cancellare '<title>'? Questa azione non può essere + annullata! ingame: keybindingsOverlay: moveMap: Sposta @@ -210,6 +287,7 @@ ingame: clearSelection: Annulla selezione pipette: Contagocce switchLayers: Cambia livello + clearBelts: Clear belts buildingPlacement: cycleBuildingVariants: Premi <key> per cambiare variante. hotkeyLabel: "Hotkey: <key>" @@ -359,8 +437,52 @@ ingame: title: Sostienimi desc: Lo sviluppo nel tempo libero! achievements: - title: Achievements - desc: Hunt them all! + title: Achievement + desc: Collezionali tutti! + puzzleEditorSettings: + zoneTitle: Zona + zoneWidth: Larghezza + zoneHeight: Altezza + trimZone: Riduci + clearItems: Elimina oggetti + share: Condividi + report: Segnala + clearBuildings: Clear Buildings + resetPuzzle: Reset Puzzle + puzzleEditorControls: + title: Creazione puzzle + instructions: + - 1. Posiziona dei <strong>produttori costanti</strong> per fornire + forme e colori al giocatore + - 2. Costruisci una o più forme che vuoi che il giocatore costruisca + e consegni a uno o più degli <strong>Accettori di + obiettivi</strong> + - 3. Una volta che un accettore di obiettivi riceve una forma per un + certo lasso di tempo, lo <strong>salva come obiettivo</strong> che + il giocatore dovrà poi produrre (Indicato dal <strong>simbolo + verde</strong>). + - 4. Clicca il <strong>bottone di blocco</strong> su un edificio per + disabilitarlo. + - 5. Una volta che cliccherai verifica, il tuo puzzle sarà + convalidato e potrai pubblicarlo. + - 6. Una volta rilasciato, <strong>tutti gli edifici saranno + rimossi</strong> ad eccezione di produttori e accettori di + obiettivi. Quella è la parte che il giocatore deve capire da solo, + dopo tutto :) + puzzleCompletion: + title: Puzzle completato! + titleLike: "Clicca il cuore se ti è piaciuto:" + titleRating: Quanto è stato difficile il puzzle? + titleRatingDesc: La tua valutazione mi aiuterà a darti raccomandazioni migliori + in futuro + continueBtn: Continua a giocare + menuBtn: Menù + puzzleMetadata: + author: Autore + shortKey: Codice + rating: Punteggio difficoltà + averageDuration: Durata media + completionRate: Tasso di completamento shopUpgrades: belt: name: Nastri, distribuzione e tunnel @@ -574,15 +696,28 @@ buildings: name: Generatore di oggetti description: Disponibile solo nella modalità sandbox, emette il segnale dal livello elettrico come oggetti sul livello normale. + constant_producer: + default: + name: Produttore costante + description: Produce costantemente una forma o un colore specificati. + goal_acceptor: + default: + name: Accettore di obiettivi. + description: Consegna forme all'accettore di obiettivi per impostarli come + obiettivo. + block: + default: + name: Blocco + description: Blocca una casella. storyRewards: reward_cutter_and_trash: title: Taglio forme - desc: Il <strong>taglierino</strong> è stato bloccato! Taglia le forme a metà da - sopra a sotto <strong>indipendentemente dal suo + desc: Il <strong>taglierino</strong> è stato sbloccato! Taglia le forme a metà + da sopra a sotto <strong>indipendentemente dal suo orientamento</strong>!<br><br> Assicurati di buttare via lo scarto, - sennò <strong>si intaserà e andrà in stallo </strong> - Per questo - ti ho dato il <strong>certino</strong>, che distrugge tutto quello - che riceve! + altrimenti <strong>si intaserà e andrà in stallo </strong> - Per + questo ti ho dato il <strong>cestino</strong>, che distrugge tutto + quello che riceve! reward_rotater: title: Rotazione desc: Il <strong>ruotatore</strong> è stato sbloccato! Ruota le forme di 90 @@ -699,7 +834,7 @@ storyRewards: letto? Prova a mostrarlo su di un display!" reward_constant_signal: title: Sengale costante - desc: Hai sblocatto l'edificio <strong>segnale costante</strong> sul livello + desc: Hai sbloccato l'edificio <strong>segnale costante</strong> sul livello elettrico! È utile collegarlo ai <strong>filtri di oggetti</strong> per esempio.<br><br> Il segnale costante può emettere una <strong>forma</strong>, un <strong>colore</strong> o un @@ -987,10 +1122,14 @@ keybindings: comparator: Comparatore item_producer: Generatore di oggetti (Sandbox) copyWireValue: "Cavi: Copia valore sotto il cursore" - rotateToUp: "Rotate: Point Up" - rotateToDown: "Rotate: Point Down" - rotateToRight: "Rotate: Point Right" - rotateToLeft: "Rotate: Point Left" + rotateToUp: "Ruota: punta in alto" + rotateToDown: "Ruota: punta in basso" + rotateToRight: "Ruota: punta a destra" + rotateToLeft: "Ruota: punta a sinistra" + constant_producer: Produttore costante + goal_acceptor: Accettore di obiettivi + block: Blocco + massSelectClear: Sgombra nastri about: title: Riguardo questo gioco body: >- @@ -1089,3 +1228,75 @@ tips: - Premi F4 due volte per mostrare la casella del cursore e della telecamera. - Puoi cliccare a sinistra di una forma fermata a schermo per rimuoverla dalla lista. +puzzleMenu: + play: Gioca + edit: Modifica + title: Modalità puzzle + createPuzzle: Crea puzzle + loadPuzzle: Carica + reviewPuzzle: Verifica e pubblica + validatingPuzzle: Convalidazione puzzle + submittingPuzzle: Pubblicazione puzzle + noPuzzles: Al momento non ci sono puzzle in questa sezione. + categories: + levels: Livelli + new: Nuovo + top-rated: Più votati + mine: I miei puzzle + easy: Facili + hard: Difficili + completed: Completati + medium: Medi + official: Ufficiali + trending: Più popolari di oggi + trending-weekly: Più popolari della settimana + categories: Categorie + difficulties: Per difficoltà + account: I miei puzzle + search: Cerca + validation: + title: Puzzle non valido + noProducers: Per favore posiziona un Produttore costante! + noGoalAcceptors: Per favore posiziona un accettore di obiettivi! + goalAcceptorNoItem: Uno o più degli accettori di obiettivi non hanno un oggetto + assegnato. Consgnagli una forma per impostare l'obiettivo. + goalAcceptorRateNotMet: Uno o più degli accettori di obiettivi non ricevono + abbastanza oggetti. Assicurati che gli indicatori siano verdi per + tutti gli accettori. + buildingOutOfBounds: Uno o più edifici sono fuori dalla zona di costruzione. + Ingrandisci l'area o rimuovili. + autoComplete: Il tuo puzzle si autocompleta! Per favore assicurati che i tuoi + produttori costanti non consegnino direttamente agli accettori di + obiettivi. + difficulties: + easy: Facile + medium: Medio + hard: Difficile + dlcHint: Purchased the DLC already? Make sure it is activated by right clicking + shapez.io in your library, selecting Properties > DLCs. +backendErrors: + ratelimit: Stai facendo troppe azioni velocemente. Per favore attendi un attimo. + invalid-api-key: Comunicazione con il backend fallita, per favore prova ad + aggiornare o riavviare il gioco (Invalid Api Key). + unauthorized: Comunicazione con il backend fallita, per favore prova ad + aggiornare o riavviare il gioco (Unauthorized). + bad-token: Comunicazione con il backend fallita, per favore prova ad aggiornare + o riavviare il gioco (Bad Token). + bad-id: Identificativo puzzle non valido. + not-found: Non è stato possibile trovare il puzzle. + bad-category: Non è stato possibile trovare la categoria. + bad-short-key: Il codice non è valido. + profane-title: Il titolo del tuo puzzle contiene volgarità. + bad-title-too-many-spaces: Il titolo del tuo puzzle è troppo breve. + bad-shape-key-in-emitter: Un produttore costante ha un oggetto non valido. + bad-shape-key-in-goal: Un accettore di obiettivi ha un oggetto non valido. + no-emitters: Il tuo puzzle non contiente alcun produttore costante. + no-goals: Il tuo puzzle non contiene alcun accettore di obiettivi. + short-key-already-taken: Queesto codice forma è già in uso, per favore scegline un altro. + can-not-report-your-own-puzzle: Non puoi segnlare il tuo puzzle. + bad-payload: La richiesta contiene dati non validi. + bad-building-placement: Il tuo puzzle contiene edifici non validi. + timeout: La richiesta è scaduta. + too-many-likes-already: Questo puzzle ha già ricevuto troppi "mi piace". Se vuoi + ancora rimuoverlo, per favore contatta support@shapez.io! + no-permission: Non hai i permessi per eseguire questa azione. diff --git a/translations/base-ja.yaml b/translations/base-ja.yaml index 2632a4c8..2fe56bec 100644 --- a/translations/base-ja.yaml +++ b/translations/base-ja.yaml @@ -45,6 +45,7 @@ global: escape: ESC shift: SHIFT space: SPACE + loggingIn: Logging in demoBanners: title: デモ版 intro: スタンドアローン版を手に入れ、すべての機能をアンロックしましょう! @@ -63,6 +64,12 @@ mainMenu: savegameLevel: レベル <x> savegameLevelUnknown: 不明なレベル savegameUnnamed: 無名のデータ + puzzleMode: Puzzle Mode + back: Back + puzzleDlcText: Do you enjoy compacting and optimizing factories? Get the Puzzle + DLC now on Steam for even more fun! + puzzleDlcWishlist: Wishlist now! + puzzleDlcViewNow: View Dlc dialogs: buttons: ok: OK @@ -76,6 +83,9 @@ dialogs: viewUpdate: アップデートを見る showUpgrades: アップグレード表示 showKeybindings: キー設定表示 + retry: Retry + continue: Continue + playOffline: Play Offline importSavegameError: title: インポートエラー text: "セーブデータのインポートに失敗しました:" @@ -157,6 +167,70 @@ dialogs: tutorialVideoAvailableForeignLanguage: title: チュートリアル視聴可能 desc: このレベルで利用できるチュートリアル動画がありますが、言語は英語です。確認しますか? + editConstantProducer: + title: Set Item + puzzleLoadFailed: + title: Puzzles failed to load + desc: "Unfortunately the puzzles could not be loaded:" + submitPuzzle: + title: Submit Puzzle + descName: "Give your puzzle a name:" + descIcon: "Please enter a unique short key, which will be shown as the icon of + your puzzle (You can generate them <link>here</link>, or choose one + of the randomly suggested shapes below):" + placeholderName: Puzzle Title + puzzleResizeBadBuildings: + title: Resize not possible + desc: You can't make the zone any smaller, because then some buildings would be + outside the zone. + puzzleLoadError: + title: Bad Puzzle + desc: "The puzzle failed to load:" + offlineMode: + title: Offline Mode + desc: We couldn't reach the servers, so the game has to run in offline mode. + Please make sure you have an active internet connection. + puzzleDownloadError: + title: Download Error + desc: "Failed to download the puzzle:" + puzzleSubmitError: + title: Submission Error + desc: "Failed to submit your puzzle:" + puzzleSubmitOk: + title: Puzzle Published + desc: Congratulations! Your puzzle has been published and can now be played by + others. You can now find it in the "My puzzles" section. + puzzleCreateOffline: + title: Offline Mode + desc: Since you are offline, you will not be able to save and/or publish your + puzzle. Would you still like to continue? + puzzlePlayRegularRecommendation: + title: Recommendation + desc: I <strong>strongly</strong> recommend playing the normal game to level 12 + before attempting the puzzle DLC, otherwise you may encounter + mechanics not yet introduced. Do you still want to continue? + puzzleShare: + title: Short Key Copied + desc: The short key of the puzzle (<key>) has been copied to your clipboard! It + can be entered in the puzzle menu to access the puzzle. + puzzleReport: + title: Report Puzzle + options: + profane: Profane + unsolvable: Not solvable + trolling: Trolling + puzzleReportComplete: + title: Thank you for your feedback! + desc: The puzzle has been flagged. + puzzleReportError: + title: Failed to report + desc: "Your report could not get processed:" + puzzleLoadShortKey: + title: Enter short key + desc: Enter the short key of the puzzle to load it. + puzzleDelete: + title: Delete Puzzle? + desc: Are you sure you want to delete '<title>'? This can not be undone! ingame: keybindingsOverlay: moveMap: マップ移動 @@ -178,6 +252,7 @@ ingame: clearSelection: 選択範囲をクリア pipette: スポイト switchLayers: レイヤーを変更 + clearBelts: Clear belts colors: red: 赤 green: 緑 @@ -313,6 +388,46 @@ ingame: achievements: title: アチーブメント desc: 取り尽くせ! + puzzleEditorSettings: + zoneTitle: Zone + zoneWidth: Width + zoneHeight: Height + trimZone: Trim + clearItems: Clear Items + share: Share + report: Report + clearBuildings: Clear Buildings + resetPuzzle: Reset Puzzle + puzzleEditorControls: + title: Puzzle Creator + instructions: + - 1. Place <strong>Constant Producers</strong> to provide shapes and + colors to the player + - 2. Build one or more shapes you want the player to build later and + deliver it to one or more <strong>Goal Acceptors</strong> + - 3. Once a Goal Acceptor receives a shape for a certain amount of + time, it <strong>saves it as a goal</strong> that the player must + produce later (Indicated by the <strong>green badge</strong>). + - 4. Click the <strong>lock button</strong> on a building to disable + it. + - 5. Once you click review, your puzzle will be validated and you + can publish it. + - 6. Upon release, <strong>all buildings will be removed</strong> + except for the Producers and Goal Acceptors - That's the part that + the player is supposed to figure out for themselves, after all :) + puzzleCompletion: + title: Puzzle Completed! + titleLike: "Click the heart if you liked the puzzle:" + titleRating: How difficult did you find the puzzle? + titleRatingDesc: Your rating will help me to make you better suggestions in the future + continueBtn: Keep Playing + menuBtn: Menu + puzzleMetadata: + author: Author + shortKey: Short Key + rating: Difficulty score + averageDuration: Avg. Duration + completionRate: Completion rate shopUpgrades: belt: name: ベルト、分配機とトンネル @@ -493,6 +608,18 @@ buildings: default: name: なんでも抽出機 description: サンドボックスモードでのみ使用可能で、ワイヤレイヤーで与えられた信号の形状を通常レイヤーに出力します。 + constant_producer: + default: + name: Constant Producer + description: Constantly outputs a specified shape or color. + goal_acceptor: + default: + name: Goal Acceptor + description: Deliver shapes to the goal acceptor to set them as a goal. + block: + default: + name: Block + description: Allows you to block a tile. storyRewards: reward_cutter_and_trash: title: 形の切断 @@ -558,18 +685,21 @@ storyRewards: desc: <strong>回転機</strong>のバリエーションが利用可能になりました! 180°の回転ができるようになります!(サプライズ! :D) reward_wires_painter_and_levers: title: ワイヤ&着色機(四分割) - desc: "<strong>ワイヤレイヤ</strong>が利用可能になりました: これは通常の レイヤーの上にある別のレイヤであり、多くの新しい要素があります!<br><br> - まず最初に、<strong>四色着色機</strong>が利用可能になりました - 着色するスロットをワイヤレイヤで接続してください!<br><br> - ワイヤレイヤに切り替えるには、<strong>E</strong>を押します。 <br><br> - 補足: 設定で<strong>ヒントを有効にする</strong>と、 ワイヤのチュートリアルが有効になります。" + desc: "<strong>ワイヤレイヤ</strong>が利用可能になりました: これは通常の + レイヤーの上にある別のレイヤであり、多くの新しい要素があります!<br><br> + まず最初に、<strong>四色着色機</strong>が利用可能になりました - + 着色するスロットをワイヤレイヤで接続してください!<br><br> + ワイヤレイヤに切り替えるには、<strong>E</strong>を押します。 <br><br> 補足: + 設定で<strong>ヒントを有効にする</strong>と、 ワイヤのチュートリアルが有効になります。" reward_filter: title: アイテムフィルタ desc: <strong>アイテムフィルタ</strong>が利用可能になりました! ワイヤレイヤの信号と一致するかどうかに応じて、アイテムを上側または右側の出力に分離します。<br><br> また、真偽値(0/1)信号を入力すれば全てのアイテムの通過・非通過を制御できます。 reward_display: title: ディスプレイ - desc: "<strong>ディスプレイ</strong>が利用可能になりました - ワイヤレイヤで信号を接続することで、その内容を表示できます!<br><br> - 補足: ベルトリーダーとストレージが最後に通過したアイテムを出力していることに気づきましたか? それをディスプレイに表示してみてください!" + desc: "<strong>ディスプレイ</strong>が利用可能になりました - + ワイヤレイヤで信号を接続することで、その内容を表示できます!<br><br> 補足: + ベルトリーダーとストレージが最後に通過したアイテムを出力していることに気づきましたか? それをディスプレイに表示してみてください!" reward_constant_signal: title: 定数信号 desc: <strong>定数信号</strong>がワイヤレイヤで利用可能になりました! これは例えば<strong>アイテムフィルタ</strong>に接続すると便利です。<br><br> @@ -814,6 +944,10 @@ keybindings: rotateToDown: "回転: 下向きにする" rotateToRight: "回転: 右向きにする" rotateToLeft: "回転: 左向きにする" + constant_producer: Constant Producer + goal_acceptor: Goal Acceptor + block: Block + massSelectClear: Clear belts about: title: このゲームについて body: >- @@ -860,9 +994,9 @@ tips: - 切断機は配置された向きを考慮せず、常に垂直に切断します。 - ストレージは左側の出力を優先します。 - 増築可能なデザインを作るために時間を使ってください。それだけの価値があります! - - <b>SHIFT</b>を使用すると複数の建物を一度に配置できます。 + - Invest time to build repeatable designs - it's worth it! - <b>ALT</b>を押しながらベルトを設置すると、向きを逆転できます。 - - 効率が重要です! + - You can hold <b>ALT</b> to invert the direction of placed belts. - ハブから遠くに離れるほど、形状資源はより複雑な形になります。 - 機械の速度には上限があるので、最大効率を得るためには入力を分割してください。 - 効率を最大化するために分配機/合流機を使用できます。 @@ -878,8 +1012,8 @@ tips: - モジュールがあれば、空間はただの認識に過ぎなくなる――生ある人間に対する気遣いだ。 - 設計図としての工場を別に作っておくと、工場のモジュール化において重要な役割を果たします。 - 混色機をよく見ると、色の混ぜ方が解ります。 - - <b>CTRL</b> + クリックで範囲選択ができます。 - - ハブに近すぎる設計物を作ると、のちの設計の邪魔になるかもしれません。 + - Have a closer look at the color mixer, and your questions will be answered. + - Use <b>CTRL</b> + Click to select an area. - アップグレードリストの各形状の横にあるピンのアイコンは、その形状を画面左に固定表示します。 - 三原色全てを混ぜ合わせると白になります! - マップは無限の広さがあります。臆せずに拡張してください。 @@ -893,3 +1027,74 @@ tips: - F4を押すことで、FPSとTickレートを表示できます。 - F4を2回押すと、マウスとカメラの座標を表示できます。 - 左のピン留めされた図形をクリックすると、固定を解除できます。 + - You can click a pinned shape on the left side to unpin it. +puzzleMenu: + play: Play + edit: Edit + title: Puzzle Mode + createPuzzle: Create Puzzle + loadPuzzle: Load + reviewPuzzle: Review & Publish + validatingPuzzle: Validating Puzzle + submittingPuzzle: Submitting Puzzle + noPuzzles: There are currently no puzzles in this section. + categories: + levels: Levels + new: New + top-rated: Top Rated + mine: My Puzzles + easy: Easy + hard: Hard + completed: Completed + medium: Medium + official: Official + trending: Trending today + trending-weekly: Trending weekly + categories: Categories + difficulties: By Difficulty + account: My Puzzles + search: Search + validation: + title: Invalid Puzzle + noProducers: Please place a Constant Producer! + noGoalAcceptors: Please place a Goal Acceptor! + goalAcceptorNoItem: One or more Goal Acceptors have not yet assigned an item. + Deliver a shape to them to set a goal. + goalAcceptorRateNotMet: One or more Goal Acceptors are not getting enough items. + Make sure that the indicators are green for all acceptors. + buildingOutOfBounds: One or more buildings are outside of the buildable area. + Either increase the area or remove them. + autoComplete: Your puzzle autocompletes itself! Please make sure your constant + producers are not directly delivering to your goal acceptors. + difficulties: + easy: Easy + medium: Medium + hard: Hard + dlcHint: Purchased the DLC already? Make sure it is activated by right clicking + shapez.io in your library, selecting Properties > DLCs. +backendErrors: + ratelimit: You are performing your actions too frequent. Please wait a bit. + invalid-api-key: Failed to communicate with the backend, please try to + update/restart the game (Invalid Api Key). + unauthorized: Failed to communicate with the backend, please try to + update/restart the game (Unauthorized). + bad-token: Failed to communicate with the backend, please try to update/restart + the game (Bad Token). + bad-id: Invalid puzzle identifier. + not-found: The given puzzle could not be found. + bad-category: The given category could not be found. + bad-short-key: The given short key is invalid. + profane-title: Your puzzle title contains profane words. + bad-title-too-many-spaces: Your puzzle title is too short. + bad-shape-key-in-emitter: A constant producer has an invalid item. + bad-shape-key-in-goal: A goal acceptor has an invalid item. + no-emitters: Your puzzle does not contain any constant producers. + no-goals: Your puzzle does not contain any goal acceptors. + short-key-already-taken: This short key is already taken, please use another one. + can-not-report-your-own-puzzle: You can not report your own puzzle. + bad-payload: The request contains invalid data. + bad-building-placement: Your puzzle contains invalid placed buildings. + timeout: The request timed out. + too-many-likes-already: The puzzle alreay got too many likes. If you still want + to remove it, please contact support@shapez.io! + no-permission: You do not have the permission to perform this action. diff --git a/translations/base-kor.yaml b/translations/base-kor.yaml index 5d207b82..dc8d190f 100644 --- a/translations/base-kor.yaml +++ b/translations/base-kor.yaml @@ -8,7 +8,7 @@ steamPage: 심지어 그것만으로는 충분하지 않을 겁니다. 수요는 기하급수적으로 늘어나게 될 것이고, 더욱 복잡한 도형을 더욱 많이 생산하여야 하므로, 유일하게 도움이 되는 것은 끊임없이 확장을 하는 것입니다! 처음에는 단순한 도형만을 만들지만, 나중에는 색소를 추출하고 혼합하여 도형에 색칠을 해야 합니다! Steam에서 게임을 구매하여 정식 버전의 콘텐츠를 사용하실 수 있지만, 먼저 Shapez.io의 체험판 버전을 플레이해보시고 구매를 고려하셔도 됩니다! - what_others_say: What people say about shapez.io + what_others_say: shapez.io에 대한 의견들 nothernlion_comment: This game is great - I'm having a wonderful time playing, and time has flown by. notch_comment: Oh crap. I really should sleep, but I think I just figured out @@ -47,6 +47,7 @@ global: escape: ESC shift: SHIFT space: SPACE + loggingIn: 로그인 중 demoBanners: title: 체험판 버전 intro: 정식 버전을 구매해서 모든 콘텐츠를 사용해 보세요! @@ -66,6 +67,11 @@ mainMenu: madeBy: 제작 <author-link> subreddit: Reddit savegameUnnamed: 이름 없음 + puzzleMode: 퍼즐 모드 + back: Back + puzzleDlcText: 공장의 크기를 줄이고 최적화하는데 관심이 많으신가요? 지금 퍼즐 DLC를 구입하세요! + puzzleDlcWishlist: 지금 찜 목록에 추가하세요! + puzzleDlcViewNow: View Dlc dialogs: buttons: ok: 확인 @@ -79,6 +85,9 @@ dialogs: viewUpdate: 업데이트 보기 showUpgrades: 업그레이드 보기 showKeybindings: 조작법 보기 + retry: 재시작 + continue: 계속하기 + playOffline: 오프라인 플레이 importSavegameError: title: 가져오기 오류 text: "세이브 파일을 가져오지 못했습니다:" @@ -163,6 +172,63 @@ dialogs: tutorialVideoAvailableForeignLanguage: title: 활성화된 튜토리얼 desc: 현재 레벨에서 사용할 수 있는 튜토리얼 비디오가 있습니다! 허나 영어로만 제공될 것입니다. 보시겠습니까? + editConstantProducer: + title: 아이템 설정 + puzzleLoadFailed: + title: 퍼즐 불러오기 실패 + desc: "불행히도 이 퍼즐은 불러오는데 실패하였습니다:" + submitPuzzle: + title: 퍼즐 보내기 + descName: "퍼즐에 이름을 지어 주세요:" + descIcon: "퍼즐의 아이콘으로 보여지게 될 짧은 단어를 지정해 주세요. (<link>이곳</link>에서 생성하시거나, 아래 랜덤한 모양 + 중 하나를 선택하세요):" + placeholderName: 퍼즐 제목 + puzzleResizeBadBuildings: + title: 크기 조절 불가능 + desc: 몇몇 건물이 구역 밖으로 벗어나게 되므로 크기를 조절할 수 없습니다. + puzzleLoadError: + title: 잘못된 퍼즐 + desc: "퍼즐을 불러올 수 없었습니다:" + offlineMode: + title: 오프라인 모드 + desc: 서버에 접속할 수 없었으므로 오프라인 모드로 게임이 시작되었습니다. 인터넷 연결 상태를 다시 한번 확인해 주세요. + puzzleDownloadError: + title: 다운로드 오류 + desc: "퍼즐을 다운로드할 수 없습니다:" + puzzleSubmitError: + title: 전송 오류 + desc: "퍼즐을 전송할 수 없습니다:" + puzzleSubmitOk: + title: 퍼즐 공개됨 + desc: 축하합니다! 퍼즐이 업로드되었고 이제 다른 사람이 플레이할 수 있습니다. "내 퍼즐" 섹션에서 찾으실 수 있습니다. + puzzleCreateOffline: + title: 오프라인 모드 + desc: 오프라인 모드임으로 퍼즐을 저장하거나 업로드할 수 없습니다. 그래도 계속하시겠습니까? + puzzlePlayRegularRecommendation: + title: 권장 사항 + desc: 퍼즐 DLC 플레이시 소개되지 않은 요소를 접하시게 될 수 있으므로, 적어도 일반 게임을 12레벨까지 플레이하시는것을 + <strong>강력히</strong> 권장드립니다. 그래도 계속하시겠습니까? + puzzleShare: + title: 짧은 키 복사됨 + desc: 퍼즐의 짧은 키 (<key>) 가 클립보드에 복사되었습니다! 메인 메뉴에서 퍼즐 접근 시 사용할 수 있습니다. + puzzleReport: + title: 퍼즐 신고 + options: + profane: 부적절함 + unsolvable: 풀 수 없음 + trolling: 낚시성 + puzzleReportComplete: + title: 피드백을 보내주셔서 감사드립니다! + desc: 퍼즐이 신고되었습니다. + puzzleReportError: + title: 신고 실패 + desc: "오류로 신고가 처리되지 못했습니다:" + puzzleLoadShortKey: + title: 짧은 키 입력 + desc: 불러올 퍼즐의 짧은 키를 입력해 주세요. + puzzleDelete: + title: 퍼즐을 지우시겠습니까? + desc: 정말로 퍼즐:'<title>'을 지우시겠습니까? 이것은 돌릴수 없습니다! ingame: keybindingsOverlay: moveMap: 이동 @@ -184,6 +250,7 @@ ingame: clearSelection: 지우기 pipette: 피펫 switchLayers: 레이어 전환 + clearBelts: Clear belts buildingPlacement: cycleBuildingVariants: <key> 키를 눌러 변형 전환 hotkeyLabel: "단축키: <key>" @@ -269,9 +336,8 @@ ingame: 21_2_switch_to_wires: <strong>E 키</strong>를 눌러 전선 레이어 로 전환하세요!<br><br> 그 후 색칠기의 <strong>네 입력 부분</strong>을 모두 케이블로 연결하세요! 21_3_place_button: 훌륭해요! 이제 <strong>스위치</strong>를 배치하고 전선으로 연결하세요! - 21_4_press_button: "Press the switch to make it <strong>emit a truthy - signal</strong> and thus activate the painter.<br><br> PS: You - don't have to connect all inputs! Try wiring only two." + 21_4_press_button: "스위치를 눌러서 색칠기에 <strong>참 신호를 보내</strong> 활성화해보세요.<br><br> 추신: + 모든 입력을 연결할 필요는 없습니다! 두개만 연결해 보세요." colors: red: 빨간색 green: 초록색 @@ -320,8 +386,42 @@ ingame: title: 저를 지원해주세요 desc: 저는 여가 시간에 게임을 개발합니다! achievements: - title: Achievements - desc: Hunt them all! + title: 도전 과제 + desc: 모두 잡아 보세요! + puzzleEditorSettings: + zoneTitle: 구역 + zoneWidth: 너비 + zoneHeight: 높이 + trimZone: 자르기 + clearItems: 아이템 초기화 + share: 공유 + report: 신고 + clearBuildings: Clear Buildings + resetPuzzle: Reset Puzzle + puzzleEditorControls: + title: 퍼즐 생성기 + instructions: + - 1. <strong>일정 생성기</strong>를 배치해 플레이어가 사용할 색깔과 모양 을 생성하세요 + - 2. 플레이어가 나중에 만들 모양을 하나 이상 만들어 <strong>목표 수집기</strong> 로 배달하도록 만드세요 + - 3. 목표 수집기가 모양을 일정 양 이상 수집하면 그것은 플레이어가 반드시 <strong>생산해야 할 목표 + 모양</strong>으로 저장합니다 (<strong>초록색 뱃지</strong> 로 표시됨) + - 4. 건물의 <strong>잠금 버튼</strong>을 눌러서 비활성화하세요 + - 5. 리뷰 버튼을 누르면 퍼즐이 검증되고 업로드할수 있게 됩니다. + - 6. 공개시, 생성기와 목표 수집기를 제외한 <strong>모든 건물이</strong> 제거됩니다 - 그 부분이 바로 + 플레이어가 스스로 알아내야 하는 것들이죠 :) + puzzleCompletion: + title: 퍼즐 완료됨! + titleLike: "퍼즐이 좋았다면 하트를 눌러주세요:" + titleRating: 퍼즐의 난이도는 어땠나요? + titleRatingDesc: 당신의 평가는 제가 미래에 더 나은 평가를 만들수 있도록 도울 수 있습니다. + continueBtn: 계속 플레이하기 + menuBtn: 메뉴 + puzzleMetadata: + author: 제작자 + shortKey: 짧은 키 + rating: 난이도 + averageDuration: 평균 플레이 시간 + completionRate: 완료율 shopUpgrades: belt: name: 벨트, 밸런서, 터널 @@ -439,7 +539,7 @@ buildings: constant_signal: default: name: 일정 신호기 - description: 모양, 색상, 또는 불 값 (1 또는 0) 상수 신호를 방출합니다. + description: 모양, 색상, 또는 불 값 (1 또는 0)이 될 수 있는 일정한 신호를 방출합니다. lever: default: name: 스위치 @@ -506,6 +606,18 @@ buildings: default: name: 아이템 생성기 description: 샌드박스 모드에서만 사용할 수 있는 아이템으로, 일반 레이어 위에 있는 전선 레이어에서 주어진 신호를 출력합니다. + constant_producer: + default: + name: 일정 생성기 + description: 지정한 모양이나 색깔을 일정하게 생성합니다. + goal_acceptor: + default: + name: 목표 수집기 + description: 목표 수집기로 모양을 보내 목표로 설정하세요. + block: + default: + name: 차단기 + description: 타일을 사용하지 못하게 차단합니다. storyRewards: reward_cutter_and_trash: title: 절단기 @@ -622,13 +734,11 @@ storyRewards: - 평소처럼 게임을 진행합니다.<br><br> 어떤 방식으로든, 재미있게 게임을 플레이해주시길 바랍니다! reward_wires_painter_and_levers: title: 전선과 4단 색칠기 - desc: "You just unlocked the <strong>Wires Layer</strong>: It is a separate - layer on top of the regular layer and introduces a lot of new - mechanics!<br><br> For the beginning I unlocked you the <strong>Quad - Painter</strong> - Connect the slots you would like to paint with on - the wires layer!<br><br> To switch to the wires layer, press - <strong>E</strong>. <br><br> PS: <strong>Enable hints</strong> in - the settings to activate the wires tutorial!" + desc: " 방금 <strong>전선 레이어</strong>를 활성화하셨습니다: 이것은 일반 레이어 위에 존재하는 별개의 레이어로 수많은 + 새로운 요소를 사용할 수 있습니다!<br><br> <strong>4단 색칠기</strong>를 활성화해 드리겠습니다 - + 전선 레이어에서 색을 칠할 부분에 연결해 보세요!<br><br> 전선 레이어로 전환하시려면 + <strong>E</strong>키를 눌러주세요.<br><br> 추신: <strong>힌트를 활성화</strong>해서 + 전선 튜토리얼을 활성화해 보세요!" reward_filter: title: 아이템 선별기 desc: <strong>아이템 선별기</strong>가 잠금 해제되었습니다! 전선 레이어의 신호와 일치하는지에 대한 여부로 아이템을 위쪽 @@ -765,8 +875,8 @@ settings: description: 기본적으로 활성화되어 있으며, 자원 패치에서 피펫 기능을 사용 시 즉시 추출기를 선택합니다. simplifiedBelts: title: 벨트 단순화 (못생김) - description: 성능 향상을 위해 벨트를 가리킬 때를 제외한 모든 상황에서 벨트 아이템을 렌더링하지 않습니다. 이 기능을 사용할 할 - 정도로 심각한 성능 문제가 일어나지 않는 한, 이 설정을 사용할 필요는 없습니다. + description: 성능 향상을 위해 벨트를 가리킬 때를 제외한 모든 상황에서 벨트 아이템을 렌더링하지 않습니다. 이 기능을 사용할 정도로 + 심각한 성능 문제가 일어나지 않는 한, 이 설정을 사용할 필요는 없습니다. enableMousePan: title: 화면 가장자리 패닝 description: 커서를 화면 가장자리로 옮기면 스크롤되어 지도를 이동할 수 있습니다. 스크롤 속도는 이동 속도 설정에 따릅니다. @@ -849,10 +959,14 @@ keybindings: comparator: 비교기 item_producer: 아이템 생성기 (샌드박스) copyWireValue: "전선: 커서 아래 값 복사" - rotateToUp: "Rotate: Point Up" - rotateToDown: "Rotate: Point Down" - rotateToRight: "Rotate: Point Right" - rotateToLeft: "Rotate: Point Left" + rotateToUp: 위로 향하게 회전 + rotateToDown: 아래로 향하게 회전 + rotateToRight: 오른쪽으로 향하게 회전 + rotateToLeft: 아래쪽으로 향하게 회전 + constant_producer: 일정 생성기 + goal_acceptor: 목표 수집기 + block: 블록 + massSelectClear: 벨트 초기화 about: title: 게임 정보 body: >- @@ -922,7 +1036,7 @@ tips: - 공장을 허브에 가까이 지으면 나중에 거대한 프로젝트에 방해가 될 수 있습니다. - 업그레이드 목록에 나타나는 도형 옆의 핀 아이콘을 누르면 화면에 고정됩니다. - 세 가지의 기본 색상을 모두 섞어서 흰색을 만드세요! - - 당신에겐 무한한 땅이 있습니다. 굳이 공간을 적게 쓸 필요는 없으니, 맘껏 확장하세요! + - 당신에겐 무한한 땅이 있습니다. 굳이 공간을 적게 쓸 필요는 없으니, 마음껏 확장하세요! - Factorio도 플레이 해보세요! 제가 가장 좋아하는 게임입니다. - 4단 절단기는 오른쪽 상단부터 시계 방향으로 차례로 절단합니다. - 메인 메뉴에서 세이브 파일을 다운로드 할 수 있습니다. @@ -933,3 +1047,65 @@ tips: - F4 키를 누르면 FPS와 틱 비율을 표시합니다. - F4 키를 두번 누르면 마우스와 카메라의 타일을 표시합니다. - 왼쪽에 고정된 도형을 클릭하여 고정을 해제할 수 있습니다. +puzzleMenu: + play: 플레이 + edit: 편집 + title: 퍼즐 모드 + createPuzzle: 퍼즐 만들기 + loadPuzzle: 불러오기 + reviewPuzzle: 리뷰 & 공개 + validatingPuzzle: 퍼즐 검증중 + submittingPuzzle: 퍼즐 전송중 + noPuzzles: 이 섹션에 퍼즐이 없습니다. + categories: + levels: 레벨순 + new: 최신순 + top-rated: 등급순 + mine: 내 퍼즐 + easy: 쉬움 + hard: 어러움 + completed: 완료함 + medium: 중간 + official: 공식 + trending: 오늘의 인기 + trending-weekly: 이 주의 인기 + categories: 카테고리 + difficulties: 난이도순 + account: 내 퍼즐들 + search: 검색 + validation: + title: 잘못된 퍼즐 + noProducers: 일정 생성기를 배치해주세요! + noGoalAcceptors: 목표 수집기를 배치해주세요! + goalAcceptorNoItem: 하나 이상의 목표 수집기에 아이템이 지정되지 않았습니다. 모양을 운반해서 목표를 지정해 주세요. + goalAcceptorRateNotMet: 하나 이상의 목표 수집기에 아이템이 충분하지 않습니다. 모든 수집기의 표시가 초록색인지 확인해주세요. + buildingOutOfBounds: 하나 이상의 건물이 지을 수 있는 영역 밖에 존재합니다. 영역을 늘리거나 건물을 제거하세요. + autoComplete: 퍼즐이 스스로 완료됩니다! 일정 생성기가 만든 모양이 목표 수집기로 바로 들어가고 있는건 아닌지 확인해주세요. + difficulties: + easy: 쉬움 + medium: 중간 + hard: 어려움 + dlcHint: Purchased the DLC already? Make sure it is activated by right clicking + shapez.io in your library, selecting Properties > DLCs. +backendErrors: + ratelimit: 너무 빠른 시간 내 작업을 반복하고 있습니다. 조금만 기다려 주세요. + invalid-api-key: 백엔드 서버와 통신할 수 없습니다. 게임을 업데이트하거나 재시작해 주세요 (잘못된 API 키). + unauthorized: 백엔드 서버와 통신할 수 없습니다. 게임을 업데이트하거나 재시작해 주세요 (인증되지 않음). + bad-token: 백엔드 서버와 통신할 수 없습니다. 게임을 업데이트하거나 재시작해 주세요 (잘못된 토큰). + bad-id: 잘못된 퍼즐 구분자. + not-found: 해당하는 퍼즐을 찾을 수 없습니다. + bad-category: 해당하는 분류를 찾을 수 없습니다. + bad-short-key: 입력한 짧은 키가 올바르지 않습니다. + profane-title: 퍼즐 제목에 부적절한 단어가 포함되어 있습니다. + bad-title-too-many-spaces: 퍼즐 제목이 너무 짧습니다. + bad-shape-key-in-emitter: 일정 생성기에 잘못된 아이템이 지정되어 있습니다. + bad-shape-key-in-goal: 목표 수집기에 잘못된 아이템이 지정되어 있습니다. + no-emitters: 퍼즐에 일정 생성기가 하나도 존재하지 않습니다. + no-goals: 퍼즐에 목표 생성기가 하나도 존재하지 않습니다. + short-key-already-taken: 이 짧은 키는 이미 사용중입니다. 다른 것을 이용해 주세요. + can-not-report-your-own-puzzle: 자신이 만든 퍼즐을 신고할 수 없습니다. + bad-payload: 요청이 잘못된 데이터를 포함하고 있습니다. + bad-building-placement: 퍼즐에 잘못된 곳에 위치한 건물이 있습니다. + timeout: 요청 시간이 초과되었습니다. + too-many-likes-already: 이 퍼즐은 이미 너무 많은 하트를 받았습니다. 그래도 제거하고 싶다면 support@shapez.io로 문의하세요! + no-permission: 이 작업을 할 권한이 없습니다. diff --git a/translations/base-lt.yaml b/translations/base-lt.yaml index 889fbad3..0c963f33 100644 --- a/translations/base-lt.yaml +++ b/translations/base-lt.yaml @@ -52,6 +52,7 @@ global: escape: ESC shift: SHIFT space: SPACE + loggingIn: Logging in demoBanners: title: Demo Version intro: Get the standalone to unlock all features! @@ -71,6 +72,12 @@ mainMenu: madeBy: Made by <author-link> subreddit: Reddit savegameUnnamed: Unnamed + puzzleMode: Puzzle Mode + back: Back + puzzleDlcText: Do you enjoy compacting and optimizing factories? Get the Puzzle + DLC now on Steam for even more fun! + puzzleDlcWishlist: Wishlist now! + puzzleDlcViewNow: View Dlc dialogs: buttons: ok: OK @@ -84,6 +91,9 @@ dialogs: viewUpdate: View Update showUpgrades: Show Upgrades showKeybindings: Show Keybindings + retry: Retry + continue: Continue + playOffline: Play Offline importSavegameError: title: Import Error text: "Failed to import your savegame:" @@ -180,6 +190,70 @@ dialogs: title: Tutorial Available desc: There is a tutorial video available for this level, but it is only available in English. Would you like to watch it? + editConstantProducer: + title: Set Item + puzzleLoadFailed: + title: Puzzles failed to load + desc: "Unfortunately the puzzles could not be loaded:" + submitPuzzle: + title: Submit Puzzle + descName: "Give your puzzle a name:" + descIcon: "Please enter a unique short key, which will be shown as the icon of + your puzzle (You can generate them <link>here</link>, or choose one + of the randomly suggested shapes below):" + placeholderName: Puzzle Title + puzzleResizeBadBuildings: + title: Resize not possible + desc: You can't make the zone any smaller, because then some buildings would be + outside the zone. + puzzleLoadError: + title: Bad Puzzle + desc: "The puzzle failed to load:" + offlineMode: + title: Offline Mode + desc: We couldn't reach the servers, so the game has to run in offline mode. + Please make sure you have an active internet connection. + puzzleDownloadError: + title: Download Error + desc: "Failed to download the puzzle:" + puzzleSubmitError: + title: Submission Error + desc: "Failed to submit your puzzle:" + puzzleSubmitOk: + title: Puzzle Published + desc: Congratulations! Your puzzle has been published and can now be played by + others. You can now find it in the "My puzzles" section. + puzzleCreateOffline: + title: Offline Mode + desc: Since you are offline, you will not be able to save and/or publish your + puzzle. Would you still like to continue? + puzzlePlayRegularRecommendation: + title: Recommendation + desc: I <strong>strongly</strong> recommend playing the normal game to level 12 + before attempting the puzzle DLC, otherwise you may encounter + mechanics not yet introduced. Do you still want to continue? + puzzleShare: + title: Short Key Copied + desc: The short key of the puzzle (<key>) has been copied to your clipboard! It + can be entered in the puzzle menu to access the puzzle. + puzzleReport: + title: Report Puzzle + options: + profane: Profane + unsolvable: Not solvable + trolling: Trolling + puzzleReportComplete: + title: Thank you for your feedback! + desc: The puzzle has been flagged. + puzzleReportError: + title: Failed to report + desc: "Your report could not get processed:" + puzzleLoadShortKey: + title: Enter short key + desc: Enter the short key of the puzzle to load it. + puzzleDelete: + title: Delete Puzzle? + desc: Are you sure you want to delete '<title>'? This can not be undone! ingame: keybindingsOverlay: moveMap: Move @@ -201,6 +275,7 @@ ingame: clearSelection: Clear Selection pipette: Pipette switchLayers: Switch layers + clearBelts: Clear belts buildingPlacement: cycleBuildingVariants: Press <key> to cycle variants. hotkeyLabel: "Hotkey: <key>" @@ -348,6 +423,46 @@ ingame: achievements: title: Achievements desc: Hunt them all! + puzzleEditorSettings: + zoneTitle: Zone + zoneWidth: Width + zoneHeight: Height + trimZone: Trim + clearItems: Clear Items + share: Share + report: Report + clearBuildings: Clear Buildings + resetPuzzle: Reset Puzzle + puzzleEditorControls: + title: Puzzle Creator + instructions: + - 1. Place <strong>Constant Producers</strong> to provide shapes and + colors to the player + - 2. Build one or more shapes you want the player to build later and + deliver it to one or more <strong>Goal Acceptors</strong> + - 3. Once a Goal Acceptor receives a shape for a certain amount of + time, it <strong>saves it as a goal</strong> that the player must + produce later (Indicated by the <strong>green badge</strong>). + - 4. Click the <strong>lock button</strong> on a building to disable + it. + - 5. Once you click review, your puzzle will be validated and you + can publish it. + - 6. Upon release, <strong>all buildings will be removed</strong> + except for the Producers and Goal Acceptors - That's the part that + the player is supposed to figure out for themselves, after all :) + puzzleCompletion: + title: Puzzle Completed! + titleLike: "Click the heart if you liked the puzzle:" + titleRating: How difficult did you find the puzzle? + titleRatingDesc: Your rating will help me to make you better suggestions in the future + continueBtn: Keep Playing + menuBtn: Menu + puzzleMetadata: + author: Author + shortKey: Short Key + rating: Difficulty score + averageDuration: Avg. Duration + completionRate: Completion rate shopUpgrades: belt: name: Belts, Distributor & Tunnels @@ -554,6 +669,18 @@ buildings: name: Item Producer description: Available in sandbox mode only, outputs the given signal from the wires layer on the regular layer. + constant_producer: + default: + name: Constant Producer + description: Constantly outputs a specified shape or color. + goal_acceptor: + default: + name: Goal Acceptor + description: Deliver shapes to the goal acceptor to set them as a goal. + block: + default: + name: Block + description: Allows you to block a tile. storyRewards: reward_cutter_and_trash: title: Cutting Shapes @@ -954,6 +1081,10 @@ keybindings: rotateToDown: "Rotate: Point Down" rotateToRight: "Rotate: Point Right" rotateToLeft: "Rotate: Point Left" + constant_producer: Constant Producer + goal_acceptor: Goal Acceptor + block: Block + massSelectClear: Clear belts about: title: About this Game body: >- @@ -1039,3 +1170,73 @@ tips: - 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. +puzzleMenu: + play: Play + edit: Edit + title: Puzzle Mode + createPuzzle: Create Puzzle + loadPuzzle: Load + reviewPuzzle: Review & Publish + validatingPuzzle: Validating Puzzle + submittingPuzzle: Submitting Puzzle + noPuzzles: There are currently no puzzles in this section. + categories: + levels: Levels + new: New + top-rated: Top Rated + mine: My Puzzles + easy: Easy + hard: Hard + completed: Completed + medium: Medium + official: Official + trending: Trending today + trending-weekly: Trending weekly + categories: Categories + difficulties: By Difficulty + account: My Puzzles + search: Search + validation: + title: Invalid Puzzle + noProducers: Please place a Constant Producer! + noGoalAcceptors: Please place a Goal Acceptor! + goalAcceptorNoItem: One or more Goal Acceptors have not yet assigned an item. + Deliver a shape to them to set a goal. + goalAcceptorRateNotMet: One or more Goal Acceptors are not getting enough items. + Make sure that the indicators are green for all acceptors. + buildingOutOfBounds: One or more buildings are outside of the buildable area. + Either increase the area or remove them. + autoComplete: Your puzzle autocompletes itself! Please make sure your constant + producers are not directly delivering to your goal acceptors. + difficulties: + easy: Easy + medium: Medium + hard: Hard + dlcHint: Purchased the DLC already? Make sure it is activated by right clicking + shapez.io in your library, selecting Properties > DLCs. +backendErrors: + ratelimit: You are performing your actions too frequent. Please wait a bit. + invalid-api-key: Failed to communicate with the backend, please try to + update/restart the game (Invalid Api Key). + unauthorized: Failed to communicate with the backend, please try to + update/restart the game (Unauthorized). + bad-token: Failed to communicate with the backend, please try to update/restart + the game (Bad Token). + bad-id: Invalid puzzle identifier. + not-found: The given puzzle could not be found. + bad-category: The given category could not be found. + bad-short-key: The given short key is invalid. + profane-title: Your puzzle title contains profane words. + bad-title-too-many-spaces: Your puzzle title is too short. + bad-shape-key-in-emitter: A constant producer has an invalid item. + bad-shape-key-in-goal: A goal acceptor has an invalid item. + no-emitters: Your puzzle does not contain any constant producers. + no-goals: Your puzzle does not contain any goal acceptors. + short-key-already-taken: This short key is already taken, please use another one. + can-not-report-your-own-puzzle: You can not report your own puzzle. + bad-payload: The request contains invalid data. + bad-building-placement: Your puzzle contains invalid placed buildings. + timeout: The request timed out. + too-many-likes-already: The puzzle alreay got too many likes. If you still want + to remove it, please contact support@shapez.io! + no-permission: You do not have the permission to perform this action. diff --git a/translations/base-nl.yaml b/translations/base-nl.yaml index 798b8cda..92efd04c 100644 --- a/translations/base-nl.yaml +++ b/translations/base-nl.yaml @@ -1,17 +1,17 @@ steamPage: shortText: shapez.io is een spel dat draait om het bouwen van fabrieken voor het produceren en automatiseren van steeds complexere vormen in een oneindig - groot speelveld. + grote wereld. discordLinkShort: Officiële Discord server intro: >- - Shapez.io is een spel waarin je fabrieken moet bouwen voor de - automatische productie van geometrische vormen. + Shapez.io is een spel waarin je fabrieken bouwt voor de automatische + productie van geometrische vormen. - Naarmate het spel vordert, worden de vormen complexer, en moet je uitbreiden in het oneindige speelveld. + Naarmate het spel vordert, worden de vormen complexer, en zul je moeten uitbreiden in de oneindige wereld. - En als dat nog niet genoeg is moet je ook nog eens steeds meer produceren om aan de vraag te kunnen voldoen. Het enige dat helpt is uitbreiden! + En als dat nog niet genoeg is produceer je ook nog steeds meer om aan de vraag te kunnen voldoen. Het enige dat helpt is uitbreiden! - Ondanks het feit dat je in het begin alleen vormen maakt, komt er het punt waarop je ze moet kleuren. Deze kleuren moet je vinden en mengen! + Ondanks het feit dat je in het begin alleen vormen maakt, komt er een punt waarop je ze gaat kleuren. Deze kleuren kun je vinden en mengen! Door het spel op Steam te kopen kun je de volledige versie spelen. Je kunt echter ook een demo versie spelen op shapez.io en later beslissen om over te schakelen zonder voortgang te verliezen. what_others_say: What people say about shapez.io @@ -53,6 +53,7 @@ global: escape: ESC shift: SHIFT space: SPATIE + loggingIn: Inloggen demoBanners: title: Demoversie intro: Koop de standalone om alle functies te ontgrendelen! @@ -64,7 +65,8 @@ mainMenu: discordLink: Officiële Discord-server (Engelstalig) helpTranslate: Help ons met vertalen! browserWarning: Sorry, maar dit spel draait langzaam in je huidige browser! Koop - de standalone versie of download chrome voor de volledige ervaring. + de standalone versie of download Google Chrome voor de volledige + ervaring. savegameLevel: Level <x> savegameLevelUnknown: Level onbekend continue: Ga verder @@ -72,6 +74,12 @@ mainMenu: madeBy: Gemaakt door <author-link> subreddit: Reddit savegameUnnamed: Naamloos + puzzleMode: Puzzel Modus + back: Terug + puzzleDlcText: Houd je van het comprimeren en optimaliseren van fabrieken? + Verkrijg de puzzel DLC nu op Steam voor nog meer plezier! + puzzleDlcWishlist: Voeg nu toe aan je verlanglijst! + puzzleDlcViewNow: View Dlc dialogs: buttons: ok: OK @@ -85,6 +93,9 @@ dialogs: viewUpdate: Zie Update showUpgrades: Zie Upgrades showKeybindings: Zie Sneltoetsen + retry: Opnieuw Proberen + continue: Ga Verder + playOffline: Offline Spelen importSavegameError: title: Importeerfout text: "Het importeren van je savegame is mislukt:" @@ -95,7 +106,7 @@ dialogs: title: Corrupte savegame text: "Het laden van je savegame is mislukt:" confirmSavegameDelete: - title: Bevestig het verwijderen + title: Bevestig verwijderen van savegame text: Ben je zeker dat je het volgende spel wil verwijderen?<br><br> '<savegameName>' op niveau <savegameLevel><br><br> Dit kan niet ongedaan worden gemaakt! @@ -104,7 +115,7 @@ dialogs: text: "Het verwijderen van de savegame is mislukt:" restartRequired: title: Opnieuw opstarten vereist - text: Je moet het spel opnieuw opstarten om de instellingen toe te passen. + text: Start het spel opnieuw op om de instellingen toe te passen. editKeybinding: title: Verander sneltoetsen desc: Druk op de toets of muisknop die je aan deze functie toe wil wijzen, of @@ -153,8 +164,8 @@ dialogs: van lopende banden om te draaien wanneer je ze plaatst.<br>" createMarker: title: Nieuwe markering - desc: Geef het een nuttige naam, Je kan ook een <strong>snel toets</strong> van - een vorm gebruiken (die je <link>here</link> kan genereren). + desc: Geef het een nuttige naam, Je kan ook een <strong>sleutel</strong> van een + vorm gebruiken (die je <link>hier</link> kunt genereren). titleEdit: Bewerk markering markerDemoLimit: desc: Je kunt maar twee markeringen plaatsen in de demo. Koop de standalone voor @@ -175,7 +186,7 @@ dialogs: editSignal: title: Stel het signaal in. descItems: "Kies een ingesteld item:" - descShortKey: ... of voer de <strong>hotkey</strong> in van een vorm (Die je + descShortKey: ... of voer de <strong>sleutel</strong> in van een vorm (Die je <link>hier</link> kunt vinden). renameSavegame: title: Hernoem opgeslagen spel @@ -185,16 +196,82 @@ dialogs: desc: Er is een tutorial video beschikbaar voor dit level! Zou je het willen bekijken? tutorialVideoAvailableForeignLanguage: - title: Tutorial Available + title: Tutorial Beschikbaar desc: Er is een tutorial beschikbaar voor dit level, maar het is alleen - beschikbaar in het Engels. Zou je het toch graag kijken? + beschikbaar in het Engels. Wil je toch een kijkje nemen? + editConstantProducer: + title: Item instellen + puzzleLoadFailed: + title: Puzzels kunnen niet geladen worden + desc: "Helaas konden de puzzels niet worden geladen:" + submitPuzzle: + title: Puzzel indienen + descName: "Geef je puzzel een naam:" + descIcon: "Voer een unieke vorm sleutel in, die wordt weergegeven als het icoon + van je puzzel (je kunt ze <link>hier</link> genereren, of je kunt er + een kiezen van de willekeurig voorgestelde vormen hieronder):" + placeholderName: Puzzel Naam + puzzleResizeBadBuildings: + title: Formaat wijzigen niet mogelijk + desc: Je kunt het gebied niet kleiner maken, want dan zouden sommige gebouwen + buiten het gebied zijn. + puzzleLoadError: + title: Foute Puzzel + desc: "De puzzel kon niet geladen worden:" + offlineMode: + title: Offline Modus + desc: We konden de server niet bereiken, het spel draait nu in de offline modus. + Zorg ervoor dat je een actieve internetverbinding heeft. + puzzleDownloadError: + title: Download fout + desc: "Downloaden van de puzzel is mislukt:" + puzzleSubmitError: + title: Indieningsfout + desc: "Het indienen van je puzzel is mislukt:" + puzzleSubmitOk: + title: Puzzel Gepubliceerd + desc: Gefeliciteerd! Je puzzel is gepubliceerd en kan nu worden gespeeld door + anderen. Je kunt het nu vinden in het gedeelte "Mijn puzzels". + puzzleCreateOffline: + title: Offline Modus + desc: Aangezien je offline bent, kun je je puzzels niet opslaan of publiceren. + Wil je nog steeds doorgaan? + puzzlePlayRegularRecommendation: + title: Aanbeveling + desc: Ik raad <strong>sterk</strong> aan om het normale spel tot niveau 12 te + spelen voordat je de puzzel-DLC probeert, anders kan je mechanica + tegenkomen die je nog niet kent. Wil je toch doorgaan? + puzzleShare: + title: Vorm Sleutel Gekopieerd + desc: De vorm sleutel van de puzzel (<key>) is naar je klembord gekopieerd! Het + kan in het puzzelmenu worden ingevoerd om toegang te krijgen tot de + puzzel. + puzzleReport: + title: Puzzel Rapporteren + options: + profane: Ongepast + unsolvable: Niet oplosbaar + trolling: Trollen + puzzleReportComplete: + title: Bedankt voor je feedback! + desc: De puzzel is gemarkeerd. + puzzleReportError: + title: Melden mislukt + desc: "Je melding kan niet worden verwerkt:" + puzzleLoadShortKey: + title: Voer een vorm sleutel in + desc: Voer de vorm sleutel van de puzzel in om deze te laden. + puzzleDelete: + title: Puzzel verwijderen? + desc: Weet je zeker dat je '<title>' wilt verwijderen? Dit kan niet ongedaan + gemaakt worden! ingame: keybindingsOverlay: - moveMap: Beweeg speelveld + moveMap: Beweeg rond de wereld selectBuildings: Selecteer gebied stopPlacement: Stop met plaatsen rotateBuilding: Draai gebouw - placeMultiple: Plaats meerdere + placeMultiple: Plaats meerderen reverseOrientation: Omgekeerde oriëntatie disableAutoOrientation: Schakel auto-oriëntatie uit toggleHud: HUD aan-/uitzetten @@ -209,6 +286,7 @@ ingame: clearSelection: Annuleer selectie pipette: Pipet switchLayers: Wissel lagen + clearBelts: Lopende banden leeg maken buildingPlacement: cycleBuildingVariants: Druk op <key> om tussen varianten te wisselen. hotkeyLabel: "Hotkey: <key>" @@ -266,48 +344,49 @@ ingame: waypoints: Markeringen hub: HUB description: Klik met de linkermuisknop op een markering om hier naartoe te - gaan, klik met de rechtermuisknop om de markering te - verwijderen.<br><br>Druk op <keybinding> om een markering te maken - in het huidige zicht, of <strong>rechtermuisknop</strong> om een - markering te maken bij het geselecteerde gebied. + gaan, klik met de rechtermuisknop om hem te verwijderen.<br><br>Druk + op <keybinding> om een markering te maken in het huidige zicht, of + <strong>rechtermuisknop</strong> om een markering te maken bij het + geselecteerde gebied. creationSuccessNotification: Markering is gemaakt. interactiveTutorial: title: Tutorial hints: 1_1_extractor: Plaats een <strong>ontginner</strong> op een - <strong>cirkelvorm</strong> om deze te onttrekken! + <strong>cirkelvorm</strong> om deze te ontginnen! 1_2_conveyor: "Verbind de ontginner met een <strong>lopende band</strong> aan je - hub!<br><br>Tip: <strong>Klik en sleep</strong> de lopende band + HUB!<br><br>Tip: <strong>Klik en sleep</strong> de lopende band met je muis!" 1_3_expand: "Dit is <strong>GEEN</strong> nietsdoen-spel! Bouw meer ontginners en lopende banden om het doel sneller te behalen.<br><br>Tip: Houd <strong>SHIFT</strong> ingedrukt om meerdere ontginners te plaatsen en gebruik <strong>R</strong> om ze te draaien." 2_1_place_cutter: "Plaats nu een <strong>Knipper</strong> om de cirkels in twee - te knippen halves!<br><br> PS: De knipper knipt altijd van + helften te knippen<br><br> PS: De knipper knipt altijd van <strong>boven naar onder</strong> ongeacht zijn oriëntatie." 2_2_place_trash: De knipper kan vormen <strong>verstoppen en - bijhouden</strong>!<br><br> Gebruik een <strong>vuilbak</strong> - om van het (!) niet nodige afval vanaf te geraken. + bijhouden</strong>!<br><br> Gebruik een + <strong>vuilnisbak</strong> om van het momenteel (!) niet nodige + afval af te geraken. 2_3_more_cutters: "Goed gedaan! Plaats nu <strong>2 extra knippers</strong> om - dit traag process te versnellen! <br><br> PS: Gebruik de + dit trage process te versnellen! <br><br> PS: Gebruik de <strong>0-9 sneltoetsen</strong> om gebouwen sneller te selecteren." 3_1_rectangles: "Laten we nu rechthoeken ontginnen! <strong>Bouw 4 - ontginners</strong> en verbind ze met de lever.<br><br> PS: Houd - <strong>SHIFT</strong> Ingedrukt terwijl je lopende banden + ontginners</strong> en verbind ze met de schakelaar.<br><br> PS: + Houd <strong>SHIFT</strong> Ingedrukt terwijl je lopende banden plaats om ze te plannen!" 21_1_place_quad_painter: Plaats de <strong>quad painter</strong> en krijg een paar <strong>cirkels</strong> in <strong>witte</strong> en <strong>rode</strong> kleur! - 21_2_switch_to_wires: Schakel naar de draden laag door te duwen op + 21_2_switch_to_wires: Schakel naar de draden-laag door te drukken op <strong>E</strong>!<br><br> <strong>verbind daarna alle inputs</strong> van de verver met kabels! 21_3_place_button: Mooi! Plaats nu een <strong>schakelaar</strong> en verbind het met draden! 21_4_press_button: "Druk op de schakelaar om het een <strong>Juist signaal door - te geven</strong> en de verver te activeren.<br><br> PS: Je moet - niet alle inputs verbinden! Probeer er eens 2." + te geven</strong> en de verver te activeren.<br><br> PS: Verbind + niet alle inputs! Probeer er eens 2." colors: red: Rood green: Groen @@ -323,8 +402,8 @@ ingame: empty: Leeg copyKey: Kopieer sleutel connectedMiners: - one_miner: 1 Miner - n_miners: <amount> Miners + one_miner: 1 Ontginner + n_miners: <amount> Ontginners limited_items: "Gelimiteerd tot: <max_throughput>" watermark: title: Demo versie @@ -342,13 +421,13 @@ ingame: desc: Automatiseer je fabrieken nog beter en maak ze nog sneller! upgrades: title: ∞ Upgrade Levels - desc: Deze demo heeft er enkel 5! + desc: Deze demo heeft er slechts 5! markers: title: ∞ Markeringen desc: Verdwaal nooit meer in je fabriek! wires: title: Kabels - desc: Een volledig nieuwe wereld! + desc: Een volledig nieuwe laag! darkmode: title: Dark Mode desc: Minder vervelend voor je ogen! @@ -357,13 +436,57 @@ ingame: desc: Ik maak dit spel in mijn vrije tijd! achievements: title: Achievements - desc: Hunt them all! + desc: Krijg ze allemaal! + puzzleEditorSettings: + zoneTitle: Gebied + zoneWidth: Breedte + zoneHeight: Hoogte + trimZone: Bijsnijden + clearItems: Items leeg maken + share: Delen + report: Rapporteren + clearBuildings: Clear Buildings + resetPuzzle: Reset Puzzle + puzzleEditorControls: + title: Puzzel Maker + instructions: + - 1. Plaats <strong>Constante Producenten</strong> om vormen en + kleuren aan de speler te bieden. + - 2. Bouw een of meer vormen waarvan je wil dat de speler ze later + maakt en lever het aan een of meerdere + <strong>Ontvangers</strong>. + - 3. Wanneer een Ontvanger voor een bepaalde tijd lang een vorm + ontvangt, <strong>wordt het opgeslagen als een doel</strong> dat + de speler later moet produceren (Aangegeven door de <strong>groene + indicator</strong>). + - 4. Klik de <strong>vergrendelknop</strong> om een gebouw uit te + schakelen. + - 5. Zodra je op review klikt, wordt je puzzel gevalideerd en kun je + het publiceren. + - 6. Bij publicatie, <strong>worden alle gebouwen + verwijderd</strong> behalve de Muren, Constante Producenten en + Ontvangers - Dat is het deel dat de speler tenslotte voor zichzelf + moet uitzoeken :) + puzzleCompletion: + title: Puzzel Voltooid! + titleLike: "Klik op het hartje als je de puzzel leuk vond:" + titleRating: Hoe moeilijk vond je de puzzel? + titleRatingDesc: Je beoordeling helpt me om je in de toekomst betere suggesties + te geven + continueBtn: Blijf Spelen + menuBtn: Menu + puzzleMetadata: + author: Auteur + shortKey: Vorm Sleutel + rating: Moeilijkheidsgraad + averageDuration: Gem. Speel Tijd + completionRate: Voltooiïngspercentage shopUpgrades: belt: - name: Banden, Verdeler & Tunnels + name: Lopende banden, Verdeler & Tunnels description: Snelheid x<currentMult> → x<newMult> miner: - name: Mijner + name: Ontginner description: Snelheid x<currentMult> → x<newMult> processors: name: Knippen, draaien & stapelen @@ -378,11 +501,11 @@ buildings: description: Transporteert voorwerpen, klik en sleep om meerdere te plaatsen. miner: default: - name: Mijner - description: Plaats op een vorm of kleur om deze te onttrekken. + name: Ontginner + description: Plaats op een vorm of kleur om deze te ontginnen. chainable: name: Ontginner (Ketting) - description: Plaats op een vorm of kleur om deze te onttrekken. Kan achter + description: Plaats op een vorm of kleur om deze te ontginnen. Kunnen achter elkaar worden geplaatst. underground_belt: default: @@ -460,19 +583,19 @@ buildings: balancer: default: name: Balanceerder - description: Multifunctioneel - Verdeel alle invoeren over alle uitvoeren. + description: Multifunctioneel - Verdeelt alle invoeren over alle uitvoeren. merger: name: Samenvoeger (compact) - description: Voeg 2 lopende banden samen. + description: Voegt 2 lopende banden samen. merger-inverse: name: Samenvoeger - description: Voeg 2 lopende banden samen. + description: Voegt 2 lopende banden samen. splitter: name: Splitser (compact) - description: Splits een lopende band in tweeën. + description: Splitst een lopende band in tweeën. splitter-inverse: name: Splitser - description: Splits een lopende band in tweeën. + description: Splitst een lopende band in tweeën. storage: default: name: Opslag @@ -485,7 +608,7 @@ buildings: constant_signal: default: name: Constant Signaal - description: Zend een constant signaal, dit kan een vorm, kleur of boolean (1 / + description: Zendt een constant signaal, dit kan een vorm, kleur of boolean (1 / 0) zijn. lever: default: @@ -494,18 +617,18 @@ buildings: logic_gate: default: name: AND poort - description: Zend een 1 uit als beide invoeren hetzelfde zijn. (Kan een vorm, + description: Zendt een 1 uit als beide invoeren hetzelfde zijn. (Kan een vorm, kleur of boolean (1/0) zijn) not: name: NOT poort - description: Zend een 1 uit als de invoer een 0 is. + description: Zendt een 1 uit als de invoer een 0 is. xor: name: XOR poort - description: Zend een 1 uit als de invoeren niet hetzelfde zijn. (Kan een vorm, + description: Zendt een 1 uit als de invoeren niet hetzelfde zijn. (Kan een vorm, kleur of boolean (1/0) zijn) or: name: OR poort - description: Zend een 1 uit als de invoeren wel of niet hetzelfde zijn, maar + description: Zendt een 1 uit als de invoeren wel of niet hetzelfde zijn, maar niet uit zijn. (Kan een vorm, kleur of boolean (1/0) zijn) transistor: default: @@ -527,22 +650,22 @@ buildings: reader: default: name: Lopende band lezer - description: Meet de gemiddelde doorvoer op de band. Geeft het laatste gelezen - item door aan de kabel. + description: Meet de gemiddelde doorvoer op de lopende band. Geeft het laatste + gelezen item door aan de kabel. analyzer: default: name: Vorm Analyse - description: Analiseert de onderste laag rechts boven en geeft de kleur en vorm + description: Analiseert de onderste laag rechtsboven en geeft de kleur en vorm door aan de kabel. comparator: default: name: Vergelijker - description: Zend 1 uit als beiden invoeren gelijk zijn, kunnen vormen, kleuren - of booleans (1/0) zijn + description: Zendt 1 uit als beiden invoeren gelijk zijn, dat kunnen vormen, + kleuren of booleans (1/0) zijn virtual_processor: default: - name: Virtuele Snijder - description: Snijdt de vorm virtueel in twee helften. + name: Virtuele Knipper + description: Knipt de vorm virtueel in twee helften. rotater: name: Virtuele Draaier description: Draait de vorm virtueel met de klok mee en tegen de klok in. @@ -556,37 +679,47 @@ buildings: painter: name: Virtuele Schilder description: Schildert de vorm virtueel vanaf de onderste invoer met de vorm aan - de rechter ingang. + de rechter invoer. item_producer: default: name: Item Producent - description: Alleen beschikbaar in sandbox-modus, geeft het gegeven signaal van - de kabel laag op de reguliere laag. + description: Alleen beschikbaar in sandbox-modus. Geeft het gegeven signaal van + de draden-laag op de normale laag. + constant_producer: + default: + name: Constante Producent + description: Voert constant een bepaalde vorm of kleur uit. + goal_acceptor: + default: + name: Ontvanger + description: Lever vormen aan de Ontvanger om ze als doel te stellen. + block: + default: + name: Blokkade + description: Hiermee kan je een tegel blokkeren. storyRewards: reward_cutter_and_trash: title: Vormen Knippen desc: Je hebt juist de <strong>knipper</strong> ontgrendeld, die vormen in de helft kan knippen van boven naar onder <strong>ongeacht zijn rotatie </strong>!<br><br>Wees zeker dat je het afval weggooit, want anders - <strong>zal het vastlopen</strong> - Voor deze reden heb ik je de - <strong>vuilbak</strong> gegeven, die alles vernietigd wat je erin - laat stromen! + <strong>zal het vastlopen</strong> - Daarom heb ik je de + <strong>vuilnisbak</strong> gegeven, die alles vernietigt wat je + erin laat stromen! reward_rotater: title: Roteren desc: De <strong>roteerder</strong> is ontgrendeld - Het draait vormen 90 graden met de klok mee. reward_painter: title: Verven - desc: "De <strong>verver</strong> is ontgrendeld - Onttrek wat kleur (op - dezelfde manier hoe je vormen onttrekt) en combineer het met een - vorm in de verver om ze te kleuren!<br><br>PS: Als je kleurenblind - bent, is er een <strong>kleurenblindmodus</strong> in de - instellingen!" + desc: "De <strong>verver</strong> is ontgrendeld - Ontgin wat kleur (op dezelfde + manier hoe je vormen ontgint) en combineer het met een vorm in de + verver om ze te kleuren!<br><br>PS: Als je kleurenblind bent, is er + een <strong>kleurenblind-modus</strong> in de instellingen!" reward_mixer: title: Kleuren mengen - desc: De <strong>menger</strong> is ontgrendeld - Gebruik dit gebouw om twee - kleuren te mengen met behulp van <strong>additieve - kleurmenging</strong>! + desc: De <strong>menger</strong> is ontgrendeld - Gebruik deze om twee kleuren + te mengen met behulp van <strong>additieve kleurmenging</strong>! reward_stacker: title: Stapelaar desc: Je kunt nu vormen combineren met de <strong>stapelaar</strong>! De inputs @@ -597,7 +730,7 @@ storyRewards: reward_splitter: title: Splitser/samenvoeger desc: Je hebt de <strong>splitser</strong> ontgrendeld, een variant van de - <strong>samenvoeger</strong> - Het accepteert 1 input en verdeelt + <strong>samenvoeger</strong>. - Het accepteert 1 input en verdeelt het in 2! reward_tunnel: title: Tunnel @@ -605,24 +738,24 @@ storyRewards: gebouwen en lopende banden door laten lopen. reward_rotater_ccw: title: Roteren (andersom) - desc: Je hebt een variant van de <strong>roteerder</strong> ontgrendeld - Het - roteert voorwerpen tegen de klok in! Om het te bouwen selecteer je + desc: Je hebt een variant van de <strong>roteerder</strong> ontgrendeld - Deze + roteert voorwerpen tegen de klok in! Om hem te plaatsen selecteer je de roteerder en <strong>druk je op 'T' om tussen varianten te wisselen</strong>! reward_miner_chainable: title: Ketting-ontginner - desc: "Je hebt de <strong>Ketting-ontginner</strong> ontgrendeld! Het kan - <strong>zijn materialen ontginnen</strong> via andere ontginners - zodat je meer materialen tegelijkertijd kan ontginnen!<br><br> PS: - De oude ontginner is vervangen in je toolbar!" + desc: "Je hebt de <strong>Ketting-ontginner</strong> ontgrendeld! Je kunt hem + <strong>koppelen aan andere ontginners</strong> zodat je meer + materialen tegelijkertijd kunt ontginnen!<br><br> PS: De oude + ontginner is vervangen in je toolbar!" reward_underground_belt_tier_2: title: Tunnel Niveau II desc: Je hebt een nieuwe variant van de <strong>tunnel</strong> ontgrendeld. - - Het heeft een <strong>groter bereik</strong>, en je kan nu ook die - tunnels mixen over en onder elkaar! + Het heeft een <strong>groter bereik</strong>, en je kunt nu ook + tunnels over en onder elkaar mixen! reward_cutter_quad: title: Quad Knippen - desc: Je hebt een variant van de <strong>knipper</strong> ontgrendeld - Dit + desc: Je hebt een variant van de <strong>knipper</strong> ontgrendeld - Deze knipt vormen in <strong>vier stukken</strong> in plaats van twee! reward_painter_double: title: Dubbel verven @@ -633,19 +766,18 @@ storyRewards: title: Opslagbuffer desc: Je hebt een variant van de <strong>opslag</strong> ontgrendeld - Het laat je toe om vormen op te slagen tot een bepaalde capaciteit!<br><br> - Het verkiest de linkse output, dus je kan het altijd gebruiken als + Het verkiest de linkse output, dus je kunt het altijd gebruiken als een <strong>overloop poort</strong>! reward_freeplay: - title: Vrij spel + title: Free-play modus desc: Je hebt het gedaan! Je hebt de <strong>free-play modus</strong> - ontgrendeld! Dit betekend dat vormen nu <strong>willekeurig</strong> - gegenereerd worden!<br><br> Omdat de hub vanaf nu een + ontgrendeld! Dit betekent dat vormen nu <strong>willekeurig</strong> + gegenereerd worden!<br><br> Omdat de HUB vanaf nu een <strong>bepaald aantal vormen per seconden</strong> nodig heeft, Raad ik echt aan een machine te maken die automatisch de juiste - vormen genereert!<br><br> De HUB geeft de vorm die je nodig hebt op - de tweede laag met draden, dus alles wat je moet doen is dat - analyseren en je fabriek dat automatisch laten maken op basis van - dat. + vormen genereert!<br><br> De HUB geeft de vorm die je nodig hebt + door op de draden-laag, dus je hoeft dat alleen te analyseren en je + fabriek dat automatisch te laten maken op basis daarvan. reward_blueprints: title: Blauwdrukken desc: Je kunt nu delen van je fabriek <strong>kopiëren en plakken</strong>! @@ -676,13 +808,13 @@ storyRewards: lopende banden 1! reward_belt_reader: title: Lopende band sensor - desc: Je hebt de <strong>lopende band lezer</strong> vrijgespeeld! Dit gebouw + desc: Je hebt de <strong>lopende band sensor</strong> vrijgespeeld! Dit gebouw geeft de doorvoer op een lopende band weer.<br><br>Wacht maar tot je kabels vrijspeeld, dan wordt het pas echt interessant! reward_rotater_180: title: Draaier (180 graden) - desc: Je hebt de 180 graden <strong>draaier</strong> vrijgespeeld! - Hiermee kun - je een item op de band 180 graden draaien! + desc: Je hebt de <strong>180 graden draaier</strong> vrijgespeeld! - Hiermee kun + je een item op de lopende band 180 graden draaien! reward_display: title: Scherm desc: "Je hebt het <strong>scherm</strong> ontgrendeld - Verbind een signaal met @@ -691,8 +823,8 @@ storyRewards: Probeer het te tonen op een scherm!" reward_constant_signal: title: Constant Signaal - desc: Je hebt het <strong>constante signaal</strong> vrijgespeeld op de kabel - dimensie! Dit gebouw is handig in samenwerking met <strong>item + desc: Je hebt het <strong>constante signaal</strong> vrijgespeeld op de draden + laag! Dit gebouw is handig in samenwerking met <strong>item filters</strong>.<br><br> Het constante signaal kan een <strong>vorm</strong>, <strong>kleur</strong> of <strong>boolean</strong> (1/0) zijn. @@ -702,26 +834,26 @@ storyRewards: je hier nog niet zo vrolijk van, maar eigenlijk zijn ze heel erg handig!<br><br> Met logische poorten kun je AND, OR en XOR operaties uitvoeren.<br><br> Als bonus krijg je ook nog een - <strong>transistor</strong> van mij! + <strong>transistor</strong> van me! reward_virtual_processing: title: Virtuele verwerking desc: Ik heb juist een hele hoop nieuwe gebouwen toegevoegd die je toetstaan om <strong>het process van vormen te stimuleren</strong>!<br><br> Je - kan nu de knipper, draaier, stapelaar en meer op de dradenlaag - stimuleren! Met dit heb je nu 3 opties om verder te gaan met het + kunt nu de knipper, draaier, stapelaar en meer op de dradenlaag + stimuleren! Daarmee heb je nu 3 opties om verder te gaan met het spel:<br><br> - Bouw een <strong>automatische fabriek</strong> om - eender welke mogelijke vorm te maken gebraagd door de HUB (Ik raad - aan dit te proberen!).<br><br> - Bouw iets cool met draden.<br><br> + elke mogelijke vorm te maken gevraagd door de HUB (Ik raad aan dit + te proberen!).<br><br> - Bouw iets cool met de draden-laag.<br><br> - Ga verder met normaal spelen.<br><br> Wat je ook kiest, onthoud - dat je plezier hoort te hebben! + dat je plezier blijft hebben! reward_wires_painter_and_levers: title: Wires & Quad Painter - desc: "Je hebt juist de <strong>draden laag</strong> ontgrendeld: Het is een + desc: "Je hebt juist de <strong>draden-laag</strong> ontgrendeld: Het is een aparte laag boven op de huidige laag en introduceert heel veel - nieuwe manieren om te spelen!<br><br> Voor het begin heb ik voor jou + nieuwe manieren om te spelen!<br><br> Aan het begin heb ik voor jou de <strong>Quad Painter</strong> ontgrendeld - Verbind de gleuf - waarin je wilt verven op de draden laag!<br><br> Om over te - schakelen naar de draden laag, Duw op <strong>E</strong>. <br><br> + waarin je wilt verven op de draden-laag!<br><br> Om over te + schakelen naar de draden-laag, Druk op <strong>E</strong>. <br><br> PS: <strong>Zet hints aan</strong> in de instellingen om de draden tutorial te activeren!" reward_filter: @@ -748,7 +880,7 @@ settings: labels: uiScale: title: Interface-schaal - description: Veranderd de grootte van de gebruikersinterface. De interface + description: Verandert de grootte van de gebruikersinterface. De interface schaalt nog steeds gebaseerd op de resolutie van je apparaat, maar deze optie heeft invloed op de hoeveelheid schaling. scales: @@ -759,7 +891,7 @@ settings: huge: Ultragroot scrollWheelSensitivity: title: Zoom-gevoeligheid - description: Veranderd hoe gevoelig het zoomen is (muiswiel of trackpad). + description: Verandert hoe gevoelig het zoomen is (muiswiel of trackpad). sensitivity: super_slow: Super langzaam slow: Langzaam @@ -804,7 +936,7 @@ settings: gebruikt worden om de instap in het spel makkelijker te maken. movementSpeed: title: Bewegingssnelheid - description: Veranderd hoe snel het beeld beweegt wanneer je het toetsenbord + description: Verandert hoe snel het beeld beweegt wanneer je het toetsenbord gebruikt. speeds: super_slow: Super langzaam @@ -824,7 +956,7 @@ settings: zodat de tekst makkelijker te lezen is. autosaveInterval: title: Autosave Interval - description: Bepaalt hoe vaak het spel automatisch opslaat. Je kan het hier ook + description: Bepaalt hoe vaak het spel automatisch opslaat. Je kunt het hier ook volledig mee uitschakelen. intervals: one_minute: 1 Minuut @@ -842,7 +974,7 @@ settings: description: Schakelt de waarschuwing uit die wordt weergegeven wanneer je meer dan 100 dingen probeert te knippen/verwijderen. enableColorBlindHelper: - title: Kleurenblindmodus + title: Kleurenblind-modus description: Schakelt verschillende hulpmiddelen in zodat je het spel alsnog kunt spelen wanneer je kleurenblind bent. rotationByBuilding: @@ -857,8 +989,8 @@ settings: title: Muziekvolume description: Stel het volume voor muziek in. lowQualityMapResources: - title: Lage kwaliteit van resources - description: Versimpeldde resources op de wereld wanneer ingezoomd om de + title: Lage kwaliteit van uiterlijk + description: Versimpelt het uiterlijk op de wereld wanneer ingezoomd om de performance te verbeteren. Het lijkt ook opgeruimder, dus probeer het zelf een keertje uit! disableTileGrid: @@ -885,9 +1017,9 @@ settings: met de pipet boven het vakje van een resource staat. simplifiedBelts: title: Versimpelde lopende banden - description: Toont geen items op de band tenzij je over de lopende band beweegt - met je muis. De functie wordt niet aangeraden tenzij het qua - performance echt niet anders kan! + description: Toont geen items op de lopende band tenzij je over de lopende band + beweegt met je muis. De functie wordt niet aangeraden tenzij het + wat je computer betreft echt niet anders kan! enableMousePan: title: Schakel bewegen met muis in description: Schakel deze functie in om met je muis het veld te kunnen bewegen. @@ -922,7 +1054,7 @@ keybindings: mapMoveRight: Beweeg naar rechts mapMoveDown: Beweeg omlaag mapMoveLeft: Beweeg naar links - centerMap: Ga naar het midden van het speelveld + centerMap: Ga naar het midden van de wereld mapZoomIn: Zoom in mapZoomOut: Zoom uit createMarker: Plaats een markering @@ -969,16 +1101,20 @@ keybindings: wire_tunnel: Kabel kruising display: Scherm reader: Lopende band lezer - virtual_processor: Virtuele Snijder + virtual_processor: Virtuele Knipper transistor: Transistor analyzer: Vorm Analyse comparator: Vergelijk - item_producer: Item Producent (Sandbox) + item_producer: Voorwerp Producent (Sandbox) copyWireValue: "Kabels: Kopieer waarde onder cursor" - rotateToUp: "Rotate: Point Up" - rotateToDown: "Rotate: Point Down" - rotateToRight: "Rotate: Point Right" - rotateToLeft: "Rotate: Point Left" + rotateToUp: "Rotate: Wijs omhoog" + rotateToDown: "Rotate: Wijs omlaag" + rotateToRight: "Rotate: Wijs naar rechts" + rotateToLeft: "Rotate: Wijs naar links" + constant_producer: Constante Producent + goal_acceptor: Ontvanger + block: Blokkade + massSelectClear: Lopende banden leeg maken about: title: Over dit spel body: >- @@ -1004,79 +1140,150 @@ demo: exportingBase: Exporteer volledige basis als afbeelding settingNotAvailable: Niet beschikbaar in de demo. tips: - - De hub accepteert elke vorm van invoer, niet alleen de huidige vorm! - - Zorg ervoor dat uw fabrieken modulair zijn - het loont! - - Bouw niet te dicht bij de hub, anders wordt het een enorme chaos! - - Als het stapelen niet werkt, probeer dan de ingangen om te wisselen. - - U kunt de richting van de lopende band planner wijzigen door op <b>R</b> + - De HUB accepteert elke vorm van invoer, niet alleen de huidige vorm! + - Zorg ervoor dat je fabrieken modulair zijn - het loont! + - Bouw niet te dicht bij de HUB, anders wordt het een enorme chaos! + - Als het stapelen niet werkt, probeer dan de invoeren om te wisselen. + - Je kunt de richting van de lopende band planner wijzigen door op <b>R</b> te drukken. - - Door <b> CTRL </b> ingedrukt te houden, kunnen lopende banden worden + - Door <b>CTRL</b> ingedrukt te houden, kunnen lopende banden worden gesleept zonder automatische oriëntatie. - - Verhoudingen blijven hetzelfde, zolang alle upgrades zich op hetzelfde - niveau bevinden. + - Verhoudingen van gebouw-snelheden blijven hetzelfde, zolang alle upgrades + zich op hetzelfde niveau bevinden. - Opeenvolgende uitvoering is efficiënter dan parallele uitvoering. - Je ontgrendelt later in het spel meer varianten van gebouwen! - - U kunt <b>T</b> gebruiken om tussen verschillende varianten te schakelen. + - Je kunt <b>T</b> gebruiken om tussen verschillende varianten te schakelen. - Symmetrie is de sleutel! - Je kunt verschillende tunnels weven. - Probeer compacte fabrieken te bouwen - het loont! - - De schilder heeft een gespiegelde variant die u kunt selecteren met + - De schilder heeft een gespiegelde variant die je kunt selecteren met <b>T</b> - Met de juiste bouwverhoudingen wordt de efficiëntie gemaximaliseerd. - - Op het maximale niveau vullen 5 ontginners een enkele band. + - Op het maximale niveau vullen 5 ontginners één lopende band. - Vergeet tunnels niet! - - U hoeft de items niet gelijkmatig te verdelen voor volledige efficiëntie. - - Als u <b>SHIFT</b> ingedrukt houdt tijdens het bouwen van lopende banden, + - Je hoeft de items niet gelijkmatig te verdelen voor volledige efficiëntie. + - Als je <b>SHIFT</b> ingedrukt houdt tijdens het bouwen van lopende banden, wordt de planner geactiveerd, zodat je gemakkelijk lange rijen kunt plaatsen. - - Snijders snijden altijd verticaal, ongeacht hun oriëntatie. + - Knippers knippen altijd verticaal, ongeacht hun oriëntatie. - Meng alle drie de kleuren om wit te krijgen. - De opslagbuffer geeft prioriteit aan de eerste uitvoer. - Investeer tijd om herhaalbare ontwerpen te maken - het is het waard! - Door <b>SHIFT</b> ingedrukt te houden, kunnen meerdere gebouwen worden geplaatst. - - U kunt <b>ALT</b> ingedrukt houden om de richting van de geplaatste banden - om te keren. + - Je kunt <b>ALT</b> ingedrukt houden om de richting van de geplaatste + lopende banden om te keren. - Efficiëntie is de sleutel! - - Vormontginningen die verder van de hub verwijderd zijn, zijn complexer. + - Vormontginningen die verder van de HUB verwijderd zijn, zijn complexer. - Machines hebben een beperkte snelheid, verdeel ze voor maximale efficiëntie. - - Gebruik verdelers om uw efficiëntie te maximaliseren. - - Organisatie is belangrijk. Probeer de transportbanden niet te veel over te + - Gebruik verdelers om je efficiëntie te maximaliseren. + - Organisatie is belangrijk. Probeer de lopende banden niet te veel over te steken. - Plan van tevoren, anders wordt het een enorme chaos! - - Verwijder uw oude fabrieken niet! Je hebt ze nodig om upgrades te + - Verwijder je oude fabrieken niet! Je hebt ze nodig om upgrades te ontgrendelen. - Probeer in je eentje level 20 te verslaan voordat je hulp zoekt! - - Maak de dingen niet ingewikkeld, probeer eenvoudig te blijven en u zult + - Maak de dingen niet ingewikkeld, probeer eenvoudig te blijven en je zult ver komen. - - Mogelijk moet u later in het spel fabrieken hergebruiken. Plan uw + - Mogelijk zul je later in het spel fabrieken moeten hergebruiken. Plan je fabrieken zodat ze herbruikbaar zijn. - - Soms kunt u een gewenste vorm op de kaart vinden zonder deze met + - Soms kun je een gewenste vorm op de kaart vinden zonder deze met stapelaars te maken. - - Volle windmolens / vuurwielen kunnen nooit op natuurlijke wijze spawnen. - - Kleur uw vormen voordat u ze snijdt voor maximale efficiëntie. + - Volle windmolens kunnen nooit op natuurlijke wijze spawnen. + - Kleur je vormen voordat je ze knipt voor maximale efficiëntie. - Bij modules is ruimte slechts een beleving; een zorg voor sterfelijke mannen. - Maak een aparte blueprint fabriek. Ze zijn belangrijk voor modules. - - Bekijk de kleurenmixer eens wat beter, en uw vragen worden beantwoord. + - Bekijk de kleurenmixer eens wat beter, en je vragen worden beantwoord. - Gebruik <b>CTRL</b> + klik om een gebied te selecteren. - - Te dicht bij de hub bouwen kan latere projecten in de weg staan. - - Het speldpictogram naast elke vorm in de upgradelijst zet deze vast op het - scherm. + - Te dicht bij de HUB bouwen kan latere projecten in de weg staan. + - Met het speldpictogram naast elke vorm in de upgradelijst zet deze vast op + het scherm. - Meng alle primaire kleuren door elkaar om wit te maken! - - Je hebt een oneindige kaart, verkramp je fabriek niet, breid uit! + - Je hebt een oneindige wereld, verkramp je fabriek niet, breid uit! - Probeer ook Factorio! Het is mijn favoriete spel. - - De quad-snijder snijdt met de klok mee vanaf de rechterbovenhoek! + - De quad-knipper knipt met de klok mee vanaf de rechterbovenhoek! - Je kunt je savegames downloaden in het hoofdmenu! - - Deze game heeft veel handige sneltoetsen! Bekijk zeker de + - Dit spel heeft veel handige sneltoetsen! Bekijk zeker de instellingenpagina. - - Deze game heeft veel instellingen, bekijk ze zeker! - - De markering naar uw hub heeft een klein kompas om de richting aan te + - Dit spel heeft veel instellingen, bekijk ze zeker! + - De markering naar je HUB heeft een klein kompas om de richting aan te geven! - - Om de banden leeg te maken, knipt u het gebied af en plakt u het op - dezelfde locatie. - - Druk op F4 om uw FPS en Tick Rate weer te geven. + - Om de lopende banden leeg te maken, kun je een gebied kopiëren en plakken + op dezelfde locatie. + - Druk op F4 om je FPS en Tick Rate weer te geven. - Druk twee keer op F4 om de tegel van je muis en camera weer te geven. - Je kan aan de linkerkant op een vastgezette vorm klikken om deze los te maken. +puzzleMenu: + play: Spelen + edit: Bewerken + title: Puzzel Modus + createPuzzle: Puzzel Maken + loadPuzzle: Laden + reviewPuzzle: Beoordeel en publiceer + validatingPuzzle: Puzzel Valideren + submittingPuzzle: Puzzel Indienen + noPuzzles: Er zijn momenteel geen puzzels in deze sectie. + dlcHint: Heb je de DLC al gekocht? Zorg ervoor dat het is geactiveerd door met + de rechtermuisknop op shapez.io in uw bibliotheek te klikken, + Eigenschappen > DLC's te selecteren. + categories: + levels: Levels + new: Nieuw + top-rated: Best Beoordeeld + mine: Mijn Puzzels + easy: Makkelijk + hard: Moeilijk + completed: Voltooid + medium: Medium + official: Officieell + trending: Trending vandaag + trending-weekly: Trending wekelijks + categories: Categorieën + difficulties: Op Moeilijkheidsgraad + account: Mijn Puzzels + search: Zoeken + validation: + title: Ongeldige Puzzel + noProducers: Plaats alstublieft een Constante Producent! + noGoalAcceptors: Plaats alstublieft een Ontvanger! + goalAcceptorNoItem: Een of meer Ontvangers hebben nog geen item toegewezen. Geef + ze een vorm om een doel te stellen. + goalAcceptorRateNotMet: Een of meerdere Ontvangers krijgen niet genoeg items. + Zorg ervoor dat de indicatoren groen zijn voor alle acceptanten. + buildingOutOfBounds: Een of meer gebouwen bevinden zich buiten het bebouwbare + gebied. Vergroot het gebied of verwijder ze. + autoComplete: Je puzzel voltooit zichzelf automatisch! Zorg ervoor dat je + Constante Producenten niet rechtstreeks aan je Ontvangers leveren. + difficulties: + easy: Makkelijk + medium: Medium + hard: Moeilijk +backendErrors: + ratelimit: Je voert je handelingen te vaak uit. Wacht alstublieft even. + invalid-api-key: Kan niet communiceren met de servers, probeer alstublieft het + spel te updaten/herstarten (ongeldige API-sleutel). + unauthorized: Kan niet communiceren met de servers, probeer alstublieft het spel + te updaten/herstarten (Onbevoegd). + bad-token: Kan niet communiceren met de servers, probeer alstublieft het spel te + updaten/herstarten (Ongeldige Token). + bad-id: Ongeldige puzzel-ID. + not-found: De opgegeven puzzel is niet gevonden. + bad-category: De opgegeven categorie is niet gevonden. + bad-short-key: De opgegeven vorm sleutel is ongeldig. + profane-title: Je puzzel titel bevat groffe woorden. + bad-title-too-many-spaces: Je puzzel titel is te kort. + bad-shape-key-in-emitter: Een Ontvanger heeft een ongeldig item. + bad-shape-key-in-goal: Een Constante Producent heeft een ongeldig item. + no-emitters: Je puzzel heeft geen Constante Producenten. + no-goals: Je puzzel heeft geen Ontvangers. + short-key-already-taken: Deze vorm sleutel is al in gebruik, gebruik een andere. + can-not-report-your-own-puzzle: Je kunt je eigen puzzel niet melden. + bad-payload: Het verzoek bevat ongeldige gegevens. + bad-building-placement: Je puzzel bevat ongeldig geplaatste gebouwen. + timeout: Het verzoek is verlopen. + too-many-likes-already: De puzzel heeft al te veel likes. Als je het nog steeds + wilt verwijderen, neem dan contact op support@shapez.io! + no-permission: Je bent niet gemachtigd om deze actie uit te voeren. diff --git a/translations/base-no.yaml b/translations/base-no.yaml index 22679991..7a29d5c6 100644 --- a/translations/base-no.yaml +++ b/translations/base-no.yaml @@ -14,14 +14,14 @@ steamPage: Mens du kun produserer former i starten må du fargelegge de senere - for å gjøre dette må du hente ut og blande farger! Ved å kjøpe spillet på Steam får du tilgang til fullversjonen, men du kan også spille en demo på shapez.io først og bestemme deg senere! - what_others_say: What people say about shapez.io - nothernlion_comment: This game is great - I'm having a wonderful time playing, - and time has flown by. - notch_comment: Oh crap. I really should sleep, but I think I just figured out - how to make a computer in shapez.io - steam_review_comment: This game has stolen my life and I don't want it back. - Very chill factory game that won't let me stop making my lines more - efficient. + what_others_say: Hva sier andre om shapez.io + nothernlion_comment: Dette er et fantastisk spill - Jeg storkoser meg når jeg + spiller det, og tiden bare flyr avgårde. + notch_comment: Oops. Jeg burde egentlig sove, men jeg tror jeg nettop fant ut + hvordan man lager en PC i shapez.io + steam_review_comment: Dette spillet har sjålet livet mitt, og jeg vil ikke ha + det tilbake. Veldig avslappende fabrikkspill som ikke stopper meg fra å + lage båndene mere effektive. global: loading: Laster error: Feil @@ -53,6 +53,7 @@ global: escape: ESC shift: SHIFT space: MELLOMROM + loggingIn: Logger inn demoBanners: title: Demo Versjon intro: Skaff deg frittstående versjon for å åpne alle funksjoner! @@ -72,7 +73,13 @@ mainMenu: newGame: Nytt Spill madeBy: Laget av <author-link> subreddit: Reddit - savegameUnnamed: Unnamed + savegameUnnamed: Ingen Navn + puzzleMode: Puzzle Modus + back: Tilbake + puzzleDlcText: Liker du å bygge kompate og optimaliserte fabrikker? Skaff deg + Puzzle tilleggspakken nå på Steam for mere moro! + puzzleDlcWishlist: Legg i ønskeliste nå! + puzzleDlcViewNow: View Dlc dialogs: buttons: ok: OK @@ -86,6 +93,9 @@ dialogs: viewUpdate: Vis Oppdatering showUpgrades: Vis Oppgraderinger showKeybindings: Se Hurtigtaster + retry: Prøv på nytt + continue: Fortsett + playOffline: Spill Offline importSavegameError: title: Importeringsfeil text: "Kunne ikke importere lagringsfilen:" @@ -97,9 +107,8 @@ dialogs: text: "Kunne ikke laste inn lagringsfilen:" confirmSavegameDelete: title: Bekreft sletting - text: Are you sure you want to delete the following game?<br><br> - '<savegameName>' at level <savegameLevel><br><br> This can not be - undone! + text: Er du sikker på at du vil slette følgende spill?<br><br> '<savegameName>' + på nivå <savegameLevel><br><br> Handlinge kan ikke reversjeres! savegameDeletionError: title: Kunne ikke slette text: "Kunne ikke slette lagringsfilen:" @@ -155,8 +164,8 @@ dialogs: samlebånd.<br>" createMarker: title: Ny Markør - desc: Give it a meaningful name, you can also include a <strong>short - key</strong> of a shape (Which you can generate <link>here</link>) + desc: Gi den et meningsfult navn, du kan også inkludere <strong>korte + koder</strong> av en form (Som du kan generere <link>her</link>) titleEdit: Rediger markør markerDemoLimit: desc: Du kan kun ha to markører i demoverjsonen. Skaff deg den frittstående @@ -171,21 +180,86 @@ dialogs: desc: Du har ikke råd til å lime inn dette området! er du sikker på at du vil klippe det ut? editSignal: - title: Set Signal - descItems: "Choose a pre-defined item:" - descShortKey: ... or enter the <strong>short key</strong> of a shape (Which you - can generate <link>here</link>) + title: Velg Signal + descItems: "Velg en forhåndsvalgt gjenstand:" + descShortKey: ... eller skriv inn <strong>korte koden</strong> av en form (Som + du kan generere <link>her</link>) renameSavegame: - title: Rename Savegame - desc: You can rename your savegame here. + title: Bytt Spillnavn + desc: Du kan bytte navn på ditt lagrede spill her. tutorialVideoAvailable: - title: Tutorial Available - desc: There is a tutorial video available for this level! Would you like to - watch it? + title: Introduksjon tilgjengelig + desc: Det er en introduksjonsvideo tilgjengelig for dette nivået! Ønsker du å ta + en titt? tutorialVideoAvailableForeignLanguage: - title: Tutorial Available - desc: There is a tutorial video available for this level, but it is only - available in English. Would you like to watch it? + title: Introduksjon tilgjengelig + desc: Det er en introduksjonsvideo tilgjengelig, men den er kun tilgjengelig på + Engelsk. Ønsker du å ta en titt? + editConstantProducer: + title: Velg Gjenstand + puzzleLoadFailed: + title: Puslespill feilet å laste inn + desc: "Beklageligvis, kunne ikke puslespillet lastes inn:" + submitPuzzle: + title: Send inn Puslespill + descName: "Gi ditt puslespill et navn:" + descIcon: "Vennligst skriv en kort, unik gjenkjenner, som vil bli vist som et + ikon av ditt brett (Du kan lage dem <link>her</link>, eller velge en + av de tilfeldigve genererte nedenfor):" + placeholderName: Puslespilltittel + puzzleResizeBadBuildings: + title: Omgjøring av størrelse ikke mulig + desc: Du kan ikke gjøre sonen noe mindre, for da vil noen bygninger være utenfor + sonen. + puzzleLoadError: + title: Feil i puslespillet + desc: "Puslespillet kunne ikke laste inn:" + offlineMode: + title: Offline Modus + desc: Fikk ikke kontakt med serveren, så spillet må kjøres i offline modus. Sørg + for at du har en fungerende internett tilkobling. + puzzleDownloadError: + title: Nedlastningsfeil + desc: "Kunne ikke laste ned puslespillet:" + puzzleSubmitError: + title: Innsendingsfeil + desc: "Kunne ikke sende inn puslespillet:" + puzzleSubmitOk: + title: Puslespill sendt inn + desc: Gratulerer! Ditt puslespill har blitt publisert og kan nå bli spilt av + andre. Du kan finne den under "Mine puslespill" seksjonen. + puzzleCreateOffline: + title: Offline Modus + desc: Siden du er frakoblet, så kan du ikke lagre og/eller publisere dine + puslespill. Ønsker du å fortsette? + puzzlePlayRegularRecommendation: + title: Anbefalinger + desc: Jeg anbefaler på det <strong>sterkeste</strong> å spille hovedspillet til + nivå 12 før du prøver Puzzle tillegspakken, ellers kan du oppleve + mekanikk som du ikke har prøvd før. Ønsker du å fortsette? + puzzleShare: + title: Kort kode kopiert + desc: Korte koden av puslespillet (<key>) har blitt kopiert til din + utklippstavle! Den kan skrives inn i puslespillmenyen for å få + tilgang til puslespillet. + puzzleReport: + title: Rapporter Puslespill + options: + profane: Vulgært + unsolvable: Ikke løsbart + trolling: Trolling + puzzleReportComplete: + title: Takk for din tilbakemelding! + desc: Puslespillet har blitt markert. + puzzleReportError: + title: Kunne ikke rapportere + desc: "Din rapport kunne ikke prosesseres:" + puzzleLoadShortKey: + title: Skriv inn kort kode + desc: Skriv inn korte koden til puslespillet for å laste den inn. + puzzleDelete: + title: Delete Puzzle? + desc: Are you sure you want to delete '<title>'? This can not be undone! ingame: keybindingsOverlay: moveMap: Beveg @@ -207,9 +281,10 @@ ingame: clearSelection: Fjern Valgte pipette: Pipette switchLayers: Bytt lag + clearBelts: Tøm belter buildingPlacement: cycleBuildingVariants: Trykk <key> for å veksle mellom variantene. - hotkeyLabel: "Hotkey: <key>" + hotkeyLabel: "Hurtigtast: <key>" infoTexts: speed: Hastighet range: Lengde @@ -226,7 +301,7 @@ ingame: notifications: newUpgrade: En ny oppgradering er tilgjengelig! gameSaved: Spillet ditt er lagret. - freeplayLevelComplete: Level <level> has been completed! + freeplayLevelComplete: Nivå <level> har blitt løst! shop: title: Oppgraderinger buttonUnlock: Oppgrader @@ -249,7 +324,7 @@ ingame: shapesDisplayUnits: second: <shapes> / s minute: <shapes> / m - hour: <shapes> / h + hour: <shapes> / t settingsMenu: playtime: Spilletid buildingsPlaced: Bygninger @@ -280,30 +355,30 @@ ingame: og belter for å nå målet raskere.<br><br>Tips: Hold <strong>SHIFT</strong> for å plassere flere utdragere, og bruk <strong>R</strong> for å rotere dem." - 2_1_place_cutter: "Now place a <strong>Cutter</strong> to cut the circles in two - halves!<br><br> PS: The cutter always cuts from <strong>top to - bottom</strong> regardless of its orientation." - 2_2_place_trash: The cutter can <strong>clog and stall</strong>!<br><br> Use a - <strong>trash</strong> to get rid of the currently (!) not - needed waste. - 2_3_more_cutters: "Good job! Now place <strong>2 more cutters</strong> to speed - up this slow process!<br><br> PS: Use the <strong>0-9 - hotkeys</strong> to access buildings faster!" - 3_1_rectangles: "Now let's extract some rectangles! <strong>Build 4 - extractors</strong> and connect them to the hub.<br><br> PS: - Hold <strong>SHIFT</strong> while dragging a belt to activate - the belt planner!" - 21_1_place_quad_painter: Place the <strong>quad painter</strong> and get some - <strong>circles</strong>, <strong>white</strong> and - <strong>red</strong> color! - 21_2_switch_to_wires: Switch to the wires layer by pressing - <strong>E</strong>!<br><br> Then <strong>connect all four - inputs</strong> of the painter with cables! - 21_3_place_button: Awesome! Now place a <strong>Switch</strong> and connect it - with wires! - 21_4_press_button: "Press the switch to make it <strong>emit a truthy - signal</strong> and thus activate the painter.<br><br> PS: You - don't have to connect all inputs! Try wiring only two." + 2_1_place_cutter: "Nå plasser en <strong>kutter</strong> for å kutte sirklene i + to halve biter!<br><br> NB: Kutteren kutter alltid fra + <strong>toppen til bunnen</strong> uansett orienteringen." + 2_2_place_trash: Kutteren kan <strong>tette og lage kø</strong>!<br><br> Bruk en + <strong>søppelbøtte</strong> for å bli kvitt nåværende (!) ikke + nødvendige rester. + 2_3_more_cutters: "Godt jobba! Nå plasser <strong>2 flere kuttere</strong> for å + øke hastigheten på denne prosessen!<br><br> NB: Bruk <strong>0-9 + hurtigtastene</strong> for å velge bygning raskere!" + 3_1_rectangles: "Nå la oss fremvinne noen rektangler! <strong>Bygg 4 + utvinnere</strong> og koble de til hovedbygningen.<br><br> NB: + Hold inne <strong>SHIFT</strong> mens du drar beltet for å + aktivere belteplanleggeren!" + 21_1_place_quad_painter: Plasser <strong>4veis maleren</strong> og få noen + <strong>sirkler</strong>, <strong>hvite</strong> og + <strong>rød</strong> farget! + 21_2_switch_to_wires: Bytt til kabelnivået ved å trykke + <strong>E</strong>!<br><br> Deretter <strong>koble alle fire + inngangene</strong> til 4veis malerene med kabler! + 21_3_place_button: Flott! Nå plasser en <strong>bryter</strong> og koble den med + kabler! + 21_4_press_button: "Trykk på bryteren for å <strong>utgi et gyldig + signal</strong> som deretter aktiverer malerene.<br><br> NB: Du + må ikke koble til alle inngangene! Prøv bare med to." colors: red: Rød green: Grønn @@ -317,43 +392,84 @@ ingame: shapeViewer: title: Lag empty: Tom - copyKey: Copy Key + copyKey: Kopier Key connectedMiners: - one_miner: 1 Miner - n_miners: <amount> Miners - limited_items: Limited to <max_throughput> + one_miner: 1 Utgraver + n_miners: <amount> Utgravere + limited_items: Begrenset til <max_throughput> watermark: - title: Demo version - desc: Click here to see the Steam version advantages! - get_on_steam: Get on steam + title: Demo versjon + desc: Trykk her for å se fordelene med Steam verjsonen! + get_on_steam: Få på Steam standaloneAdvantages: - title: Get the full version! - no_thanks: No, thanks! + title: Få fullversjonen! + no_thanks: Nei takk! points: levels: - title: 12 New Levels - desc: For a total of 26 levels! + title: 12 Nye Nivåer + desc: Tilsvarer totalt 26 nivåer! buildings: - title: 18 New Buildings - desc: Fully automate your factory! + title: 18 Nye Bygninger + desc: Fullautomatiser din fabrikk! upgrades: - title: ∞ Upgrade Tiers - desc: This demo version has only 5! + title: ∞ Oppgraderingsnivåer + desc: Demoversjonen har kun 5! markers: - title: ∞ Markers - desc: Never get lost in your factory! + title: ∞ Markører + desc: Aldri gå deg vill i din fabrikk! wires: - title: Wires - desc: An entirely new dimension! + title: Kabler + desc: En helt ny dimensjon! darkmode: - title: Dark Mode - desc: Stop hurting your eyes! + title: Mørkmodus + desc: Slutt å skade øynene dine! support: - title: Support me - desc: I develop it in my spare time! + title: Støtt meg + desc: Jeg utvikler dette på min fritid! achievements: - title: Achievements - desc: Hunt them all! + title: Prestasjoner + desc: Skaff dem alle! + puzzleEditorSettings: + zoneTitle: Sone + zoneWidth: Bredde + zoneHeight: Høyde + trimZone: Kamtsone + clearItems: Fjern gjenstander + share: Del + report: Rapporter + clearBuildings: Clear Buildings + resetPuzzle: Reset Puzzle + puzzleEditorControls: + title: Puslespill lager + instructions: + - 1. Plasser <strong>konstante produseringer</strong> for å gi + former og farger til spilleren + - 2. Bygg en eller flere former du vil at spilleren skal bygge + senere og lever det til en eller fler <strong>Mål mottak</strong> + - 3. Når et målmottak mottar en form over en viss periode, vil den + <strong>lagres som et mål</strong> som spilleren må produsere + senere (Indikert av <strong>grønne skiltet</strong>). + - 4. Trykk <strong>låse knappen</strong> på en bygning for å + deaktivere den. + - 5. Så fort du trykker gjennomgå, vil ditt puslespill bli validert + og du kan publisere det. + - 6. Når det er publisert, <strong>vil alle bygningene bli + fjernet</strong> utenom produseringene og mål mottakene - Det er + delen som spilleren skal finne ut av dem selv, tross alt :) + puzzleCompletion: + title: Puslespill Fullført! + titleLike: "Trykk på hjertet hvis du likte puslespillet:" + titleRating: Hvor vanskelig syntes du det var? + titleRatingDesc: Ditt omdømme vil hjelpe hjelpe meg med å gi deg bedre forslag i + fremtiden + continueBtn: Fortsett å spill + menuBtn: Meny + puzzleMetadata: + author: Laget av + shortKey: Kort Kode + rating: Vanskelighetsscore + averageDuration: Gj. Varighet + completionRate: Fullføringsratio shopUpgrades: belt: name: Belter, Distributører & Tunneler @@ -372,7 +488,7 @@ buildings: deliver: Lever toUnlock: for å låse opp levelShortcut: nivå - endOfDemo: End of Demo + endOfDemo: Slutt på demoen belt: default: name: Samlebånd @@ -413,8 +529,8 @@ buildings: name: Roter (Mot klokken) description: Roter former mot klokken, 90 grader. rotate180: - name: Rotate (180) - description: Rotates shapes by 180 degrees. + name: Roter (180) + description: Roter former med 180 grader. stacker: default: name: Stabler @@ -435,9 +551,8 @@ buildings: inngang. quad: name: Maler (Firedobbel) - description: Allows you to color each quadrant of the shape individually. Only - slots with a <strong>truthy signal</strong> on the wires layer - will be painted! + description: Lar deg male hver del av en form individuelt. Kun innganger med et + <strong>ekte signal</strong> på kabel nivået vil bli malt! mirrored: name: Maler description: Maler hele formen på venstre inngang med fargen fra øverste @@ -452,128 +567,141 @@ buildings: name: Energikabel description: Lar deg transportere 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: Overfører signaler, som kan være gjenstander, farger eller + booleanere (1 / 0). Forskjellige fargede kabler kobler seg ikke + sammen. balancer: default: - name: Balancer - description: Multifunctional - Evenly distributes all inputs onto all outputs. + name: Balanserer + description: Multifunksjon - Distribuerer inngangene på utgangene jevnt. merger: - name: Merger (compact) - description: Merges two conveyor belts into one. + name: Balanserer (kompakt) + description: Slår sammen to belter til ett. merger-inverse: - name: Merger (compact) - description: Merges two conveyor belts into one. + name: Balanserer (kompakt) + description: Slår sammen to belter til ett. splitter: - name: Splitter (compact) - description: Splits one conveyor belt into two. + name: Splitter (kompakt) + description: Deler ett belte i to. splitter-inverse: - name: Splitter (compact) - description: Splits one conveyor belt into two. + name: Splitter (kompakt) + description: Deler ett belte i to. 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: Lager + description: Lagre overflødige gjenstander, opp til en viss antall. Prioriterer + venstre utgang og kan bli brukt som en overløpsventil. wire_tunnel: default: - name: Wire Crossing - description: Allows to cross two wires without connecting them. + name: Kabelkryss + description: Tillater å krysse to kabler uten å koble de sammen. constant_signal: default: - name: Constant Signal - description: Emits a constant signal, which can be either a shape, color or - boolean (1 / 0). + name: Konstant Signal + description: Utgir et konstant signal, som enten kan være en form, farge eller + booleaner (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: Bryter + description: Kan skru av/på et booleaner signal (1 / 0) på kabel nivået, som + igjen kan brukes for å kontrolere for eksempel filtrering. logic_gate: default: name: AND Gate - description: Emits a boolean "1" if both inputs are truthy. (Truthy means shape, - color or boolean "1") + description: Utgir en booleaner "1" hvis begge innganger er sanne. (Sanne betyr + form, farge eller booleaner "1") not: name: NOT Gate - description: Emits a boolean "1" if the input is not truthy. (Truthy means - shape, color or boolean "1") + description: Utgir en booleaner "1" hvis begge innganger ikke er sanne. (Sanne + betyr form, farge eller booleaner "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: Utgir en booleaner "1" hvis en av inngangene er sanne, men ikke + begge. (Sanne betyr form, farge eller booleaner "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: Utgir en booleaner "1" hvis en av inngangene er sanne. (Sanne betyr + form, farge eller booleaner "1") transistor: default: - name: Transistor - description: Forwards the bottom input if the side input is truthy (a shape, - color or "1"). + name: Transistorer + description: Vidresender nedre inngang om sideinngangen er sann (en form, farge + eller "1"). mirrored: - name: Transistor - description: Forwards the bottom input if the side input is truthy (a shape, - color or "1"). + name: Transistorer + description: Vidresender nedre inngang om sideinngangen er sann (en form, farge + eller "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: Koble til et signal for å rute alle tilsvarende gjenstander til + toppen og gjenværende til høyre. Kan bli kontroller med + booleaner signaler også. display: default: - name: Display - description: Connect a signal to show it on the display - It can be a shape, - color or boolean. + name: Skjerm + description: Koble til et signal for å vise det på skjermen - Det kan være en + form, farge eller booleaner. 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: Belteleser + description: Tillater å telle gjennomsnittelig flyt på beltet. Utgir det siste + leste gjenstanden på kabel nivået (når tilgjengelig). 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: Form Analyserer + description: Analyserer øverst i høyre av det laveste nivået på en form og + returnerer dens form og farge. comparator: default: - name: Compare - description: Returns boolean "1" if both signals are exactly equal. Can compare - shapes, items and booleans. + name: Sammenlign + description: Returnerer booleaner "1" hvis begge signalene er like. Kan + sammenligne former, gjenstander og booleaner. virtual_processor: default: - name: Virtual Cutter - description: Virtually cuts the shape into two halves. + name: Virituell Kutter + description: Kutt former virituelt i to deler. rotater: - name: Virtual Rotater - description: Virtually rotates the shape, both clockwise and counter-clockwise. + name: Virituell Roterer + description: Virituelt roterer formen, både med klokken og mot klokken. unstacker: - name: Virtual Unstacker - description: Virtually extracts the topmost layer to the right output and the - remaining ones to the left. + name: Virituell Avstabler + description: Virituelt separerer øverste nivået på høyre utgang og gjenværende + nede på venstre. stacker: - name: Virtual Stacker - description: Virtually stacks the right shape onto the left. + name: Virituell Stabler + description: Virituelt stabler formen på høyre med den på venstre. painter: - name: Virtual Painter - description: Virtually paints the shape from the bottom input with the shape on - the right input. + name: Virituell Fargelegger + description: Virituelt fargelegger formen fra nederste ingang med formen på + høyre inngang. 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: Gjenstands lager + description: Tilgjengelig kun i in sandbox modus, returnerer det gitte signalet + fra kabel nivået på det vanlige nivået. + constant_producer: + default: + name: Konstante Produseringer + description: Produserer konstant en spesifik form eller farge. + goal_acceptor: + default: + name: Mål Mottaker + description: Lever former til mål mottakeren for å sette dem som et mål. + block: + default: + name: Blokker + description: Lar deg blokkere en rute. storyRewards: reward_cutter_and_trash: title: Kutt Objekter - desc: You just unlocked the <strong>cutter</strong>, which cuts shapes in half - from top to bottom <strong>regardless of its - orientation</strong>!<br><br>Be sure to get rid of the waste, or - otherwise <strong>it will clog and stall</strong> - For this purpose - I have given you the <strong>trash</strong>, which destroys - everything you put into it! + desc: Du åpnet nettop <strong>kutteren</strong>, som kutter former i to fra + toppen til bunnen <strong>uavhengig av dens + orientasjon</strong>!<br><br>Sørg for å bli kvitt søppel, ellers + <strong>vil det samle seg og tette</strong> - For dette formålet så + har jeg gitt deg en <strong>søppelkasse</strong>, som ødelegger alt + du kaster i den! reward_rotater: title: Rotering desc: <strong>Rotereren</strong> har blitt tilgjengelig! Den roterer objekter @@ -596,10 +724,9 @@ storyRewards: bil <strong>til en</strong>. Hvis ikke, blir høyre inngang <strong>plassert over</strong> venstre inngang! reward_splitter: - title: Fordeler/Sammenslåer - desc: You have unlocked a <strong>splitter</strong> variant of the - <strong>balancer</strong> - It accepts one input and splits them - into two! + title: Fordeler + desc: Du har åpnet opp <strong>fordler</strong> varianten av + <strong>balansereren</strong> - Den godtar en inn og deler dem i to! reward_tunnel: title: Tunnel desc: <strong>Tunnelen</strong> har blitt tilgjengelig - Du kan nå transportere @@ -611,10 +738,10 @@ storyRewards: <strong>trykk 'T' for å veksle mellom variantene</strong>! reward_miner_chainable: title: Kjedeutdrager - desc: "You have unlocked the <strong>chained extractor</strong>! It can - <strong>forward its resources</strong> to other extractors so you - can more efficiently extract resources!<br><br> PS: The old - extractor has been replaced in your toolbar now!" + desc: "Du har åpnet <strong>kjedeutdrageren</strong>! Den kan <strong>vidresende + sine ressursser</strong> til andre utdragere så du kan mer effektivt + hente ut ressursser!<br><br> NB: Gamle utdrageren har blitt byttet + ut på din hurtigbar nå!" reward_underground_belt_tier_2: title: Tunnel Nivå II desc: Du har åpnet en ny variant av <strong>tunnelen</strong> - Den har @@ -631,18 +758,19 @@ storyRewards: konsumerer bare en farge istedenfor to! reward_storage: title: Lagringsbuffer - desc: You have unlocked the <strong>storage</strong> building - It allows you to - store items up to a given capacity!<br><br> It priorities the left - output, so you can also use it as an <strong>overflow gate</strong>! + desc: Du har åpnet <strong>lagringsbuffer</strong> bygningen - Den lar deg lagre + gjenstander opp til et gitt antall!<br><br> Den prioriterer venstre + utgangen, så du kan også bruke det som en + <strong>overløpsventil</strong>! reward_freeplay: title: Frispill - desc: You did it! You unlocked the <strong>free-play mode</strong>! This means - that shapes are now <strong>randomly</strong> generated!<br><br> - Since the hub will require a <strong>throughput</strong> from now - on, I highly recommend to build a machine which automatically - delivers the requested shape!<br><br> 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: Du klarte det! Du låste opp <strong>frispill modus</strong>! Dette betyr + at former er nå <strong>tilfeldige</strong> generert!<br><br> Siden + hovedbygningen nå krever en <strong>gjennomgang</strong> fra nå av, + anbefaler jeg å bygge en maskin som automatisk leverer ønskede + formen!<br><br> Hovedbygningen utgir den forespurte formen på kabel + nivået, så alt du må gjøre er å analysere det og automatisk + konfigerer din fabrikk basert på det. reward_blueprints: title: Blåkopier desc: Du kan nå <strong>kopiere og lime inn</strong> deler av fabrikken din! @@ -661,78 +789,79 @@ storyRewards: desc: Gratulerer!! Forresten, mer innhold er planlagt for den frittstående versjonen! reward_balancer: - title: Balancer - desc: The multifunctional <strong>balancer</strong> has been unlocked - It can - be used to build bigger factories by <strong>splitting and merging - items</strong> onto multiple belts! + title: Balanserer + desc: Den multifunksjonible <strong>balansereren</strong> har blitt tilgjengelig + - Den kan bli brukt for å bygge større fabrikker ved å <strong>dele + opp og slå sammen gjenstander</strong> på flere belter! reward_merger: - title: Compact Merger - desc: You have unlocked a <strong>merger</strong> variant of the - <strong>balancer</strong> - It accepts two inputs and merges them - into one belt! + title: Kompakt Sammenslåer + desc: Du har åpnet opp en <strong>sammenslåer</strong> variant av + <strong>balansereren</strong> - Den godtar to innganger og slår de + sammen til ett belte! reward_belt_reader: - title: Belt reader - desc: You have now unlocked the <strong>belt reader</strong>! It allows you to - measure the throughput of a belt.<br><br>And wait until you unlock - wires - then it gets really useful! + title: Belte Leser + desc: Du har låst opp <strong>belte leseren</strong>! Den lar deg måle trafikken + på et belte.<br><br>Og vent til du låser opp kabler - da blir den + veldig nyttig! reward_rotater_180: - title: Rotater (180 degrees) - desc: You just unlocked the 180 degrees <strong>rotater</strong>! - It allows - you to rotate a shape by 180 degrees (Surprise! :D) + title: Roterer (180 grader) + desc: Du åpnet opp 180 graders <strong>rotereren</strong>! - Den lar deg rotere + en form 180 grader (Overraskelse! :D) reward_display: - title: Display - desc: "You have unlocked the <strong>Display</strong> - Connect a signal on the - wires layer to visualize it!<br><br> PS: Did you notice the belt - reader and storage output their last read item? Try showing it on a - display!" + title: Skjerm + desc: "Du har åpnet opp <strong>Skjermen</strong> - Koble til et signal på kabel + nivået for å visualisere det!<br><br> NB: La du merke til belte + leseren og lagringsbygningen utgir siste gjenstanden de så? Prøv å + vis det på en skjerm!" reward_constant_signal: - title: Constant Signal - desc: You unlocked the <strong>constant signal</strong> building on the wires - layer! This is useful to connect it to <strong>item filters</strong> - for example.<br><br> The constant signal can emit a - <strong>shape</strong>, <strong>color</strong> or - <strong>boolean</strong> (1 / 0). + title: Konstant Signal + desc: Du åpnet opp <strong>konstant signal</strong> bygningen på kabel nivået! + Denne er brukbar for å koble til <strong>gjenstandsfilter</strong> + for eksempel.<br><br> Det konstante signalet kan utgi en + <strong>form</strong>, <strong>farge</strong> eller + <strong>booleaner</strong> (1 / 0). reward_logic_gates: title: Logic Gates - desc: You unlocked <strong>logic gates</strong>! You don't have to be excited - about this, but it's actually super cool!<br><br> With those gates - you can now compute AND, OR, XOR and NOT operations.<br><br> As a - bonus on top I also just gave you a <strong>transistor</strong>! + desc: Du åpnet opp <strong>logic gates</strong>! Du trenger ikke være så + overlykkelig for dette, men det er faktisk super kult!<br><br> Med + disse kan du nå utregne AND, OR, XOR og NOT operasjoner.<br><br> Som + en bonus på toppen, ga jeg deg også <strong>transistorer</strong>! reward_virtual_processing: - title: Virtual Processing - desc: I just gave a whole bunch of new buildings which allow you to - <strong>simulate the processing of shapes</strong>!<br><br> 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:<br><br> - - Build an <strong>automated machine</strong> to create any possible - shape requested by the HUB (I recommend to try it!).<br><br> - Build - something cool with wires.<br><br> - Continue to play - regulary.<br><br> Whatever you choose, remember to have fun! + title: Virtuell Prosessering + desc: Du fikk nettop en hel del av bygninger som lar deg <strong>simulere + prosessen av former</strong>!<br><br> Du kan nå simulere kutting, + rotering, stabling og mer på kabel nivået! Med dette har du nå tre + muligheter for å fortsette spillet:<br><br> - Bygg en + <strong>automatisert maskin</strong> som lager alle mulige former + forespurt av hovedbygningen (Jeg anbefaler deg å prøve + dette!).<br><br> - Bygg noe kult med kabler.<br><br> - Fortsett å + spill vanlig.<br><br> Hva nå enn du velger, husk å ha det gøy! reward_wires_painter_and_levers: - title: Wires & Quad Painter - desc: "You just unlocked the <strong>Wires Layer</strong>: It is a separate - layer on top of the regular layer and introduces a lot of new - mechanics!<br><br> For the beginning I unlocked you the <strong>Quad - Painter</strong> - Connect the slots you would like to paint with on - the wires layer!<br><br> To switch to the wires layer, press - <strong>E</strong>. <br><br> PS: <strong>Enable hints</strong> in - the settings to activate the wires tutorial!" + title: Kabler & 4veis Fargelegger + desc: "Du åpnet nettop <strong>kabel nivået</strong>: Det er et separat nivå på + toppen av det vanlige som introduserer mye nye mekanismer!<br><br> I + begynnelsen åpnet du <strong>4 veis fargeleggeren</strong> - Koble + til inngangene du vil male med på kabel nivået!<br><br> For å bytte + til kabel nivået, trykk <strong>E</strong>. <br><br> NB: + <strong>Skru på tips</strong> på instillinger for å aktivere kabel + nivå introduksjonen!" reward_filter: - title: Item Filter - desc: You unlocked the <strong>Item Filter</strong>! 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.<br><br> You can also pass in a - boolean signal (1 / 0) to entirely activate or disable it. + title: Gjenstandsfilter + desc: Du åpnet opp <strong>Gjenstandsfilter</strong>! Den vil rute gjenstander + enten til toppen eller høyre utgang avhengig av om de matcher + signalet fra kabel nivået eller ikke.<br><br> Du kan også legge inn + et booleaner signal (1 / 0) for å aktivere eller deaktivere den i + sin helhet. reward_demo_end: - title: End of Demo - desc: You have reached the end of the demo version! + title: Slutt på Demo + desc: Du har nådd slutten på demoversjonen! settings: title: Instillinger categories: general: Generelt userInterface: Brukergrensesnitt advanced: Avansert - performance: Performance + performance: Ytelse versionBadges: dev: Utvikling staging: Iscenesettelse @@ -843,55 +972,56 @@ settings: kan være mer komfortabelt hvis du ofte veksler mellom plassering av forskjellige bygninger. soundVolume: - title: Sound Volume - description: Set the volume for sound effects + title: Lyd Volum + description: Sett volumet på lydeffekter musicVolume: - title: Music Volume - description: Set the volume for music + title: Musikk Volum + description: Sett volumet på musikk 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: Lavkvalitets Kart Ressursser + description: Simplifiserer fremvisningen av ressursser på kartet når zoomet inn + for å forbedre ytelsen. Det ser også renere ut, så sørg for å + sjekke det ut! disableTileGrid: - title: Disable Grid - description: Disabling the tile grid can help with the performance. This also - makes the game look cleaner! + title: Deaktiver Rutenett + description: Deaktiver rutenettet kan hjelpe på ytelse. Dette vil også få + spillet til å se renere ut! 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: Fjern musepil ved høyreklikk + description: Skrudd på standard, fjerner hva enn du har ved musen når du + høyreklikker mens du har en bygning valgt for plassering. Hvis + deaktivert, så kan du slette bytninger ved å høyreklikke mens du + plasserer en bytgning. lowQualityTextures: - title: Low quality textures (Ugly) - description: Uses low quality textures to save performance. This will make the - game look very ugly! + title: Lavkvalitets teksturer (Stygt) + description: Bruker lavkvalitets teksturer for å forbedre ytelsen. Dette vil få + spillet til å se veldig sygt ut! 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: Vis Sektor Rammer + description: Spillet er delt inn i sektorer på 16x16 ruter, hvis denne + instillingen er skrudd på, vil kantene i hver sektor vises. 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: Velg utdrager på ressurss felt + description: Aktivert standard, velger utdrageren hvis du bruker pipette når du + holder over et ressurss felt. 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: Simplifiserte Belter (Sygt) + description: Fremviser ikke ting på belter forutenom når du holder musen over + beltet for å forbedre ytelsen. Jeg anbefaler ikke å spille med + dette med mindre du absolutt trenger ytelsen. 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: Aktiver Musebeveget Kart + description: Lar deg flytte rundt på kartet ved å bevege musen til kanten av + skjermen. Hastigheten avgjøres av Bevegelses Hastighet + instillingen. 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. + title: Forstørr mot musepekeren + description: Hvis aktivert, vil forstørring skje mot der du har musepekeren + posisjonert, ellers vil det være midt i kjermen. mapResourcesScale: - title: Map Resources Size - description: Controls the size of the shapes on the map overview (when zooming - out). + title: Kart Ressursser Størrelse + description: Kontrollerer størrelsen på former på kartoversikten (når zoomet + ut). rangeSliderPercentage: <amount> % keybindings: title: Hurtigtaster @@ -951,25 +1081,29 @@ keybindings: menuClose: Lukk meny switchLayers: Bytt lag wire: Energikabel - balancer: Balancer - storage: Storage - constant_signal: Constant Signal + balancer: Balanserer + storage: Lagringsboks + constant_signal: Konstant Signal logic_gate: Logic Gate - lever: Switch (regular) + lever: Bryter (vanlig) 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" - rotateToUp: "Rotate: Point Up" - rotateToDown: "Rotate: Point Down" - rotateToRight: "Rotate: Point Right" - rotateToLeft: "Rotate: Point Left" + wire_tunnel: Kabel Krysser + display: Skjerm + reader: Belte Leser + virtual_processor: Virituell Kutter + transistor: Transistorer + analyzer: Form Analyserer + comparator: Sammenlign + item_producer: Gjenstands Produserer (Sandbox) + copyWireValue: "Kabler: Kopier verdi under musen" + rotateToUp: "Roter: Pek Opp" + rotateToDown: "Roter: Pek Ned" + rotateToRight: "Roter: Pek Høyre" + rotateToLeft: "Roter: Pek Venstre" + constant_producer: Konstant Produserer + goal_acceptor: Mål Mottaker + block: Blokker + massSelectClear: Tøm Belter about: title: Om dette spillet body: >- @@ -995,63 +1129,138 @@ demo: exportingBase: Eksporter hele basen som bilde settingNotAvailable: Ikke tilgjengelig i demoversjonen. 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 <b>R</b>. - - Holding <b>CTRL</b> 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 <b>T</b> 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 <b>T</b> - - 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 <b>SHIFT</b> 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 <b>CTRL</b> allows to place multiple buildings. - - You can hold <b>ALT</b> 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 <b>CTRL</b> + 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. + - Hovedbygningen godtar alle mulige former, ikke kun nåværende form! + - Sørg for at dine fabrikker er modulære - det vil betale for seg selv! + - Ikke bygg for nære hovedbygningen. Ellers vil det bli et stort kaos! + - Hvis stabling ikke funker, prøv å bytt inngangene. + - Du kan veksle mellom belteplanleggerens orienterinv ved å trykke <b>R</b>. + - Hold nede <b>CTRL</b> for å dra belter uten auto orientering. + - Fordelingen forblir det samme, så lenge alle oppgraderingene er på samme + nivå. + - Seriell utføring er mer effektivt enn paralell utføring. + - Du vil låse opp flere varianter av bygninger senere i spillet! + - Du kan bruke <b>T</b> for å bytte mellom forskjellige varianter. + - Symmetri er nøkkelen! + - Du kan veve forskjellige typer av tuneller. + - Prøv å bygg kompate fabrikker - Det vil betale for seg selv! + - Fargeleggeren har en speilvent variant som du kan velge med <b>T</b> + - Å ha riktig forhold mellom bygningsantall vil maksimisere effektiviteten. + - På maks nivå, vil 5 utdragere fylle et eget belte. + - Ikke glem tunneller! + - Du trenger ikke å dele opp gjenstander jent for maks effektivitet. + - Holdt nede <b>SHIFT</b> for å aktivere belte planleggeren, den lar deg + plassere lange linjer med belter veldig lett. + - Kutteren vil alltid kutte vertikalt, uavhengig av orientasjonen. + - For hvit farge, kombiner alle tre fargene. + - Lagringbygningen prioriterer første utgangen. + - Sett av tid til å bygge repeterbare løsninger - det er verdt det! + - Hold nede <b>CTRL</b> for å plassere flere av samme bygning. + - Du kan holde nede <b>ALT</b> for å reversjere plasseringen av plasserte + belter. + - Effektivitet er nøkkelen! + - Form feltene som er lengt unna fra hovedbygget er mer avanserte. + - Bygninger har en begrenset hastighet. Del de opp for maksimum effektivitet. + - Bruk balanserere for å maksimere effektivitet. + - Organisering er viktig. Prøv å ikke kryss belter for mye. + - Planlegg i forveien, ellers vil det bli kaos! + - Ikke fjern dine gamle fabrikker! Du trenger de for å åpne oppgraderinger. + - Prøv å slå nivå 20 på egenhånd før du oppsøker hjelp! + - Ikke overkompliser ting, prøv å gjør det simpelt så kommer du langt. + - Det kan hende at du må gjenbruke fabrikker senere. Planlegg dine fabrikker + til å være gjenbrukbare. + - Noen ganger, kan du finne formen du trenger på kartet uten å lage det med + stablere. + - Fulle vindmøller / pinnehjul finnes ikke naturlig. + - Fargelegg dine former før du kutter de for maks effektivitet. + - Med moduler er plass bare en oppfatning; en bekymring for dødelige + mennesker. + - Lag en separat blueprint fabrikk. De er viktig for moduler. + - Sjekk ut fargeblanderen, og dine spørsmål vil bli besvart. + - Bruk <b>CTRL</b> + Trykk for å velge et område. + - Bygge for nære hovedbygnignen kan sette en stopper for senere prosjekter. + - Tegnestift ikonet ved siden av hver form i oppgraderingslisten låser det + fast til skjermen. + - Bland alle primærfargene sammen for å lage hvis farge! + - Du har et evit kart. Ikke tett sammen fabrikken, utvid! + - Sjekk også ut Factorio! Det er mitt favorittspill. + - 4veis Kutteren kutter med klokken, starter fra øverst i høyre! + - Du kan laste ned dine lagrede spill på hovedmenyen! + - Dette spillet har mange fornuftige hurtigtaster! Sjekk de ut på + instillinger. + - Dette spillet har masse instillinger, sørg for å sjekke de ut! + - Markøren på hjovedbygningen har et lite kompass for å indikere retningen + til den! + - For å tømme belter, kutt området også lim det inn igjen på samme område. + - Trykk F4 for å vise din FPS og Tick Rate. + - Trykk F4 to ganger for å vise ruten til din mus og kamera. + - Du kan trykke på en festet form på venstre for å fjerne festingen. +puzzleMenu: + play: Spill + edit: Endre + title: Puslespillmodus + createPuzzle: Lag Puslespill + loadPuzzle: Last inn + reviewPuzzle: Gjennomgå & Publiser + validatingPuzzle: Validerer Puslespill + submittingPuzzle: Publiserer Puslespill + noPuzzles: Det er for tiden ingen puslespill i denne seksjonen. + categories: + levels: Nivåer + new: Ny + top-rated: Høyest Rangert + mine: Mine Puslespill + easy: Lett + hard: Vanskelig + completed: Fullført + medium: Medium + official: Official + trending: Trending today + trending-weekly: Trending weekly + categories: Categories + difficulties: By Difficulty + account: My Puzzles + search: Search + validation: + title: Ugyldig Puslespill + noProducers: Venligst plasser en Konstant Produserer! + noGoalAcceptors: Vennligst plasser en Mål Mottaker! + goalAcceptorNoItem: En eller flere Mål Mottakere har ikke blitt tildelt en + gjenstand. Lever en form til dem for å sette målet. + goalAcceptorRateNotMet: En eller flere Mål Mottakere får ikke nok gjenstander. + Sørg for at indikatorene er grønn for alle mottakerene. + buildingOutOfBounds: En eller flere bygninger er utenfor det byggbare området. + Enten øk området eller fjern dem. + autoComplete: Ditt puslespill fullførte seg selv automatisk! Sørg for at dine + Konstant Pr Produserere ikke leverer direkte til dine Mål Mottakere. + difficulties: + easy: Easy + medium: Medium + hard: Hard + dlcHint: Purchased the DLC already? Make sure it is activated by right clicking + shapez.io in your library, selecting Properties > DLCs. +backendErrors: + ratelimit: Du gjør en handling for ofte. Vennligst vent litt. + invalid-api-key: Kunne ikke kommunisere med kjernen, vennligst prøv å + oppdater/start spillet på nytt (Ugyldig Api Nøkkel). + unauthorized: Kunne ikke kommunisere med kjernen, vennligst prøv å + oppdater/start spillet på nytt (Uautorisert). + bad-token: Kunne ikke kommunisere med kjernen, vennligst prøv å oppdater/ start + spillet på nytt (Ugyldig token). + bad-id: Ugyldig Puslespill identifikator. + not-found: Det gitte puslespillet kunne ikke bli funnet. + bad-category: Den gitte kategorien kunne ikke bli funnet. + bad-short-key: Den gitte korte koden er ugyldig. + profane-title: Ditt puslespill sitt navn inneholder stygge ord. + bad-title-too-many-spaces: Ditt puslespill sitt navn er for kort. + bad-shape-key-in-emitter: En Konstant Produserer har en ugyldig gjenstand. + bad-shape-key-in-goal: En Mål Mottaker har en ugyldig gjenstand. + no-emitters: Ditt puslespill inneholder ingen Konstante Produserere. + no-goals: Ditt puslespill inndeholder ingen Mål Mottakere. + short-key-already-taken: Denne korte koden er allerede i bruk, vennligst bruk en annen. + can-not-report-your-own-puzzle: Du kan ikke rapportere ditt eget puslespill. + bad-payload: Forespørselen inneholder ugyldig data. + bad-building-placement: Ditt puslespill inneholder ugyldig plasserte bygninger. + timeout: Forespørselen timet ut. + too-many-likes-already: The puzzle alreay got too many likes. If you still want + to remove it, please contact support@shapez.io! + no-permission: You do not have the permission to perform this action. diff --git a/translations/base-pl.yaml b/translations/base-pl.yaml index b6e9b2b5..180233c0 100644 --- a/translations/base-pl.yaml +++ b/translations/base-pl.yaml @@ -53,6 +53,7 @@ global: escape: ESC shift: SHIFT space: SPACJA + loggingIn: Logging in demoBanners: title: Wersja demonstracyjna intro: Kup pełną wersję gry, by odblokować więcej funkcji! @@ -72,6 +73,12 @@ mainMenu: madeBy: Gra wykonana przez <author-link> subreddit: Reddit savegameUnnamed: Zapis bez nazwy + puzzleMode: Puzzle Mode + back: Back + puzzleDlcText: Do you enjoy compacting and optimizing factories? Get the Puzzle + DLC now on Steam for even more fun! + puzzleDlcWishlist: Wishlist now! + puzzleDlcViewNow: View Dlc dialogs: buttons: ok: OK @@ -85,6 +92,9 @@ dialogs: viewUpdate: Zobacz aktualizację showUpgrades: Pokaż ulepszenia showKeybindings: Pokaż Klawiszologię + retry: Retry + continue: Continue + playOffline: Play Offline importSavegameError: title: Błąd importowania text: "Nie udało się zaimportować twojego zapisu gry:" @@ -187,6 +197,70 @@ dialogs: title: Dostępny tutorial desc: Dla tego poziomu dostępny jest video tutorial w języku angielskim. Chcesz go obejrzeć? + editConstantProducer: + title: Set Item + puzzleLoadFailed: + title: Puzzles failed to load + desc: "Unfortunately the puzzles could not be loaded:" + submitPuzzle: + title: Submit Puzzle + descName: "Give your puzzle a name:" + descIcon: "Please enter a unique short key, which will be shown as the icon of + your puzzle (You can generate them <link>here</link>, or choose one + of the randomly suggested shapes below):" + placeholderName: Puzzle Title + puzzleResizeBadBuildings: + title: Resize not possible + desc: You can't make the zone any smaller, because then some buildings would be + outside the zone. + puzzleLoadError: + title: Bad Puzzle + desc: "The puzzle failed to load:" + offlineMode: + title: Offline Mode + desc: We couldn't reach the servers, so the game has to run in offline mode. + Please make sure you have an active internet connection. + puzzleDownloadError: + title: Download Error + desc: "Failed to download the puzzle:" + puzzleSubmitError: + title: Submission Error + desc: "Failed to submit your puzzle:" + puzzleSubmitOk: + title: Puzzle Published + desc: Congratulations! Your puzzle has been published and can now be played by + others. You can now find it in the "My puzzles" section. + puzzleCreateOffline: + title: Offline Mode + desc: Since you are offline, you will not be able to save and/or publish your + puzzle. Would you still like to continue? + puzzlePlayRegularRecommendation: + title: Recommendation + desc: I <strong>strongly</strong> recommend playing the normal game to level 12 + before attempting the puzzle DLC, otherwise you may encounter + mechanics not yet introduced. Do you still want to continue? + puzzleShare: + title: Short Key Copied + desc: The short key of the puzzle (<key>) has been copied to your clipboard! It + can be entered in the puzzle menu to access the puzzle. + puzzleReport: + title: Report Puzzle + options: + profane: Profane + unsolvable: Not solvable + trolling: Trolling + puzzleReportComplete: + title: Thank you for your feedback! + desc: The puzzle has been flagged. + puzzleReportError: + title: Failed to report + desc: "Your report could not get processed:" + puzzleLoadShortKey: + title: Enter short key + desc: Enter the short key of the puzzle to load it. + puzzleDelete: + title: Delete Puzzle? + desc: Are you sure you want to delete '<title>'? This can not be undone! ingame: keybindingsOverlay: moveMap: Ruch @@ -208,6 +282,7 @@ ingame: clearSelection: Wyczyść zaznaczenie pipette: Wybierz obiekt z mapy switchLayers: Przełącz warstwy + clearBelts: Clear belts colors: red: Czerwony green: Zielony @@ -356,6 +431,46 @@ ingame: achievements: title: Achievements desc: Hunt them all! + puzzleEditorSettings: + zoneTitle: Zone + zoneWidth: Width + zoneHeight: Height + trimZone: Trim + clearItems: Clear Items + share: Share + report: Report + clearBuildings: Clear Buildings + resetPuzzle: Reset Puzzle + puzzleEditorControls: + title: Puzzle Creator + instructions: + - 1. Place <strong>Constant Producers</strong> to provide shapes and + colors to the player + - 2. Build one or more shapes you want the player to build later and + deliver it to one or more <strong>Goal Acceptors</strong> + - 3. Once a Goal Acceptor receives a shape for a certain amount of + time, it <strong>saves it as a goal</strong> that the player must + produce later (Indicated by the <strong>green badge</strong>). + - 4. Click the <strong>lock button</strong> on a building to disable + it. + - 5. Once you click review, your puzzle will be validated and you + can publish it. + - 6. Upon release, <strong>all buildings will be removed</strong> + except for the Producers and Goal Acceptors - That's the part that + the player is supposed to figure out for themselves, after all :) + puzzleCompletion: + title: Puzzle Completed! + titleLike: "Click the heart if you liked the puzzle:" + titleRating: How difficult did you find the puzzle? + titleRatingDesc: Your rating will help me to make you better suggestions in the future + continueBtn: Keep Playing + menuBtn: Menu + puzzleMetadata: + author: Author + shortKey: Short Key + rating: Difficulty score + averageDuration: Avg. Duration + completionRate: Completion rate shopUpgrades: belt: name: Taśmociągi, Dystrybutory & Tunele @@ -573,6 +688,18 @@ buildings: name: Producent kształtów description: Dostępne tylko w trybie piaskownicy. Produkuje przedmioty z sygnału danego na warstwie przewodów na główną warstwę. + constant_producer: + default: + name: Constant Producer + description: Constantly outputs a specified shape or color. + goal_acceptor: + default: + name: Goal Acceptor + description: Deliver shapes to the goal acceptor to set them as a goal. + block: + default: + name: Block + description: Allows you to block a tile. storyRewards: reward_cutter_and_trash: title: Przecinanie Kształtów @@ -640,7 +767,7 @@ storyRewards: reward_storage: title: Magazyn desc: Właśnie odblokowałeś <strong>magazyn</strong> - Pozwala na przechowywanie - przedmiotów, do pewnej ilości!<br><br> Prawe wyjście posiada większy + przedmiotów, do pewnej ilości!<br><br> Lewe wyjście posiada większy piorytet, więc może być on użyty jako <strong>brama przepełnieniowa</strong>! reward_freeplay: @@ -980,6 +1107,10 @@ keybindings: rotateToDown: "Rotate: Point Down" rotateToRight: "Rotate: Point Right" rotateToLeft: "Rotate: Point Left" + constant_producer: Constant Producer + goal_acceptor: Goal Acceptor + block: Block + massSelectClear: Clear belts about: title: O Grze body: 'Ta gra jest open-source. Rozwijana jest przez <a @@ -1078,3 +1209,73 @@ tips: - Naciśnij F4, by zobaczyć ilość FPS i tempo ticków. - Naciśnij F4 dwa razy, by zobaczyć kratkę twojej myszy i kamery. - Możesz kliknąć przypięty kształt po lewej stronie, by go odpiąć. +puzzleMenu: + play: Play + edit: Edit + title: Puzzle Mode + createPuzzle: Create Puzzle + loadPuzzle: Load + reviewPuzzle: Review & Publish + validatingPuzzle: Validating Puzzle + submittingPuzzle: Submitting Puzzle + noPuzzles: There are currently no puzzles in this section. + categories: + levels: Levels + new: New + top-rated: Top Rated + mine: My Puzzles + easy: Easy + hard: Hard + completed: Completed + medium: Medium + official: Official + trending: Trending today + trending-weekly: Trending weekly + categories: Categories + difficulties: By Difficulty + account: My Puzzles + search: Search + validation: + title: Invalid Puzzle + noProducers: Please place a Constant Producer! + noGoalAcceptors: Please place a Goal Acceptor! + goalAcceptorNoItem: One or more Goal Acceptors have not yet assigned an item. + Deliver a shape to them to set a goal. + goalAcceptorRateNotMet: One or more Goal Acceptors are not getting enough items. + Make sure that the indicators are green for all acceptors. + buildingOutOfBounds: One or more buildings are outside of the buildable area. + Either increase the area or remove them. + autoComplete: Your puzzle autocompletes itself! Please make sure your constant + producers are not directly delivering to your goal acceptors. + difficulties: + easy: Easy + medium: Medium + hard: Hard + dlcHint: Purchased the DLC already? Make sure it is activated by right clicking + shapez.io in your library, selecting Properties > DLCs. +backendErrors: + ratelimit: You are performing your actions too frequent. Please wait a bit. + invalid-api-key: Failed to communicate with the backend, please try to + update/restart the game (Invalid Api Key). + unauthorized: Failed to communicate with the backend, please try to + update/restart the game (Unauthorized). + bad-token: Failed to communicate with the backend, please try to update/restart + the game (Bad Token). + bad-id: Invalid puzzle identifier. + not-found: The given puzzle could not be found. + bad-category: The given category could not be found. + bad-short-key: The given short key is invalid. + profane-title: Your puzzle title contains profane words. + bad-title-too-many-spaces: Your puzzle title is too short. + bad-shape-key-in-emitter: A constant producer has an invalid item. + bad-shape-key-in-goal: A goal acceptor has an invalid item. + no-emitters: Your puzzle does not contain any constant producers. + no-goals: Your puzzle does not contain any goal acceptors. + short-key-already-taken: This short key is already taken, please use another one. + can-not-report-your-own-puzzle: You can not report your own puzzle. + bad-payload: The request contains invalid data. + bad-building-placement: Your puzzle contains invalid placed buildings. + timeout: The request timed out. + too-many-likes-already: The puzzle alreay got too many likes. If you still want + to remove it, please contact support@shapez.io! + no-permission: You do not have the permission to perform this action. diff --git a/translations/base-pt-BR.yaml b/translations/base-pt-BR.yaml index 79435979..0fba5f0f 100644 --- a/translations/base-pt-BR.yaml +++ b/translations/base-pt-BR.yaml @@ -14,10 +14,10 @@ steamPage: Comprar o jogo na Steam te garante acesso à versão completa, mas você pode jogar a versão demo em shapez.io primeiro e decidir depois! what_others_say: O que as pessoas dizem sobre o shapez.io - nothernlion_comment: Este jogo é ótimo - estou me divertindo muito jogando - e o tempo passou voando. - notch_comment: Droga. Eu realmente deveria dormir, mas eu acho que acabei - de descobrir como fazer um computador no shapez.io + nothernlion_comment: Este jogo é ótimo - estou me divertindo muito jogando e o + tempo passou voando. + notch_comment: Droga. Eu realmente deveria dormir, mas eu acho que acabei de + descobrir como fazer um computador no shapez.io steam_review_comment: Este jogo roubou minha vida e eu não a quero de volta. Jogo de fábrica muito descontraído que não me deixa parar de fazer linhas mais eficientes. @@ -52,6 +52,7 @@ global: escape: ESC shift: Shift space: Espaço + loggingIn: Entrando demoBanners: title: Versão Demo intro: Compre a versão completa para desbloquear todos os recursos! @@ -71,6 +72,12 @@ mainMenu: savegameLevel: Nível <x> savegameLevelUnknown: Nível desconhecido savegameUnnamed: Sem nome + puzzleMode: Modo Puzzle + back: Voltar + puzzleDlcText: Você gosta de compactar e otimizar fábricas? Adquira a Puzzle DLC + já disponível na Steam para se divertir ainda mais! + puzzleDlcWishlist: Adicione já a sua lista de desejos! + puzzleDlcViewNow: View Dlc dialogs: buttons: ok: OK @@ -84,6 +91,9 @@ dialogs: viewUpdate: Atualizações showUpgrades: Melhorias showKeybindings: Controles + retry: Tentar novamente + continue: Continue + playOffline: Jogar Offline importSavegameError: title: Erro de importação text: "Houve uma falha ao importar seu jogo salvo:" @@ -181,6 +191,72 @@ dialogs: title: Tutorial disponível desc: Existe um tutorial em vídeo para esse nível, mas está disponível apenas em Inglês. Gostaria de assistí-lo? + editConstantProducer: + title: Selecionar Item + puzzleLoadFailed: + title: O carregamento dos desafios falhou + desc: "Infelizmente os desafios não puderam ser carregados:" + submitPuzzle: + title: Enviar desafio + descName: "Dê um nome ao seu desafio:" + descIcon: "Por favor crie um código exclusivo, o qual será o ícone do seu + Desafio (Você pode gera-los <link>aqui</link>, ou escolha um dos + gerados aleatoriamente abaixo):" + placeholderName: Nome do desafio + puzzleResizeBadBuildings: + title: Mudar o tamanho não é possível + desc: Você não pode deixar a zona menor, porque algumas construções ficariam + fora dela. + puzzleLoadError: + title: Desafio Ruim + desc: "O desafio não pôde ser carregado:" + offlineMode: + title: Modo Offline + desc: Não conseguimos nos conectar aos servidores, então o jogo terá que ser + jogado no Modo Offline. Por favor garanta que você tenha uma conexão + ativa com a internet. + puzzleDownloadError: + title: Erro no download + desc: "Falha ao baixar o desafio:" + puzzleSubmitError: + title: Erro no envio + desc: "Erro ao enviar seu desafio:" + puzzleSubmitOk: + title: Desafio publicado + desc: Parabéns! Seu desafio foi publicado e pode ser acessado por outros + jogadores. Você pode acha-lo na categoria "Meus Desafios". + puzzleCreateOffline: + title: Modo Offline + desc: Como você está no Modo Offline, não será possível salvar e/ou publicar + seus desafios. Você deseja continuar? + puzzlePlayRegularRecommendation: + title: Recomendação + desc: Eu <strong>fortemente</strong> recomendo jogar o jogo normal até o nível + 12 antes de se aventurar na Puzzle DLC, senão você poderá encontrar + mecânicas que ainda não foram introduzidas. Você ainda deseja + continuar? + puzzleShare: + title: Código copiado + desc: O código do desafio (<key>) foi copiado para sua área de transferência! + Ele pode ser inserido no menu de desafios para acessar o desafio. + puzzleReport: + title: Denunciar Desafio + options: + profane: Ofensivo + unsolvable: Impossível + trolling: Antijogo + puzzleReportComplete: + title: Obrigado pelo seu feedback! + desc: O desafio foi marcado. + puzzleReportError: + title: Falha ao denunciar + desc: "Sua denúncia não pôde ser processada:" + puzzleLoadShortKey: + title: Insira código + desc: Insira o código do desafio para carrega-lo. + puzzleDelete: + title: Delete Puzzle? + desc: Are you sure you want to delete '<title>'? This can not be undone! ingame: keybindingsOverlay: moveMap: Mover @@ -202,6 +278,7 @@ ingame: clearSelection: Limpar Seleção pipette: Conta-Gotas switchLayers: Trocar Camadas + clearBelts: Clear belts colors: red: Vermelho green: Verde @@ -351,8 +428,50 @@ ingame: title: Me ajuda desc: Eu desenvolvo o jogo no meu tempo livre! achievements: - title: Achievements - desc: Hunt them all! + title: Conquistas + desc: Consiga todas elas! + puzzleEditorSettings: + zoneTitle: Zona + zoneWidth: Largura + zoneHeight: Altura + trimZone: Cortar + clearItems: Limpar Items + share: Compartilhar + report: Denunciar + clearBuildings: Clear Buildings + resetPuzzle: Reset Puzzle + puzzleEditorControls: + title: Criador de Desafios + instructions: + - 1. Coloque <strong>Produtores Constantes</strong> para gerar itens + e cores ao jogador + - 2. Monte uma ou mais itens que você quer que o jogador produza e + entregue em um ou mais <strong>Receptores de Objetivo</strong> + - 3. Uma vez que um Receptor de Objetivo recebe uma item por uma + certa quantidade de tempo, ele <strong>a salva como seu + objetivo</strong> , o qual o jogador terá que produzir depois + (Indicato pela <strong>insígnia verde</strong>). + - 4. Clique no <strong>botao de travar</strong> de uma construção + para desabilita-la. + - 5. Uma vez que você clicou em revisar, seu desafio será validado e + você poderá publica-lo. + - 6. Quando seu desafio for publicado, <strong>todas as construções + serão removidas</strong> exceto os Produtores Constantes e + Receptores de Objetivo - Essa é a parte que o jogador terá que + descobrir sozinho, por isso se chama desafio :) + puzzleCompletion: + title: Desafio Completo! + titleLike: "Clique no coração se você gostou do desafio:" + titleRating: O qual difícil foi esse desafio? + titleRatingDesc: Sua avaliação me ajuda a te fazer sugestões melhores no futuro! + continueBtn: Continuar jogando + menuBtn: Menu + puzzleMetadata: + author: Autor + shortKey: Código + rating: Dificuldade + averageDuration: Duração média + completionRate: Taxa de sucesso shopUpgrades: belt: name: Esteiras, Distribuidores e Túneis @@ -569,6 +688,19 @@ buildings: name: Fabricante de Itens description: Disponível no modo sandbox apenas, envia o sinal recebido do plano de fios para o plano regular. + constant_producer: + default: + name: Produtor Constante + description: Produz constantemente um item ou cor específica. + goal_acceptor: + default: + name: Receptor de Objetivo + description: Entregue itens ao Receptor de Objetivo para os definir como o + objetivo. + block: + default: + name: Bloco + description: Te permite bloquear um espaço. storyRewards: reward_cutter_and_trash: title: Cortando formas @@ -980,10 +1112,14 @@ keybindings: comparator: Comparador item_producer: Produtor de Itens (Sandbox) copyWireValue: "Fios: Copiar valor abaixo do cursor" - rotateToUp: "Rotate: Point Up" - rotateToDown: "Rotate: Point Down" - rotateToRight: "Rotate: Point Right" - rotateToLeft: "Rotate: Point Left" + rotateToUp: "Rotação: Para cima" + rotateToDown: "Rotação: Para baixo" + rotateToRight: "Rotação: Para direita" + rotateToLeft: "Rotação: Para esquerda" + constant_producer: Produtor Constante + goal_acceptor: Receptor de Objetivo + block: Bloco + massSelectClear: Limpar esteiras about: title: Sobre o jogo body: >- @@ -1077,3 +1213,75 @@ tips: - Pressione F4 para mostrar seu FPS e taxa de tiques. - Pressione F4 duas vezes para mostrar o ladrilho do seu mouse e da câmera. - Você pode clicar em uma forma fixada na esquerda para tirá-la de lá. +puzzleMenu: + play: Jogar + edit: Editar + title: Modo Puzzle + createPuzzle: Criar Desafio + loadPuzzle: Carregar + reviewPuzzle: Revisar e Publicar + validatingPuzzle: Validando Desafio + submittingPuzzle: Enviando Desafio + noPuzzles: Não existem desafios nesta categoria atualmente. + categories: + levels: Níveis + new: Novo + top-rated: Melhor Avaliados + mine: Meus Desafios + easy: Fácil + hard: Difícil + completed: Completados + medium: Medium + official: Official + trending: Trending today + trending-weekly: Trending weekly + categories: Categories + difficulties: By Difficulty + account: My Puzzles + search: Search + validation: + title: Desafio inválido + noProducers: Por favor coloque um Produtor Constante! + noGoalAcceptors: Por favor coloque um Receptor de Objetivo! + goalAcceptorNoItem: Um ou mais Receptores de Objetivo ainda não tiveram um item + determinado. Entregue um item a ele para definir seu objetivo. + goalAcceptorRateNotMet: Um ou mais Receptores de Objetivo não estão recebendo + itens suficientes. Garanta que os indicadores estejam verdes para + todos os Receptores. + buildingOutOfBounds: Uma ou mais construções estão fora da área construível. + Você pode aumentar a área ou removê-los. + autoComplete: Seu desafio se completa sozinho! Por favor garanta que seus + Produtores Constantes não estão entregando diretamente aos seus + Receptores de Objetivo. + difficulties: + easy: Easy + medium: Medium + hard: Hard + dlcHint: Purchased the DLC already? Make sure it is activated by right clicking + shapez.io in your library, selecting Properties > DLCs. +backendErrors: + ratelimit: Você está fazendo coisas muito rapidamente. Por favor espere um pouco. + invalid-api-key: Falha ao comunicar com o backend, por favor tente + atualizar/reiniciar o jogo (Chave API Inválida). + unauthorized: Falha ao comunicar com o backend, por favor tente + atualizar/reiniciar o jogo (Não autorizado). + bad-token: Falha ao comunicar com o backend, por favor tente atualizar/reiniciar + o jogo (Bad Token). + bad-id: Indentificador de desafio inválido. + not-found: O desafio não pôde ser achado. + bad-category: A categoria não pôde ser achada. + bad-short-key: O código é inválido. + profane-title: O nome do seu desafio contém palavras proibidas. + bad-title-too-many-spaces: O nome do seu desafio é muito curto. + bad-shape-key-in-emitter: Um Produtor Constante contém um item inválido. + bad-shape-key-in-goal: Um Receptor de Objetivo contém um item inválido. + no-emitters: Seu desafio não contém nenhum Produtor Constante. + no-goals: Seu desafio não contém nenhum Receptor de Objetivo. + short-key-already-taken: Esse código já está sendo usado, por favor escolha outro. + can-not-report-your-own-puzzle: Você não pode denunciar seu próprio desafio. + bad-payload: O pedido contém dados inválidos. + bad-building-placement: Seu desafio contém construções colocadas de forma inválida. + timeout: Acabou o tempo do pedido. + too-many-likes-already: The puzzle alreay got too many likes. If you still want + to remove it, please contact support@shapez.io! + no-permission: You do not have the permission to perform this action. diff --git a/translations/base-pt-PT.yaml b/translations/base-pt-PT.yaml index ab10e6cd..3f3ece0c 100644 --- a/translations/base-pt-PT.yaml +++ b/translations/base-pt-PT.yaml @@ -15,12 +15,12 @@ steamPage: 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! what_others_say: O que dizem sobre o shapez.io - nothernlion_comment: Este é um jogo fantástico - Estou a ter um bom bocado enquanto o jogo, - e o tempo parece que voa. + nothernlion_comment: Este é um jogo fantástico - Estou a ter um bom bocado + enquanto o jogo, e o tempo parece que voa. notch_comment: Ora bolas. Eu devia ir dormir, mas acho que acabei de descobrir como criar computorizar no shapez.io - steam_review_comment: Este jogo roubou a minha vida e não a quero de volta. - Jogo de fábrica relaxante que não me deixa parar de fazer as minhas linhas + steam_review_comment: Este jogo roubou a minha vida e não a quero de volta. Jogo + de fábrica relaxante que não me deixa parar de fazer as minhas linhas cada vez mais eficientes. global: loading: A Carregar @@ -53,6 +53,7 @@ global: escape: ESC shift: SHIFT space: SPACE + loggingIn: Logging in demoBanners: title: Versão Demo intro: Compra a versão completa para desbloqueares todas as funcionalidades! @@ -73,6 +74,12 @@ mainMenu: madeBy: Criado por <author-link> subreddit: Reddit savegameUnnamed: Sem Nome + puzzleMode: Modo Puzzle + back: Voltar + puzzleDlcText: Gostas de compactar e otimizar fábricas? Adquire agora o DLC + Puzzle na Steam para ainda mais diversão! + puzzleDlcWishlist: Lista de desejos agora! + puzzleDlcViewNow: View Dlc dialogs: buttons: ok: OK @@ -86,6 +93,9 @@ dialogs: viewUpdate: Ver Update showUpgrades: Mostrar Upgrades showKeybindings: Mostrar Atalhos + retry: Tentar novamente + continue: Continuar + playOffline: Jogar Offline importSavegameError: title: Erro de importação text: "Erro ao importar o teu savegame:" @@ -189,6 +199,74 @@ dialogs: title: Tutorial Disponível desc: Existe um vídeo de tutorial disponível para este nível, mas apenas está disponível em Inglês. Gostarias de o ver? + editConstantProducer: + title: Selecionar Item + puzzleLoadFailed: + title: Puzzles falharam a carregar + desc: "Infelizmente os puzzles não podem ser carregados:" + submitPuzzle: + title: Submeter Puzzle + descName: "Dá um nome ao teu puzzle:" + descIcon: "Por favor insere um pequeno código único que será a imagem do ícone + da teu puzzle (Podes gerar o código <link>aqui</link>, ou escolher + uma das seguintes sugestões aleatoriamente geradas.):" + placeholderName: Título do Puzzle + puzzleResizeBadBuildings: + title: Não é possível alterar o tamanho + desc: Não podes tornar a zona mais pequena, assim algumas das construções + ficariam fora da zona. + puzzleLoadError: + title: Mau puzzle + desc: "O puzzle falhou ao carregar:" + offlineMode: + title: Modo Offline + desc: Não conseguimos correr os servidores, sendo assim o jogo tem de ser jogado + em modo offline. Por favor assegura-te de que tens uma boa conexão + de internet. + puzzleDownloadError: + title: Falha no Download + desc: "Falha ao fazer o download do puzzle:" + puzzleSubmitError: + title: Erro ao submeter + desc: "Falha ao submeter o teu puzzle:" + puzzleSubmitOk: + title: Puzzle Publicado + desc: Parabéns! O teu puzzle foi publicado e agora pode ser jogado por outros + jogadores. Agora podes encontrar o teu puzzle na zona "Meus + puzzles". + puzzleCreateOffline: + title: Modo Offline + desc: Como estás no modo offline, tu não poderás salvar e/ou publicar o teu + puzzle. Mesmo assim queres continuar? + puzzlePlayRegularRecommendation: + title: Recomendação + desc: Eu recomendo <strong>fortemente</strong> a jogares no modo normal até ao + nível 12 antes de tentares o "puzzle DLC", caso contrário poderás + encontrar mecanicas às quais ainda não foste introduzido. Mesmo + assim queres continuar? + puzzleShare: + title: Pequeno código copiado + desc: O pequeno código do puzzle (<key>) foi copiado para a tua área de + transferências! Poderá ser introduzido no menu puzzle para teres + acesso ao puzzle. + puzzleReport: + title: Reportar Puzzle + options: + profane: Inapropriado + unsolvable: Não solucionável + trolling: Trolling + puzzleReportComplete: + title: Obrigado pelo teu feedback! + desc: O puzzle foi sinalizado. + puzzleReportError: + title: Falha ao reportar + desc: "Não foi possível proceder com o ter reporte:" + puzzleLoadShortKey: + title: Introduzir pequeno código + desc: Introduz um pequeno código para o puzzle carregar. + puzzleDelete: + title: Apagar Puzzle? + desc: Tens a certeza de que queres apagar '<title>'? Isto não pode ser anulado! ingame: keybindingsOverlay: moveMap: Mover @@ -210,6 +288,7 @@ ingame: clearSelection: Cancelar pipette: Pipeta switchLayers: Troca de camadas + clearBelts: Limpar tapetes rolantes buildingPlacement: cycleBuildingVariants: Pressionar <key> para obter variações. hotkeyLabel: "Atalho: <key>" @@ -358,8 +437,51 @@ ingame: title: Ajuda-me desc: Eu desenvolvo este jogo no meu tempo livre! achievements: - title: Achievements - desc: Hunt them all! + title: Conquistas + desc: Tenta obtê-las todas! + puzzleEditorSettings: + zoneTitle: Zona + zoneWidth: Largura + zoneHeight: Altura + trimZone: Aparar + clearItems: Limpar Itens + share: Partilhar + report: Reportar + clearBuildings: Clear Buildings + resetPuzzle: Reset Puzzle + puzzleEditorControls: + title: Criador de Puzzle + instructions: + - 1. Coloca um <strong>Produtor Constante</strong> para fornecer + formas e cores ao jogador + - 2. Constrói uma ou mais formas que queiras que o jogador tenha de + contruir mais tarde e a tenha de entregar a um ou mais + <strong>Recetor de Objetivo</strong> + - 3. Assim que o Recetor de Objetivo receba uma forma durante um + certo espaço de tempo, ele <strong>guarda-a num objetivo</strong> + que o jogador terá de produzir mais tarde (Indicatdo pelo + <strong>distintivo verde</strong>). + - 4. Clcica no <strong>botão de bloqueio</strong> numa construção + para desátiva-lo. + - 5. Assim que clicares em analisar, o teu puzzle será validado e + poderás publicá-lo. + - 6. Após publicado, <strong>todas as construções serão + removidas</strong> excepto os Produtores e Recetores de Objetivo - + Esta é a parte em que é suposto o jogador tentar descobrir como + resolver o teu Puzzle :) + puzzleCompletion: + title: Puzzle Completo! + titleLike: "Clica no coração se gostaste do puzzle:" + titleRating: Quão difícil achaste que foi o puzzle? + titleRatingDesc: A tua avaliação ajudar-me-á a fazer melhores sugestões no futuro + continueBtn: Continua a Jogar + menuBtn: Menu + puzzleMetadata: + author: Autor + shortKey: Pequeno Código + rating: Grau de dificuldade + averageDuration: Média de duração + completionRate: Taxa de conclusão shopUpgrades: belt: name: Tapetes, Distribuidores e Túneis @@ -576,6 +698,19 @@ buildings: name: Produtor de Itens description: Disponível apenas no modo sandbox, produz o sinal dado na camada de fios na camada normal. + constant_producer: + default: + name: Produtor Constante + description: Produz constantemente uma forma ou cor específica. + goal_acceptor: + default: + name: Recetor de Objetivo + description: Entrega formas ao recetor de objetivo para defini-las como um + objetivo. + block: + default: + name: Bloqueador + description: Permite-te bloquear uma quadrícula. storyRewards: reward_cutter_and_trash: title: Corte de formas @@ -990,6 +1125,10 @@ keybindings: rotateToDown: "Rotate: Point Down" rotateToRight: "Rotate: Point Right" rotateToLeft: "Rotate: Point Left" + constant_producer: Produtor Constante + goal_acceptor: Recetor de Objetivo + block: Bloqueador + massSelectClear: Limpar tapetes rolante about: title: Sobre o Jogo body: >- @@ -1085,3 +1224,75 @@ tips: - 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 desafixa-la. +puzzleMenu: + play: Jogar + edit: Editar + title: Modo Puzzle + createPuzzle: Criar Puzzle + loadPuzzle: Carregar + reviewPuzzle: Analisar e Publicar + validatingPuzzle: A validar Puzzle + submittingPuzzle: A submeter Puzzle + noPuzzles: Não há atualmente puzzles nesta secção. + categories: + levels: Nivéis + new: Novo + top-rated: Melhor Avaliado + mine: Meus Puzzles + easy: Fácil + hard: Difícil + completed: Completo + medium: Médio + official: Oficial + trending: Tendências de Hoje + trending-weekly: Tendências da Semana + categories: Categorias + difficulties: Por Dificuldade + account: Os meus Puzzles + search: Procurar + validation: + title: Puzzle Inválido + noProducers: Por favor coloca um Produtor Constante! + noGoalAcceptors: Por favor coloca um Recetor de Objetivo! + goalAcceptorNoItem: Um ou mais Recetores de Objetivo ainda não tem itens + atrbuídos. Entrega uma forma nele para definires um objetivo. + goalAcceptorRateNotMet: Um ou mais Recetores de Objetivo não está a receber + itens suficientes. Assegura-te de que tens o indicador verde em + todos os Recetores. + buildingOutOfBounds: Uma ou mais formas estão fora da área de construção. Ou + aumentas a área ou removes esses itens. + autoComplete: O teu Puzzle completa-se sozinho! Por favor assegura-te de que os + teus Produtores Constantes não estão automaticamente direcionados + para os Recetores de Objetivo. + difficulties: + easy: Fácil + medium: Médio + hard: Difícil + dlcHint: Purchased the DLC already? Make sure it is activated by right clicking + shapez.io in your library, selecting Properties > DLCs. +backendErrors: + ratelimit: Estás a realizar as tuas ações demasiado rápido. Aguarda um pouco. + invalid-api-key: Falha ao cominucar com o backend, por favor tenta + atualizar/resetar o Jogo (Chave Api inválida). + unauthorized: Falha ao cominucar com o backend, or favor tenta atualizar/resetar + o Jogo (Não autorizado). + bad-token: Falha ao cominucar com o backend, por favor tenta atualizar/resetar o + Jogo (Mau Token). + bad-id: Identificador de Puzzle inválido. + not-found: O Puzzle pedido não foi encontrado. + bad-category: A categoria pedida não foi encontrada. + bad-short-key: O pequeno código inserido não é válido. + profane-title: O título do teu Puzzle contém palavras impróprias. + bad-title-too-many-spaces: O título do teu Puzzle é demasiado pequeno. + bad-shape-key-in-emitter: Um Produtor Constante tem um item inválido. + bad-shape-key-in-goal: Um Recetor de Objetivo tem um item inválido. + no-emitters: O teu Puzzle não contém nenhum Produtor Constante. + no-goals: O teu Puzzle não contém nenhum Recetor de Objetivo. + short-key-already-taken: Este pequeno código já foi utilizado, por favor tenta outro. + can-not-report-your-own-puzzle: Não podes reportar o teu próprio puzzle. + bad-payload: O pedido contém informção inválida. + bad-building-placement: O teu Puzzle contém construções posicionadas de forma inválida. + timeout: O tempo do pedido esgotou. + too-many-likes-already: O puzzle já tem imensos gostos. Se ainda o quiseres + remover, por favor contacta support@shapez.io! + no-permission: Não tens permissão para realizar esta ação. diff --git a/translations/base-ro.yaml b/translations/base-ro.yaml index b5cbde49..76f0c731 100644 --- a/translations/base-ro.yaml +++ b/translations/base-ro.yaml @@ -53,6 +53,7 @@ global: escape: ESC shift: SHIFT space: SPACE + loggingIn: Logging in demoBanners: title: Versiunea Demo intro: Instalează versiunea Standalone pentru a debloca toate funcțiile! @@ -73,6 +74,12 @@ mainMenu: madeBy: Făcut de <author-link> subreddit: Reddit savegameUnnamed: Fară nume + puzzleMode: Puzzle Mode + back: Back + puzzleDlcText: Do you enjoy compacting and optimizing factories? Get the Puzzle + DLC now on Steam for even more fun! + puzzleDlcWishlist: Wishlist now! + puzzleDlcViewNow: View Dlc dialogs: buttons: ok: OK @@ -86,6 +93,9 @@ dialogs: viewUpdate: Vezi Update-ul showUpgrades: Vezi Upgrade-urile showKeybindings: Arată tastele configurate + retry: Retry + continue: Continue + playOffline: Play Offline importSavegameError: title: Eroare la Importare text: "Încercarea de importare a eșuat:" @@ -188,6 +198,70 @@ dialogs: title: Tutorial Available desc: There is a tutorial video available for this level, but it is only available in English. Would you like to watch it? + editConstantProducer: + title: Set Item + puzzleLoadFailed: + title: Puzzles failed to load + desc: "Unfortunately the puzzles could not be loaded:" + submitPuzzle: + title: Submit Puzzle + descName: "Give your puzzle a name:" + descIcon: "Please enter a unique short key, which will be shown as the icon of + your puzzle (You can generate them <link>here</link>, or choose one + of the randomly suggested shapes below):" + placeholderName: Puzzle Title + puzzleResizeBadBuildings: + title: Resize not possible + desc: You can't make the zone any smaller, because then some buildings would be + outside the zone. + puzzleLoadError: + title: Bad Puzzle + desc: "The puzzle failed to load:" + offlineMode: + title: Offline Mode + desc: We couldn't reach the servers, so the game has to run in offline mode. + Please make sure you have an active internet connection. + puzzleDownloadError: + title: Download Error + desc: "Failed to download the puzzle:" + puzzleSubmitError: + title: Submission Error + desc: "Failed to submit your puzzle:" + puzzleSubmitOk: + title: Puzzle Published + desc: Congratulations! Your puzzle has been published and can now be played by + others. You can now find it in the "My puzzles" section. + puzzleCreateOffline: + title: Offline Mode + desc: Since you are offline, you will not be able to save and/or publish your + puzzle. Would you still like to continue? + puzzlePlayRegularRecommendation: + title: Recommendation + desc: I <strong>strongly</strong> recommend playing the normal game to level 12 + before attempting the puzzle DLC, otherwise you may encounter + mechanics not yet introduced. Do you still want to continue? + puzzleShare: + title: Short Key Copied + desc: The short key of the puzzle (<key>) has been copied to your clipboard! It + can be entered in the puzzle menu to access the puzzle. + puzzleReport: + title: Report Puzzle + options: + profane: Profane + unsolvable: Not solvable + trolling: Trolling + puzzleReportComplete: + title: Thank you for your feedback! + desc: The puzzle has been flagged. + puzzleReportError: + title: Failed to report + desc: "Your report could not get processed:" + puzzleLoadShortKey: + title: Enter short key + desc: Enter the short key of the puzzle to load it. + puzzleDelete: + title: Delete Puzzle? + desc: Are you sure you want to delete '<title>'? This can not be undone! ingame: keybindingsOverlay: moveMap: Move @@ -209,6 +283,7 @@ ingame: clearSelection: Golește Secțiunea pipette: Pipette switchLayers: Switch layers + clearBelts: Clear belts buildingPlacement: cycleBuildingVariants: Apasă <key> pentru a cicla variantele. hotkeyLabel: "Tasta: <key>" @@ -357,6 +432,46 @@ ingame: achievements: title: Achievements desc: Hunt them all! + puzzleEditorSettings: + zoneTitle: Zone + zoneWidth: Width + zoneHeight: Height + trimZone: Trim + clearItems: Clear Items + share: Share + report: Report + clearBuildings: Clear Buildings + resetPuzzle: Reset Puzzle + puzzleEditorControls: + title: Puzzle Creator + instructions: + - 1. Place <strong>Constant Producers</strong> to provide shapes and + colors to the player + - 2. Build one or more shapes you want the player to build later and + deliver it to one or more <strong>Goal Acceptors</strong> + - 3. Once a Goal Acceptor receives a shape for a certain amount of + time, it <strong>saves it as a goal</strong> that the player must + produce later (Indicated by the <strong>green badge</strong>). + - 4. Click the <strong>lock button</strong> on a building to disable + it. + - 5. Once you click review, your puzzle will be validated and you + can publish it. + - 6. Upon release, <strong>all buildings will be removed</strong> + except for the Producers and Goal Acceptors - That's the part that + the player is supposed to figure out for themselves, after all :) + puzzleCompletion: + title: Puzzle Completed! + titleLike: "Click the heart if you liked the puzzle:" + titleRating: How difficult did you find the puzzle? + titleRatingDesc: Your rating will help me to make you better suggestions in the future + continueBtn: Keep Playing + menuBtn: Menu + puzzleMetadata: + author: Author + shortKey: Short Key + rating: Difficulty score + averageDuration: Avg. Duration + completionRate: Completion rate shopUpgrades: belt: name: Benzi, Distribuitor & Tunele @@ -567,6 +682,18 @@ buildings: name: Item Producer description: Available in sandbox mode only, outputs the given signal from the wires layer on the regular layer. + constant_producer: + default: + name: Constant Producer + description: Constantly outputs a specified shape or color. + goal_acceptor: + default: + name: Goal Acceptor + description: Deliver shapes to the goal acceptor to set them as a goal. + block: + default: + name: Block + description: Allows you to block a tile. storyRewards: reward_cutter_and_trash: title: Tăierea formelor @@ -975,6 +1102,10 @@ keybindings: rotateToDown: "Rotate: Point Down" rotateToRight: "Rotate: Point Right" rotateToLeft: "Rotate: Point Left" + constant_producer: Constant Producer + goal_acceptor: Goal Acceptor + block: Block + massSelectClear: Clear belts about: title: Despre acest joc body: >- @@ -1060,3 +1191,73 @@ tips: - 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. +puzzleMenu: + play: Play + edit: Edit + title: Puzzle Mode + createPuzzle: Create Puzzle + loadPuzzle: Load + reviewPuzzle: Review & Publish + validatingPuzzle: Validating Puzzle + submittingPuzzle: Submitting Puzzle + noPuzzles: There are currently no puzzles in this section. + categories: + levels: Levels + new: New + top-rated: Top Rated + mine: My Puzzles + easy: Easy + hard: Hard + completed: Completed + medium: Medium + official: Official + trending: Trending today + trending-weekly: Trending weekly + categories: Categories + difficulties: By Difficulty + account: My Puzzles + search: Search + validation: + title: Invalid Puzzle + noProducers: Please place a Constant Producer! + noGoalAcceptors: Please place a Goal Acceptor! + goalAcceptorNoItem: One or more Goal Acceptors have not yet assigned an item. + Deliver a shape to them to set a goal. + goalAcceptorRateNotMet: One or more Goal Acceptors are not getting enough items. + Make sure that the indicators are green for all acceptors. + buildingOutOfBounds: One or more buildings are outside of the buildable area. + Either increase the area or remove them. + autoComplete: Your puzzle autocompletes itself! Please make sure your constant + producers are not directly delivering to your goal acceptors. + difficulties: + easy: Easy + medium: Medium + hard: Hard + dlcHint: Purchased the DLC already? Make sure it is activated by right clicking + shapez.io in your library, selecting Properties > DLCs. +backendErrors: + ratelimit: You are performing your actions too frequent. Please wait a bit. + invalid-api-key: Failed to communicate with the backend, please try to + update/restart the game (Invalid Api Key). + unauthorized: Failed to communicate with the backend, please try to + update/restart the game (Unauthorized). + bad-token: Failed to communicate with the backend, please try to update/restart + the game (Bad Token). + bad-id: Invalid puzzle identifier. + not-found: The given puzzle could not be found. + bad-category: The given category could not be found. + bad-short-key: The given short key is invalid. + profane-title: Your puzzle title contains profane words. + bad-title-too-many-spaces: Your puzzle title is too short. + bad-shape-key-in-emitter: A constant producer has an invalid item. + bad-shape-key-in-goal: A goal acceptor has an invalid item. + no-emitters: Your puzzle does not contain any constant producers. + no-goals: Your puzzle does not contain any goal acceptors. + short-key-already-taken: This short key is already taken, please use another one. + can-not-report-your-own-puzzle: You can not report your own puzzle. + bad-payload: The request contains invalid data. + bad-building-placement: Your puzzle contains invalid placed buildings. + timeout: The request timed out. + too-many-likes-already: The puzzle alreay got too many likes. If you still want + to remove it, please contact support@shapez.io! + no-permission: You do not have the permission to perform this action. diff --git a/translations/base-ru.yaml b/translations/base-ru.yaml index 001c3642..3661be37 100644 --- a/translations/base-ru.yaml +++ b/translations/base-ru.yaml @@ -5,14 +5,14 @@ steamPage: intro: >- Любите игры про автоматизацию? Тогда вы по адресу! - shapez.io это спокойная игра, в которой вам предстоит строить фабрики по автоматизированному производству геометрических фигур. По мере управления уровня, фигуры становятся все сложнее, так что придется расширять фабрику засчет бесконечной карты. + shapez.io это спокойная игра, в которой вам предстоит строить фабрики по автоматизированному производству геометрических фигур. По мере управления уровня, фигуры становятся все сложнее, так что придется расширять фабрику за счет бесконечной карты. - Если этого мало, то Вам так же придется экспоненциально увеличивать производство, чтобы удовлетворить потребности Вашей фабрики. Ключ к успеху - масштабирование! И если в начале вам понадобится обрабатывать только формы, то позже вы начнёте их раскрашивать, добывая и комбенируя красители. + Если этого мало, то Вам так же придется экспоненциально увеличивать производство, чтобы удовлетворить потребности Вашей фабрики. Ключ к успеху - масштабирование! И если в начале вам понадобится обрабатывать только формы, то позже вы начнёте их раскрашивать, добывая и комбинируя красители. Вначале игры Вам понадобится производить только фигуры, однако позже фигуры надо будет окрашивать. Для этого добывайте и смешивайте краски! Приобретение игры в Steam предоставляет доступ к полной версии игры, но вы можете опробовать демоверсию игры на shapez.io и принять решение позже! - what_others_say: What people say about shapez.io + what_others_say: Что говорят люди о shapez.io nothernlion_comment: This game is great - I'm having a wonderful time playing, and time has flown by. notch_comment: Oh crap. I really should sleep, but I think I just figured out @@ -51,26 +51,33 @@ global: escape: ESC shift: SHIFT space: ПРОБЕЛ + loggingIn: Logging in demoBanners: title: Демоверсия intro: Приобретите полную версию, чтобы разблокировать все возможности! mainMenu: play: Играть + continue: Продолжить + newGame: Новая Игра changelog: Список изменений + subreddit: Reddit importSavegame: Импорт openSourceHint: Это игра с открытым исходным кодом! discordLink: Официальный Дискорд сервер helpTranslate: Помоги с переводом! + madeBy: Создал <author-link> browserWarning: Извините, но игра работает медленно в вашем браузере! Приобретите полную версию или загрузите Google Chrome, чтобы ознакомится с игрой в полной мере. savegameLevel: Уровень <x> savegameLevelUnknown: Неизвестный уровень - continue: Продолжить - newGame: Новая Игра - madeBy: Создал <author-link> - subreddit: Reddit savegameUnnamed: Без названия + puzzleMode: Puzzle Mode + back: Back + puzzleDlcText: Do you enjoy compacting and optimizing factories? Get the Puzzle + DLC now on Steam for even more fun! + puzzleDlcWishlist: Wishlist now! + puzzleDlcViewNow: View Dlc dialogs: buttons: ok: OK @@ -84,6 +91,9 @@ dialogs: viewUpdate: Посмотреть Обновление showUpgrades: Показать Улучшения showKeybindings: Показать Управление (Привязку клавиш) + retry: Retry + continue: Continue + playOffline: Play Offline importSavegameError: title: Ошибка импортирования text: Не удалось импортировать сохранение игры. @@ -109,15 +119,15 @@ dialogs: «Esc» для отмены. resetKeybindingsConfirmation: title: Сброс управления - desc: Это сбросит все настройки управления к их значениям по умолчанию. - Подтвердите это действие. + desc: Это сбросит все настройки управления к значениям по умолчанию. Подтвердите + это действие. keybindingsResetOk: title: Сброс управления desc: Настройки управления сброшены до соответствующих значений по умолчанию! featureRestriction: title: Демоверсия desc: Вы попытались получить доступ к функции (<feature>), которая недоступна в - демоверсии. Вы можете приобрести полную версию чтобы пользоваться + демоверсии. Вы можете приобрести полную версию, чтобы пользоваться всеми функциями! oneSavegameLimit: title: Лимит сохранений @@ -136,6 +146,14 @@ dialogs: title: Подтвердить удаление desc: "Вы собираетесь удалить много построек (точнее: <count>)! Вы действительно хотите сделать это?" + massCutConfirm: + title: Подтвердите вырезку + desc: "Вы собираетесь вырезать много зданий (точнее: <count>)! Вы уверены, что + хотите это сделать?" + massCutInsufficientConfirm: + title: Подтвердите вырезку + desc: Вы не можете позволить себе вставить эту область! Вы уверены, что хотите + вырезать её? blueprintsNotUnlocked: title: Еще не открыто desc: Чертежи еще не открыты! Завершите больше уровней, чтобы открыть их. @@ -150,31 +168,23 @@ dialogs: Инвертировать направление размещаемых конвейерных лент.<br>" createMarker: title: Новый маркер + titleEdit: Редактирование маркера desc: Дайте ему значимое название, вы также можете добавить <strong>короткий ключ</strong> фигуры (Который можно сгенерировать <link>здесь</link>) - titleEdit: Редактирование маркера - markerDemoLimit: - desc: Вы можете создать только 2 своих маркера в демоверсии. Приобретите полную - версию для безлимитных маркеров. - massCutConfirm: - title: Подтвердите вырезку - desc: "Вы собираетесь вырезать много зданий (точнее: <count>)! Вы уверены, что - хотите это сделать?" - exportScreenshotWarning: - title: Экспорт скриншота - desc: Вы собираетесь экспортировать вашу базу в виде скриншота. Обратите - внимание, что это может быть довольно медленным процессом для - большой базы и даже привести к аварийному завершению игры! - massCutInsufficientConfirm: - title: Подтвердите вырезку - desc: Вы не можете позволить себе вставить эту область! Вы уверены, что хотите - вырезать ее? editSignal: title: Установить Сигнал descItems: "Выберите объект:" descShortKey: ... или введите <strong>короткий ключ</strong> фигуры (Который можно сгенерировать <link>здесь</link>) + markerDemoLimit: + desc: Вы можете создать только 2 своих маркера в демоверсии. Приобретите полную + версию для безлимитных маркеров. + exportScreenshotWarning: + title: Экспорт скриншота + desc: Вы собираетесь экспортировать вашу базу в виде скриншота. Обратите + внимание, что это может быть довольно медленным процессом для + большой базы и даже привести к аварийному завершению игры! renameSavegame: title: Переименовать Сохранение desc: Здесь вы можете изменить название своего сохранения. @@ -185,6 +195,71 @@ dialogs: title: Доступно обучение desc: Для этого уровня доступно видео-обучение, но только на английском языке. Посмотрите его? + editConstantProducer: + title: Установить предмет + puzzleLoadFailed: + title: Не удалось загрузить головоломки + desc: "К сожалению, не удалось загрузить головоломки:" + submitPuzzle: + title: Отправить головоломку + descName: "Дайте имя вашей головоломке:" + descIcon: "Введите уникальный короткий ключ, который будет показан как иконка + вашей головоломки (Вы можете сгенерировать их <link>здесь</link>, + или выбрать один из случайно предложенных фигур ниже):" + placeholderName: Название головоломки + puzzleResizeBadBuildings: + title: Невозможно изменить размер + desc: Нельзя уменьшить область, потому что некоторые постройки будут вне + области. + puzzleLoadError: + title: Bad Puzzle + desc: "Не удалось загрузить головоломки:" + offlineMode: + title: Оффлайн режим + desc: Нам не удалось связаться с сервеами, поэтому игра будет работать в оффлайн + режиме. Убедитесь, что вы подключены к интернету. + puzzleDownloadError: + title: Ошибка загрузки + desc: "Не удалось загрузить головломку:" + puzzleSubmitError: + title: Ошибка отправки + desc: "Не удалось отправить вашу головоломку:" + puzzleSubmitOk: + title: Головоломка опубликована + desc: Поздравляю! Ваша головоломка была опубликована, и теперь в нее могут + играть остальные. Теперь вы можете найти ее в разделе "Мои + головоломки". + puzzleCreateOffline: + title: Оффлайн режим + desc: Поскольку вы не в сети, вы не сможете сохранять и / или публиковать свои + головоломки. Вы все еще хотите продолжить? + puzzlePlayRegularRecommendation: + title: Рекомендация + desc: Я <strong>настоятельно</strong> рекомендую пройти обычную игру до уровня + 12 перед игрой в Puzzle DLC, иначе вы можете встретить + непредставленные механики. Вы все еще хотите продолжить? + puzzleShare: + title: Короткий ключ скопирован + desc: Короткий ключ головоломки (<key>) был скопирован в буфер обмена! Он может + быть введен в меню головолом для доступа к головоломке. + puzzleReport: + title: Жалоба на головоломку + options: + profane: Оскорбительная + unsolvable: Не решается + trolling: Троллинг + puzzleReportComplete: + title: Спасибо за ваш отзыв! + desc: Головоломка была помечена. + puzzleReportError: + title: Не удалось сообщить + desc: "Ваша жалоба не может быть обработана:" + puzzleLoadShortKey: + title: Ввод короткого ключа + desc: Введите короткий ключ головоломки, чтобы загрузить ее. + puzzleDelete: + title: Удалить головоломку? + desc: Вы уверены, что хотите удалить '<title>'? Это действие нельзя отменить! ingame: keybindingsOverlay: moveMap: Передвижение @@ -206,6 +281,17 @@ ingame: clearSelection: Отменить pipette: Пипетка switchLayers: Переключить слои + clearBelts: Clear belts + colors: + red: Красный + green: Зеленый + blue: Синий + yellow: Желтый + purple: Фиолетовый + cyan: Бирюзовый + white: Белый + uncolored: Бесцветный + black: Черный buildingPlacement: cycleBuildingVariants: Нажмите <key> для переключения вариантов. hotkeyLabel: "Клавиша: <key>" @@ -263,36 +349,41 @@ ingame: waypoints: Маркеры hub: ХАБ description: ЛКМ по маркеру, чтобы переместиться к нему, ПКМ, чтобы удалить. - <br><br>Нажмите <keybinding> чтобы создать маркер в текущей позиции + <br><br> Нажмите <keybinding> чтобы создать маркер в текущей позиции или <strong>ПКМ</strong>, чтобы выбрать другое место для создания маркера. creationSuccessNotification: Маркер создан. + shapeViewer: + title: Слои + empty: Пусто + copyKey: Копировать interactiveTutorial: title: Обучение hints: 1_1_extractor: Поместите <strong>экстрактор</strong> на <strong>фигуру в форме - круга</strong> чтобы добыть ее! - 1_2_conveyor: "Соедините экстрактор <strong>конвейером</strong> с - хабом!<br><br>Подсказка: Необходимо выбрать конвейер и - <strong>нажать и потащить</strong> мышку!" + круга</strong>, чтобы добыть её! + 1_2_conveyor: "Соедините экстрактор <strong>конвейером</strong> с хабом!<br><br> + Подсказка: Необходимо выбрать конвейер и <strong>нажать и + потащить</strong> мышку!" 1_3_expand: "Это <strong>НЕ</strong> idle-игра! Постройте больше экстракторов и - конвейеров, чтобы достичь цели быстрее.<br><br>Подсказка: - Удерживайте <strong>SHIFT</strong> чтобы разместить несколько - экстракторов, а <strong>R</strong> чтобы вращать их." + конвейеров, чтобы достичь цели быстрее.<br><br> Подсказка: + Удерживайте <strong>SHIFT</strong>, чтобы разместить несколько + экстракторов, а <strong>R</strong>, чтобы вращать их." 2_1_place_cutter: "Разместите <strong>Резак</strong> для разрезания кругов на - две половины! <br><br> PS: Резак всегда разрезает <strong>сверху - вниз</strong> независимо от ориентации." + две половины!<br><br> Подсказка: Резак всегда разрезает + <strong>сверху вниз</strong> независимо от ориентации." 2_2_place_trash: Резак может <strong>засориться и остановиться</strong>!<br><br> - Используйте <strong>мусорку</strong> что бы избавиться от в - данный момент (!) ненужных частей. + Используйте <strong>мусорку</strong>, чтобы избавиться в данный + момент (!) от ненужных частей. 2_3_more_cutters: "Хорошая работа! Теперь разместите <strong>ещё 2 - резака</strong> что бы ускорить этот медленный процесс!<br><br> - PS: Используйте <strong>клавиши 0-9 </strong> для быстрого - доступа к постройкам!" + резака</strong>, чтобы ускорить этот медленный процесс!<br><br> + Подсказка: Используйте <strong>клавиши 0-9 </strong> для + быстрого доступа к постройкам!" 3_1_rectangles: "Теперь давайте извлечём немного прямоугольников! <strong>Постройте 4 экстрактора</strong>и соедините их с - хабом.<br><br> PS: Удерживайте <strong>SHIFT</strong> во время - удерживания конвейера для активации планировщика конвейеров!" + хабом.<br><br> Подсказка: Удерживайте <strong>SHIFT</strong> во + время удерживания конвейера для активации планировщика + конвейеров!" 21_1_place_quad_painter: Разместите <strong>покрасчик для 4 предметов</strong> и получите <strong>круги</strong>, <strong>белого</strong> и <strong>красного</strong> цветов! @@ -301,24 +392,10 @@ ingame: входы</strong> покрасчика кабелями! 21_3_place_button: Отлично! Теперь разместите <strong>Переключатель</strong> и присоедини его проводами! - 21_4_press_button: "Нажмите на переключатель что бы заставить его + 21_4_press_button: "Нажмите на переключатель, чтобы заставить его <strong>выдавать истинный сигнал </strong> и активировать этим - покрасчика.<br><br> PS: Не обязательно соединять все входы! - Достаточно двух." - colors: - red: Красный - green: Зеленый - blue: Синий - yellow: Желтый - purple: Фиолетовый - cyan: Бирюзовый - white: Белый - uncolored: Бесцветный - black: Черный - shapeViewer: - title: Слои - empty: Пусто - copyKey: Копировать + покрасчик.<br><br> Подсказка: Не обязательно соединять все + входы! Достаточно двух." connectedMiners: one_miner: 1 Экстрактор n_miners: <amount> Экстрактора(-ов) @@ -332,29 +409,71 @@ ingame: no_thanks: Нет, спасибо! points: levels: - title: 12 Новых Уровней! + title: 12 Новых Уровней desc: Всего 26 уровней! buildings: - title: 18 новых Построек! + title: 18 новых Построек desc: Полностью автоматизируйте свою фабрику! + achievements: + title: Достижения + desc: Получи их все! upgrades: - title: ∞ стадий улучшений! + title: ∞ стадий улучшений desc: В демоверсии всего 5! markers: - title: ∞ Маркеров! + title: ∞ Маркеров desc: Никогда не теряйтесь в своей фабрике! wires: - title: Провода! + title: Провода desc: Полноценное дополнительное измерение! darkmode: - title: Темная Тема! + title: Темная Тема desc: Дайте глазам отдохнуть! support: title: Поддержите меня desc: Я занимаюсь разработкой в свободное время! - achievements: - title: Achievements - desc: Hunt them all! + puzzleEditorSettings: + zoneTitle: Область + zoneWidth: Ширина + zoneHeight: Высота + trimZone: Обрезать + clearItems: Очистить предметы + share: Поделиться + report: Пожаловаться + clearBuildings: Clear Buildings + resetPuzzle: Reset Puzzle + puzzleEditorControls: + title: Редактор головоломок + instructions: + - 1. Разместите <strong>постоянный производитель</strong>, чтоб + предоставить фигуры и цвета игроку + - 2. Постройте одну или несколько фигур, которые вы хотите, чтобы + игрок построил позже, и доставьте их к одному или нескольким + <strong>приемникам цели</strong> + - 3. Как только приемник цели получил фигуру определенное количество + раз, он <strong>сохраняет фигуру как цель</strong>, которую игрок + должен произвести позже (Обозначается <strong>зеленым + значком</strong>). + - 4. Нажмите <strong>кнопу блокировки</strong> на здании, чтоб + выключить его. + - 5. Как только вы нажали "Обзор", ваша головоломка будет проверена + и вы сможете опубликовать ее. + - 6. После публикации, <strong>все постройки будут удалены</strong> + за исключением производителей и приемников цели - Это часть, в + которой игрок должен разобраться сам :) + puzzleCompletion: + title: Головоломка завершена! + titleLike: "Нажмите на сердечко, если головоломка вам понравилась:" + titleRating: Насколько сложной была головоломка? + titleRatingDesc: Ваша оценка поможет мне в будущем делать вам лучшие предложения + continueBtn: Продолжить игру + menuBtn: Меню + puzzleMetadata: + author: Автор + shortKey: Короткий ключ + rating: Сложность + averageDuration: Средн. продолжительность + completionRate: Процент завершения shopUpgrades: belt: name: Конвейеры, Разделители & Туннели @@ -369,6 +488,11 @@ shopUpgrades: name: Смешивание & Покраска description: Скорость x<currentMult> → x<newMult> buildings: + hub: + deliver: Доставить + toUnlock: чтобы открыть + levelShortcut: Ур. + endOfDemo: Конец Демо belt: default: name: Конвейер @@ -390,6 +514,22 @@ buildings: tier2: name: Туннель II description: Позволяет перевозить ресурсы под зданиями и конвейерами. + balancer: + default: + name: Балансер + description: Многофункциональный - равномерно распределяет все входы на выходы. + merger: + name: Соединитель (компактный) + description: Соединяет две линии конвейера в одну. + merger-inverse: + name: Соединитель (компактный) + description: Соединяет две линии конвейера в одну. + splitter: + name: Разделитель (компактный) + description: Разделяет одну линию конвейера на две. + splitter-inverse: + name: Разделитель (компактный) + description: Разделяет одну линию конвейера на две. cutter: default: name: Резак @@ -415,7 +555,7 @@ buildings: default: name: Объединитель description: Объединяет два предмета. Если они не могут быть соединены, правый - элемент помещается над левым. + элемент помещается поверх левого. mixer: default: name: Смешиватель @@ -424,6 +564,9 @@ buildings: default: name: Покрасчик description: Красит всю фигуру из левого входа красителем из перпендикулярного. + mirrored: + name: Покрасчик + description: Красит всю фигуру из левого входа красителем из перпендикулярного. double: name: Покрасчик (2Вх.) description: Красит фигуру из левых входов красителем из перпендикулярного. @@ -432,18 +575,15 @@ buildings: description: Позволяет отдельно окрашивать каждую часть фигуры. Только ячейки с <strong>положительным сигналом</strong> на слое с проводами будут окрашены! - mirrored: - name: Покрасчик - description: Красит всю фигуру из левого входа красителем из перпендикулярного. trash: default: name: Мусорка description: Имеет входы со всех сторон. Уничтожает все принимаемые ресурсы. - hub: - deliver: Доставить - toUnlock: чтобы открыть - levelShortcut: Ур. - endOfDemo: Конец Демо + storage: + default: + name: Буферное Хранилище + description: Хранит излишние ресурсы пока есть место. Левый выход в приоритете, + может быть использован как буфер. wire: default: name: Энерг. провод @@ -453,31 +593,10 @@ buildings: description: Передает сигналы, которые могут быть ресурсами, цветами или логическими значениями (1 / 0). Провода разных цветов не соединяются. - balancer: - default: - name: Балансер - description: Многофункциональный - равномерно распределяет все входы на выходы. - merger: - name: Соединитель (компактный) - description: Соединяет две линии ковейера в одну. - merger-inverse: - name: Соединитель (компактный) - description: Соединяет две линии ковейера в одну. - splitter: - name: Разделитель (компактный) - description: Разделяет одну линию конвейера на две. - splitter-inverse: - name: Разделитель (компактный) - description: Разделяет одну линию конвейера на две. - storage: - default: - name: Буферное Хранилище - description: Хранит излишние ресурсы пока есть место. Левый выход в приоритете, - может быть использован как буфер. wire_tunnel: default: name: Пересечение Проводов - description: Позволяет пересекать провода при этом их не соединяя. + description: Позволяет пересекать провода при этом не соединяя их. constant_signal: default: name: Постоянный Сигнал @@ -487,8 +606,8 @@ buildings: default: name: Переключатель description: Может быть переключен, чтобы издавать логический сигнал (1 / 0) на - слое с проводами, который может быть использован для управления - Фильтром, например. + слое с проводами, который может быть использован, например, для + управления Фильтром. logic_gate: default: name: И @@ -507,16 +626,26 @@ buildings: name: ИЛИ description: Издает значение "1" если хотя бы один вход положительный. (Положительный - значит ресурс, цвет или логическое значение - "1"). + "1") + transistor: + default: + name: Транзистор + description: Пропускает предметы только если вход сбоку имеет истинное значение + (фигура, цвет или "1"). + mirrored: + name: Транзистор + description: Пропускает предметы только если вход сбоку имеет истинное значение + (фигура, цвет или "1"). filter: default: name: Фильтр description: Подключите сигнал, чтобы направить все подходящие ресурсы наверх, а - остальные направо. Также контролируемо логическими сигналами. + остальные направо. Также может контролироваться логическими + сигналами. display: default: name: Экран - description: Подключите сигнал, чтобы отобразить его на экране. Это может + description: Подключите сигнал, чтобы отобразить его на экране. Это может быть ресурс, цвет или логическое значение (1 / 0). reader: default: @@ -528,7 +657,7 @@ buildings: default: name: Анализатор Фигур description: Анализирует правую верхнюю часть низшего слоя фигуры и возвращает - ее форму и цвет. + её форму и цвет. comparator: default: name: Сравнить @@ -558,23 +687,26 @@ buildings: name: Генератор Ресурсов description: Доступен только в режиме песочницы, производит заданный на слое с проводами сигнал на обычном слое. - transistor: + constant_producer: default: - name: Транзистор - description: Пропускает предметы только если вход сбоку имеет истинноре значение - (фигура, цвет или "1"). - mirrored: - name: Транзистор - description: Пропускает предметы только если вход сбоку имеет истинноре значение - (фигура, цвет или "1"). + name: Постоянный производитель + description: Постоянно выводит указанную фигуру или цвет. + goal_acceptor: + default: + name: Приемник цели + description: Доставьте фигуру в приемник, чтобы установить их в качестве цели. + block: + default: + name: Block + description: Allows you to block a tile. storyRewards: reward_cutter_and_trash: title: Разрезание Фигур desc: Разблокирован <strong>резак</strong>, который разрезает фигуры пополам по - вертикали <strong>независимо от ориентации</strong>!<br><br>Не + вертикали <strong>независимо от ориентации</strong>!<br><br> Не забудьте избавляться от излишков, иначе <strong>он забьется и остановится</strong> - для этого я также открыл для Вас - <strong>мусорку</strong>, которая уничтожает все, что в нее + <strong>мусорку</strong>, которая уничтожает все, что в неё попадает! reward_rotater: title: Вращение @@ -584,29 +716,29 @@ storyRewards: title: Покраска desc: "Разблокирован <strong>покрасчик</strong>! Добудьте краситель из жилы (так же, как и фигуры) и объедините его с фигурой в покрасчике, чтобы - раскрасить ее!<br><br>PS: Если вы дальтоник, то в настройках есть - <strong>Режим Дальтоника</strong>!" + раскрасить её!<br><br> Подсказка: Если вы дальтоник, то в настройках + есть <strong>Режим Дальтоника</strong>!" reward_mixer: title: Смешивание Цветов - desc: Разблокирован <strong>смешиватель</strong>! Объедините два цвета в этом - здании, используя <strong>аддитивное смешивание</strong>! + desc: Разблокирован <strong>смешиватель</strong>! Позволяет объединять два + цвета, используя <strong>аддитивное смешивание</strong>! reward_stacker: title: Объединитель desc: Теперь вы можете объединять фигуры <strong>объединителем</strong>! Фигуры - из обеих входов объединяются. Если они могут быть расположены рядом + из обоих входов совмещаются. Если они могут быть расположены рядом друг с другом, они будут <strong>соединены</strong>, иначе фигура из правого входа <strong>наложится</strong> на фигуру из левого! - reward_splitter: - title: Разделитель / Соединитель - desc: Разблокирован <strong>разделитель</strong> один из вариантов - <strong>балансера</strong> - он принимает один вход и разделяет его - на два! + reward_balancer: + title: Балансер + desc: Многофункциональный <strong>балансер</strong> разблокирован - Он + используется для <strong>разделения и обьединения потока + предметов</strong> на несколько конвейеров! reward_tunnel: title: Туннель desc: Разблокирован <strong>туннель</strong>! Теперь вы можете транспортировать предметы под другими конвейерами и зданиями! reward_rotater_ccw: - title: Вращатель (обратный) + title: Обратный Вращатель desc: Разблокирован вариант <strong>вращателя</strong>, вращающий фигуры против часовой стрелки! Чтобы построить его, выберите вращатель и <strong>нажмите 'T', чтобы переключить вариант</strong>! @@ -614,12 +746,27 @@ storyRewards: title: Цепной Экстрактор desc: "Разблокирован <strong>цепной экстрактор</strong>! Он может <strong>передавать свои ресурсы</strong> другим экстракторам, чтобы - вы могли эффективнее извлекать ресурсы!<br><br> PS: Старый + вы могли эффективнее извлекать ресурсы!<br><br> Подсказка: Старый экстрактор был заменен в панели инструментов!" reward_underground_belt_tier_2: title: Туннель II desc: Разблокирован новый вариант <strong>туннеля</strong> с <strong>большей дальностью</strong>, а также вы можете совмещать эти туннели! + reward_merger: + title: Компактный Соединитель + desc: Разблокирован <strong>соединитель</strong> - вариант + <strong>балансера</strong> - он принимает два входа и объединяет их + в один конвейер. + reward_splitter: + title: Разделитель / Соединитель + desc: Разблокирован <strong>разделитель</strong>, один из вариантов + <strong>балансера</strong> - он принимает один вход и разделяет его + на два! + reward_belt_reader: + title: Измеритель + desc: Разблокирован <strong>измеритель</strong>! Он позволяет измерять + пропускную способность конвейера.<br><br> Вы узнаете, как он + полезен, когда разблокируете провода! reward_cutter_quad: title: Резак (4 Выхода) desc: Разблокирован вариант <strong>резака</strong>, разрезающий фигуры на @@ -632,64 +779,46 @@ storyRewards: reward_storage: title: Буферное Хранилище desc: Разблокировано <strong>буферное хранилище</strong> - оно позволяет хранить - в нем ресурсы пока есть место! <br><br> Левый выход в приоритете, + в нем ресурсы пока есть место!<br><br> Левый выход в приоритете, может быть использован как <strong>буфер</strong>! - reward_freeplay: - title: Свободная игра - desc: У Вас получилось! Разблокирован <strong>свободный режим</strong>! Это - означает что теперь фигуры будут генерироваться - <strong>случайно</strong>!<br><br> Так как ХАБ отныне будет - требовать определенную <strong>пропускную способность</strong>, я - настоятельно рекомендую построить механизм, автоматически - доставляющий запрашиваемую фигуру!<br><br> ХАБ выводит запрашиваемую - фигуру на слое с проводами, так что все, что необходимо сделать, - - это проанализировать ее и автоматически настроить вашу фабрику. reward_blueprints: title: Чертежи desc: Теперь вы можете <strong>копировать и вставлять</strong> части вашей фабрики! Выберите область (Удерживая CTRL, перетащите мышь) и - нажмите 'C', чтобы скопировать ее.<br><br>Вставка <strong>не + нажмите 'C', чтобы скопировать её.<br><br> Вставка <strong>не бесплатна</strong>, для этого необходимо произвести <strong>фигуры - для чертежей</strong>! (Которые вы только что доставили). - no_reward: - title: Следующий уровень - desc: "Этот уровень не дал вам награды, но следующий даст! <br><br> PS: Лучше не - разрушайте вашу существующую фабрику - Вам понадобятся - <strong>все</strong> эти фигуры позже, чтобы <strong>разблокировать - улучшения</strong>!" - no_reward_freeplay: - title: Следующий уровень - desc: Поздравляем! Кстати, больше контента планируется для полной версии! - reward_balancer: - title: Балансер - desc: Многофункциональный <strong>балансер</strong> разблокирован - Он - используется для <strong>разделения и обьединения потока - предметов</strong> на несколько конвейеров! - reward_merger: - title: Компактный Соединитель - desc: Разблокирован <strong>соединитель</strong> - вариант - <strong>балансера</strong> - он принимает два входа и объединяет их - в один конвейер. - reward_belt_reader: - title: Измеритель - desc: Разблокирован <strong>измеритель</strong>! Он позволяет измерять - пропускную способность конвейера.<br><br>Вы узнаете, как он полезен, - когда разблокируете провода! + для чертежей</strong>! (Которые вы только что доставили) reward_rotater_180: title: Вращатель (180 градусов) desc: Разблокирован <strong>вращатель</strong> на 180 градусов! - Он позволяет - вращать фигур на 180 градусов (Сюрприз! :D) + вращать фигуры на 180 градусов (Сюрприз! :D) + reward_wires_painter_and_levers: + title: Провода & Покрасчик (4 входа) + desc: "Вы разблокировали <strong>Слой проводов</strong>: Это отдельный слой выше + обычного слоя и он предоставляет много новых механик!<br><br> Для + начала я разблокировал вам <strong>Покрасчик на 4 входа</strong> - + Соедини слоты которые нужно покрасить на слое проводов!<br><br> Для + переключения видимости слоя проводов, нажми + <strong>E</strong>.<br><br> Подсказка: <strong>Включи + подсказки</strong> в настройках, чтобы активировать обучение по + проводам!" + reward_filter: + title: Фильтр + desc: Разблокирован <strong>Фильтр</strong>! Он направит ресурсы наверх или + направо в зависмости от того, совпадают ли они с установленным + сигналом.<br><br> Вы также можете передавать логические значения (1 + / 0), чтобы полностью отключить или включить его. reward_display: title: Экран desc: "Разблокирован <strong>Экран</strong> - Подключите сигнал на слое с - проводами чтобы отобразить его!<br><br> PS: Заметили ли вы, что - измеритель и буферное хранилище отображают последний ресурс, + проводами, чтобы отобразить его!<br><br> Подсказка: Заметили ли вы, + что измеритель и буферное хранилище отображают последний ресурс, прошедший через них? Попробуйте отобразить его на экране!" reward_constant_signal: title: Постоянный Сигнал desc: Разблокирован <strong>постоянный сигнал</strong> на слое с проводами! Он - полезен для подключения к <strong>фильтрам</strong>, - например.<br><br> Постоянный сигнал может издавать + полезен, например, для подключения к + <strong>фильтрам</strong>.<br><br> Постоянный сигнал может издавать <strong>фигуру</strong>, <strong>цвет</strong> или <strong>логическое значение</strong> (1 / 0). reward_logic_gates: @@ -708,23 +837,27 @@ storyRewards: - Построить <strong>автоматический механизм</strong> для производства любой фигуры, запрашиваемой ХАБ (рекомендую попробовать!).<br><br> - Построить что-то клевое, используя - провода.<br><br> - Продолжить обычную игру. <br><br> Что бы вы не + провода.<br><br> - Продолжить обычную игру.<br><br> Чтобы вы не выбрали, не забывайте хорошо проводить время! - reward_wires_painter_and_levers: - title: Провода & Покрасчик (4 входа) - desc: "Вы разблокировали <strong>Слой проводов</strong>: Это отдельный слой выше - обычного слоя и он предоставляет много новых механик!<br><br> Для - начала я разблокировал тебе <strong>Покрасчик на 4 входа </strong> - - Соедини слоты которые нужно покрасить на слое проводов!<br><br> Для - переключения видимости слоя проводов, нажми <strong>E</strong>. - <br><br> PS: <strong>Включи подсказки</strong> в настройках что бы - активировать обучение по проводам!" - reward_filter: - title: Фильтр - desc: Разблокирован <strong>Фильтр</strong>! Он направит ресурсы наверх или - направо в зависмости от того, совпадают ли они с установленным - сигналом. <br><br> Вы также можете передавать логические значения (1 - / 0), чтобы полностью отключить или включить его. + no_reward: + title: Следующий уровень + desc: "Этот уровень не дал вам награды, но следующий даст!<br><br> Подсказка: + Лучше не разрушайте вашу существующую фабрику - Вам понадобятся + <strong>все</strong> эти фигуры позже, чтобы <strong>разблокировать + улучшения</strong>!" + no_reward_freeplay: + title: Следующий уровень + desc: Поздравляем! Кстати, больше контента планируется для полной версии! + reward_freeplay: + title: Свободная игра + desc: У Вас получилось! Разблокирован <strong>свободный режим</strong>! Это + означает что теперь фигуры будут генерироваться + <strong>случайно</strong>!<br><br> Так как ХАБ отныне будет + требовать определенную <strong>пропускную способность</strong>, я + настоятельно рекомендую построить механизм, автоматически + доставляющий запрашиваемую фигуру!<br><br> ХАБ выводит запрашиваемую + фигуру на слое с проводами, так что все, что необходимо сделать, - + это проанализировать её и автоматически настроить вашу фабрику. reward_demo_end: title: Конец Демо desc: Вы достигли конца демоверсии игры! @@ -740,6 +873,7 @@ settings: staging: Постановка prod: Произведена buildDate: Сборка <at-date> + rangeSliderPercentage: <amount> % labels: uiScale: title: Размер интерфейса @@ -753,6 +887,17 @@ settings: regular: Средний large: Большой huge: Огромный + autosaveInterval: + title: Интервал автосохранения + description: Управляет тем, как часто игра автоматически сохраняется. Также + здесь можно полностью отключить автосохранение. + intervals: + one_minute: 1 Минута + two_minutes: 2 Минуты + five_minutes: 5 Минут + ten_minutes: 10 Минут + twenty_minutes: 20 Минут + disabled: Отключено scrollWheelSensitivity: title: Чувствительность зума description: Изменяет чувствительность зума (колесико мыши или сенсорная @@ -763,10 +908,25 @@ settings: regular: Средне fast: Быстро super_fast: Очень быстро + movementSpeed: + title: Скорость движения + description: Изменяет скорость перемещения изображения при использовании + клавиатуры. + speeds: + super_slow: Очень медленно + slow: Медленно + regular: Средне + fast: Быстро + super_fast: Очень быстро + extremely_fast: Чрезвычайно быстро language: title: Язык description: Выберите язык. Все переводы сделаны пользователями и могут быть не законченными! + enableColorBlindHelper: + title: Режим Дальтоника + description: Включает различные инструменты, которые позволяют играть в игру + дальтоникам. fullscreen: title: Полный экран description: Для лучшей игры рекомендуется играть в полноэкранном режиме. @@ -777,6 +937,12 @@ settings: musicMuted: title: Выключить музыку description: Если включено, выключает музыку. + soundVolume: + title: Громкость Звука + description: Задает громкость звуковых эффектов. + musicVolume: + title: Громкость музыки + description: Задает громкость музыки. theme: title: Тема игры description: Выберите тему игры (светлая / темная). @@ -800,17 +966,6 @@ settings: Также скрывает определенные элементы пользовательского интерфейса для данного уровня, предназначенные для облегчения "входа" в игру. - movementSpeed: - title: Скорость движения - description: Изменяет скорость перемещения изображения при использовании - клавиатуры. - speeds: - super_slow: Очень медленно - slow: Медленно - regular: Средне - fast: Быстро - super_fast: Очень быстро - extremely_fast: Чрезвычайно быстро enableTunnelSmartplace: title: Умные Туннели description: Если включено, то при размещении туннелей автоматически удаляются @@ -820,17 +975,11 @@ settings: title: Виньетирование description: Включает виньетирование, которое затемняет углы экрана и облегчает чтение текста. - autosaveInterval: - title: Интервал автосохранения - description: Управляет тем, как часто игра автоматически сохраняется. Также - здесь можно полностью отключить автосохранение. - intervals: - one_minute: 1 Минута - two_minutes: 2 Минуты - five_minutes: 5 Минут - ten_minutes: 10 Минут - twenty_minutes: 20 Минут - disabled: Отключено + rotationByBuilding: + title: Поворот по типу здания + description: Каждый тип здания запоминает поворот, который в последний раз был + установлен. С этой настройкой может быть удобнее, при частом + переключении между различными типами зданий. compactBuildingInfo: title: Компактная Информация о Зданиях description: Сокращает отображаемую информацию о зданиях, показывая только их @@ -840,21 +989,6 @@ settings: title: Отключить Предупреждение о Вырезании/Удалении description: Отключает диалоговые окна с предупреждениями, появляющиеся при вырезании/удалении более 100 объектов. - enableColorBlindHelper: - title: Режим Дальтоника - description: Включает различные инструменты, которые позволяют играть в игру - дальтоникам. - rotationByBuilding: - title: Поворот по типу здания - description: Каждый тип здания запоминает поворот, который в последний раз был - установлен. С этой настройкой может быть удобнее, при частом - переключении между различными типами зданий. - soundVolume: - title: Громкость Звука - description: Задает громкость звуковых эффектов. - musicVolume: - title: Громкость музыки - description: Задает громкость музыки. lowQualityMapResources: title: Низкое качество ресурсов на карте description: Упрощает отображение ресурсов на карте при приближении для @@ -883,8 +1017,8 @@ settings: пипетку над жилой с ресурсами. simplifiedBelts: title: Упрощенные Конвейеры (Некрасиво) - description: Не отображает ресурсы, находящиеся на конвейере, если не навести - курсор для улучшения производительности. Не рекомендую играть с + description: Для улучшения производительности не отображает ресурсы, находящиеся + на конвейере, если не навести курсор. Не рекомендую играть с этой настройкой, если вас устраивает производительность. enableMousePan: title: Включить Перемещение Мышкой @@ -897,7 +1031,6 @@ settings: mapResourcesScale: title: Размер ресурсов на карте description: Устанавливает размер фигур на карте (когда вид достаточно отдалён). - rangeSliderPercentage: <amount> % keybindings: title: Настройки управления hint: "Подсказка: Обязательно используйте CTRL, SHIFT и ALT! Они дают разные @@ -918,15 +1051,20 @@ keybindings: mapMoveRight: Вправо mapMoveDown: Вниз mapMoveLeft: Влево + mapMoveFaster: Ускорение передвижения centerMap: Центрировать карту mapZoomIn: Приблизить mapZoomOut: Отдалить createMarker: Создать Маркер menuOpenShop: Улучшения menuOpenStats: Статистика + menuClose: Закрыть меню toggleHud: Переключить HUD toggleFPSInfo: Включить/выключить FPS и информацию отладки + switchLayers: Переключить слои + exportScreenshot: Экспорт всей Базы в виде Изображения belt: Конвейер + balancer: Балансер underground_belt: Туннель miner: Экстрактор cutter: Резак @@ -935,29 +1073,8 @@ keybindings: mixer: Смешиватель painter: Покрасчик trash: Мусорка - rotateWhilePlacing: Вращать - rotateInverseModifier: "Модификатор: Вращать против часовой стрелки" - cycleBuildingVariants: Переключение Вариантов - confirmMassDelete: Подтверждение Массового Удаления - cycleBuildings: Переключение Построек - massSelectStart: Модификатор для выделения области - massSelectSelectMultiple: Выбрать несколько областей - massSelectCopy: Копировать область - placementDisableAutoOrientation: Отключить автоопределение направления - placeMultiple: Оставаться в режиме размещения - placeInverse: Инвертировать автоопределение направления конвейеров - pasteLastBlueprint: Вставить последний чертеж - massSelectCut: Вырезать область - exportScreenshot: Экспорт всей Базы в виде Изображения - mapMoveFaster: Ускорение передвижения - lockBeltDirection: Включает конвейерный планировщик - switchDirectionLockSide: "Планировщик: Переключение сторон" - pipette: Пипетка - menuClose: Закрыть меню - switchLayers: Переключить слои - wire: Энергетический провод - balancer: Балансер storage: Буферное Хранилище + wire: Энергетический провод constant_signal: Постоянный Сигнал logic_gate: Логический Элемент lever: Переключатель (обычный) @@ -970,11 +1087,31 @@ keybindings: analyzer: Анализатор Фигур comparator: Сравнить item_producer: Генератор Ресурсов (Песочница) + pipette: Пипетка + rotateWhilePlacing: Вращать + rotateInverseModifier: "Модификатор: Вращать против часовой стрелки" + rotateToUp: "Повернуть: Направление Вверх" + rotateToDown: "Повернуть: Направление Вниз" + rotateToRight: "Повернуть: Направление Вправо" + rotateToLeft: "Повернуть: Направление Влево" + cycleBuildingVariants: Переключение Вариантов + confirmMassDelete: Подтверждение Массового Удаления + pasteLastBlueprint: Вставить последний чертеж + cycleBuildings: Переключение Построек + lockBeltDirection: Включает конвейерный планировщик + switchDirectionLockSide: "Планировщик: Переключение сторон" copyWireValue: "Провода: скопировать значение под курсором" - rotateToUp: "Rotate: Point Up" - rotateToDown: "Rotate: Point Down" - rotateToRight: "Rotate: Point Right" - rotateToLeft: "Rotate: Point Left" + massSelectStart: Модификатор для выделения области + massSelectSelectMultiple: Выбрать несколько областей + massSelectCopy: Копировать область + massSelectCut: Вырезать область + placementDisableAutoOrientation: Отключить автоопределение направления + placeMultiple: Оставаться в режиме размещения + placeInverse: Инвертировать автоопределение направления конвейеров + constant_producer: Constant Producer + goal_acceptor: Goal Acceptor + block: Block + massSelectClear: Clear belts about: title: Об игре body: >- @@ -1009,7 +1146,7 @@ tips: - Соотношения всегда одинаковы, если уровни улучшений равны. - Последовательное выполнение эффективнее, чем параллельное. - Вам будут открываться новые варианты построек по мере прохождения! - - Нажмите <b>T</b>, чтобы переключаться между различными вариантами. + - Нажмите <b>T</b>, чтобы переключаться между различными вариантами. - Симметрия - ключ к успеху! - Вы можете переплетать между собой туннели разных уровней. - Попробуйте строить компактные фабрики - вы не пожалеете! @@ -1017,52 +1154,125 @@ tips: <b>T</b>. - Правильные соотношения построек позволяет улучшить эффективность фабрики. - На максимальном уровне, 5 экстракторов заполняют один конвейер. + - Не забывайте про туннели! + - Вам не нужно делить предметы равномерно для полной эффективности. + - Удерживание <b>SHIFT</b> активирует планировщик конвейеров, что упрощает + постройку длинных конвейеров. - Резаки всегда разрезают пополам по вертикали вне зависимости от ориентации. - Чтобы получить белый цвет, смешайте все три цвета. - - Удерживание <b>SHIFT</b> активирует планировщик конвейеров, что упрощает - простройку длинных конвейеров. + - Буффер хранилища с большим приоритетом выдаёт предметы на левый выход. - Вкладывайте время в строительство повторяемых механизмов - оно того стоит! - - Смешайте все три цвета для получения белого. - - Буффер хранилища с большим приоритетом выдаёт на левый выход. - - Эффективность - ключ к успеху! - - Удерживание <b>CTRL</b> даёт возможность размещения нескольких построек. + - Удерживание <b>SHIFT</b> даёт возможность размещения нескольких построек. - Можно зажать <b>ALT</b> для инвертирования направления размещаемых конвейеров. + - Эффективность - ключ к успеху! + - Чем дальше от ХАБ-а, тем сложнее формы в жилах! + - Здания имеют лимит скорости, разделяйте их для максимальной эффективности. - Используйте балансеры, чтобы максимизировать эффективность. - Организация очень важна, старайтесь не пересекать конвейеры слишком часто. - Планируйте заранее, иначе начнется ужасный хаос! - Не удаляйте свои старые фабрики! Они понадобятся вам, чтобы открывать улучшения. - - Попробуйте пройти 20-ый уровень самостоятельно, прежде чем искать помощи! + - Попробуйте пройти 20-ый или 26-ой уровень самостоятельно, прежде чем + искать помощи! - Не усложняйте себе жизнь, старайтесь думать проще и вы достигните больших высот. - Вам может снова понадобиться ваша старая фабрика позже в игре. Старайтесь планировать фабрику, чтобы она могла быть повторно использована. - Иногда, вы можете найти необходимую фигуру на карте, вместо того, чтобы - создавать ее самостоятельно. + создавать её самостоятельно. - Полноценные мельницы/вертушки никогда не генерируются натурально. - - Окрашивайте свои фигуры, прежде чем разрезать для максимальной - эффективности. + - Для максимальной эффективности окрашивайте свои фигуры, прежде чем + разрезать. - С модулями теряется восприятие пространства; забота смертных. - Создайте отдельную фабрику чертежей. Они очень важны для модулей. - Взгляните внимательнее на смешиватель и вы найдете ответы на свои вопросы. + - Используйте <b>CTRL</b> + Потащить, чтобы выделить область - Строительство вблизи ХАБ-а может помешать будущим фабрикам. - - Иконка булавки на каждой фигуре закрепляет ее на экране. - - Have a closer look at the color mixer, and your questions will be answered. - - Use <b>CTRL</b> + Click to select an area. + - Иконка булавки на каждой фигуре закрепляет её на экране. + - Смешайте все три цвета для получения белого. + - У вас есть бесконечная карта. Не зажимайте свою фабрику, расширяйтесь! - Также попробуйте Factorio. Это моя любимая игра. - - Резак(4 входа) разрезает по часовой стрелке, начиная с правой верхней + - Резак (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. - - You can click a pinned shape on the left side to unpin it. + - Вы можете нажать на закрепленную фигуру слева, чтобы открепить её. +puzzleMenu: + play: Играть + edit: Редактировать + title: Режим головоломок + createPuzzle: Создать головоломку + loadPuzzle: Загрузить + reviewPuzzle: Просмотреть и опубликовать + validatingPuzzle: Подтверждение головоломки + submittingPuzzle: Отправка головоломки + noPuzzles: В данный момент в этом разделе нет головоломок. + categories: + levels: Уровни + new: Новые + top-rated: Популярные + mine: Мои головоломки + easy: Простые + hard: Сложные + completed: Завершенные + medium: Средние + official: Официальные + trending: Популярные сегодня + trending-weekly: Популярные за неделю + categories: Категории + difficulties: По сложности + account: Мои головоломки + search: Поиск + validation: + title: Недействительная головоломка + noProducers: Пожалуйста, разместисте постоянный производитель! + noGoalAcceptors: Пожалуйста, разместите приемник цели! + goalAcceptorNoItem: Одному или несколькоим приеминкам цели не назначен предмет. + Доставьте к ним фигуру, чтоб установить цель. + goalAcceptorRateNotMet: Один или несколько приемников цели не получают + достаточно предметов. Убедитесь, что индикаторы всех приемников + зеленые. + buildingOutOfBounds: Одно или несколько зданий находятся за пределами зоны + строительства. Либо увеличьте зону, либо удалите их. + autoComplete: Ваша головоломка завершится автоматически! Убедитесь, что ваши + постоянные производители не доставляют фигуры напрямую приемникам + цели. + difficulties: + easy: Лего + medium: Средне + hard: Сложно + dlcHint: Purchased the DLC already? Make sure it is activated by right clicking + shapez.io in your library, selecting Properties > DLCs. +backendErrors: + ratelimit: Вы слишком часто выполняете свои действия. Подождите немного. + invalid-api-key: Не удалось связаться с сервером, попробуйте + обновить/перезапустить игру (Invalid Api Key). + unauthorized: Не удалось связаться с сервером, попробуйте обновить/перезапустить + игру (Unauthorized). + bad-token: Не удалось связаться с сервером, попробуйте обновить/перезапустить + игру (Bad Token). + bad-id: Недействительный идентификатор головоломки. + not-found: Данная головломка не может быть найдена. + bad-category: Данная категория не может быть найдена. + bad-short-key: Данный короткий ключ недействителен. + profane-title: Название вашей головоломки содержит нецензурные слова. + bad-title-too-many-spaces: Название вашей головоломки слишком короткое. + bad-shape-key-in-emitter: Недопустимный предмет у постоянного производителя. + bad-shape-key-in-goal: Недопустимный предмет у применика цели. + no-emitters: Ваша головоломка не содержит постоянных производителей. + no-goals: Ваша головоломка не содержит приемников цели. + short-key-already-taken: Этот короткий ключ уже занят, используйте другой. + can-not-report-your-own-puzzle: Вы не можете пожаловаться на собственную головоломку. + bad-payload: Запрос содержит неверные данные. + bad-building-placement: Ваша головоломка содержит неверно размещенные здания. + timeout: Время ожидания запроса истекло. + too-many-likes-already: Головоломка уже заработала слишком много лайков. Если вы + все еще хотите удалить ее, обратитесь в support@shapez.io! + no-permission: У вас нет прав на выполнение этого действия. diff --git a/translations/base-sl.yaml b/translations/base-sl.yaml index 727a5301..b5400263 100644 --- a/translations/base-sl.yaml +++ b/translations/base-sl.yaml @@ -53,6 +53,7 @@ global: escape: ESC shift: SHIFT space: SPACE + loggingIn: Logging in demoBanners: title: Demo Version intro: Get the standalone to unlock all features! @@ -72,6 +73,12 @@ mainMenu: savegameLevel: Level <x> savegameLevelUnknown: Unknown Level savegameUnnamed: Unnamed + puzzleMode: Puzzle Mode + back: Back + puzzleDlcText: Do you enjoy compacting and optimizing factories? Get the Puzzle + DLC now on Steam for even more fun! + puzzleDlcWishlist: Wishlist now! + puzzleDlcViewNow: View Dlc dialogs: buttons: ok: OK @@ -85,6 +92,9 @@ dialogs: viewUpdate: View Update showUpgrades: Show Upgrades showKeybindings: Show Keybindings + retry: Retry + continue: Continue + playOffline: Play Offline importSavegameError: title: Import Error text: "Failed to import your savegame:" @@ -182,6 +192,70 @@ dialogs: title: Tutorial Available desc: There is a tutorial video available for this level, but it is only available in English. Would you like to watch it? + editConstantProducer: + title: Set Item + puzzleLoadFailed: + title: Puzzles failed to load + desc: "Unfortunately the puzzles could not be loaded:" + submitPuzzle: + title: Submit Puzzle + descName: "Give your puzzle a name:" + descIcon: "Please enter a unique short key, which will be shown as the icon of + your puzzle (You can generate them <link>here</link>, or choose one + of the randomly suggested shapes below):" + placeholderName: Puzzle Title + puzzleResizeBadBuildings: + title: Resize not possible + desc: You can't make the zone any smaller, because then some buildings would be + outside the zone. + puzzleLoadError: + title: Bad Puzzle + desc: "The puzzle failed to load:" + offlineMode: + title: Offline Mode + desc: We couldn't reach the servers, so the game has to run in offline mode. + Please make sure you have an active internet connection. + puzzleDownloadError: + title: Download Error + desc: "Failed to download the puzzle:" + puzzleSubmitError: + title: Submission Error + desc: "Failed to submit your puzzle:" + puzzleSubmitOk: + title: Puzzle Published + desc: Congratulations! Your puzzle has been published and can now be played by + others. You can now find it in the "My puzzles" section. + puzzleCreateOffline: + title: Offline Mode + desc: Since you are offline, you will not be able to save and/or publish your + puzzle. Would you still like to continue? + puzzlePlayRegularRecommendation: + title: Recommendation + desc: I <strong>strongly</strong> recommend playing the normal game to level 12 + before attempting the puzzle DLC, otherwise you may encounter + mechanics not yet introduced. Do you still want to continue? + puzzleShare: + title: Short Key Copied + desc: The short key of the puzzle (<key>) has been copied to your clipboard! It + can be entered in the puzzle menu to access the puzzle. + puzzleReport: + title: Report Puzzle + options: + profane: Profane + unsolvable: Not solvable + trolling: Trolling + puzzleReportComplete: + title: Thank you for your feedback! + desc: The puzzle has been flagged. + puzzleReportError: + title: Failed to report + desc: "Your report could not get processed:" + puzzleLoadShortKey: + title: Enter short key + desc: Enter the short key of the puzzle to load it. + puzzleDelete: + title: Delete Puzzle? + desc: Are you sure you want to delete '<title>'? This can not be undone! ingame: keybindingsOverlay: moveMap: Move @@ -203,6 +277,7 @@ ingame: clearSelection: Clear selection pipette: Pipette switchLayers: Switch layers + clearBelts: Clear belts colors: red: Red green: Green @@ -350,6 +425,46 @@ ingame: achievements: title: Achievements desc: Hunt them all! + puzzleEditorSettings: + zoneTitle: Zone + zoneWidth: Width + zoneHeight: Height + trimZone: Trim + clearItems: Clear Items + share: Share + report: Report + clearBuildings: Clear Buildings + resetPuzzle: Reset Puzzle + puzzleEditorControls: + title: Puzzle Creator + instructions: + - 1. Place <strong>Constant Producers</strong> to provide shapes and + colors to the player + - 2. Build one or more shapes you want the player to build later and + deliver it to one or more <strong>Goal Acceptors</strong> + - 3. Once a Goal Acceptor receives a shape for a certain amount of + time, it <strong>saves it as a goal</strong> that the player must + produce later (Indicated by the <strong>green badge</strong>). + - 4. Click the <strong>lock button</strong> on a building to disable + it. + - 5. Once you click review, your puzzle will be validated and you + can publish it. + - 6. Upon release, <strong>all buildings will be removed</strong> + except for the Producers and Goal Acceptors - That's the part that + the player is supposed to figure out for themselves, after all :) + puzzleCompletion: + title: Puzzle Completed! + titleLike: "Click the heart if you liked the puzzle:" + titleRating: How difficult did you find the puzzle? + titleRatingDesc: Your rating will help me to make you better suggestions in the future + continueBtn: Keep Playing + menuBtn: Menu + puzzleMetadata: + author: Author + shortKey: Short Key + rating: Difficulty score + averageDuration: Avg. Duration + completionRate: Completion rate shopUpgrades: belt: name: Belts, Distributor & Tunnels @@ -556,6 +671,18 @@ buildings: name: Item Producer description: Available in sandbox mode only, outputs the given signal from the wires layer on the regular layer. + constant_producer: + default: + name: Constant Producer + description: Constantly outputs a specified shape or color. + goal_acceptor: + default: + name: Goal Acceptor + description: Deliver shapes to the goal acceptor to set them as a goal. + block: + default: + name: Block + description: Allows you to block a tile. storyRewards: reward_cutter_and_trash: title: Cutting Shapes @@ -957,6 +1084,10 @@ keybindings: rotateToDown: "Rotate: Point Down" rotateToRight: "Rotate: Point Right" rotateToLeft: "Rotate: Point Left" + constant_producer: Constant Producer + goal_acceptor: Goal Acceptor + block: Block + massSelectClear: Clear belts about: title: About this Game body: >- @@ -1042,3 +1173,73 @@ tips: - 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. +puzzleMenu: + play: Play + edit: Edit + title: Puzzle Mode + createPuzzle: Create Puzzle + loadPuzzle: Load + reviewPuzzle: Review & Publish + validatingPuzzle: Validating Puzzle + submittingPuzzle: Submitting Puzzle + noPuzzles: There are currently no puzzles in this section. + categories: + levels: Levels + new: New + top-rated: Top Rated + mine: My Puzzles + easy: Easy + hard: Hard + completed: Completed + medium: Medium + official: Official + trending: Trending today + trending-weekly: Trending weekly + categories: Categories + difficulties: By Difficulty + account: My Puzzles + search: Search + validation: + title: Invalid Puzzle + noProducers: Please place a Constant Producer! + noGoalAcceptors: Please place a Goal Acceptor! + goalAcceptorNoItem: One or more Goal Acceptors have not yet assigned an item. + Deliver a shape to them to set a goal. + goalAcceptorRateNotMet: One or more Goal Acceptors are not getting enough items. + Make sure that the indicators are green for all acceptors. + buildingOutOfBounds: One or more buildings are outside of the buildable area. + Either increase the area or remove them. + autoComplete: Your puzzle autocompletes itself! Please make sure your constant + producers are not directly delivering to your goal acceptors. + difficulties: + easy: Easy + medium: Medium + hard: Hard + dlcHint: Purchased the DLC already? Make sure it is activated by right clicking + shapez.io in your library, selecting Properties > DLCs. +backendErrors: + ratelimit: You are performing your actions too frequent. Please wait a bit. + invalid-api-key: Failed to communicate with the backend, please try to + update/restart the game (Invalid Api Key). + unauthorized: Failed to communicate with the backend, please try to + update/restart the game (Unauthorized). + bad-token: Failed to communicate with the backend, please try to update/restart + the game (Bad Token). + bad-id: Invalid puzzle identifier. + not-found: The given puzzle could not be found. + bad-category: The given category could not be found. + bad-short-key: The given short key is invalid. + profane-title: Your puzzle title contains profane words. + bad-title-too-many-spaces: Your puzzle title is too short. + bad-shape-key-in-emitter: A constant producer has an invalid item. + bad-shape-key-in-goal: A goal acceptor has an invalid item. + no-emitters: Your puzzle does not contain any constant producers. + no-goals: Your puzzle does not contain any goal acceptors. + short-key-already-taken: This short key is already taken, please use another one. + can-not-report-your-own-puzzle: You can not report your own puzzle. + bad-payload: The request contains invalid data. + bad-building-placement: Your puzzle contains invalid placed buildings. + timeout: The request timed out. + too-many-likes-already: The puzzle alreay got too many likes. If you still want + to remove it, please contact support@shapez.io! + no-permission: You do not have the permission to perform this action. diff --git a/translations/base-sr.yaml b/translations/base-sr.yaml index 2ac49bb2..4cac7750 100644 --- a/translations/base-sr.yaml +++ b/translations/base-sr.yaml @@ -52,6 +52,7 @@ global: escape: ESC shift: SHIFT space: SPACE + loggingIn: Logging in demoBanners: title: Demo Verzija intro: Nabavite punu igru kako biste otključali sve funkcije! @@ -72,6 +73,12 @@ mainMenu: savegameLevel: Nivo <x> savegameLevelUnknown: Nepoznat Nivo savegameUnnamed: Unnamed + puzzleMode: Puzzle Mode + back: Back + puzzleDlcText: Do you enjoy compacting and optimizing factories? Get the Puzzle + DLC now on Steam for even more fun! + puzzleDlcWishlist: Wishlist now! + puzzleDlcViewNow: View Dlc dialogs: buttons: ok: OK @@ -85,6 +92,9 @@ dialogs: viewUpdate: Pogledajte ažuriranje showUpgrades: Prikaži Nadogradnje showKeybindings: Prikaži podešavanje tastera + retry: Retry + continue: Continue + playOffline: Play Offline importSavegameError: title: Greška prilikom uvoza text: "Neuspešan uvoz sačuvane igre:" @@ -182,6 +192,70 @@ dialogs: title: Tutorial Available desc: There is a tutorial video available for this level, but it is only available in English. Would you like to watch it? + editConstantProducer: + title: Set Item + puzzleLoadFailed: + title: Puzzles failed to load + desc: "Unfortunately the puzzles could not be loaded:" + submitPuzzle: + title: Submit Puzzle + descName: "Give your puzzle a name:" + descIcon: "Please enter a unique short key, which will be shown as the icon of + your puzzle (You can generate them <link>here</link>, or choose one + of the randomly suggested shapes below):" + placeholderName: Puzzle Title + puzzleResizeBadBuildings: + title: Resize not possible + desc: You can't make the zone any smaller, because then some buildings would be + outside the zone. + puzzleLoadError: + title: Bad Puzzle + desc: "The puzzle failed to load:" + offlineMode: + title: Offline Mode + desc: We couldn't reach the servers, so the game has to run in offline mode. + Please make sure you have an active internet connection. + puzzleDownloadError: + title: Download Error + desc: "Failed to download the puzzle:" + puzzleSubmitError: + title: Submission Error + desc: "Failed to submit your puzzle:" + puzzleSubmitOk: + title: Puzzle Published + desc: Congratulations! Your puzzle has been published and can now be played by + others. You can now find it in the "My puzzles" section. + puzzleCreateOffline: + title: Offline Mode + desc: Since you are offline, you will not be able to save and/or publish your + puzzle. Would you still like to continue? + puzzlePlayRegularRecommendation: + title: Recommendation + desc: I <strong>strongly</strong> recommend playing the normal game to level 12 + before attempting the puzzle DLC, otherwise you may encounter + mechanics not yet introduced. Do you still want to continue? + puzzleShare: + title: Short Key Copied + desc: The short key of the puzzle (<key>) has been copied to your clipboard! It + can be entered in the puzzle menu to access the puzzle. + puzzleReport: + title: Report Puzzle + options: + profane: Profane + unsolvable: Not solvable + trolling: Trolling + puzzleReportComplete: + title: Thank you for your feedback! + desc: The puzzle has been flagged. + puzzleReportError: + title: Failed to report + desc: "Your report could not get processed:" + puzzleLoadShortKey: + title: Enter short key + desc: Enter the short key of the puzzle to load it. + puzzleDelete: + title: Delete Puzzle? + desc: Are you sure you want to delete '<title>'? This can not be undone! ingame: keybindingsOverlay: moveMap: Kretanje @@ -203,6 +277,7 @@ ingame: clearSelection: Očisti odabir pipette: Pipeta switchLayers: Promeni sloj + clearBelts: Clear belts colors: red: Crvena green: Zelena @@ -350,6 +425,46 @@ ingame: achievements: title: Achievements desc: Hunt them all! + puzzleEditorSettings: + zoneTitle: Zone + zoneWidth: Width + zoneHeight: Height + trimZone: Trim + clearItems: Clear Items + share: Share + report: Report + clearBuildings: Clear Buildings + resetPuzzle: Reset Puzzle + puzzleEditorControls: + title: Puzzle Creator + instructions: + - 1. Place <strong>Constant Producers</strong> to provide shapes and + colors to the player + - 2. Build one or more shapes you want the player to build later and + deliver it to one or more <strong>Goal Acceptors</strong> + - 3. Once a Goal Acceptor receives a shape for a certain amount of + time, it <strong>saves it as a goal</strong> that the player must + produce later (Indicated by the <strong>green badge</strong>). + - 4. Click the <strong>lock button</strong> on a building to disable + it. + - 5. Once you click review, your puzzle will be validated and you + can publish it. + - 6. Upon release, <strong>all buildings will be removed</strong> + except for the Producers and Goal Acceptors - That's the part that + the player is supposed to figure out for themselves, after all :) + puzzleCompletion: + title: Puzzle Completed! + titleLike: "Click the heart if you liked the puzzle:" + titleRating: How difficult did you find the puzzle? + titleRatingDesc: Your rating will help me to make you better suggestions in the future + continueBtn: Keep Playing + menuBtn: Menu + puzzleMetadata: + author: Author + shortKey: Short Key + rating: Difficulty score + averageDuration: Avg. Duration + completionRate: Completion rate shopUpgrades: belt: name: Trake, Delioci i Tuneli @@ -554,6 +669,18 @@ buildings: name: Item Producer description: Available in sandbox mode only, outputs the given signal from the wires layer on the regular layer. + constant_producer: + default: + name: Constant Producer + description: Constantly outputs a specified shape or color. + goal_acceptor: + default: + name: Goal Acceptor + description: Deliver shapes to the goal acceptor to set them as a goal. + block: + default: + name: Block + description: Allows you to block a tile. storyRewards: reward_cutter_and_trash: title: Rezanje Oblika @@ -955,6 +1082,10 @@ keybindings: rotateToDown: "Rotate: Point Down" rotateToRight: "Rotate: Point Right" rotateToLeft: "Rotate: Point Left" + constant_producer: Constant Producer + goal_acceptor: Goal Acceptor + block: Block + massSelectClear: Clear belts about: title: O Igri body: >- @@ -1040,3 +1171,73 @@ tips: - 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. +puzzleMenu: + play: Play + edit: Edit + title: Puzzle Mode + createPuzzle: Create Puzzle + loadPuzzle: Load + reviewPuzzle: Review & Publish + validatingPuzzle: Validating Puzzle + submittingPuzzle: Submitting Puzzle + noPuzzles: There are currently no puzzles in this section. + categories: + levels: Levels + new: New + top-rated: Top Rated + mine: My Puzzles + easy: Easy + hard: Hard + completed: Completed + medium: Medium + official: Official + trending: Trending today + trending-weekly: Trending weekly + categories: Categories + difficulties: By Difficulty + account: My Puzzles + search: Search + validation: + title: Invalid Puzzle + noProducers: Please place a Constant Producer! + noGoalAcceptors: Please place a Goal Acceptor! + goalAcceptorNoItem: One or more Goal Acceptors have not yet assigned an item. + Deliver a shape to them to set a goal. + goalAcceptorRateNotMet: One or more Goal Acceptors are not getting enough items. + Make sure that the indicators are green for all acceptors. + buildingOutOfBounds: One or more buildings are outside of the buildable area. + Either increase the area or remove them. + autoComplete: Your puzzle autocompletes itself! Please make sure your constant + producers are not directly delivering to your goal acceptors. + difficulties: + easy: Easy + medium: Medium + hard: Hard + dlcHint: Purchased the DLC already? Make sure it is activated by right clicking + shapez.io in your library, selecting Properties > DLCs. +backendErrors: + ratelimit: You are performing your actions too frequent. Please wait a bit. + invalid-api-key: Failed to communicate with the backend, please try to + update/restart the game (Invalid Api Key). + unauthorized: Failed to communicate with the backend, please try to + update/restart the game (Unauthorized). + bad-token: Failed to communicate with the backend, please try to update/restart + the game (Bad Token). + bad-id: Invalid puzzle identifier. + not-found: The given puzzle could not be found. + bad-category: The given category could not be found. + bad-short-key: The given short key is invalid. + profane-title: Your puzzle title contains profane words. + bad-title-too-many-spaces: Your puzzle title is too short. + bad-shape-key-in-emitter: A constant producer has an invalid item. + bad-shape-key-in-goal: A goal acceptor has an invalid item. + no-emitters: Your puzzle does not contain any constant producers. + no-goals: Your puzzle does not contain any goal acceptors. + short-key-already-taken: This short key is already taken, please use another one. + can-not-report-your-own-puzzle: You can not report your own puzzle. + bad-payload: The request contains invalid data. + bad-building-placement: Your puzzle contains invalid placed buildings. + timeout: The request timed out. + too-many-likes-already: The puzzle alreay got too many likes. If you still want + to remove it, please contact support@shapez.io! + no-permission: You do not have the permission to perform this action. diff --git a/translations/base-sv.yaml b/translations/base-sv.yaml index c8bab8f8..dd30305f 100644 --- a/translations/base-sv.yaml +++ b/translations/base-sv.yaml @@ -52,6 +52,7 @@ global: escape: ESC shift: SKIFT space: MELLANSLAG + loggingIn: Logging in demoBanners: title: Demo-version intro: Skaffa den fristående versionen för att låsa upp alla funktioner! @@ -72,6 +73,12 @@ mainMenu: madeBy: Skapad av <author-link> subreddit: Reddit savegameUnnamed: Namnlöst + puzzleMode: Puzzle Mode + back: Back + puzzleDlcText: Do you enjoy compacting and optimizing factories? Get the Puzzle + DLC now on Steam for even more fun! + puzzleDlcWishlist: Wishlist now! + puzzleDlcViewNow: View Dlc dialogs: buttons: ok: OK @@ -85,6 +92,9 @@ dialogs: viewUpdate: Se uppdateringar showUpgrades: Visa uppgraderingar showKeybindings: Visa tangentbindingar + retry: Retry + continue: Continue + playOffline: Play Offline importSavegameError: title: Importfel text: "Kunde inte importera sparfil:" @@ -186,6 +196,70 @@ dialogs: title: Handledning Tillgänglig desc: Det finns en handledningsvideo tillgänglig för denna nivå, men den är bara tillgänglig på engelska. Vill du se den? + editConstantProducer: + title: Set Item + puzzleLoadFailed: + title: Puzzles failed to load + desc: "Unfortunately the puzzles could not be loaded:" + submitPuzzle: + title: Submit Puzzle + descName: "Give your puzzle a name:" + descIcon: "Please enter a unique short key, which will be shown as the icon of + your puzzle (You can generate them <link>here</link>, or choose one + of the randomly suggested shapes below):" + placeholderName: Puzzle Title + puzzleResizeBadBuildings: + title: Resize not possible + desc: You can't make the zone any smaller, because then some buildings would be + outside the zone. + puzzleLoadError: + title: Bad Puzzle + desc: "The puzzle failed to load:" + offlineMode: + title: Offline Mode + desc: We couldn't reach the servers, so the game has to run in offline mode. + Please make sure you have an active internet connection. + puzzleDownloadError: + title: Download Error + desc: "Failed to download the puzzle:" + puzzleSubmitError: + title: Submission Error + desc: "Failed to submit your puzzle:" + puzzleSubmitOk: + title: Puzzle Published + desc: Congratulations! Your puzzle has been published and can now be played by + others. You can now find it in the "My puzzles" section. + puzzleCreateOffline: + title: Offline Mode + desc: Since you are offline, you will not be able to save and/or publish your + puzzle. Would you still like to continue? + puzzlePlayRegularRecommendation: + title: Recommendation + desc: I <strong>strongly</strong> recommend playing the normal game to level 12 + before attempting the puzzle DLC, otherwise you may encounter + mechanics not yet introduced. Do you still want to continue? + puzzleShare: + title: Short Key Copied + desc: The short key of the puzzle (<key>) has been copied to your clipboard! It + can be entered in the puzzle menu to access the puzzle. + puzzleReport: + title: Report Puzzle + options: + profane: Profane + unsolvable: Not solvable + trolling: Trolling + puzzleReportComplete: + title: Thank you for your feedback! + desc: The puzzle has been flagged. + puzzleReportError: + title: Failed to report + desc: "Your report could not get processed:" + puzzleLoadShortKey: + title: Enter short key + desc: Enter the short key of the puzzle to load it. + puzzleDelete: + title: Delete Puzzle? + desc: Are you sure you want to delete '<title>'? This can not be undone! ingame: keybindingsOverlay: moveMap: Flytta @@ -207,6 +281,7 @@ ingame: clearSelection: Rensa vald pipette: Pipett switchLayers: Byt lager + clearBelts: Clear belts buildingPlacement: cycleBuildingVariants: Tryck ned <key> För att bläddra igenom varianter. hotkeyLabel: "Snabbtangent: <key>" @@ -354,6 +429,46 @@ ingame: achievements: title: Achievements desc: Hunt them all! + puzzleEditorSettings: + zoneTitle: Zone + zoneWidth: Width + zoneHeight: Height + trimZone: Trim + clearItems: Clear Items + share: Share + report: Report + clearBuildings: Clear Buildings + resetPuzzle: Reset Puzzle + puzzleEditorControls: + title: Puzzle Creator + instructions: + - 1. Place <strong>Constant Producers</strong> to provide shapes and + colors to the player + - 2. Build one or more shapes you want the player to build later and + deliver it to one or more <strong>Goal Acceptors</strong> + - 3. Once a Goal Acceptor receives a shape for a certain amount of + time, it <strong>saves it as a goal</strong> that the player must + produce later (Indicated by the <strong>green badge</strong>). + - 4. Click the <strong>lock button</strong> on a building to disable + it. + - 5. Once you click review, your puzzle will be validated and you + can publish it. + - 6. Upon release, <strong>all buildings will be removed</strong> + except for the Producers and Goal Acceptors - That's the part that + the player is supposed to figure out for themselves, after all :) + puzzleCompletion: + title: Puzzle Completed! + titleLike: "Click the heart if you liked the puzzle:" + titleRating: How difficult did you find the puzzle? + titleRatingDesc: Your rating will help me to make you better suggestions in the future + continueBtn: Keep Playing + menuBtn: Menu + puzzleMetadata: + author: Author + shortKey: Short Key + rating: Difficulty score + averageDuration: Avg. Duration + completionRate: Completion rate shopUpgrades: belt: name: Rullband, Distributörer & Tunnlar @@ -559,6 +674,18 @@ buildings: name: Item Producer description: Endast tillgänglig i sandlådeläge, avger en given signal från kabellagret till det vanliga lagret. + constant_producer: + default: + name: Constant Producer + description: Constantly outputs a specified shape or color. + goal_acceptor: + default: + name: Goal Acceptor + description: Deliver shapes to the goal acceptor to set them as a goal. + block: + default: + name: Block + description: Allows you to block a tile. storyRewards: reward_cutter_and_trash: title: Att klippa former @@ -965,6 +1092,10 @@ keybindings: rotateToDown: "Rotate: Point Down" rotateToRight: "Rotate: Point Right" rotateToLeft: "Rotate: Point Left" + constant_producer: Constant Producer + goal_acceptor: Goal Acceptor + block: Block + massSelectClear: Clear belts about: title: Om detta spel body: >- @@ -1050,3 +1181,73 @@ tips: - 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. +puzzleMenu: + play: Play + edit: Edit + title: Puzzle Mode + createPuzzle: Create Puzzle + loadPuzzle: Load + reviewPuzzle: Review & Publish + validatingPuzzle: Validating Puzzle + submittingPuzzle: Submitting Puzzle + noPuzzles: There are currently no puzzles in this section. + categories: + levels: Levels + new: New + top-rated: Top Rated + mine: My Puzzles + easy: Easy + hard: Hard + completed: Completed + medium: Medium + official: Official + trending: Trending today + trending-weekly: Trending weekly + categories: Categories + difficulties: By Difficulty + account: My Puzzles + search: Search + validation: + title: Invalid Puzzle + noProducers: Please place a Constant Producer! + noGoalAcceptors: Please place a Goal Acceptor! + goalAcceptorNoItem: One or more Goal Acceptors have not yet assigned an item. + Deliver a shape to them to set a goal. + goalAcceptorRateNotMet: One or more Goal Acceptors are not getting enough items. + Make sure that the indicators are green for all acceptors. + buildingOutOfBounds: One or more buildings are outside of the buildable area. + Either increase the area or remove them. + autoComplete: Your puzzle autocompletes itself! Please make sure your constant + producers are not directly delivering to your goal acceptors. + difficulties: + easy: Easy + medium: Medium + hard: Hard + dlcHint: Purchased the DLC already? Make sure it is activated by right clicking + shapez.io in your library, selecting Properties > DLCs. +backendErrors: + ratelimit: You are performing your actions too frequent. Please wait a bit. + invalid-api-key: Failed to communicate with the backend, please try to + update/restart the game (Invalid Api Key). + unauthorized: Failed to communicate with the backend, please try to + update/restart the game (Unauthorized). + bad-token: Failed to communicate with the backend, please try to update/restart + the game (Bad Token). + bad-id: Invalid puzzle identifier. + not-found: The given puzzle could not be found. + bad-category: The given category could not be found. + bad-short-key: The given short key is invalid. + profane-title: Your puzzle title contains profane words. + bad-title-too-many-spaces: Your puzzle title is too short. + bad-shape-key-in-emitter: A constant producer has an invalid item. + bad-shape-key-in-goal: A goal acceptor has an invalid item. + no-emitters: Your puzzle does not contain any constant producers. + no-goals: Your puzzle does not contain any goal acceptors. + short-key-already-taken: This short key is already taken, please use another one. + can-not-report-your-own-puzzle: You can not report your own puzzle. + bad-payload: The request contains invalid data. + bad-building-placement: Your puzzle contains invalid placed buildings. + timeout: The request timed out. + too-many-likes-already: The puzzle alreay got too many likes. If you still want + to remove it, please contact support@shapez.io! + no-permission: You do not have the permission to perform this action. diff --git a/translations/base-tr.yaml b/translations/base-tr.yaml index 3b95e3c2..342daf6c 100644 --- a/translations/base-tr.yaml +++ b/translations/base-tr.yaml @@ -13,14 +13,13 @@ steamPage: En başta sadece şekilleri işlerken, sonradan onları boyaman gerekecek - bunun için boyaları çıkarmalı ve karıştırmalısın! Oyunu Steam'de satın almak tam sürüme erişimi sağlayacak, ama herzaman shapez.io deneme sürümünü oynayıp sonradan karar verebilirsin! - what_others_say: What people say about shapez.io - nothernlion_comment: This game is great - I'm having a wonderful time playing, - and time has flown by. - notch_comment: Oh crap. I really should sleep, but I think I just figured out - how to make a computer in shapez.io - steam_review_comment: This game has stolen my life and I don't want it back. - Very chill factory game that won't let me stop making my lines more - efficient. + what_others_say: İnsanlar shapez.io hakkında ne düşünüyor? + nothernlion_comment: Bu oyun süper - Oynarken harika vakit geçiriyorum ve zaman akıp gidiyor. + notch_comment: Hay aksi. Gerçekten uyumalıyım, ama şimdi shapez.io'da nasıl bir + bilgisayar yapabileceğimi çözdüm. + steam_review_comment: Bu oyun hayatımı çaldı ve geri istemiyorum. Üretim + hatlarımı daha verimli yapmamı engelleyemeyecek kadar güzel bir fabrika + oyunu. global: loading: Yüklenİyor error: Hata @@ -42,8 +41,8 @@ global: oneDayAgo: bir gün önce xDaysAgo: <x> gün önce secondsShort: <seconds>sn - minutesAndSecondsShort: <minutes>dk <seconds>dk - hoursAndMinutesShort: <hours>sa <minutes>sa + minutesAndSecondsShort: <minutes>dk <seconds>sn + hoursAndMinutesShort: <hours>sa <minutes>dk xMinutes: <x> dakika keys: tab: TAB @@ -52,6 +51,7 @@ global: escape: ESC shift: SHIFT space: SPACE + loggingIn: Giriş yapılıyor demoBanners: title: Deneme Sürümü intro: Bütün özellikleri açmak için tam sürümü satın alın! @@ -70,7 +70,14 @@ mainMenu: newGame: YENİ OYUN madeBy: <author-link> tarafından yapıldı subreddit: Reddit - savegameUnnamed: Unnamed + savegameUnnamed: İsimsiz + puzzleMode: Yapboz Modu + back: Geri + puzzleDlcText: Fabrikaları küçültmeyi ve verimli hale getirmekten keyif mi + alıyorsun? Şimdi Yapboz Paketini (DLC) Steam'de alarak keyfine keyif + katabilirsin! + puzzleDlcWishlist: İstek listene ekle! + puzzleDlcViewNow: View Dlc dialogs: buttons: ok: OK @@ -84,6 +91,9 @@ dialogs: viewUpdate: Güncellemeleri Görüntüle showUpgrades: Geliştirmeleri Göster showKeybindings: Tuş Kısayollarını Göster + retry: Yeniden Dene + continue: Devam Et + playOffline: Play Offline importSavegameError: title: Kayıt yükleme hatası text: "Oyun kaydı yükleme başarısız:" @@ -94,16 +104,15 @@ dialogs: title: Oyun bozuk text: "Oyun yükleme başarısız:" confirmSavegameDelete: - title: Silme işlemini onayla - text: <br><br> Bu kayıdı silmek istiyor musunuz? - '<savegameName>' <savegameLevel><br><br>. seviyede. Bu işlem geri - alınamaz! + title: Sİlme İşlemİnİ onayla + text: Bu kaydı silmek istediğinize emin misiniz?<br><br> '<savegameName>' + seviyesi <savegameLevel><br><br> Bu işlem geri alınamaz! savegameDeletionError: title: Silme başarısız - text: "Oyun kaydını silme başarısız:" + text: "Oyun kaydı silinemedi:" restartRequired: title: Yeniden başlatma gerekiyor - text: Değişiklikleri uygulamak için oyunu yeniden başlatılmalı. + text: Değişiklikleri uygulamak için oyunu yeniden başlatın. editKeybinding: title: Tuş Atamasını Değiştir desc: Atamak isteğiniz tuşa veya fare butonun basın, veya iptal etmek için Esc @@ -121,8 +130,8 @@ dialogs: için tam versiyonu satın alın. oneSavegameLimit: title: Sınırlı Oyun Kaydı - desc: Deneme sürümünde sadece tek bir oyun kaydınız olabilir. Lütfen varolanı - silin veya tam sürümü satın alın! + desc: Deneme sürümünde sadece tek bir oyun kaydınız olabilir. Lütfen mevcut + kaydı silin veya tam sürümü satın alın! updateSummary: title: Yeni güncelleme! desc: "Son oynadığınızdan bu yana gelen değişikler:" @@ -149,8 +158,8 @@ dialogs: taşıma bantlarının yönünü ters çevirir.<br>" createMarker: title: Yeni Konum İşareti - desc: Anlamlı bir isim ver. Ayrıca <strong>Şekil koduda</strong> koyabilirsiniz - (<link>Buradan</link> kod yapabilirisinz ) + desc: Anlamlı bir isim ver. Ayrıca <strong>Şekil kodu da</strong> + koyabilirsiniz (<link>Buradan</link> kod yapabilirsiniz ) titleEdit: Konum İşaretini Düzenle markerDemoLimit: desc: Deneme sürümünde sadece iki adet yer imi oluşturabilirsiniz. Sınırsız yer @@ -172,17 +181,83 @@ dialogs: title: Sinyal Ata descItems: "Önceden tanımlı bir eşya seçin:" descShortKey: ... veya <strong>şekil kodunu</strong> girin (<link>Buradan</link> - edinebileceğiniz) + oluşturabilirsiniz) renameSavegame: - title: Oyun Kaydının Yeniden Adlandır - desc: Oyun kaydını buradan adlandırabilirsiniz. + title: Oyun Kaydını Yeniden Adlandır + desc: Oyun kayıt ismini buradan değiştirebilirsiniz. tutorialVideoAvailable: title: Eğitim Mevcut - desc: Bu seviye için eğitim vidyosu mevcut! İzlemek ister misin? + desc: Bu seviye için eğitim videosu mevcut! İzlemek ister misin? tutorialVideoAvailableForeignLanguage: title: Eğitim Mevcut - desc: Bu seviye için eğitim vidyosu mevcut, ama İngilizce dilinde. İzlemek ister + desc: Bu seviye için eğitim videosu mevcut, ama İngilizce dilinde. İzlemek ister misin? + editConstantProducer: + title: Eşya Seç + puzzleLoadFailed: + title: Yapbozlar yüklenirken hata oluştu + desc: "Malesef yapbozlar yüklenemedi:" + submitPuzzle: + title: Yapboz Yayınla + descName: "Yapbozuna bir isim ver:" + descIcon: "Lütfen yapbozunun ikonu olacak eşsiz kısa bir anahtar gir. (Anahtarı + <link>buradan</link> oluşturabilirsin, yada aşagıdaki şekillerden + rastgele birini seçebilirsin):" + placeholderName: Yapboz İsmi + puzzleResizeBadBuildings: + title: Yeniden boyutlandırma mümkün değil + desc: Alanı daha fazla küçültemezsin, çünkü bazı yapılar alanın dışında + kalabilir. + puzzleLoadError: + title: Kötü Yapboz + desc: "Yapboz yüklenirken hata oluştu:" + offlineMode: + title: Çevrİmdışı Modu + desc: Sunuculara ulaşamadık, bu yüzden oyun çevrimdışı modda çalışmak zorunda. + Lütfen aktif bir internet bağlantısı olduğundan emin olunuz. + puzzleDownloadError: + title: İndirme Hatası + desc: "Yapboz indirilemedi:" + puzzleSubmitError: + title: Yayınlama Hatası + desc: "Yapboz yayınlanamadı:" + puzzleSubmitOk: + title: Yapboz Yayınlandı + desc: Tebrikler! Yapbozun yayınlandı ve artık başkaları tarafından + oynanabilecek. Şimdi yapbozunu "Yapbozlarım" kısmında bulabilirsin. + puzzleCreateOffline: + title: Çevrimdışı Modu + desc: Çevrimdışı olduğundan yapbozunu kayıt edemeyecek veya yayınlayamayacaksın. + Devam etmek istediğinize emin misiniz? + puzzlePlayRegularRecommendation: + title: Öneri + desc: Ben <strong>muhakkak</strong> yapboz moduna başlamadan önce normal oyunu + seviye 12'ye kadar oynamayı tavsiye ediyorum, aksi takdirde henüz + sunulmamış mekaniklerle (yapılar ve oynanış şekilleri) + karşılaşabilirsiniz. + puzzleShare: + title: Kısa Anahtar Kopyalandı + desc: Yapbozun kısa anahtarı (<key>) kopyala/yapıştır hafızasına kopyalandı! Bu + anahtar yapboz menüsünde, yapboza erişmek için kullanılabilir. + puzzleReport: + title: Yapbozu Şikayet Et + options: + profane: Ayrımcılık (din, dil, ırk) + unsolvable: Çözülemez Yapboz + trolling: Trolleme + puzzleReportComplete: + title: Geri bildiriminiz için teşekkürler! + desc: Yapboz işaretlendi. + puzzleReportError: + title: Şikayet edilemedi + desc: "Şikayetiniz iletilemedi:" + puzzleLoadShortKey: + title: Kısa anahtar gir + desc: Yapbozu yüklemek için kısa anahtarı giriniz + puzzleDelete: + title: Yapboz silinsin mi? + desc: "'<title>' yapbozunu silmek istediğinize emin misiniz? Bu işlem geri + alınamaz!" ingame: keybindingsOverlay: moveMap: Hareket Et @@ -204,6 +279,7 @@ ingame: clearSelection: Seçimi temİzle pipette: Pipet switchLayers: Katman değiştir + clearBelts: Bantları temizle buildingPlacement: cycleBuildingVariants: Yapının farklı türlerine geçmek için <key> tuşuna bas. hotkeyLabel: "Kısayol: <key>" @@ -214,7 +290,7 @@ ingame: itemsPerSecond: <x> eşya / sn itemsPerSecondDouble: (x2) tiles: <x> karo - speed: Speed + speed: Hız levelCompleteNotification: levelTitle: SEVİYE <level> completed: Tamamlandı @@ -346,13 +422,55 @@ ingame: desc: Tamamen yeni bir boyut! darkmode: title: Gece Modu - desc: Gözlerini artık yorma! + desc: Gözlerin artık yorulmasın! support: title: Beni destekleyin desc: Boş zamanımda bu oyunu geliştiriyorum! achievements: - title: Achievements - desc: Hunt them all! + title: Başarımlar + desc: Bütün başarımları açmaya çalış! + puzzleEditorSettings: + zoneTitle: Alan + zoneWidth: Genişlik + zoneHeight: Yükseklik + trimZone: Alanı Sınırlandır + clearItems: Eşyaları temizle + share: Paylaş + report: Şikayet et + clearBuildings: Clear Buildings + resetPuzzle: Reset Puzzle + puzzleEditorControls: + title: Yapboz Oluşturucu + instructions: + - 1. Oyunculara şekil ve renk sağlamak için <strong>Sabit + Üreticileri</strong> yerleştir. + - 2. Oyuncuların üretmesi ve bir veya birden fazla <strong>Hedef + Merkezine</strong> teslim edebilmeleri için bir veya birden fazla + şekil oluştur. + - 3. Bir Hedef Merkezine belirli bir zaman içinde şekil teslim + edilirse, Hedef Merkezi bunu oyuncuların tekrardan üreteceği bir + <strong>hedef olarak kabul eder</strong> (<strong>Yeşil + rozetle</strong> gösterilmiş). + - 4. Bir yapıyı devre dışı bırakmak için üzerindeki <strong>kilit + butonuna</strong> basınız. + - 5. Gözat butonuna bastığınız zaman, yapbozunuz onaylanacaktır ve + sonra onu yayınlayabileceksiniz. + - 6. Yayınlandığı zaman, Sabit Üreticiler ve Hedef Merkezleri + dışındaki <strong>bütün yapılar silinecektir</strong> - Bu kısmı + oyuncuların kendisi çözmesi gerekecek sonuçta :) + puzzleCompletion: + title: Yapboz Tamamlandı! + titleLike: "Yapbozu beğendiyseniz kalbe tıklayınız:" + titleRating: Yapbozu ne kadar zor buldunuz? + titleRatingDesc: Değerlendirmeniz size daha iyi öneriler sunmamda yardımcı olacaktır + continueBtn: Oynamaya Devam Et + menuBtn: Menü + puzzleMetadata: + author: Yapımcı + shortKey: Kısa Anahtar + rating: Zorluk + averageDuration: Ortamala Süre + completionRate: Tamamlama Süresi shopUpgrades: belt: name: Taşıma Bandı, Dağıtıcılar & Tüneller @@ -380,10 +498,10 @@ buildings: miner: default: name: Üretici - description: Bir şekli veya rengi üretmek için üzerlerini yerleştir. + description: Bir şekli veya rengi üretmek için üzerine yerleştir. chainable: name: Üretici (Zİncİrleme) - description: Bir şekli veya rengi üretmek için üzerlerini yerleştir. Zincirleme + description: Bir şekli veya rengi üretmek için üzerine yerleştir. Zincirleme bağlanabilir. underground_belt: default: @@ -416,9 +534,9 @@ buildings: description: Şekilleri 180 derece döndürür. stacker: default: - name: Kaynaştırıcı - description: İki eşyayı kaynaştırır. Eğer eşyalar kaynaştırılamazsa sağdaki eşya - soldaki eşyanın üzerine kaynaştırılır. + name: Birleştirici + description: İki eşyayı birleştirir. Eğer eşyalar birleştirilemezse sağdaki eşya + soldaki eşyanın üzerine eklenir. mixer: default: name: Renk Karıştırıcısı @@ -445,12 +563,12 @@ buildings: wire: default: name: Kablo - description: Sinyali, eşyalar veya ikili değerler(1 / 0), aktarmayı sağlar. - Farklı renkteki kablolar bağlanamaz. + description: Eşya, renk ve ikili değerler(1 / 0) gibi sinyalleri aktarmayı + sağlar. Farklı renkteki kablolar bağlanamaz. second: name: Kablo - description: Sinyali, eşyalar veya ikili değerler(1 / 0), aktarmayı sağlar. - Farklı renkteki kablolar bağlanamaz. + description: Eşya, renk ve ikili değerler(1 / 0) gibi sinyalleri aktarmayı + sağlar. Farklı renkteki kablolar bağlanamaz. wire_tunnel: default: name: Kablo Tüneli @@ -474,8 +592,8 @@ buildings: storage: default: name: Depo - description: Belirli bir sınıra kadar fazla eşyaları depolar. Taşırma kapısı - olarak kullanıla bilir. + description: Belirli bir sınıra kadar fazla eşyaları depolar. Sol çıkışa öncelik + verir ve taşma kapısı olarak kullanılabilir. constant_signal: default: name: Sabit Sinyal @@ -489,11 +607,11 @@ buildings: kullanılabilir. logic_gate: default: - name: AND Kapısı + name: VE Kapısı description: Eğer iki girdi de doğruysa, bu kapı"1" sinyali gönderir. (Doğru; bir şekil, renk veya "1" girdisi demektir.) not: - name: NOT Kapısı + name: DEĞİL Kapısı description: Eğer girdi doğru değilse, bu kapı "1" sinyali gönderir. (Doğru; bir şekil, renk veya "1" girdisi demektir.) xor: @@ -501,7 +619,7 @@ buildings: description: Eğer iki girdiden sadece biri "1" sinyali alıyorsa, bu kapı "1" gönderir. (Doğru; bir şekil, renk veya "1" girdisi demektir.) or: - name: OR Kapısı + name: YADA Kapısı description: Eğer iki girdiden herhangi biri "1" sinyali alıyorsa, bu kapı "1" gönderir. (Doğru; bir şekil, renk veya "1" girdisi demektir.) transistor: @@ -526,7 +644,7 @@ buildings: veya ikili değer (1/0) olabilir. reader: default: - name: Band Okuyucu + name: Bant Okuyucu description: Bant üzerindeki ortalama hızı ölçer. Kablo katmanında son okunan eşyayı gösterir (açıldığında). analyzer: @@ -561,6 +679,18 @@ buildings: name: Eşya Üretici description: Sadece kum kutusu modunda açık, kablo katmanındaki sinyali normal katmanda çıktı olarak verir. + constant_producer: + default: + name: Sabİt Üretİcİ + description: Sabit olarak belirli bir şekli veya rengi üretir. + goal_acceptor: + default: + name: Hedef Merkezİ + description: Şekilleri hedef olarak tanımlamak için hedef merkezine teslim et. + block: + default: + name: Engel + description: Bir karoyu kullanıma kapatmayı sağlar. storyRewards: reward_cutter_and_trash: title: Şekilleri Kesmek @@ -728,14 +858,14 @@ settings: categories: general: Genel userInterface: Kullanıcı Arayüzü - advanced: Gelişmİş + advanced: Gelİşmİş performance: Performans versionBadges: dev: Geliştirme staging: Yükseltme prod: Üretim buildDate: <at-date> derlendi - rangeSliderPercentage: <amount> % + rangeSliderPercentage: "% <amount>" labels: uiScale: title: Arayüz Ölçeği @@ -760,7 +890,7 @@ settings: super_fast: Çok Hızlı language: title: Dil - description: Dili değiştirir. Bütün çevirmeler kullanıcı katkılarıyla + description: Dili değiştirir. Tüm çeviriler kullanıcı katkılarıyla oluşturulmuştur ve tam olmayabilir! fullscreen: title: Tam Ekran @@ -816,9 +946,9 @@ settings: twenty_minutes: 20 Dakika disabled: Devredışı compactBuildingInfo: - title: Derli Toplu Yapı Bilgileri + title: Derlİ Toplu Yapı Bİlgİlerİ description: Yapıların bilgi kutularını sadece oranlarını göstecek şekilde - kısaltır. Aksi taktirde yapının açıklaması ve resmi gösterilir. + kısaltır. Aksi takdirde yapının açıklaması ve resmi gösterilir. disableCutDeleteWarnings: title: Kes/Sil Uyarılarını Devre dışı Bırak description: 100 birimden fazla parçayı kesme/silme işlemlerinde beliren uyarı @@ -846,24 +976,24 @@ settings: title: Ses Ayarı description: Ses efektlerinin seviyesini ayarlar musicVolume: - title: Müzik Ayarı + title: Müzİk Ayarı description: Müzik seviyesini ayarlar lowQualityMapResources: - title: Düşük Kalite Harita Kaynakları + title: Düşük Kalİte Harİta Kaynakları description: Oyun performansını artırmak için haritada görünen kaynakların çizim - kalitesinin sadeleştirir. Kaynaklar daha açık görüneceğinde bu - özelliği bir dene! + kalitesini sadeleştirir. Hatta daha net bir görüntü sağlar, bu + yüzden bir dene! disableTileGrid: - title: Harita Çizgilerini Gizle + title: Harİta Çİzgİlerİnİ Gİzle description: Harita çizgilerini gizlemek oyun performansına yardımcı olabilir. - Aynı zamanda oyunun daha açık görünmesini sağlar! + Aynı zamanda oyunun daha net görünmesini sağlar! clearCursorOnDeleteWhilePlacing: - title: Sağ Tık İnşa İptali + title: Sağ Tık İnşa İptalİ description: Varsayılan olarak açık. Özellik açıksa, inşa modundayken sağ yık yapıldığında inşa modundan çıkar. Eğer özellik kapalıysa, inşa modundan çıkmadan var olan yapıları sağ tık ile silebilirsiniz. lowQualityTextures: - title: Düşük Kalite Görüntü (Çirkin) + title: Düşük Kalİte Görüntü (Çİrkİn) description: Performans için düşük kalite görüntü kullanır. Bu oyunun daha çirkin görünmesine sebep olur! displayChunkBorders: @@ -871,11 +1001,11 @@ settings: description: Oyun 16'ya 16 alanlardan oluşur. Bu seçenek aktif olduğunda alan sınırları görüntülenir. pickMinerOnPatch: - title: Kaynak Üzerinde Üretici Seç + title: Kaynak Üzerinde Üretİcİ Seç description: Varsayılan olarak açık. Eğer pipet bir kaynağın üzerinde kullanılırsa, üreteç yapısı inşa için seçilir. simplifiedBelts: - title: Sadeleştirilmiş Bantlar (Çirkin) + title: Sadeleştİrİlmİş Bantlar (Çİrkİn) 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. @@ -883,7 +1013,7 @@ settings: title: Fare Kaydırarak Hareket Etme description: Fareyi ekranın köşelerine getirerek hareket ettirmeyi sağlar. zoomToCursor: - title: Farenin Konumuna Yakınlaştırma + title: Farenİn 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. @@ -897,13 +1027,13 @@ keybindings: seçeneklerini kullanmanızı sağlarlar." resetKeybindings: Tuş Atamalarını Sıfırla categoryLabels: - general: Uygulama + general: Genel ingame: Oyun navigation: Hareket placement: Yerleştİrme - massSelect: Çoklu Seçim + massSelect: Çoklu Seçİm buildings: Yapı Kısayolları - placementModifiers: Yerleştirme Özellikleri + placementModifiers: Yerleştİrme Özellİklerİ mappings: confirm: Kabul back: Geri @@ -955,7 +1085,7 @@ keybindings: logic_gate: Mantık Kapısı lever: Anahtar (normal) filter: Filtre - wire_tunnel: Kablo Köprüsü + wire_tunnel: Kablo Tüneli display: Ekran reader: Bant Okuyucu virtual_processor: Sanal Kesici @@ -964,10 +1094,14 @@ keybindings: comparator: Karşılaştırıcı item_producer: Eşya Üretici (Kum Kutusu) copyWireValue: "Kablo: Fare altındaki değeri kopyala" - rotateToUp: "Rotate: Point Up" - rotateToDown: "Rotate: Point Down" - rotateToRight: "Rotate: Point Right" - rotateToLeft: "Rotate: Point Left" + rotateToUp: Yukarı Döndür + rotateToDown: Aşağı Döndür + rotateToRight: Sağa Döndür + rotateToLeft: Sola Döndür + constant_producer: Sabit Üretici + goal_acceptor: Hedef Merkezi + block: Engel + massSelectClear: Bantları temizle about: title: Oyun Hakkında body: >- @@ -1066,3 +1200,74 @@ tips: - Farenizin ve kameranızın sınırlarını göstermek için F4'e iki kez basın. - Sol tarafta sabitlenmiş bir şekle tıklayarak sabitlemesini kaldırabilirsiniz. +puzzleMenu: + play: Oyna + edit: Düzenle + title: Yapboz Modu + createPuzzle: Yapboz Oluştur + loadPuzzle: Yükle + reviewPuzzle: Gözat & Yayınla + validatingPuzzle: Yapboz onaylanıyor + submittingPuzzle: Yapboz yayınlanıyor + noPuzzles: Bu kısımda yapboz yok. + categories: + levels: Seviyeler + new: Yenİ + top-rated: En İyİ Değerlendirilen + mine: Yapbozlarım + easy: Kolay + hard: Zor + completed: Tamamlanan + medium: Orta + official: Resmİ + trending: Bugün öne çıkan + trending-weekly: Haftalık öne çıkan + categories: Kategorİler + difficulties: Zorluğa göre + account: Yapbozlarım + search: Ara + validation: + title: Geçersiz Yapboz + noProducers: Lütfen bir Sabit Üretici yerleştiriniz! + noGoalAcceptors: Lütfen bir Hedef Merkezi yerleştiriniz! + goalAcceptorNoItem: Bir veya birden fazla Hedef Merkezine şekil gönderilmedi. + Hedef belirlemek için onlara şekil gönderiniz. + goalAcceptorRateNotMet: Bir veya birden fazla Hedef Merkezi yeterince eşya + almıyor. Hedef Merkezlerindeki bütün göstergelerin yeşil olduğundan + emin olunuz. + buildingOutOfBounds: Bir veya birden fazla yapı inşa edilebilir alanın dışında. + Alanı azaltınız veya yapıları siliniz. + autoComplete: Yapbozunuz kendisini çözüyor! Sabit üreticilerin hedef + merkezlerine direkt olarak şekil göndermediğinden emin olunuz. + difficulties: + easy: Kolay + medium: Orta + hard: Zor + dlcHint: Purchased the DLC already? Make sure it is activated by right clicking + shapez.io in your library, selecting Properties > DLCs. +backendErrors: + ratelimit: Çok sık işlem yapıyorsunuz. Biraz bekleyiniz. + invalid-api-key: Arka tarafla iletişim kurulamadı, lütfen oyunu + güncellemeyi/yeniden başlatmayı deneyiniz (Geçersiz Api Anahtarı). + unauthorized: Arka tarafla iletişim kurulamadı, lütfen oyunu + güncellemeyi/yeniden başlatmayı deneyiniz (Yetkisiz erişim). + bad-token: Arka tarafla iletişim kurulamadı, lütfen oyunu güncellemeyi/yeniden + başlatmayı deneyiniz (Kötü Anahtar). + bad-id: Geçersiz Yapboz tanımlayıcısı (ID). + not-found: İstenilen Yapboz bulunamadı. + bad-category: İstenilen kategori bulunamadı. + bad-short-key: Girilen kısa anahtar geçersiz. + profane-title: Yapboz ismi ayrımcı kelimeler(din,dil,ırk) içeriyor. + bad-title-too-many-spaces: Yapboz ismi çok kısa. + bad-shape-key-in-emitter: Bir sabit üreticide geçersiz bir eşya mevcut. + bad-shape-key-in-goal: Bir hedef merkezinde geçersiz bir eşya mevcut. + no-emitters: Yapbozda hiç sabit üretici yok. + no-goals: Yapbozda hiç hedef merkezi yok. + short-key-already-taken: Bu kısa anahtar kullanılıyor, lütfen başka bir tane kullanınız. + can-not-report-your-own-puzzle: Kendi yapbozunuzu şikayet edemezsiniz. + bad-payload: İstek geçersiz veri içeriyor. + bad-building-placement: Yapbozunuzda uygun yerleştirilmeyen yapılar mevcut. + timeout: İstek zaman aşımına uğradı. + too-many-likes-already: Yapbozun zaten çok beğenisi var. Yine de silmek + istiyorsanız support@shapez.io ile iletişime geçiniz! + no-permission: Bu işlemi yapmak için izniniz yok. diff --git a/translations/base-uk.yaml b/translations/base-uk.yaml index b051f13d..79913e51 100644 --- a/translations/base-uk.yaml +++ b/translations/base-uk.yaml @@ -52,6 +52,7 @@ global: escape: ESC shift: SHIFT space: SPACE + loggingIn: Logging in demoBanners: title: Демо-версія intro: Завантажте повну версію, щоб розблокувати всі можливості та вміст! @@ -72,6 +73,12 @@ mainMenu: savegameLevel: Рівень <x> savegameLevelUnknown: Невідомий рівень savegameUnnamed: Unnamed + puzzleMode: Puzzle Mode + back: Back + puzzleDlcText: Do you enjoy compacting and optimizing factories? Get the Puzzle + DLC now on Steam for even more fun! + puzzleDlcWishlist: Wishlist now! + puzzleDlcViewNow: View Dlc dialogs: buttons: ok: Гаразд @@ -85,6 +92,9 @@ dialogs: viewUpdate: Переглянути оновлення showUpgrades: Показати поліпшення showKeybindings: Показати прив’язки клавіш + retry: Retry + continue: Continue + playOffline: Play Offline importSavegameError: title: Помилка при імпортуванні text: "Не вдалося імпортувати вашу збережену гру:" @@ -184,6 +194,70 @@ dialogs: title: Доступна інструкція desc: Для цього рівня доступна відео-інструкція, але вона доступна лише на Англійській. Чи бажаєте переглянути її? + editConstantProducer: + title: Set Item + puzzleLoadFailed: + title: Puzzles failed to load + desc: "Unfortunately the puzzles could not be loaded:" + submitPuzzle: + title: Submit Puzzle + descName: "Give your puzzle a name:" + descIcon: "Please enter a unique short key, which will be shown as the icon of + your puzzle (You can generate them <link>here</link>, or choose one + of the randomly suggested shapes below):" + placeholderName: Puzzle Title + puzzleResizeBadBuildings: + title: Resize not possible + desc: You can't make the zone any smaller, because then some buildings would be + outside the zone. + puzzleLoadError: + title: Bad Puzzle + desc: "The puzzle failed to load:" + offlineMode: + title: Offline Mode + desc: We couldn't reach the servers, so the game has to run in offline mode. + Please make sure you have an active internet connection. + puzzleDownloadError: + title: Download Error + desc: "Failed to download the puzzle:" + puzzleSubmitError: + title: Submission Error + desc: "Failed to submit your puzzle:" + puzzleSubmitOk: + title: Puzzle Published + desc: Congratulations! Your puzzle has been published and can now be played by + others. You can now find it in the "My puzzles" section. + puzzleCreateOffline: + title: Offline Mode + desc: Since you are offline, you will not be able to save and/or publish your + puzzle. Would you still like to continue? + puzzlePlayRegularRecommendation: + title: Recommendation + desc: I <strong>strongly</strong> recommend playing the normal game to level 12 + before attempting the puzzle DLC, otherwise you may encounter + mechanics not yet introduced. Do you still want to continue? + puzzleShare: + title: Short Key Copied + desc: The short key of the puzzle (<key>) has been copied to your clipboard! It + can be entered in the puzzle menu to access the puzzle. + puzzleReport: + title: Report Puzzle + options: + profane: Profane + unsolvable: Not solvable + trolling: Trolling + puzzleReportComplete: + title: Thank you for your feedback! + desc: The puzzle has been flagged. + puzzleReportError: + title: Failed to report + desc: "Your report could not get processed:" + puzzleLoadShortKey: + title: Enter short key + desc: Enter the short key of the puzzle to load it. + puzzleDelete: + title: Delete Puzzle? + desc: Are you sure you want to delete '<title>'? This can not be undone! ingame: keybindingsOverlay: moveMap: Рухатися @@ -205,6 +279,7 @@ ingame: clearSelection: Очистити виділене pipette: Піпетка switchLayers: Змінити шар + clearBelts: Clear belts colors: red: Червоний green: Зелений @@ -271,10 +346,10 @@ ingame: waypoints: waypoints: Позначки hub: Центр - description: Left-click a marker to jump to it, right-click to delete - it.<br><br>Press <keybinding> to create a marker from the current - view, or <strong>right-click</strong> to create a marker at the - selected location. + description: Натисніть ліву кнопку миші на позначці, щоб переміститися до неї, + праву - для її видалення.<br><br>Натисніть <keybinding> щоб створити + позначку на місці вашої камери, або <strong>праву кнопку + миші</strong>, щоб створити її в положенні курсора. creationSuccessNotification: Позначку створено. shapeViewer: title: Шари @@ -356,6 +431,46 @@ ingame: achievements: title: Achievements desc: Hunt them all! + puzzleEditorSettings: + zoneTitle: Zone + zoneWidth: Width + zoneHeight: Height + trimZone: Trim + clearItems: Clear Items + share: Share + report: Report + clearBuildings: Clear Buildings + resetPuzzle: Reset Puzzle + puzzleEditorControls: + title: Puzzle Creator + instructions: + - 1. Place <strong>Constant Producers</strong> to provide shapes and + colors to the player + - 2. Build one or more shapes you want the player to build later and + deliver it to one or more <strong>Goal Acceptors</strong> + - 3. Once a Goal Acceptor receives a shape for a certain amount of + time, it <strong>saves it as a goal</strong> that the player must + produce later (Indicated by the <strong>green badge</strong>). + - 4. Click the <strong>lock button</strong> on a building to disable + it. + - 5. Once you click review, your puzzle will be validated and you + can publish it. + - 6. Upon release, <strong>all buildings will be removed</strong> + except for the Producers and Goal Acceptors - That's the part that + the player is supposed to figure out for themselves, after all :) + puzzleCompletion: + title: Puzzle Completed! + titleLike: "Click the heart if you liked the puzzle:" + titleRating: How difficult did you find the puzzle? + titleRatingDesc: Your rating will help me to make you better suggestions in the future + continueBtn: Keep Playing + menuBtn: Menu + puzzleMetadata: + author: Author + shortKey: Short Key + rating: Difficulty score + averageDuration: Avg. Duration + completionRate: Completion rate shopUpgrades: belt: name: Стрічки, розподілювачі і тунелі @@ -569,6 +684,18 @@ buildings: name: Генератор елементів description: Доступно лише в режимі Пісочниці, видає отриманий на шарі дроту сигнал в якості фігури на звичайний шар. + constant_producer: + default: + name: Constant Producer + description: Constantly outputs a specified shape or color. + goal_acceptor: + default: + name: Goal Acceptor + description: Deliver shapes to the goal acceptor to set them as a goal. + block: + default: + name: Block + description: Allows you to block a tile. storyRewards: reward_cutter_and_trash: title: Різання фігур @@ -981,6 +1108,10 @@ keybindings: rotateToDown: "Rotate: Point Down" rotateToRight: "Rotate: Point Right" rotateToLeft: "Rotate: Point Left" + constant_producer: Constant Producer + goal_acceptor: Goal Acceptor + block: Block + massSelectClear: Clear belts about: title: Про гру body: >- @@ -1080,3 +1211,73 @@ tips: координати камери. - Ви можете натиснути на фігуру, зкаріплену в лівому краю екрану, аби відкріпити її. +puzzleMenu: + play: Play + edit: Edit + title: Puzzle Mode + createPuzzle: Create Puzzle + loadPuzzle: Load + reviewPuzzle: Review & Publish + validatingPuzzle: Validating Puzzle + submittingPuzzle: Submitting Puzzle + noPuzzles: There are currently no puzzles in this section. + categories: + levels: Levels + new: New + top-rated: Top Rated + mine: My Puzzles + easy: Easy + hard: Hard + completed: Completed + medium: Medium + official: Official + trending: Trending today + trending-weekly: Trending weekly + categories: Categories + difficulties: By Difficulty + account: My Puzzles + search: Search + validation: + title: Invalid Puzzle + noProducers: Please place a Constant Producer! + noGoalAcceptors: Please place a Goal Acceptor! + goalAcceptorNoItem: One or more Goal Acceptors have not yet assigned an item. + Deliver a shape to them to set a goal. + goalAcceptorRateNotMet: One or more Goal Acceptors are not getting enough items. + Make sure that the indicators are green for all acceptors. + buildingOutOfBounds: One or more buildings are outside of the buildable area. + Either increase the area or remove them. + autoComplete: Your puzzle autocompletes itself! Please make sure your constant + producers are not directly delivering to your goal acceptors. + difficulties: + easy: Easy + medium: Medium + hard: Hard + dlcHint: Purchased the DLC already? Make sure it is activated by right clicking + shapez.io in your library, selecting Properties > DLCs. +backendErrors: + ratelimit: You are performing your actions too frequent. Please wait a bit. + invalid-api-key: Failed to communicate with the backend, please try to + update/restart the game (Invalid Api Key). + unauthorized: Failed to communicate with the backend, please try to + update/restart the game (Unauthorized). + bad-token: Failed to communicate with the backend, please try to update/restart + the game (Bad Token). + bad-id: Invalid puzzle identifier. + not-found: The given puzzle could not be found. + bad-category: The given category could not be found. + bad-short-key: The given short key is invalid. + profane-title: Your puzzle title contains profane words. + bad-title-too-many-spaces: Your puzzle title is too short. + bad-shape-key-in-emitter: A constant producer has an invalid item. + bad-shape-key-in-goal: A goal acceptor has an invalid item. + no-emitters: Your puzzle does not contain any constant producers. + no-goals: Your puzzle does not contain any goal acceptors. + short-key-already-taken: This short key is already taken, please use another one. + can-not-report-your-own-puzzle: You can not report your own puzzle. + bad-payload: The request contains invalid data. + bad-building-placement: Your puzzle contains invalid placed buildings. + timeout: The request timed out. + too-many-likes-already: The puzzle alreay got too many likes. If you still want + to remove it, please contact support@shapez.io! + no-permission: You do not have the permission to perform this action. diff --git a/translations/base-zh-CN.yaml b/translations/base-zh-CN.yaml index f49dd241..6e7b2ab9 100644 --- a/translations/base-zh-CN.yaml +++ b/translations/base-zh-CN.yaml @@ -8,17 +8,16 @@ steamPage: 《异形工厂》(Shapez.io)是一款能让您尽情发挥创造力,充分享受思维乐趣的IO游戏。 游戏很轻松,只需建造工厂,布好设施,无需操作即能自动创造出各种各样的几何图形。 挑战很烧脑,随着等级提升,需要创造的图形将会越来越复杂,同时您还需要在无限扩展的地图中持续扩建优化您的工厂。 - 以为这就是全部了吗? 不!图形的生产需求将会指数性增长,持续的扩大规模和熵增带来的无序,将会是令人头痛的问题! - 这还不是全部! 一开始我们创造了图形,然后我们需要学会提取和混合来让它们五颜六色。 - 然后,还有吗? 当然,唯有思维,方能无限。 + 以为这就是全部了吗?不!图形的生产需求将会指数性增长,持续的扩大规模和熵增带来的无序,将会是令人头痛的问题! + 这还不是全部!一开始我们创造了图形,然后我们需要学会提取和混合来让它们五颜六色。 + 然后,还有吗?当然,唯有思维,方能无限。 欢迎免费体验试玩版:“让您的想象力插上翅膀!” - 和最聪明的玩家一起挑战,请访问 Steam 游戏商城购买《异形工厂》(Shapez.io)的完整版, + 和最聪明的玩家一起挑战,请访问 Steam 游戏商城购买《异形工厂》(Shapez.io)的完整版, what_others_say: 来看看玩家们对《异形工厂》(Shapez.io)的评价 nothernlion_comment: 非常棒的有游戏,我的游戏过程充满乐趣,不觉时间飞逝。 notch_comment: 哦,天哪!我真得该去睡了!但我想我刚刚搞定如何在游戏里面制造一台电脑出来。 steam_review_comment: 这是一个不知不觉偷走你时间,但你并不会想要追回的游戏。非常烧脑的挑战,让我这样的完美主义者停不下来,总是希望可以再高效一些。 - global: loading: 加载中 error: 错误 @@ -50,6 +49,7 @@ global: escape: ESC键 shift: SHIFT键 space: 空格键 + loggingIn: Logging in demoBanners: title: 试玩版 intro: 购买完整版以解锁所有游戏内容! @@ -60,7 +60,7 @@ mainMenu: openSourceHint: 本游戏已开源! discordLink: 官方Discord服务器 helpTranslate: 帮助我们翻译! - browserWarning: 很抱歉, 本游戏在当前浏览器上可能运行缓慢! 使用 Chrome 或者购买完整版以得到更好的体验。 + browserWarning: 很抱歉,本游戏在当前浏览器上可能运行缓慢!使用 Chrome 或者购买完整版以得到更好的体验。 savegameLevel: 第<x>关 savegameLevelUnknown: 未知关卡 continue: 继续游戏 @@ -68,6 +68,11 @@ mainMenu: madeBy: 作者:<author-link> subreddit: Reddit savegameUnnamed: 存档未命名 + puzzleMode: 谜题模式 + back: 返回 + puzzleDlcText: 持续优化,追求极致效率。在限定空间内使用有限的设施来创造图形!《异形工厂》(Shapez.io)的首个DLC“谜题挑战者”将会给大家带来更烧脑、更自由的全新挑战! + puzzleDlcWishlist: 添加愿望单! + puzzleDlcViewNow: View Dlc dialogs: buttons: ok: 确认 @@ -81,6 +86,9 @@ dialogs: viewUpdate: 查看更新 showUpgrades: 显示设施升级 showKeybindings: 显示按键设置 + retry: 重试 + continue: 继续 + playOffline: 离线游戏 importSavegameError: title: 读取错误 text: 未能读取您的存档: @@ -92,7 +100,7 @@ dialogs: text: 未能读取您的存档: confirmSavegameDelete: title: 确认删除 - text: 您确定要删除这个游戏吗?<br><br> '<savegameName>' 等级 <savegameLevel><br><br> 该操作无法回退! + text: 您确定要删除这个游戏吗?<br><br> '<savegameName>' 等级 <savegameLevel><br><br> 该操作无法回退! savegameDeletionError: title: 删除失败 text: 未能删除您的存档 @@ -116,34 +124,31 @@ dialogs: desc: 试玩版中只能保存一份存档。请删除旧存档或者购买完整版! updateSummary: title: 新内容更新啦! - desc: "以下为游戏最新更新内容:" + desc: 以下为游戏最新更新内容: upgradesIntroduction: title: 解锁升级 - desc: <strong>您生产过的所有图形都能被用来解锁升级。</strong> 所以不要销毁您之前建造的工厂! 注意:升级菜单在屏幕右上角。 + desc: <strong>您生产过的所有图形都能被用来解锁升级。</strong> 所以不要销毁您之前建造的工厂!注意:升级菜单在屏幕右上角。 massDeleteConfirm: title: 确认删除 - desc: 您将要删除很多设施,准确来说有<count>种! 您确定要这么做吗? + desc: 您将要删除很多设施,准确来说有<count>种!您确定要这么做吗? blueprintsNotUnlocked: title: 尚未解锁 desc: 您还没有解锁蓝图功能!通过第12关的挑战后可解锁蓝图。 keybindingsIntroduction: title: 实用快捷键 - desc: - "这个游戏有很多有用的快捷键设定。 以下是其中的一些介绍,记得在<strong>按键设置</strong>中查看其他按键设定!<br><br> + desc: 这个游戏有很多有用的快捷键设定。以下是其中的一些介绍,记得在<strong>按键设置</strong>中查看其他按键设定!<br><br> <code class='keybinding'>CTRL键</code> + 拖动:选择区域以复制或删除。<br> <code - class='keybinding'>SHIFT键</code>: 按住以放置多个同一种设施。<br> <code - class='keybinding'>ALT键</code>: 反向放置传送带。<br>" + class='keybinding'>SHIFT键</code>: 按住以放置多个同一种设施。<br> <code + class='keybinding'>ALT键</code>:反向放置传送带。<br> createMarker: title: 创建地图标记 - desc: - 填写一个有意义的名称, 还可以同时包含一个形状的 <strong>短代码</strong> (您可以 <link>点击这里</link> - 生成短代码) + desc: 填写一个有意义的名称,还可以同时包含一个形状的 <strong>短代码</strong>(您可以 <link>点击这里</link> 生成短代码) titleEdit: 编辑地图标记 markerDemoLimit: desc: 在试玩版中您只能创建两个地图标记。请获取完整版以创建更多标记。 massCutConfirm: title: 确认剪切 - desc: 您将要剪切很多设施,准确来说有<count>种! 您确定要这么做吗? + desc: 您将要剪切很多设施,准确来说有<count>种!您确定要这么做吗? exportScreenshotWarning: title: 工厂截图 desc: 您将要导出您整个工厂基地的截图。如果您已经建设了一个规模很大的基地,生成截图的过程将会很慢,且有可能导致游戏崩溃! @@ -153,16 +158,71 @@ dialogs: editSignal: title: 设置信号 descItems: "选择一个预定义的项目:" - descShortKey: ... 或者输入图形的 <strong>短代码</strong> (您可以 <link>点击这里</link> 生成短代码) + descShortKey: ... 或者输入图形的 <strong>短代码</strong> (您可以 <link>点击这里</link> 生成短代码) renameSavegame: title: 重命名游戏存档 desc: 您可以在此重命名游戏存档。 tutorialVideoAvailable: title: 教程 - desc: 这个关卡有视频攻略! 您想查看这个视频攻略? + desc: 这个关卡有视频攻略!您想查看这个视频攻略? tutorialVideoAvailableForeignLanguage: title: 教程 - desc: 这个关卡有英语版本的视频攻略! 您想查看这个视频攻略吗?? + desc: 这个关卡有英语版本的视频攻略!您想查看这个视频攻略吗? + editConstantProducer: + title: 设置项目 + puzzleLoadFailed: + title: 谜题载入失败 + desc: 很遗憾,谜题未能载入: + submitPuzzle: + title: 提交谜题 + descName: 给您的谜题设定名称: + descIcon: 请输入唯一的短代码,它将显示为标志您的谜题的图标( <link>在此</link>生成,或者从以下随机推荐的图形中选择一个): + placeholderName: 谜题标题 + puzzleResizeBadBuildings: + title: 无法调整大小 + desc: 您无法使这块区域变得更小,否则有些设施将会超出区域范围。 + puzzleLoadError: + title: 谜题出错 + desc: 谜题载入失败: + offlineMode: + title: 离线模式 + desc: 访问服务器失败,游戏只能在离线模式下进行。请确认您的网络连接正常。 + puzzleDownloadError: + title: 下载出错 + desc: 无法下载谜题: + puzzleSubmitError: + title: 提交出错 + desc: 无法提交您的谜题: + puzzleSubmitOk: + title: 谜题已发布 + desc: 恭喜!您所创造的谜题已成功发布,别的玩家已经可以对您的谜题发起挑战!您可以在“我的谜题”部分找到您发布的谜题。 + puzzleCreateOffline: + title: 离线模式 + desc: 由于您现在没有连接互联网,所以您将无法保存或发布您的谜题。是否仍要继续? + puzzlePlayRegularRecommendation: + title: 游戏建议 + desc: <strong>强烈</strong>建议您在至少完成本体第12关后再尝试体验“谜题挑战者”DLC,否则您可能在游戏过程中遇到困难,您是否仍要继续? + puzzleShare: + title: 短代码已复制 + desc: 此谜题的短代码(<key>)已经复制到了您的剪贴板!您可以在谜题菜单中输入短代码以快速访问对应谜题。 + puzzleReport: + title: 上报谜题 + options: + profane: 污言秽语 + unsolvable: 无法完成 + trolling: 恶意设计 + puzzleReportComplete: + title: 感谢您的反馈! + desc: 此谜题已标记! + puzzleReportError: + title: 上报失败 + desc: 无法处理您的上报: + puzzleLoadShortKey: + title: 输入短代码 + desc: 输入此谜题的短代码以载入。 + puzzleDelete: + title: 删除谜题? + desc: 您是否确认删除 '<title>'?删除后不可恢复! ingame: keybindingsOverlay: moveMap: 移动地图 @@ -184,9 +244,10 @@ ingame: clearSelection: 取消选择 pipette: 吸取器 switchLayers: 切换层 + clearBelts: 清除传送带 buildingPlacement: cycleBuildingVariants: 按 <key> 键以选择设施的变型体。 - hotkeyLabel: "快捷键: <key>" + hotkeyLabel: 快捷键:<key> infoTexts: speed: 速率 range: 范围 @@ -203,7 +264,7 @@ ingame: notifications: newUpgrade: 有新内容更新啦! gameSaved: 游戏已保存。 - freeplayLevelComplete: 第 <level>关 完成了! + freeplayLevelComplete: 第 <level>关 完成了! shop: title: 升级 buttonUnlock: 升级 @@ -213,14 +274,14 @@ ingame: title: 统计信息 dataSources: stored: - title: 已存储 - description: 所有图形已存储于中心。 + title: 仓库 + description: 所有存储在中心基地的图形。 produced: title: 生产 - description: 所有图形已在工厂内生产,包括中间产物。 + description: 所有在工厂内生产的图形,包括中间产物。 delivered: title: 交付 - description: 图形已交付到中心基地。 + description: 交付到中心基地的图形。 noShapesProduced: 您还没有生产任何图形。 shapesDisplayUnits: second: <shapes> / 秒 @@ -247,18 +308,15 @@ ingame: hints: 1_1_extractor: 在<strong>圆形</strong>上放置一个<strong>开采器</strong>来获取圆形!<br><br>提示:<strong>按下鼠标左键</strong>选中<strong>开采器</strong> 1_2_conveyor: 用<strong>传送带</strong>将您的开采器连接到中心基地上!<br><br>提示:选中<strong>传送带</strong>后<strong>按下鼠标左键可拖动</strong>布置传送带! - 1_3_expand: - 您可以放置更多的<strong>开采器</strong>和<strong>传送带</strong>来更有效率地完成关卡目标。<br><br> + 1_3_expand: 您可以放置更多的<strong>开采器</strong>和<strong>传送带</strong>来更有效率地完成关卡目标。<br><br> 提示:按住 <strong>SHIFT</strong> 键可放置多个<strong>开采器</strong>,注意用<strong>R</strong> 键可旋转<strong>开采器</strong>的出口方向,确保开采的图形可以顺利传送。 2_1_place_cutter: 现在放置一个<strong>切割器</strong>,这个设施可把<strong>圆形</strong>切成两半!<br><br>注意:无论如何放置,切割机总是<strong>从上到下</strong>切割。 - 2_2_place_trash: - 使用切割机后产生的废弃图形会导致<strong>堵塞</strong>。<br><br>注意使用<strong>垃圾桶</strong>清除当前 + 2_2_place_trash: 使用切割机后产生的废弃图形会导致<strong>堵塞</strong>。<br><br>注意使用<strong>垃圾桶</strong>清除当前 (!) 不需要的废物。 2_3_more_cutters: 干的好!现在放置<strong>2个以上的切割机</strong>来加快当前缓慢的过程!<br><br>提示:用<strong>快捷键0-9</strong>可以快速选择各项设施! - 3_1_rectangles: - 现在让我们开采一些矩形!找到<strong>矩形地带</strong>并<strong>放置4个开采器</strong>并将它们用<strong>传送带</strong>连接到中心基地。<br><br> + 3_1_rectangles: 现在让我们开采一些矩形!找到<strong>矩形地带</strong>并<strong>放置4个开采器</strong>并将它们用<strong>传送带</strong>连接到中心基地。<br><br> 提示:选中<strong>传送带</strong>后按住<strong>SHIFT键</strong>可快速准确地规划<strong>传送带路线!</strong> 21_1_place_quad_painter: 放置<strong>四口上色器</strong>并且获取一些<strong>圆形</strong>,<strong>白色</strong>和<strong>红色</strong>! 21_2_switch_to_wires: 按 <strong>E</strong> 键选择<strong>电线层</strong>!<br><br> @@ -292,8 +350,8 @@ ingame: no_thanks: 不需要,谢谢 points: levels: - title: 12 个全新关卡! - desc: 总共 26 个不同关卡! + title: 12 个全新关卡! + desc: 总共 26 个不同关卡! buildings: title: 18 个全新设施! desc: 呈现完全体的全自动工厂! @@ -314,7 +372,39 @@ ingame: desc: 我使用闲暇时间开发游戏! achievements: title: 成就 - desc: 挑战全成就解锁! + desc: 挑战全成就解锁! + puzzleEditorSettings: + zoneTitle: 区域 + zoneWidth: 宽度 + zoneHeight: 高度 + trimZone: 整理 + clearItems: 清除项目 + share: 共享 + report: 上报 + clearBuildings: Clear Buildings + resetPuzzle: Reset Puzzle + puzzleEditorControls: + title: 谜题编辑器 + instructions: + - 1.放置<strong>常量生成器</strong>,为玩家提供此谜题的初始图形和颜色。 + - 2.建造您希望玩家稍后建造的一个或多个图形,并将其交付给一个或多个<strong>目标接收器</strong>。 + - 3.当一个目标接收器接收到一个图形一段时间后,会<strong>将其保存为此玩家稍后必须建造的目标</strong>(由<strong>绿色充能条</strong>表示)。 + - 4.单击设施上的<strong>锁定按钮</strong>即可将其禁用。 + - 5.单击审阅后,您的谜题将通过验证,您可以正式发布它。 + - 6.谜题发布后,<strong>所有设施都将被拆除</strong>,除了<strong>常量生成器</strong>和<strong>目标接收器</strong>。然后,等着其他玩家对您创造的谜题发起挑战吧! + puzzleCompletion: + title: 谜题挑战成功! + titleLike: 喜欢此谜题的话,请为它点赞: + titleRating: 您觉得此谜题难度如何? + titleRatingDesc: 您的评分将帮助作者在未来创作出更好的谜题! + continueBtn: 继续游戏 + menuBtn: 菜单 + puzzleMetadata: + author: 作者 + shortKey: 短代码 + rating: 难度评分 + averageDuration: 平均挑战时间 + completionRate: 挑战完成率 shopUpgrades: belt: name: 传送、分发、隧道 @@ -338,7 +428,7 @@ buildings: name: 开采器 description: 放置在<strong>图形</strong>或者<strong>颜色</strong>上进行开采。 chainable: - name: 开采器(链式) + name: 开采器(链式) description: 放置在<strong>图形</strong>或者<strong>颜色</strong>上进行开采。它们可以被链接在一起。 underground_belt: default: @@ -362,7 +452,7 @@ buildings: name: 旋转机(逆时针) description: 将<strong>图形</strong>逆时针旋转90度。 rotate180: - name: 旋转机 (180度) + name: 旋转机(180度) description: 将<strong>图形</strong>旋转180度。 stacker: default: @@ -383,7 +473,7 @@ buildings: name: 上色器(四口) description: 能够为<strong>图形</strong>的四个象限单独上色。记住只有通过电线层上带有<strong>正信号</strong>的插槽才可以上色! mirrored: - name: 上色器 (镜像) + name: 上色器(镜像) description: 将整个<strong>图形</strong>涂上输入的颜色。 trash: default: @@ -392,7 +482,7 @@ buildings: hub: deliver: 交付 toUnlock: 解锁 - levelShortcut: LVL + levelShortcut: 关卡 endOfDemo: 试玩版结束 wire: default: @@ -408,21 +498,21 @@ buildings: name: 平衡器 description: 多功能的设施:可将所有输入均匀地分配到所有输出上。 merger: - name: 合并器 (小型) + name: 合并器(小型) description: 可将两条传送带合并为一条。 merger-inverse: - name: 合并器 (小型) + name: 合并器(小型) description: 可将两条传送带合并为一条。 splitter: - name: 分离器 (小型) + name: 分离器(小型) description: 可将一条传送带分成为两条。 splitter-inverse: - name: 分离器 (小型) + name: 分离器(小型) description: 可将一条传送带分成为两条。 storage: default: name: 存储器 - description: 储存多余的物品,直到储满。 优先处理左边的输出,并可以用作溢出门。 + description: 储存多余的物品,直到储满。优先处理左边的输出,并可以用作溢出门。 wire_tunnel: default: name: 交叉电线 @@ -475,7 +565,7 @@ buildings: comparator: default: name: 比较器 - description: 如果输入的两个<strong>信号</strong>一样将输出开(1)信号,可以比较图形,颜色,和开关值。 + description: 如果输入的两个<strong>信号</strong>一样将输出开(1)信号,可以比较图形,颜色,和开关值。 virtual_processor: default: name: 虚拟切割机 @@ -496,6 +586,18 @@ buildings: default: name: 物品生成器 description: 仅在沙盒模式下可用,在常规层上输出<strong>电线层</strong>给定的<strong>信号</strong>。 + constant_producer: + default: + name: 常量生成器 + description: 不断输出指定的图形或颜色。 + goal_acceptor: + default: + name: 目标接收器 + description: 将图状传递给目标接收器,以将它们设置为谜题挑战目标。 + block: + default: + name: 方块 + description: 放置了方块的格子将无法再进行其他放置。 storyRewards: reward_cutter_and_trash: title: 切割图形 @@ -507,8 +609,7 @@ storyRewards: desc: 恭喜!您解锁了<strong>旋转机</strong>。它会顺时针将输入的<strong>图形旋转90度</strong>。 reward_painter: title: 上色 - desc: - 恭喜!您解锁了<strong>上色器</strong>。开采一些颜色 (就像您开采图形一样),将其在上色器中与图形结合来将图形上色! + desc: 恭喜!您解锁了<strong>上色器</strong>。开采一些颜色(就像您开采图形一样),将其在上色器中与图形结合来将图形上色! <br>注意:如果您不幸患有色盲,可以在设置中启用<strong>色盲模式</strong> reward_mixer: title: 混合颜色 @@ -525,13 +626,11 @@ storyRewards: desc: 恭喜!您解锁了<strong>隧道</strong>。它可放置在<strong>传送带</strong>或<strong>设施</strong>下方以运送物品。 reward_rotater_ccw: title: 逆时针旋转 - desc: - 恭喜!您解锁了<strong>旋转机</strong>的<strong>逆时针</strong>变体。它可以逆时针旋转<strong>图形</strong>。 + desc: 恭喜!您解锁了<strong>旋转机</strong>的<strong>逆时针</strong>变体。它可以逆时针旋转<strong>图形</strong>。 <br>选择<strong>旋转机</strong>然后按"T"键来选取这个变体。 reward_miner_chainable: title: 链式开采器 - desc: - 您已经解锁了<strong>链式开采器</strong>!它能<strong>转发资源</strong>给其他的开采器,这样您就能更有效率的开采各类资源了!<br><br> + desc: 您已经解锁了<strong>链式开采器</strong>!它能<strong>转发资源</strong>给其他的开采器,这样您就能更有效率的开采各类资源了!<br><br> 注意:新的开采器已替换了工具栏里旧的开采器! reward_underground_belt_tier_2: title: 二级隧道 @@ -548,25 +647,23 @@ storyRewards: <br>它<strong>优先从左边</strong>输出,这样您就可以用它做一个<strong>溢流门</strong>了! reward_freeplay: title: 自由模式 - desc: - 成功了!您解锁了<strong>自由模式</strong>!挑战升级!这意味着现在将<strong>随机</strong>生成图形! + desc: 成功了!您解锁了<strong>自由模式</strong>!挑战升级!这意味着现在将<strong>随机</strong>生成图形! 从现在起,中心基地最为需要的是<strong>产量</strong>,我强烈建议您去制造一台能够自动交付所需图形的机器!<br><br> 基地会在<strong>电线层</strong>输出需要的图形,您需要去分析图形并在此基础上自动配置您的工厂。 reward_blueprints: title: 蓝图 - desc: - 您现在可以<strong>复制粘贴</strong>您的工厂的一部分了!按住 CTRL键并拖动鼠标来选择一块区域,然后按C键复制。 + desc: 您现在可以<strong>复制粘贴</strong>您的工厂的一部分了!按住 CTRL键并拖动鼠标来选择一块区域,然后按C键复制。 <br><br>粘贴并<strong>不是免费的</strong>,您需要制造<strong>蓝图图形</strong>来负担。蓝图图形是您刚刚交付的图形。 no_reward: title: 下一关 - desc: 这一关没有奖励,但是下一关有! <br><br> + desc: 这一关没有奖励,但是下一关有!<br><br> 注意:最高明的规划师都不会破坏原有的工厂设施,您生产过的<strong>所有图形</strong>都会被用于<strong>解锁升级</strong>。 no_reward_freeplay: title: 下一关 desc: 恭喜您!另外,我们已经计划在完整版中加入更多内容! reward_balancer: title: 平衡器 - desc: 恭喜!您解锁了多功能<strong>平衡器</strong>,它能够<strong>分割和合并</strong>多个传送带的资源,可以用来建造更大的工厂! + desc: 恭喜!您解锁了多功能<strong>平衡器</strong>,它能够<strong>分割和合并</strong>多个传送带的资源,可以用来建造更大的工厂! reward_merger: title: 合并器(小型) desc: 恭喜!您解锁了<strong>平衡器</strong>的变体<strong>合并器</strong>,它能合并两个输入到同一个传送带上! @@ -580,11 +677,10 @@ storyRewards: reward_display: title: 显示器 desc: 恭喜!您已经解锁了<strong>显示器</strong>,它可以显示一个在<strong>电线层上连接的信号</strong>! - <br>注意:您注意到<strong>传送读取器</strong>和<strong>存储器</strong>输出的他们最后读取的物品了吗?试着在显示屏上展示一下!" + <br>注意:您注意到<strong>传送读取器</strong>和<strong>存储器</strong>输出的他们最后读取的物品了吗?试着在显示屏上展示一下! reward_constant_signal: title: 恒定信号 - desc: - 恭喜!您解锁了生成于电线层之上的<strong>恒定信号</strong>,把它连接到<strong>过滤器</strong>时非常有用。 + desc: 恭喜!您解锁了生成于电线层之上的<strong>恒定信号</strong>,把它连接到<strong>过滤器</strong>时非常有用。 <br>比如,它能发出图形、颜色、开关值(1 / 0)的固定信号。 reward_logic_gates: title: 逻辑门 @@ -599,16 +695,15 @@ storyRewards: reward_wires_painter_and_levers: title: 电线 & 四口上色器 desc: 恭喜!您解锁了<strong>电线层</strong>:它是正常层之上的一个层,它将带来了许多新的机制!<br><br> - 首先我解锁了您的<strong>四口上色器</strong>,按<strong>E</strong>键切换到电线层,然后连接您想要染色的槽,用开关来控制开启。<br><br> - <strong>提示</strong>:可在设置中打开电线层教程!" + 首先我解锁了您的<strong>四口上色器</strong>,按<strong>E</strong>键切换到电线层,然后连接您想要染色的槽,用开关来控制开启。<br><br> + <strong>提示</strong>:可在设置中打开电线层教程! reward_filter: title: 物品过滤器 - desc: - 恭喜!您解锁了<strong>物品过滤器</strong>!它会根据在电线层上输入的信号决定是从上面还是右边输出物品。<br><br> + desc: 恭喜!您解锁了<strong>物品过滤器</strong>!它会根据在电线层上输入的信号决定是从上面还是右边输出物品。<br><br> 您也可以输入开关值(1 / 0)信号来激活或者禁用它。 reward_demo_end: title: 试玩结束 - desc: 恭喜!您已经通关了试玩版本! <br>更多挑战,请至Steam商城购买完整版!谢谢支持! + desc: 恭喜!您已经通关了试玩版本!<br>更多挑战,请至Steam商城购买完整版!谢谢支持! settings: title: 设置 categories: @@ -680,7 +775,7 @@ settings: extremely_fast: 最快 enableTunnelSmartplace: title: 智能隧道放置 - description: 启用后,放置隧道时会将多余的传送带移除。 此外,拖动隧道可以快速铺设隧道,以及移除不必要的隧道。 + description: 启用后,放置隧道时会将多余的传送带移除。此外,拖动隧道可以快速铺设隧道,以及移除不必要的隧道。 vignette: title: 晕映 description: 启用晕映功能,可将屏幕角落里的颜色变深,更容易阅读文本。 @@ -722,7 +817,7 @@ settings: title: 右键取消 description: 默认启用。在选择要放置的设施时,单击鼠标右键即可取消。如果禁用,则可以通过在放置设施时单击鼠标右键来删除设施。 lowQualityTextures: - title: 低质量纹理(丑陋) + title: 低质量纹理(丑陋) description: 使用低质量纹理提高游戏性能。但是这样游戏会看起来很丑! displayChunkBorders: title: 显示大块的边框 @@ -813,12 +908,16 @@ keybindings: transistor: 晶体管 analyzer: 图形分析器 comparator: 比较器 - item_producer: 物品生产器 (沙盒模式) + item_producer: 物品生产器(沙盒模式) copyWireValue: 电线:复制指定电线上的值 - rotateToUp: "向上旋转" - rotateToDown: "向下旋转" - rotateToRight: "向右旋转" - rotateToLeft: "向左旋转" + rotateToUp: 向上旋转 + rotateToDown: 向下旋转 + rotateToRight: 向右旋转 + rotateToLeft: 向左旋转 + constant_producer: 常量生成器 + goal_acceptor: 目标接收器 + block: 方块 + massSelectClear: 清除传送带 about: title: 关于游戏 body: >- @@ -868,14 +967,14 @@ tips: - 您值得花时间来构建可重复的设计! - 按住<b>CTRL</b>键能够放置多个设施。 - 您可以按住<b>ALT</b>来反向放置传送带的方向。 - - 效率是关键! + - 效率是关键! - 离基地越远图形越复杂。 - 机器的速度是有限的,把它们分开可以获得最高的效率。 - 使用平衡器最大化您的效率。 - 有条不紊!尽量不要过多地穿过传送带。 - - 凡事预则立!不预则废! + - 凡事预则立!不预则废! - 尽量不要删除旧的设施和生产线,您会需要他们生产的东西来升级设施并提高效率。 - - 先给自己定一个小目标:自己完成20级!!不去看别人的攻略! + - 先给自己定一个小目标:自己完成20级!不去看别人的攻略! - 不要把问题复杂化,试着保持简单,您会成功的。 - 您可能需要在游戏的后期重复使用工厂。把您的工厂规划成可重复使用的。 - 有时,您可以在地图上直接找到您需要的图形,并不需要使用堆叠机去合成它。 @@ -889,8 +988,8 @@ tips: - 使用升级列表中每个形状旁边的固定图标将其固定到屏幕上。 - 地图无限,放飞想象,尽情创造。 - 向您推荐Factorio!这是我最喜欢的游戏。向神作致敬! - - 四向切割机从右上开始进行顺时针切割! - - 在主界面您可以下载您的游戏存档文件! + - 四向切割机从右上开始进行顺时针切割! + - 在主界面您可以下载您的游戏存档文件! - 这个游戏有很多有用的快捷键!一定要到快捷键页面看看。 - 这个游戏有很多设置可以提高游戏效率,请一定要了解一下! - 中心基地有个指向它所在方向的小指南指针! @@ -898,3 +997,66 @@ tips: - 按F4显示FPS。 - 按两次F4显示您鼠标和镜头所在的块。 - 您可以点击被固定在屏幕左侧的图形来解除固定。 + - 您可以单击左侧的固定形状将其取消固定。 +puzzleMenu: + play: 游戏 + edit: 编辑 + title: 谜题模式 + createPuzzle: 创建谜题 + loadPuzzle: 载入 + reviewPuzzle: 审阅 & 发布 + validatingPuzzle: 验证谜题 + submittingPuzzle: 提交谜题 + noPuzzles: 暂无满足此部分条件的谜题。 + categories: + levels: 关卡 + new: 最新 + top-rated: 最受好评 + mine: 我的谜题 + easy: 简单 + hard: 困难 + completed: 完成 + medium: 普通 + official: 官方 + trending: 本日趋势 + trending-weekly: 本周趋势 + categories: 分类 + difficulties: 根据难度 + account: 我的谜题 + search: 查找 + validation: + title: 无效谜题 + noProducers: 请放置<strong>常量生成器</strong>! + noGoalAcceptors: 请放置<strong>目标接收器</strong>! + goalAcceptorNoItem: 一个或多个目标接收器尚未被分配图形目标。请向它们传递图形以设置目标。 + goalAcceptorRateNotMet: 一个或多个目标接收器没有获得足够数量的图形。请确保所有接收器的充能条指示器均为绿色。 + buildingOutOfBounds: 一个或多个设施位于可建造区域之外。请增加区域面积,或将超出区域的设施移除。 + autoComplete: 请确保您的常量生成器不会直接向目标接收器传递目标图形。否则您的谜题会自动完成。 + difficulties: + easy: 简单 + medium: 普通 + hard: 困难 + dlcHint: Purchased the DLC already? Make sure it is activated by right clicking + shapez.io in your library, selecting Properties > DLCs. +backendErrors: + ratelimit: 你的操作太频繁了。请稍等。 + invalid-api-key: 与后台通信失败,请尝试更新或重新启动游戏(无效的Api密钥)。 + unauthorized: 与后台通信失败,请尝试更新或重新启动游戏(未经授权)。 + bad-token: 与后台通信失败,请尝试更新或重新启动游戏(令牌错误)。 + bad-id: 谜题标识符无效。 + not-found: 找不到给定的谜题。 + bad-category: 找不到给定的类别。 + bad-short-key: 给定的短代码错误。 + profane-title: 您的谜题标题包含污言秽语。 + bad-title-too-many-spaces: 您的谜题标题过短。 + bad-shape-key-in-emitter: 常量生成器包含无效项目。 + bad-shape-key-in-goal: 目标接收器包含无效项目。 + no-emitters: 您的谜题没有任何常量生成器。 + no-goals: 您的谜题没有任何目标接收器。 + short-key-already-taken: 此短代码已被使用,请使用其他短代码。 + can-not-report-your-own-puzzle: 您无法上报您自己的谜题问题。 + bad-payload: 此请求包含无效数据。 + bad-building-placement: 您的谜题包含放置错误的设施。 + timeout: 请求超时。 + too-many-likes-already: 您的谜题已经得到了许多玩家的赞赏。如果您仍然希望删除它,请联系support@shapez.io! + no-permission: 您没有执行此操作的权限。 diff --git a/translations/base-zh-TW.yaml b/translations/base-zh-TW.yaml index bdad632a..b10f4a24 100644 --- a/translations/base-zh-TW.yaml +++ b/translations/base-zh-TW.yaml @@ -47,6 +47,7 @@ global: escape: ESC shift: SHIFT space: 空白鍵 + loggingIn: Logging in demoBanners: title: 試玩版 intro: 取得單機版以解鎖所有功能! @@ -65,6 +66,12 @@ mainMenu: madeBy: 作者:<author-link> subreddit: Reddit savegameUnnamed: 未命名 + puzzleMode: Puzzle Mode + back: Back + puzzleDlcText: Do you enjoy compacting and optimizing factories? Get the Puzzle + DLC now on Steam for even more fun! + puzzleDlcWishlist: Wishlist now! + puzzleDlcViewNow: View Dlc dialogs: buttons: ok: 確認 @@ -78,6 +85,9 @@ dialogs: viewUpdate: 查看更新 showUpgrades: 顯示建築升級 showKeybindings: 顯示按鍵設定 + retry: Retry + continue: Continue + playOffline: Play Offline importSavegameError: title: 匯入錯誤 text: 存檔匯入失敗: @@ -158,6 +168,70 @@ dialogs: tutorialVideoAvailableForeignLanguage: title: 有教學 desc: 這個等級目前只有英文版教學影片,你想觀看嗎? + editConstantProducer: + title: Set Item + puzzleLoadFailed: + title: Puzzles failed to load + desc: "Unfortunately the puzzles could not be loaded:" + submitPuzzle: + title: Submit Puzzle + descName: "Give your puzzle a name:" + descIcon: "Please enter a unique short key, which will be shown as the icon of + your puzzle (You can generate them <link>here</link>, or choose one + of the randomly suggested shapes below):" + placeholderName: Puzzle Title + puzzleResizeBadBuildings: + title: Resize not possible + desc: You can't make the zone any smaller, because then some buildings would be + outside the zone. + puzzleLoadError: + title: Bad Puzzle + desc: "The puzzle failed to load:" + offlineMode: + title: Offline Mode + desc: We couldn't reach the servers, so the game has to run in offline mode. + Please make sure you have an active internet connection. + puzzleDownloadError: + title: Download Error + desc: "Failed to download the puzzle:" + puzzleSubmitError: + title: Submission Error + desc: "Failed to submit your puzzle:" + puzzleSubmitOk: + title: Puzzle Published + desc: Congratulations! Your puzzle has been published and can now be played by + others. You can now find it in the "My puzzles" section. + puzzleCreateOffline: + title: Offline Mode + desc: Since you are offline, you will not be able to save and/or publish your + puzzle. Would you still like to continue? + puzzlePlayRegularRecommendation: + title: Recommendation + desc: I <strong>strongly</strong> recommend playing the normal game to level 12 + before attempting the puzzle DLC, otherwise you may encounter + mechanics not yet introduced. Do you still want to continue? + puzzleShare: + title: Short Key Copied + desc: The short key of the puzzle (<key>) has been copied to your clipboard! It + can be entered in the puzzle menu to access the puzzle. + puzzleReport: + title: Report Puzzle + options: + profane: Profane + unsolvable: Not solvable + trolling: Trolling + puzzleReportComplete: + title: Thank you for your feedback! + desc: The puzzle has been flagged. + puzzleReportError: + title: Failed to report + desc: "Your report could not get processed:" + puzzleLoadShortKey: + title: Enter short key + desc: Enter the short key of the puzzle to load it. + puzzleDelete: + title: Delete Puzzle? + desc: Are you sure you want to delete '<title>'? This can not be undone! ingame: keybindingsOverlay: moveMap: 移動 @@ -179,6 +253,7 @@ ingame: clearSelection: 清空選取 pipette: 滴管 switchLayers: 切換層 + clearBelts: Clear belts buildingPlacement: cycleBuildingVariants: 按<key>鍵以選擇建築變體。 hotkeyLabel: 快捷鍵:<key> @@ -253,7 +328,7 @@ ingame: 使用<strong>0-9快捷鍵</strong>可以更快選取建築 !" 3_1_rectangles: "現在來開採一些方形吧!<strong>蓋4座開採機</strong>,把形狀收集到基地。<br><br> PS: 選擇輸送帶,按住<strong>SHIFT</strong>並拖曳滑鼠可以計畫輸送帶位置!" - 21_1_place_quad_painter: 放置一個<strong>切割機(四分)</strong>並取得一些 + 21_1_place_quad_painter: 放置一個<strong>上色機(四向)</strong>並取得一些 <strong>圓形</strong>、<strong>白色</strong>和<strong>紅色</strong>! 21_2_switch_to_wires: Switch to the wires layer by pressing <strong>E</strong>!<br><br> Then <strong>connect all four @@ -311,6 +386,46 @@ ingame: achievements: title: Achievements desc: Hunt them all! + puzzleEditorSettings: + zoneTitle: Zone + zoneWidth: Width + zoneHeight: Height + trimZone: Trim + clearItems: Clear Items + share: Share + report: Report + clearBuildings: Clear Buildings + resetPuzzle: Reset Puzzle + puzzleEditorControls: + title: Puzzle Creator + instructions: + - 1. Place <strong>Constant Producers</strong> to provide shapes and + colors to the player + - 2. Build one or more shapes you want the player to build later and + deliver it to one or more <strong>Goal Acceptors</strong> + - 3. Once a Goal Acceptor receives a shape for a certain amount of + time, it <strong>saves it as a goal</strong> that the player must + produce later (Indicated by the <strong>green badge</strong>). + - 4. Click the <strong>lock button</strong> on a building to disable + it. + - 5. Once you click review, your puzzle will be validated and you + can publish it. + - 6. Upon release, <strong>all buildings will be removed</strong> + except for the Producers and Goal Acceptors - That's the part that + the player is supposed to figure out for themselves, after all :) + puzzleCompletion: + title: Puzzle Completed! + titleLike: "Click the heart if you liked the puzzle:" + titleRating: How difficult did you find the puzzle? + titleRatingDesc: Your rating will help me to make you better suggestions in the future + continueBtn: Keep Playing + menuBtn: Menu + puzzleMetadata: + author: Author + shortKey: Short Key + rating: Difficulty score + averageDuration: Avg. Duration + completionRate: Completion rate shopUpgrades: belt: name: 輸送帶、平衡機、隧道 @@ -341,7 +456,7 @@ buildings: name: 隧道 description: 可以從其他輸送帶或建築底下方運送物品。 tier2: - name: 貳級隧道 + name: 二級隧道 description: 可以從其他輸送帶或建築底下方運送物品。 cutter: default: @@ -491,6 +606,18 @@ buildings: default: name: 物品製造機 description: 沙盒模式專有,將電路層的輸入轉化成一般層的輸出。 + constant_producer: + default: + name: Constant Producer + description: Constantly outputs a specified shape or color. + goal_acceptor: + default: + name: Goal Acceptor + description: Deliver shapes to the goal acceptor to set them as a goal. + block: + default: + name: Block + description: Allows you to block a tile. storyRewards: reward_cutter_and_trash: title: 切割圖形 @@ -525,8 +652,8 @@ storyRewards: 它可以將開採出來的資源<strong>傳遞</strong>給其他的開採機,使得資源提取更加高效!<br><br> PS: 工具列中舊的開採機已被取代。" reward_underground_belt_tier_2: - title: 貳級隧道 - desc: <strong>貳級隧道</strong>變體已解鎖。這個隧道有<strong>更長的傳輸距離</strong>。你還可以混用不同的隧道變體! + title: 二級隧道 + desc: <strong>二級隧道</strong>變體已解鎖。這個隧道有<strong>更長的傳輸距離</strong>。你還可以混用不同的隧道變體! reward_cutter_quad: title: 四分切割 desc: 您已解鎖了<strong>切割機</strong>的變體:四分切割機。 @@ -809,10 +936,14 @@ keybindings: comparator: 比對機 item_producer: 物品生產機(沙盒模式) copyWireValue: 電路:複製數值於游標底下 - rotateToUp: "Rotate: Point Up" - rotateToDown: "Rotate: Point Down" - rotateToRight: "Rotate: Point Right" - rotateToLeft: "Rotate: Point Left" + rotateToUp: "轉動: 向上" + rotateToDown: "轉動: 向下" + rotateToRight: "轉動: 向右" + rotateToLeft: "轉動: 向左" + constant_producer: Constant Producer + goal_acceptor: Goal Acceptor + block: Block + massSelectClear: Clear belts about: title: 關於遊戲 body: >- @@ -893,3 +1024,73 @@ tips: - 按 F4 來顯示螢幕的幀數(FPS)與刷新率(Tick Rate)。 - 按 F4 兩次來顯示相機和游標的絕對位置。 - 在已標記的圖形上按左鍵去除標記。 +puzzleMenu: + play: Play + edit: Edit + title: Puzzle Mode + createPuzzle: Create Puzzle + loadPuzzle: Load + reviewPuzzle: Review & Publish + validatingPuzzle: Validating Puzzle + submittingPuzzle: Submitting Puzzle + noPuzzles: There are currently no puzzles in this section. + categories: + levels: Levels + new: New + top-rated: Top Rated + mine: My Puzzles + easy: Easy + hard: Hard + completed: Completed + medium: Medium + official: Official + trending: Trending today + trending-weekly: Trending weekly + categories: Categories + difficulties: By Difficulty + account: My Puzzles + search: Search + validation: + title: Invalid Puzzle + noProducers: Please place a Constant Producer! + noGoalAcceptors: Please place a Goal Acceptor! + goalAcceptorNoItem: One or more Goal Acceptors have not yet assigned an item. + Deliver a shape to them to set a goal. + goalAcceptorRateNotMet: One or more Goal Acceptors are not getting enough items. + Make sure that the indicators are green for all acceptors. + buildingOutOfBounds: One or more buildings are outside of the buildable area. + Either increase the area or remove them. + autoComplete: Your puzzle autocompletes itself! Please make sure your constant + producers are not directly delivering to your goal acceptors. + difficulties: + easy: Easy + medium: Medium + hard: Hard + dlcHint: Purchased the DLC already? Make sure it is activated by right clicking + shapez.io in your library, selecting Properties > DLCs. +backendErrors: + ratelimit: You are performing your actions too frequent. Please wait a bit. + invalid-api-key: Failed to communicate with the backend, please try to + update/restart the game (Invalid Api Key). + unauthorized: Failed to communicate with the backend, please try to + update/restart the game (Unauthorized). + bad-token: Failed to communicate with the backend, please try to update/restart + the game (Bad Token). + bad-id: Invalid puzzle identifier. + not-found: The given puzzle could not be found. + bad-category: The given category could not be found. + bad-short-key: The given short key is invalid. + profane-title: Your puzzle title contains profane words. + bad-title-too-many-spaces: Your puzzle title is too short. + bad-shape-key-in-emitter: A constant producer has an invalid item. + bad-shape-key-in-goal: A goal acceptor has an invalid item. + no-emitters: Your puzzle does not contain any constant producers. + no-goals: Your puzzle does not contain any goal acceptors. + short-key-already-taken: This short key is already taken, please use another one. + can-not-report-your-own-puzzle: You can not report your own puzzle. + bad-payload: The request contains invalid data. + bad-building-placement: Your puzzle contains invalid placed buildings. + timeout: The request timed out. + too-many-likes-already: The puzzle alreay got too many likes. If you still want + to remove it, please contact support@shapez.io! + no-permission: You do not have the permission to perform this action. diff --git a/version b/version index 6261a05b..c9929e36 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.3.1 \ No newline at end of file +1.4.2 \ No newline at end of file