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/gulp/gulpfile.js b/gulp/gulpfile.js index 06bb627b..b1ef8817 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,41 @@ 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.all", gulp.series("regular.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..b6e3ce16 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: true, + standalone: true, + wegameVersion: true, + }) + ) + ) + .pipe(gulp.dest(buildFolder)); + }); } module.exports = { diff --git a/gulp/standalone.js b/gulp/standalone.js index ffec8539..914fb95a 100644 --- a/gulp/standalone.js +++ b/gulp/standalone.js @@ -21,6 +21,11 @@ function gulptasksStandalone($, gulp) { suffix: "china", taskPrefix: "china.", }, + { + tempDestDir: path.join(__dirname, "..", "tmp_standalone_files_wegame"), + suffix: "wegame", + taskPrefix: "wegame.", + }, ]; for (const { tempDestDir, suffix, taskPrefix } of targets) { 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 dc9559b4..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", 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/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/game/modes/regular.js b/src/js/game/modes/regular.js index 8153dca9..6a939d1a 100644 --- a/src/js/game/modes/regular.js +++ b/src/js/game/modes/regular.js @@ -66,6 +66,8 @@ const preparementShape = "CpRpCp--:SwSwSwSw"; // 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} */ @@ -144,7 +146,7 @@ function generateUpgrades(limitedVersion = false) { { required: [ { - shape: G_CHINA_VERSION + shape: chinaShapes ? "CyCyCyCy:CyCyCyCy:RyRyRyRy:RuRuRuRu" : "CbRbRbCb:CwCwCwCw:WbWbWbWb", amount: 50000, @@ -205,7 +207,7 @@ function generateUpgrades(limitedVersion = false) { { required: [ { - shape: G_CHINA_VERSION ? "CuCuCuCu:CwCwCwCw:Sb--Sr--" : "RpRpRpRp:CwCwCwCw", + shape: chinaShapes ? "CuCuCuCu:CwCwCwCw:Sb--Sr--" : "RpRpRpRp:CwCwCwCw", amount: 6500, }, ], @@ -382,7 +384,7 @@ export function generateLevelDefinitions(limitedVersion = false) { // 13 // Tunnel Tier 2 { - shape: G_CHINA_VERSION ? "CuCuCuCu:CwCwCwCw:Sb--Sr--" : "RpRpRpRp:CwCwCwCw", // painting t3 + shape: chinaShapes ? "CuCuCuCu:CwCwCwCw:Sb--Sr--" : "RpRpRpRp:CwCwCwCw", // painting t3 required: 3800, reward: enumHubGoalRewards.reward_underground_belt_tier_2, }, @@ -391,7 +393,7 @@ export function generateLevelDefinitions(limitedVersion = false) { ...(limitedVersion ? [ { - shape: G_CHINA_VERSION ? "CuCuCuCu:CwCwCwCw:Sb--Sr--" : "RpRpRpRp:CwCwCwCw", + shape: chinaShapes ? "CuCuCuCu:CwCwCwCw:Sb--Sr--" : "RpRpRpRp:CwCwCwCw", required: 0, reward: enumHubGoalRewards.reward_demo_end, }, @@ -425,7 +427,7 @@ export function generateLevelDefinitions(limitedVersion = false) { // 17 // Double painter { - shape: G_CHINA_VERSION + shape: chinaShapes ? "CyCyCyCy:CyCyCyCy:RyRyRyRy:RuRuRuRu" : "CbRbRbCb:CwCwCwCw:WbWbWbWb", // miner t4 (two variants) required: 20000, @@ -467,7 +469,7 @@ export function generateLevelDefinitions(limitedVersion = false) { // 22 // Constant signal { - shape: G_CHINA_VERSION + shape: chinaShapes ? "RrSySrSy:RyCrCwCr:CyCyRyCy" : "Cg----Cr:Cw----Cw:Sy------:Cy----Cy", required: 25000, @@ -477,7 +479,7 @@ export function generateLevelDefinitions(limitedVersion = false) { // 23 // Display { - shape: G_CHINA_VERSION + shape: chinaShapes ? "CrCrCrCr:CwCwCwCw:WwWwWwWw:CrCrCrCr" : "CcSyCcSy:SyCcSyCc:CcSyCcSy", required: 25000, @@ -486,7 +488,7 @@ export function generateLevelDefinitions(limitedVersion = false) { // 24 Logic gates { - shape: G_CHINA_VERSION + shape: chinaShapes ? "Su----Su:RwRwRwRw:Cu----Cu:CwCwCwCw" : "CcRcCcRc:RwCwRwCw:Sr--Sw--:CyCyCyCy", required: 25000, diff --git a/src/js/globals.d.ts b/src/js/globals.d.ts index 5bb3bbba..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 { diff --git a/src/js/platform/api.js b/src/js/platform/api.js index 2219541e..99c61a20 100644 --- a/src/js/platform/api.js +++ b/src/js/platform/api.js @@ -101,7 +101,10 @@ export class ClientAPI { */ apiTryLogin() { if (!G_IS_STANDALONE) { - return Promise.reject("Not possible outside of standalone."); + const token = window.prompt( + "Please enter the auth token for the puzzle DLC (If you have none, you can't login):" + ); + return Promise.resolve({ token }); } const renderer = getIPCRenderer(); diff --git a/src/js/platform/browser/game_analytics.js b/src/js/platform/browser/game_analytics.js index 65fc5080..e4226ae0 100644 --- a/src/js/platform/browser/game_analytics.js +++ b/src/js/platform/browser/game_analytics.js @@ -53,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 @@ -136,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; 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/main_menu.js b/src/js/states/main_menu.js index 49fae145..38a22d4e 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"; @@ -49,7 +50,7 @@ export class MainMenuState extends GameState { return `
${ - G_CHINA_VERSION + G_CHINA_VERSION || G_WEGAME_VERSION ? "" : `` } @@ -69,10 +70,12 @@ export class MainMenuState extends GameState {
+ ${ + G_WEGAME_VERSION + ? "" + : ` +
+ ` + } `; } @@ -263,7 +272,7 @@ export class MainMenuState extends GameState { this.trackClicks(qs(".settingsButton"), this.onSettingsButtonClicked); - 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); @@ -283,14 +292,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) { @@ -305,9 +316,15 @@ 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) { 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 40261b7d..b0dbda86 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"}
@@ -112,7 +113,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"); } @@ -164,7 +165,7 @@ export class PreloadState extends GameState { return; } - if (G_CHINA_VERSION) { + if (G_CHINA_VERSION || G_WEGAME_VERSION) { return; } @@ -227,7 +228,7 @@ export class PreloadState extends GameState { } update() { - if (G_CHINA_VERSION) { + if (G_CHINA_VERSION || G_WEGAME_VERSION) { return; } const now = performance.now(); @@ -260,7 +261,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; @@ -278,9 +279,7 @@ export class PreloadState extends GameState { subElement.innerHTML = `
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 {
${ - G_CHINA_VERSION + G_CHINA_VERSION || G_WEGAME_VERSION ? "" : ` @@ -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-zh-CN.yaml b/translations/base-zh-CN.yaml index 42bb60d9..8cf6d1fd 100644 --- a/translations/base-zh-CN.yaml +++ b/translations/base-zh-CN.yaml @@ -68,11 +68,10 @@ mainMenu: madeBy: 作者: 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! + puzzleMode: 谜题模式 + back: 返回 + puzzleDlcText: 持续优化,追求极致效率。在限定空间内使用有限的设施来创造图形!《异形工厂》(Shapez.io)的首个DLC“谜题挑战者”将会给大家带来更烧脑、更自由的全新挑战! + puzzleDlcWishlist: 添加愿望单! dialogs: buttons: ok: 确认 @@ -86,9 +85,9 @@ dialogs: viewUpdate: 查看更新 showUpgrades: 显示设施升级 showKeybindings: 显示按键设置 - retry: Retry - continue: Continue - playOffline: Play Offline + retry: 重试 + continue: 继续 + playOffline: 离线游戏 importSavegameError: title: 读取错误 text: 未能读取您的存档: @@ -136,13 +135,15 @@ dialogs: desc: 您还没有解锁蓝图功能!通过第12关的挑战后可解锁蓝图。 keybindingsIntroduction: title: 实用快捷键 - desc: "这个游戏有很多有用的快捷键设定。 以下是其中的一些介绍,记得在按键设置中查看其他按键设定!

+ desc: + "这个游戏有很多有用的快捷键设定。 以下是其中的一些介绍,记得在按键设置中查看其他按键设定!

CTRL键 + 拖动:选择区域以复制或删除。
SHIFT键: 按住以放置多个同一种设施。
ALT键: 反向放置传送带。
" createMarker: title: 创建地图标记 - desc: 填写一个有意义的名称, 还可以同时包含一个形状的 短代码 (您可以 点击这里 + desc: + 填写一个有意义的名称, 还可以同时包含一个形状的 短代码 (您可以 点击这里 生成短代码) titleEdit: 编辑地图标记 markerDemoLimit: @@ -170,66 +171,59 @@ dialogs: title: 教程 desc: 这个关卡有英语版本的视频攻略! 您想查看这个视频攻略吗?? editConstantProducer: - title: Set Item + title: 设置项目 puzzleLoadFailed: - title: Puzzles failed to load - desc: "Unfortunately the puzzles could not be loaded:" + title: 谜题载入失败 + desc: "很遗憾,谜题未能载入:" 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 here, or choose one - of the randomly suggested shapes below):" - placeholderName: Puzzle Title + title: 提交谜题 + descName: "给您的谜题设定名称:" + descIcon: + "请输入唯一的短代码,它将显示为标志您的谜题的图标( 在此生成,或者从以下随机推荐的图形中选择一个): + " + placeholderName: 谜题标题 puzzleResizeBadBuildings: - title: Resize not possible - desc: You can't make the zone any smaller, because then some buildings would be - outside the zone. + title: 无法调整大小 + desc: 您无法使这块区域变得更小,否则有些设施将会超出区域范围。 puzzleLoadError: - title: Bad Puzzle - desc: "The puzzle failed to load:" + 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 internect connection. + title: 离线模式 + desc: 访问服务器失败,游戏只能在离线模式下进行。请确认您的网络连接正常。 puzzleDownloadError: - title: Download Error - desc: "Failed to download the puzzle:" + title: 下载出错 + desc: "无法下载谜题:" puzzleSubmitError: - title: Submission Error - desc: "Failed to submit your puzzle:" + title: 提交出错 + desc: "无法提交您的谜题:" 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. + 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? + title: 离线模式 + desc: 由于您现在没有连接互联网,所以您将无法保存或发布您的谜题。是否仍要继续? puzzlePlayRegularRecommendation: - title: Recommendation - desc: I strongly 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? + title: 游戏建议 + desc: 强烈建议您在至少完成本体第12关后再尝试体验“谜题挑战者”DLC,否则您可能在游戏过程中遇到困难,您是否仍要继续? puzzleShare: - title: Short Key Copied - desc: The short key of the puzzle () has been copied to your clipboard! It - can be entered in the puzzle menu to access the puzzle. + title: 短代码已复制 + desc: 此谜题的短代码()已经复制到了您的剪贴板!您可以在谜题菜单中输入短代码以快速访问对应谜题。 puzzleReport: - title: Report Puzzle + title: 上报谜题 options: - profane: Profane - unsolvable: Not solvable + profane: 污言秽语 + unsolvable: 无法完成 trolling: Trolling puzzleReportComplete: - title: Thank you for your feedback! - desc: The puzzle has been flagged. + title: 感谢您的反馈! + desc: 此谜题已标记! puzzleReportError: - title: Failed to report - desc: "Your report could not get processed:" + title: 上报失败 + desc: "无法处理您的上报:" puzzleLoadShortKey: - title: Enter short key - desc: Enter the short key of the puzzle to load it. + title: 输入短代码 + desc: 输入此谜题的短代码以载入。 ingame: keybindingsOverlay: moveMap: 移动地图 @@ -251,7 +245,7 @@ ingame: clearSelection: 取消选择 pipette: 吸取器 switchLayers: 切换层 - clearBelts: Clear belts + clearBelts: 清除传送带 buildingPlacement: cycleBuildingVariants: 按 键以选择设施的变型体。 hotkeyLabel: "快捷键: " @@ -315,15 +309,18 @@ ingame: hints: 1_1_extractor: 在圆形上放置一个开采器来获取圆形!

提示:按下鼠标左键选中开采器 1_2_conveyor: 用传送带将您的开采器连接到中心基地上!

提示:选中传送带按下鼠标左键可拖动布置传送带! - 1_3_expand: 您可以放置更多的开采器传送带来更有效率地完成关卡目标。

+ 1_3_expand: + 您可以放置更多的开采器传送带来更有效率地完成关卡目标。

提示:按住 SHIFT 键可放置多个开采器,注意用R 键可旋转开采器的出口方向,确保开采的图形可以顺利传送。 2_1_place_cutter: 现在放置一个切割器,这个设施可把圆形切成两半!

注意:无论如何放置,切割机总是从上到下切割。 - 2_2_place_trash: 使用切割机后产生的废弃图形会导致堵塞

注意使用垃圾桶清除当前 + 2_2_place_trash: + 使用切割机后产生的废弃图形会导致堵塞

注意使用垃圾桶清除当前 (!) 不需要的废物。 2_3_more_cutters: 干的好!现在放置2个以上的切割机来加快当前缓慢的过程!

提示:用快捷键0-9可以快速选择各项设施! - 3_1_rectangles: 现在让我们开采一些矩形!找到矩形地带放置4个开采器并将它们用传送带连接到中心基地。

+ 3_1_rectangles: + 现在让我们开采一些矩形!找到矩形地带放置4个开采器并将它们用传送带连接到中心基地。

提示:选中传送带后按住SHIFT键可快速准确地规划传送带路线! 21_1_place_quad_painter: 放置四口上色器并且获取一些圆形白色红色! 21_2_switch_to_wires: 按 E 键选择电线层

@@ -381,43 +378,35 @@ ingame: title: 成就 desc: 挑战全成就解锁! puzzleEditorSettings: - zoneTitle: Zone - zoneWidth: Width - zoneHeight: Height - trimZone: Trim - clearItems: Clear Items - share: Share - report: Report + zoneTitle: 区域 + zoneWidth: 宽度 + zoneHeight: 高度 + trimZone: 整理 + clearItems: 清除项目 + share: 共享 + report: 上报 puzzleEditorControls: - title: Puzzle Creator + title: 谜题编辑器 instructions: - - 1. Place Constant Producers 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 Goal Acceptors - - 3. Once a Goal Acceptor receives a shape for a certain amount of - time, it saves it as a goal that the player must - produce later (Indicated by the green badge). - - 4. Click the lock button on a building to disable - it. - - 5. Once you click review, your puzzle will be validated and you - can publish it. - - 6. Upon release, all buildings will be removed - except for the Producers and Goal Acceptors - That's the part that - the player is supposed to figure out for themselves, after all :) + - 1.放置常量生成器,为玩家提供此谜题的初始图形和颜色。 + - 2.建造您希望玩家稍后建造的一个或多个图形,并将其交付给一个或多个目标接收器。 + - 3.当一个目标接收器接收到一个图形一段时间后,会将其保存为此玩家稍后必须建造的目标(由绿色充能条表示)。 + - 4.单击设施上的锁定按钮即可将其禁用。 + - 5.单击审阅后,您的谜题将通过验证,您可以正式发布它。 + - 6.谜题发布后,所有设施都将被拆除,除了常量生成器目标接收器。然后,等着其他玩家对您创造的谜题发起挑战吧! 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 + title: 谜题挑战成功! + titleLike: "喜欢此谜题的话,请为它点赞:" + titleRating: 您觉得此谜题难度如何? + titleRatingDesc: 您的评分将帮助作者在未来创作出更好的谜题! + continueBtn: 继续游戏 + menuBtn: 菜单 puzzleMetadata: - author: Author - shortKey: Short Key - rating: Difficulty score - averageDuration: Avg. Duration - completionRate: Completion rate + author: 作者 + shortKey: 短代码 + rating: 难度评分 + averageDuration: 平均挑战时间 + completionRate: 挑战完成率 shopUpgrades: belt: name: 传送、分发、隧道 @@ -495,7 +484,7 @@ buildings: hub: deliver: 交付 toUnlock: 解锁 - levelShortcut: LVL + levelShortcut: 关卡 endOfDemo: 试玩版结束 wire: default: @@ -601,16 +590,16 @@ buildings: description: 仅在沙盒模式下可用,在常规层上输出电线层给定的信号。 constant_producer: default: - name: Constant Producer - description: Constantly outputs a specified shape or color. + name: 常量生成器 + description: 不断输出指定的图形或颜色。 goal_acceptor: default: - name: Goal Acceptor - description: Deliver shapes to the goal acceptor to set them as a goal. + name: 目标接收器 + description: 将图状传递给目标接收器,以将它们设置为谜题挑战目标。 block: default: - name: Block - description: Allows you to block a tile. + name: 方块 + description: 放置了方块的格子将无法再进行其他放置。 storyRewards: reward_cutter_and_trash: title: 切割图形 @@ -622,7 +611,8 @@ storyRewards: desc: 恭喜!您解锁了旋转机。它会顺时针将输入的图形旋转90度。 reward_painter: title: 上色 - desc: 恭喜!您解锁了上色器。开采一些颜色 (就像您开采图形一样),将其在上色器中与图形结合来将图形上色! + desc: + 恭喜!您解锁了上色器。开采一些颜色 (就像您开采图形一样),将其在上色器中与图形结合来将图形上色!
注意:如果您不幸患有色盲,可以在设置中启用色盲模式 reward_mixer: title: 混合颜色 @@ -639,11 +629,13 @@ storyRewards: desc: 恭喜!您解锁了隧道。它可放置在传送带设施下方以运送物品。 reward_rotater_ccw: title: 逆时针旋转 - desc: 恭喜!您解锁了旋转机逆时针变体。它可以逆时针旋转图形。 + desc: + 恭喜!您解锁了旋转机逆时针变体。它可以逆时针旋转图形
选择旋转机然后按"T"键来选取这个变体。 reward_miner_chainable: title: 链式开采器 - desc: 您已经解锁了链式开采器!它能转发资源给其他的开采器,这样您就能更有效率的开采各类资源了!

+ desc: + 您已经解锁了链式开采器!它能转发资源给其他的开采器,这样您就能更有效率的开采各类资源了!

注意:新的开采器已替换了工具栏里旧的开采器! reward_underground_belt_tier_2: title: 二级隧道 @@ -660,12 +652,14 @@ storyRewards:
优先从左边输出,这样您就可以用它做一个溢流门了! reward_freeplay: title: 自由模式 - desc: 成功了!您解锁了自由模式!挑战升级!这意味着现在将随机生成图形! + desc: + 成功了!您解锁了自由模式!挑战升级!这意味着现在将随机生成图形! 从现在起,中心基地最为需要的是产量,我强烈建议您去制造一台能够自动交付所需图形的机器!

基地会在电线层输出需要的图形,您需要去分析图形并在此基础上自动配置您的工厂。 reward_blueprints: title: 蓝图 - desc: 您现在可以复制粘贴您的工厂的一部分了!按住 CTRL键并拖动鼠标来选择一块区域,然后按C键复制。 + desc: + 您现在可以复制粘贴您的工厂的一部分了!按住 CTRL键并拖动鼠标来选择一块区域,然后按C键复制。

粘贴并不是免费的,您需要制造蓝图图形来负担。蓝图图形是您刚刚交付的图形。 no_reward: title: 下一关 @@ -693,7 +687,8 @@ storyRewards:
注意:您注意到传送读取器存储器输出的他们最后读取的物品了吗?试着在显示屏上展示一下!" reward_constant_signal: title: 恒定信号 - desc: 恭喜!您解锁了生成于电线层之上的恒定信号,把它连接到过滤器时非常有用。 + desc: + 恭喜!您解锁了生成于电线层之上的恒定信号,把它连接到过滤器时非常有用。
比如,它能发出图形、颜色、开关值(1 / 0)的固定信号。 reward_logic_gates: title: 逻辑门 @@ -712,7 +707,8 @@ storyRewards: 提示:可在设置中打开电线层教程!" reward_filter: title: 物品过滤器 - desc: 恭喜!您解锁了物品过滤器!它会根据在电线层上输入的信号决定是从上面还是右边输出物品。

+ desc: + 恭喜!您解锁了物品过滤器!它会根据在电线层上输入的信号决定是从上面还是右边输出物品。

您也可以输入开关值(1 / 0)信号来激活或者禁用它。 reward_demo_end: title: 试玩结束 @@ -927,10 +923,10 @@ keybindings: rotateToDown: 向下旋转 rotateToRight: 向右旋转 rotateToLeft: 向左旋转 - constant_producer: Constant Producer - goal_acceptor: Goal Acceptor - block: Block - massSelectClear: Clear belts + constant_producer: 常量生成器 + goal_acceptor: 目标接收器 + block: 方块 + massSelectClear: 清除传送带 about: title: 关于游戏 body: >- @@ -1010,58 +1006,51 @@ tips: - 按F4显示FPS。 - 按两次F4显示您鼠标和镜头所在的块。 - 您可以点击被固定在屏幕左侧的图形来解除固定。 - - 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. + play: 游戏 + edit: 编辑 + title: 谜题模式 + createPuzzle: 创建谜题 + loadPuzzle: 载入 + reviewPuzzle: 审阅 & 发布 + validatingPuzzle: 验证谜题 + submittingPuzzle: 提交谜题 + noPuzzles: 暂无满足此部分条件的谜题。 categories: - levels: Levels - new: New - top-rated: Top Rated - mine: My Puzzles - short: Short - easy: Easy - hard: Hard - completed: Completed + levels: 关卡 + new: 最新 + top-rated: 最受好评 + mine: 我的谜题 + short: 速通 + easy: 简单 + hard: 困难 + completed: 完成 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. + title: 无效谜题 + noProducers: 请放置常量生成器! + noGoalAcceptors: 请放置目标接收器! + goalAcceptorNoItem: 一个或多个目标接收器尚未被分配图形目标。请向它们传递图形以设置目标。 + goalAcceptorRateNotMet: 一个或多个目标接收器没有获得足够数量的图形。请确保所有接收器的充能条指示器均为绿色。 + buildingOutOfBounds: 一个或多个设施位于可建造区域之外。请增加区域面积,或将超出区域的设施移除。 + autoComplete: 请确保您的常量生成器不会直接向目标接收器传递目标图形。否则您的谜题会自动完成。 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. + 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: 请求超时。