mirror of
https://github.com/tobspr/shapez.io.git
synced 2024-10-27 20:34:29 +00:00
Wegame adjustments
This commit is contained in:
parent
34754964d3
commit
9c1bac5afe
@ -1,30 +1,39 @@
|
||||
/* eslint-disable quotes,no-undef */
|
||||
|
||||
const { app, BrowserWindow, Menu, MenuItem, ipcMain, shell } = require("electron");
|
||||
|
||||
app.commandLine.appendSwitch("in-process-gpu");
|
||||
|
||||
const { app, BrowserWindow, Menu, MenuItem, ipcMain, shell, dialog, session } = require("electron");
|
||||
const path = require("path");
|
||||
const url = require("url");
|
||||
const fs = require("fs");
|
||||
const wegame = require("./wegame");
|
||||
const asyncLock = require("async-lock");
|
||||
const windowStateKeeper = require("electron-window-state");
|
||||
|
||||
const isDev = process.argv.indexOf("--dev") >= 0;
|
||||
const isLocal = process.argv.indexOf("--local") >= 0;
|
||||
// Disable hardware key handling, i.e. being able to pause/resume the game music
|
||||
// with hardware keys
|
||||
app.commandLine.appendSwitch("disable-features", "HardwareMediaKeyHandling");
|
||||
|
||||
const isDev = app.commandLine.hasSwitch("dev");
|
||||
const isLocal = app.commandLine.hasSwitch("local");
|
||||
const safeMode = app.commandLine.hasSwitch("safe-mode");
|
||||
const externalMod = app.commandLine.getSwitchValue("load-mod");
|
||||
|
||||
const roamingFolder =
|
||||
process.env.APPDATA ||
|
||||
(process.platform == "darwin"
|
||||
? process.env.HOME + "/Library/Preferences"
|
||||
: process.env.HOME + "/.local/share");
|
||||
let storePath = path.join(roamingFolder, "shapez-wegame", "saves");
|
||||
let storePath = path.join(roamingFolder, "shapez-china", "saves");
|
||||
let modsPath = path.join(roamingFolder, "shapez-china", "mods");
|
||||
|
||||
if (!fs.existsSync(storePath)) {
|
||||
// No try-catch by design
|
||||
fs.mkdirSync(storePath, { recursive: true });
|
||||
}
|
||||
|
||||
if (!fs.existsSync(modsPath)) {
|
||||
fs.mkdirSync(modsPath, { recursive: true });
|
||||
}
|
||||
|
||||
/** @type {BrowserWindow} */
|
||||
let win = null;
|
||||
let menu = null;
|
||||
@ -35,30 +44,44 @@ function createWindow() {
|
||||
faviconExtension = ".ico";
|
||||
}
|
||||
|
||||
const mainWindowState = windowStateKeeper({
|
||||
defaultWidth: 1000,
|
||||
defaultHeight: 800,
|
||||
});
|
||||
|
||||
win = new BrowserWindow({
|
||||
width: 1280,
|
||||
height: 800,
|
||||
x: mainWindowState.x,
|
||||
y: mainWindowState.y,
|
||||
width: mainWindowState.width,
|
||||
height: mainWindowState.height,
|
||||
show: false,
|
||||
backgroundColor: "#222428",
|
||||
useContentSize: true,
|
||||
useContentSize: false,
|
||||
minWidth: 800,
|
||||
minHeight: 600,
|
||||
title: "图形工厂",
|
||||
transparent: false,
|
||||
icon: path.join(__dirname, "favicon" + faviconExtension),
|
||||
// fullscreen: true,
|
||||
autoHideMenuBar: true,
|
||||
autoHideMenuBar: !isDev,
|
||||
webPreferences: {
|
||||
nodeIntegration: false,
|
||||
nodeIntegrationInWorker: false,
|
||||
nodeIntegrationInSubFrames: false,
|
||||
contextIsolation: true,
|
||||
enableRemoteModule: false,
|
||||
disableBlinkFeatures: "Auxclick",
|
||||
|
||||
webSecurity: true,
|
||||
sandbox: true,
|
||||
|
||||
contextIsolation: true,
|
||||
preload: path.join(__dirname, "preload.js"),
|
||||
experimentalFeatures: false,
|
||||
},
|
||||
allowRunningInsecureContent: false,
|
||||
});
|
||||
|
||||
mainWindowState.manage(win);
|
||||
|
||||
if (isLocal) {
|
||||
win.loadURL("http://localhost:3005");
|
||||
} else {
|
||||
@ -70,12 +93,70 @@ function createWindow() {
|
||||
})
|
||||
);
|
||||
}
|
||||
win.webContents.session.clearCache(() => null);
|
||||
win.webContents.session.clearCache();
|
||||
win.webContents.session.clearStorageData();
|
||||
|
||||
////// SECURITY
|
||||
|
||||
// Disable permission requests
|
||||
win.webContents.session.setPermissionRequestHandler((webContents, permission, callback) => {
|
||||
callback(false);
|
||||
});
|
||||
session.fromPartition("default").setPermissionRequestHandler((webContents, permission, callback) => {
|
||||
callback(false);
|
||||
});
|
||||
|
||||
app.on("web-contents-created", (event, contents) => {
|
||||
// Disable vewbiew
|
||||
contents.on("will-attach-webview", (event, webPreferences, params) => {
|
||||
event.preventDefault();
|
||||
});
|
||||
// Disable navigation
|
||||
contents.on("will-navigate", (event, navigationUrl) => {
|
||||
event.preventDefault();
|
||||
});
|
||||
});
|
||||
|
||||
win.webContents.on("will-redirect", (contentsEvent, navigationUrl) => {
|
||||
// Log and prevent the app from redirecting to a new page
|
||||
console.error(
|
||||
`The application tried to redirect to the following address: '${navigationUrl}'. This attempt was blocked.`
|
||||
);
|
||||
contentsEvent.preventDefault();
|
||||
});
|
||||
|
||||
// Filter loading any module via remote;
|
||||
// you shouldn't be using remote at all, though
|
||||
// https://electronjs.org/docs/tutorial/security#16-filter-the-remote-module
|
||||
app.on("remote-require", (event, webContents, moduleName) => {
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
// built-ins are modules such as "app"
|
||||
app.on("remote-get-builtin", (event, webContents, moduleName) => {
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
app.on("remote-get-global", (event, webContents, globalName) => {
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
app.on("remote-get-current-window", (event, webContents) => {
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
app.on("remote-get-current-web-contents", (event, webContents) => {
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
//// END SECURITY
|
||||
|
||||
win.webContents.on("new-window", (event, pth) => {
|
||||
event.preventDefault();
|
||||
shell.openExternal(pth);
|
||||
|
||||
if (pth.startsWith("https://") || pth.startsWith("steam://")) {
|
||||
shell.openExternal(pth);
|
||||
}
|
||||
});
|
||||
|
||||
win.on("closed", () => {
|
||||
@ -86,6 +167,8 @@ function createWindow() {
|
||||
if (isDev) {
|
||||
menu = new Menu();
|
||||
|
||||
win.webContents.toggleDevTools();
|
||||
|
||||
const mainItem = new MenuItem({
|
||||
label: "Toggle Dev Tools",
|
||||
click: () => win.webContents.toggleDevTools(),
|
||||
@ -94,7 +177,7 @@ function createWindow() {
|
||||
menu.append(mainItem);
|
||||
|
||||
const reloadItem = new MenuItem({
|
||||
label: "Restart",
|
||||
label: "Reload",
|
||||
click: () => win.reload(),
|
||||
accelerator: "F5",
|
||||
});
|
||||
@ -107,7 +190,15 @@ function createWindow() {
|
||||
});
|
||||
menu.append(fullscreenItem);
|
||||
|
||||
Menu.setApplicationMenu(menu);
|
||||
const mainMenu = new Menu();
|
||||
mainMenu.append(
|
||||
new MenuItem({
|
||||
label: "shapez.io",
|
||||
submenu: menu,
|
||||
})
|
||||
);
|
||||
|
||||
Menu.setApplicationMenu(mainMenu);
|
||||
} else {
|
||||
Menu.setApplicationMenu(null);
|
||||
}
|
||||
@ -121,7 +212,7 @@ function createWindow() {
|
||||
if (!app.requestSingleInstanceLock()) {
|
||||
app.exit(0);
|
||||
} else {
|
||||
app.on("second-instance", (event, commandLine, workingDirectory) => {
|
||||
app.on("second-instance", () => {
|
||||
// Someone tried to run a second instance, we should focus
|
||||
if (win) {
|
||||
if (win.isMinimized()) {
|
||||
@ -143,7 +234,7 @@ ipcMain.on("set-fullscreen", (event, flag) => {
|
||||
win.setFullScreen(flag);
|
||||
});
|
||||
|
||||
ipcMain.on("exit-app", (event, flag) => {
|
||||
ipcMain.on("exit-app", () => {
|
||||
win.close();
|
||||
app.quit();
|
||||
});
|
||||
@ -224,7 +315,7 @@ async function writeFileSafe(filename, contents) {
|
||||
}
|
||||
|
||||
ipcMain.handle("fs-job", async (event, job) => {
|
||||
const filenameSafe = job.filename.replace(/[^a-z\.\-_0-9]/i, "");
|
||||
const filenameSafe = job.filename.replace(/[^a-z\.\-_0-9]/gi, "_");
|
||||
const fname = path.join(storePath, filenameSafe);
|
||||
switch (job.type) {
|
||||
case "read": {
|
||||
@ -249,5 +340,45 @@ ipcMain.handle("fs-job", async (event, job) => {
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle("open-mods-folder", async () => {
|
||||
shell.openPath(modsPath);
|
||||
});
|
||||
|
||||
console.log("Loading mods ...");
|
||||
|
||||
function loadMods() {
|
||||
if (safeMode) {
|
||||
console.log("Safe Mode enabled for mods, skipping mod search");
|
||||
}
|
||||
console.log("Loading mods from", modsPath);
|
||||
let modFiles = safeMode
|
||||
? []
|
||||
: fs
|
||||
.readdirSync(modsPath)
|
||||
.filter(filename => filename.endsWith(".js"))
|
||||
.map(filename => path.join(modsPath, filename));
|
||||
|
||||
if (externalMod) {
|
||||
console.log("Adding external mod source:", externalMod);
|
||||
const externalModPaths = externalMod.split(",");
|
||||
modFiles = modFiles.concat(externalModPaths);
|
||||
}
|
||||
|
||||
return modFiles.map(filename => fs.readFileSync(filename, "utf8"));
|
||||
}
|
||||
|
||||
let mods = [];
|
||||
try {
|
||||
mods = loadMods();
|
||||
console.log("Loaded", mods.length, "mods");
|
||||
} catch (ex) {
|
||||
console.error("Failed to load mods");
|
||||
dialog.showErrorBox("Failed to load mods:", ex);
|
||||
}
|
||||
|
||||
ipcMain.handle("get-mods", async () => {
|
||||
return mods;
|
||||
});
|
||||
|
||||
wegame.init(isDev);
|
||||
wegame.listen();
|
||||
|
@ -13,6 +13,7 @@
|
||||
"electron": "^13.1.6"
|
||||
},
|
||||
"dependencies": {
|
||||
"async-lock": "^1.2.8"
|
||||
"async-lock": "^1.2.8",
|
||||
"electron-window-state": "^5.0.3"
|
||||
}
|
||||
}
|
||||
|
7
electron_wegame/preload.js
Normal file
7
electron_wegame/preload.js
Normal file
@ -0,0 +1,7 @@
|
||||
const { contextBridge, ipcRenderer } = require("electron");
|
||||
|
||||
contextBridge.exposeInMainWorld("ipcRenderer", {
|
||||
invoke: ipcRenderer.invoke.bind(ipcRenderer),
|
||||
on: ipcRenderer.on.bind(ipcRenderer),
|
||||
send: ipcRenderer.send.bind(ipcRenderer),
|
||||
});
|
@ -146,6 +146,14 @@ duplexer3@^0.1.4:
|
||||
resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2"
|
||||
integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=
|
||||
|
||||
electron-window-state@^5.0.3:
|
||||
version "5.0.3"
|
||||
resolved "https://registry.yarnpkg.com/electron-window-state/-/electron-window-state-5.0.3.tgz#4f36d09e3f953d87aff103bf010f460056050aa8"
|
||||
integrity sha512-1mNTwCfkolXl3kMf50yW3vE2lZj0y92P/HYWFBrb+v2S/pCka5mdwN3cagKm458A7NjndSwijynXgcLWRodsVg==
|
||||
dependencies:
|
||||
jsonfile "^4.0.0"
|
||||
mkdirp "^0.5.1"
|
||||
|
||||
electron@^13.1.6:
|
||||
version "13.1.6"
|
||||
resolved "https://registry.yarnpkg.com/electron/-/electron-13.1.6.tgz#6ecaf969255d62ce82cc0b5c948bf26e7dfb489b"
|
||||
@ -357,6 +365,18 @@ minimist@^1.2.5:
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
|
||||
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
|
||||
|
||||
minimist@^1.2.6:
|
||||
version "1.2.8"
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
|
||||
integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
|
||||
|
||||
mkdirp@^0.5.1:
|
||||
version "0.5.6"
|
||||
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6"
|
||||
integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==
|
||||
dependencies:
|
||||
minimist "^1.2.6"
|
||||
|
||||
mkdirp@^0.5.4:
|
||||
version "0.5.5"
|
||||
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
|
||||
|
@ -281,13 +281,10 @@ function gulptasksStandalone($, gulp) {
|
||||
);
|
||||
|
||||
gulp.task(taskPrefix + ".package.win64", cb => packageStandalone("win32", "x64", cb));
|
||||
gulp.task(taskPrefix + ".package.linux64", cb => packageStandalone("linux", "x64", cb));
|
||||
// gulp.task(taskPrefix + ".package.linux64", cb => packageStandalone("linux", "x64", cb));
|
||||
gulp.task(
|
||||
taskPrefix + ".build-from-windows",
|
||||
gulp.series(
|
||||
taskPrefix + ".prepare",
|
||||
gulp.parallel(taskPrefix + ".package.win64", taskPrefix + ".package.linux64")
|
||||
)
|
||||
gulp.series(taskPrefix + ".prepare", gulp.parallel(taskPrefix + ".package.win64"))
|
||||
);
|
||||
gulp.task(
|
||||
taskPrefix + ".build-from-darwin",
|
||||
|
@ -48,7 +48,7 @@ export class MainMenuState extends GameState {
|
||||
!G_GOG_VERSION;
|
||||
const showWegameFooter = G_WEGAME_VERSION;
|
||||
const hasMods = MODS.anyModsActive();
|
||||
const hasSteamBridge = !G_GOG_VERSION && !G_IS_STEAM_DEMO;
|
||||
const hasSteamBridge = !G_GOG_VERSION && !G_IS_STEAM_DEMO && !G_WEGAME_VERSION;
|
||||
|
||||
let showExternalLinks = true;
|
||||
|
||||
|
@ -141,9 +141,6 @@ export class PreloadState extends GameState {
|
||||
.then(() => this.app.analytics.initialize())
|
||||
.then(() => this.app.gameAnalytics.initialize())
|
||||
|
||||
.then(() => this.setStatus("Connecting to api", 15))
|
||||
.then(() => this.fetchDiscounts())
|
||||
|
||||
.then(() => this.setStatus("Initializing settings", 20))
|
||||
.then(() => {
|
||||
return this.app.settings.initialize();
|
||||
|
@ -30,14 +30,9 @@ export class SettingsState extends TextualGameState {
|
||||
: ""
|
||||
}
|
||||
|
||||
${
|
||||
G_WEGAME_VERSION
|
||||
? ""
|
||||
: `
|
||||
<button class="styledButton categoryButton manageMods">${T.mods.title}
|
||||
<span class="newBadge">${T.settings.newBadge}</span>
|
||||
</button>`
|
||||
}
|
||||
<button class="styledButton categoryButton manageMods">${T.mods.title}
|
||||
<span class="newBadge">${T.settings.newBadge}</span>
|
||||
</button>
|
||||
|
||||
|
||||
<div class="other ${G_CHINA_VERSION || G_WEGAME_VERSION ? "noabout" : ""}">
|
||||
|
@ -19,6 +19,7 @@ export class WegameSplashState extends GameState {
|
||||
onEnter() {
|
||||
setTimeout(
|
||||
() => {
|
||||
document.querySelector("body > .wrapper").remove();
|
||||
this.app.stateMgr.moveToState("PreloadState");
|
||||
},
|
||||
G_IS_DEV ? 1 : 6000
|
||||
|
@ -1,6 +1,5 @@
|
||||
steamPage:
|
||||
shortText: “唯一能限制您的,只有您的想象力!” 《图形工厂》
|
||||
是一款在无限拓展的地图上,通过建造各类工厂设施,来自动化生产与组合出愈加复杂图形的游戏。
|
||||
shortText: “唯一能限制您的,只有您的想象力!” 《图形工厂》 是一款在无限拓展的地图上,通过建造各类工厂设施,来自动化生产与组合出愈加复杂图形的游戏。
|
||||
discordLinkShort: 官方讨论区
|
||||
intro: |-
|
||||
“奇形怪状,放飞想象!”
|
||||
@ -154,15 +153,13 @@ dialogs:
|
||||
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>"
|
||||
createMarker:
|
||||
title: 创建地图标记
|
||||
desc:
|
||||
填写一个有意义的名称, 还可以同时包含一个形状的 <strong>短代码</strong> (您可以 <link>点击这里</link>
|
||||
desc: 填写一个有意义的名称, 还可以同时包含一个形状的 <strong>短代码</strong> (您可以 <link>点击这里</link>
|
||||
生成短代码)
|
||||
titleEdit: 编辑地图标记
|
||||
markerDemoLimit:
|
||||
@ -193,27 +190,27 @@ dialogs:
|
||||
title: 设置项目
|
||||
puzzleLoadFailed:
|
||||
title: 谜题载入失败
|
||||
desc: "谜题未能载入!"
|
||||
desc: 谜题未能载入!
|
||||
submitPuzzle:
|
||||
title: 提交谜题
|
||||
descName: "为您的谜题命名!"
|
||||
descIcon: "请输入唯一的短代码,它将作为您的谜题图标显示(您可以在<link>这里</link>生成,或者从以下随机推荐的图形中选择一个):"
|
||||
descName: 为您的谜题命名!
|
||||
descIcon: 请输入唯一的短代码,它将作为您的谜题图标显示(您可以在<link>这里</link>生成,或者从以下随机推荐的图形中选择一个):
|
||||
placeholderName: 谜题标题
|
||||
puzzleResizeBadBuildings:
|
||||
title: 无法重新定义尺寸
|
||||
desc: 由于某些设施将会超出区域范围,因此您无法将区域变得更小。
|
||||
puzzleLoadError:
|
||||
title: 谜题出错!
|
||||
desc: "谜题未能载入!"
|
||||
desc: 谜题未能载入!
|
||||
offlineMode:
|
||||
title: 离线模式
|
||||
desc: 无法访问服务器,所以游戏以离线模式进行。请确认您的互联网访问正常。
|
||||
puzzleDownloadError:
|
||||
title: 下载出错!
|
||||
desc: "无法下载谜题!"
|
||||
desc: 无法下载谜题!
|
||||
puzzleSubmitError:
|
||||
title: 提交出错!
|
||||
desc: "无法提交谜题!"
|
||||
desc: 无法提交谜题!
|
||||
puzzleSubmitOk:
|
||||
title: 谜题成功发布!
|
||||
desc: 恭喜!您的谜题已经成功发布,其他玩家已经可以玩到。您可以在“我的谜题”中找到自己已发布的谜题。
|
||||
@ -237,7 +234,7 @@ dialogs:
|
||||
desc: 此谜已被标记!
|
||||
puzzleReportError:
|
||||
title: 上报失败
|
||||
desc: "无法处理您的上报!"
|
||||
desc: 无法处理您的上报!
|
||||
puzzleLoadShortKey:
|
||||
title: 输入短代码
|
||||
desc: 输入谜题的短代码并载入。
|
||||
@ -358,22 +355,18 @@ ingame:
|
||||
interactiveTutorial:
|
||||
title: 新手教程
|
||||
hints:
|
||||
1_1_extractor:
|
||||
亲爱的玩家,欢迎来到<strong>《图形工厂》<strong>!在这里你可以通过创造各种图形设施与传送带模拟流水线生产,尽情发挥创造力,创办属于自己的工厂!<br><br>
|
||||
1_1_extractor: 亲爱的玩家,欢迎来到<strong>《图形工厂》<strong>!在这里你可以通过创造各种图形设施与传送带模拟流水线生产,尽情发挥创造力,创办属于自己的工厂!<br><br>
|
||||
在<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>
|
||||
@ -670,8 +663,7 @@ storyRewards:
|
||||
desc: 恭喜!您解锁了<strong>旋转机</strong>。它会顺时针将输入的<strong>图形旋转90度</strong>。
|
||||
reward_painter:
|
||||
title: 上色
|
||||
desc:
|
||||
恭喜!您解锁了<strong>上色器</strong>。开采一些颜色 (就像您开采图形一样),将其在上色器中与图形结合来将图形上色!
|
||||
desc: 恭喜!您解锁了<strong>上色器</strong>。开采一些颜色 (就像您开采图形一样),将其在上色器中与图形结合来将图形上色!
|
||||
<br>注意:如果您不幸患有色盲,可以在设置中启用<strong>色盲模式</strong>
|
||||
reward_mixer:
|
||||
title: 混合颜色
|
||||
@ -688,13 +680,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: 二级隧道
|
||||
@ -711,14 +701,12 @@ 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: 下一关
|
||||
@ -746,8 +734,7 @@ storyRewards:
|
||||
<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: 逻辑门
|
||||
@ -766,8 +753,7 @@ storyRewards:
|
||||
<strong>提示</strong>:可在设置中打开电线层教程!"
|
||||
reward_filter:
|
||||
title: 物品过滤器
|
||||
desc:
|
||||
恭喜!您解锁了<strong>物品过滤器</strong>!它会根据在电线层上输入的信号决定是从上面还是右边输出物品。<br><br>
|
||||
desc: 恭喜!您解锁了<strong>物品过滤器</strong>!它会根据在电线层上输入的信号决定是从上面还是右边输出物品。<br><br>
|
||||
您也可以输入开关值(1 / 0)信号来激活或者禁用它。
|
||||
reward_demo_end:
|
||||
title: 试玩结束
|
||||
@ -995,13 +981,10 @@ keybindings:
|
||||
showShapeTooltip: 显示图形输出提示。
|
||||
about:
|
||||
title: 关于游戏
|
||||
body: >-
|
||||
body: |-
|
||||
本游戏由托比亚斯开发,并且已经开源。<br><br>
|
||||
|
||||
这个游戏的开发获得了热情玩家的巨大支持。非常感谢!<br><br>
|
||||
|
||||
本游戏的音乐由佩普森制作——他是个很棒的伙伴。<br><br>
|
||||
|
||||
最后,我想感谢我最好的朋友尼可拉斯——如果没有他的《异星工厂》带给我的体验和启发,《图形工厂》将不会存在。
|
||||
changelog:
|
||||
title: 版本日志
|
||||
|
Loading…
Reference in New Issue
Block a user