Compare commits

...

9 Commits

Author SHA1 Message Date
tobspr b50ec8e3b8 Steam China improvements 2023-10-24 10:01:46 +02:00
tobspr 728c8118e1 Update appid 2023-10-13 10:54:25 +02:00
tobspr 3f4ed3143f Update translations 2023-10-13 10:53:05 +02:00
tobspr 138d2e15fb Adjustments for steam china 2023-10-13 10:31:03 +02:00
tobspr 9c1bac5afe Wegame adjustments 2023-09-13 11:25:22 +02:00
tobspr 34754964d3 Update translations 2023-09-13 09:52:47 +02:00
tobspr 6b10a79a10 Change wegame path 2023-09-13 09:51:46 +02:00
tobspr cd086f7fd0 Wegame adjustments 2/2 2023-09-13 09:51:45 +02:00
tobspr 74ab44df3a Wegame adjustments 1/2 2023-09-13 09:50:58 +02:00
48 changed files with 1617 additions and 215 deletions

1
electron_steam_isbn/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
mods/*.js

View File

@ -0,0 +1,13 @@
{
"folders": [
{
"path": "."
}
],
"settings": {
"files.exclude": {
"**/node_modules": true,
"**/typedefs_gen": true
}
}
}

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View File

@ -0,0 +1,344 @@
/* eslint-disable quotes,no-undef */
const { app, BrowserWindow, Menu, MenuItem, ipcMain, shell, dialog, session } = require("electron");
const path = require("path");
const url = require("url");
const fs = require("fs");
const steam = require("./steam");
const asyncLock = require("async-lock");
const windowStateKeeper = require("electron-window-state");
// 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-china", "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";
}
const mainWindowState = windowStateKeeper({
defaultWidth: 1000,
defaultHeight: 800,
});
win = new BrowserWindow({
x: mainWindowState.x,
y: mainWindowState.y,
width: mainWindowState.width,
height: mainWindowState.height,
show: false,
backgroundColor: "#222428",
useContentSize: false,
minWidth: 800,
minHeight: 600,
title: "图形工厂",
transparent: false,
icon: path.join(__dirname, "favicon" + faviconExtension),
// fullscreen: true,
autoHideMenuBar: !isDev,
webPreferences: {
nodeIntegration: false,
nodeIntegrationInWorker: false,
nodeIntegrationInSubFrames: false,
contextIsolation: true,
enableRemoteModule: false,
disableBlinkFeatures: "Auxclick",
webSecurity: true,
sandbox: true,
preload: path.join(__dirname, "preload.js"),
experimentalFeatures: false,
},
allowRunningInsecureContent: false,
});
mainWindowState.manage(win);
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();
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();
if (pth.startsWith("https://") || pth.startsWith("steam://")) {
shell.openExternal(pth);
}
});
win.on("closed", () => {
console.log("Window closed");
win = null;
});
if (isDev) {
menu = new Menu();
win.webContents.toggleDevTools();
const mainItem = new MenuItem({
label: "Toggle Dev Tools",
click: () => win.webContents.toggleDevTools(),
accelerator: "F12",
});
menu.append(mainItem);
const reloadItem = new MenuItem({
label: "Reload",
click: () => win.reload(),
accelerator: "F5",
});
menu.append(reloadItem);
const fullscreenItem = new MenuItem({
label: "Fullscreen",
click: () => win.setFullScreen(!win.isFullScreen()),
accelerator: "F11",
});
menu.append(fullscreenItem);
const mainMenu = new Menu();
mainMenu.append(
new MenuItem({
label: "shapez.io",
submenu: menu,
})
);
Menu.setApplicationMenu(mainMenu);
} else {
Menu.setApplicationMenu(null);
}
win.once("ready-to-show", () => {
win.show();
win.focus();
});
}
if (!app.requestSingleInstanceLock()) {
app.exit(0);
} else {
app.on("second-instance", () => {
// 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", () => {
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);
}
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));
await fs.promises.writeFile(filename, contents, "utf8");
return;
}
// first, write a temporary file (.tmp-XXX)
const tempName = filename + ".tmp-" + transactionId;
console.log(prefix, "Writing temporary file", niceFileName(tempName));
await fs.promises.writeFile(tempName, contents, "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)
);
await fs.promises.rename(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)
);
await fs.promises.rename(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
await fs.promises.unlink(backupFileName);
}
// rename the old file to the new backup file
console.log(prefix, "Moving", niceFileName(oldTemporaryName), "to the backup file location");
await fs.promises.rename(oldTemporaryName, backupFileName);
} catch (ex) {
console.error(prefix, "Failed to switch backup files:", ex);
}
});
}
ipcMain.handle("fs-job", async (event, job) => {
const filenameSafe = job.filename.replace(/[^a-z\.\-_0-9]/gi, "_");
const fname = path.join(storePath, filenameSafe);
switch (job.type) {
case "read": {
if (!fs.existsSync(fname)) {
// Special FILE_NOT_FOUND error code
return { error: "file_not_found" };
}
return await fs.promises.readFile(fname, "utf8");
}
case "write": {
await writeFileSafe(fname, job.contents);
return job.contents;
}
case "delete": {
await fs.promises.unlink(fname);
return;
}
default:
throw new Error("Unknown fs job: " + job.type);
}
});
ipcMain.handle("get-mods", async () => {
return [];
});
steam.init(isDev);
steam.listen();

View File

@ -0,0 +1,6 @@
Here you can place mods. Every mod should be a single file ending with ".js".
--- WARNING ---
Mods can potentially access to your filesystem.
Please only install mods from trusted sources and developers.
--- WARNING ---

View File

@ -0,0 +1,21 @@
{
"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": {},
"optionalDependencies": {
"shapez.io-private-artifacts": "github:tobspr/shapez.io-private-artifacts#abi-v99"
},
"dependencies": {
"async-lock": "^1.2.8",
"electron": "16.2.8",
"electron-window-state": "^5.0.3"
}
}

View 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),
});

View File

@ -0,0 +1,112 @@
const fs = require("fs");
const path = require("path");
const { ipcMain } = require("electron");
let greenworks = null;
let appId = null;
let initialized = false;
try {
greenworks = require("shapez.io-private-artifacts/steam/greenworks");
appId = parseInt(fs.readFileSync(path.join(__dirname, "steam_appid.txt"), "utf8"));
} catch (err) {
// greenworks is not installed
console.warn("Failed to load steam api:", err);
}
console.log("App ID:", appId);
function init(isDev) {
if (!greenworks) {
return;
}
if (!isDev) {
if (greenworks.restartAppIfNecessary(appId)) {
console.log("Restarting ...");
process.exit(0);
}
}
if (!greenworks.init()) {
console.log("Failed to initialize greenworks");
process.exit(1);
}
initialized = true;
}
function listen() {
ipcMain.handle("steam:is-initialized", isInitialized);
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:activate-achievement", activateAchievement);
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) {
return Promise.resolve(initialized);
}
function getAchievementNames(event) {
return new Promise((resolve, reject) => {
try {
const achievements = greenworks.getAchievementNames();
resolve(achievements);
} catch (err) {
reject(err);
}
});
}
function activateAchievement(event, id) {
return new Promise((resolve, reject) => {
greenworks.activateAchievement(
id,
() => resolve(),
err => reject(err)
);
});
}
module.exports = {
init,
listen,
};

View File

@ -0,0 +1 @@
1318690

View File

@ -0,0 +1,584 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@electron/get@^1.13.0":
version "1.13.1"
resolved "https://registry.yarnpkg.com/@electron/get/-/get-1.13.1.tgz#42a0aa62fd1189638bd966e23effaebb16108368"
integrity sha512-U5vkXDZ9DwXtkPqlB45tfYnnYBN8PePp1z/XDCupnSpdrxT8/ThCv9WCwPLf9oqiSGZTkH6dx2jDUPuoXpjkcA==
dependencies:
debug "^4.1.1"
env-paths "^2.2.0"
fs-extra "^8.1.0"
got "^9.6.0"
progress "^2.0.3"
semver "^6.2.0"
sumchecker "^3.0.1"
optionalDependencies:
global-agent "^3.0.0"
global-tunnel-ng "^2.7.1"
"@sindresorhus/is@^0.14.0":
version "0.14.0"
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea"
integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==
"@szmarczak/http-timer@^1.1.2":
version "1.1.2"
resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421"
integrity sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==
dependencies:
defer-to-connect "^1.0.1"
"@types/node@^14.6.2":
version "14.18.20"
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.20.tgz#268f028b36eaf51181c3300252f605488c4f0650"
integrity sha512-Q8KKwm9YqEmUBRsqJ2GWJDtXltBDxTdC4m5vTdXBolu2PeQh8LX+f6BTwU+OuXPu37fLxoN6gidqBmnky36FXA==
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==
boolean@^3.0.1:
version "3.0.2"
resolved "https://registry.yarnpkg.com/boolean/-/boolean-3.0.2.tgz#df1baa18b6a2b0e70840475e1d93ec8fe75b2570"
integrity sha512-RwywHlpCRc3/Wh81MiCKun4ydaIFyW5Ea6JbL6sRCVx5q5irDw7pMXBUFYF/jArQ6YrG36q0kpovc9P/Kd3I4g==
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==
cacheable-request@^6.0.0:
version "6.1.0"
resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912"
integrity sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==
dependencies:
clone-response "^1.0.2"
get-stream "^5.1.0"
http-cache-semantics "^4.0.0"
keyv "^3.0.0"
lowercase-keys "^2.0.0"
normalize-url "^4.1.0"
responselike "^1.0.2"
clone-response@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b"
integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=
dependencies:
mimic-response "^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"
config-chain@^1.1.11:
version "1.1.12"
resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.12.tgz#0fde8d091200eb5e808caf25fe618c02f48e4efa"
integrity sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA==
dependencies:
ini "^1.3.4"
proto-list "~1.2.1"
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=
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@^4.1.0, debug@^4.1.1:
version "4.3.1"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee"
integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==
dependencies:
ms "2.1.2"
decompress-response@^3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3"
integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=
dependencies:
mimic-response "^1.0.0"
defer-to-connect@^1.0.1:
version "1.1.3"
resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591"
integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==
define-properties@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1"
integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==
dependencies:
object-keys "^1.0.12"
detect-node@^2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.4.tgz#014ee8f8f669c5c58023da64b8179c083a28c46c"
integrity sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==
duplexer3@^0.1.4:
version "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@16.2.8:
version "16.2.8"
resolved "https://registry.yarnpkg.com/electron/-/electron-16.2.8.tgz#b7f2bd1184701e54a1bc902839d5a3ec95bb8982"
integrity sha512-KSOytY6SPLsh3iCziztqa/WgJyfDOKzCvNqku9gGIqSdT8CqtV66dTU1SOrKZQjRFLxHaF8LbyxUL1vwe4taqw==
dependencies:
"@electron/get" "^1.13.0"
"@types/node" "^14.6.2"
extract-zip "^1.0.3"
encodeurl@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=
end-of-stream@^1.1.0:
version "1.4.4"
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==
dependencies:
once "^1.4.0"
env-paths@^2.2.0:
version "2.2.1"
resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2"
integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==
es6-error@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d"
integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==
escape-string-regexp@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
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"
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"
fs-extra@^8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0"
integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==
dependencies:
graceful-fs "^4.2.0"
jsonfile "^4.0.0"
universalify "^0.1.0"
get-stream@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5"
integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==
dependencies:
pump "^3.0.0"
get-stream@^5.1.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3"
integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==
dependencies:
pump "^3.0.0"
global-agent@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/global-agent/-/global-agent-3.0.0.tgz#ae7cd31bd3583b93c5a16437a1afe27cc33a1ab6"
integrity sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==
dependencies:
boolean "^3.0.1"
es6-error "^4.1.1"
matcher "^3.0.0"
roarr "^2.15.3"
semver "^7.3.2"
serialize-error "^7.0.1"
global-tunnel-ng@^2.7.1:
version "2.7.1"
resolved "https://registry.yarnpkg.com/global-tunnel-ng/-/global-tunnel-ng-2.7.1.tgz#d03b5102dfde3a69914f5ee7d86761ca35d57d8f"
integrity sha512-4s+DyciWBV0eK148wqXxcmVAbFVPqtc3sEtUE/GTQfuU80rySLcMhUmHKSHI7/LDj8q0gDYI1lIhRRB7ieRAqg==
dependencies:
encodeurl "^1.0.2"
lodash "^4.17.10"
npm-conf "^1.1.3"
tunnel "^0.0.6"
globalthis@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.2.tgz#2a235d34f4d8036219f7e34929b5de9e18166b8b"
integrity sha512-ZQnSFO1la8P7auIOQECnm0sSuoMeaSq0EEdXMBFF2QJO4uNcwbyhSgG3MruWNbFTqCLmxVwGOl7LZ9kASvHdeQ==
dependencies:
define-properties "^1.1.3"
got@^9.6.0:
version "9.6.0"
resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85"
integrity sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==
dependencies:
"@sindresorhus/is" "^0.14.0"
"@szmarczak/http-timer" "^1.1.2"
cacheable-request "^6.0.0"
decompress-response "^3.3.0"
duplexer3 "^0.1.4"
get-stream "^4.1.0"
lowercase-keys "^1.0.1"
mimic-response "^1.0.1"
p-cancelable "^1.0.0"
to-readable-stream "^1.0.0"
url-parse-lax "^3.0.0"
graceful-fs@^4.1.6, graceful-fs@^4.2.0:
version "4.2.6"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee"
integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==
http-cache-semantics@^4.0.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390"
integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==
inherits@^2.0.3, 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.4:
version "1.3.8"
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c"
integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==
isarray@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
json-buffer@3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898"
integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=
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"
keyv@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9"
integrity sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==
dependencies:
json-buffer "3.0.0"
lodash@^4.17.10:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
lowercase-keys@^1.0.0, lowercase-keys@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f"
integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==
lowercase-keys@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479"
integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==
lru-cache@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==
dependencies:
yallist "^4.0.0"
matcher@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/matcher/-/matcher-3.0.0.tgz#bd9060f4c5b70aa8041ccc6f80368760994f30ca"
integrity sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==
dependencies:
escape-string-regexp "^4.0.0"
mimic-response@^1.0.0, mimic-response@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b"
integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==
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.1, 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.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
normalize-url@^4.1.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.0.tgz#453354087e6ca96957bd8f5baf753f5982142129"
integrity sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==
npm-conf@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/npm-conf/-/npm-conf-1.1.3.tgz#256cc47bd0e218c259c4e9550bf413bc2192aff9"
integrity sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw==
dependencies:
config-chain "^1.1.11"
pify "^3.0.0"
object-keys@^1.0.12:
version "1.1.1"
resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==
once@^1.3.1, once@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
dependencies:
wrappy "1"
p-cancelable@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc"
integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==
pend@~1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"
integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA=
pify@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176"
integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=
prepend-http@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897"
integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=
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@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
proto-list@~1.2.1:
version "1.2.4"
resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849"
integrity sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=
pump@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64"
integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==
dependencies:
end-of-stream "^1.1.0"
once "^1.3.1"
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"
responselike@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7"
integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=
dependencies:
lowercase-keys "^1.0.0"
roarr@^2.15.3:
version "2.15.4"
resolved "https://registry.yarnpkg.com/roarr/-/roarr-2.15.4.tgz#f5fe795b7b838ccfe35dc608e0282b9eba2e7afd"
integrity sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==
dependencies:
boolean "^3.0.1"
detect-node "^2.0.4"
globalthis "^1.0.1"
json-stringify-safe "^5.0.1"
semver-compare "^1.0.0"
sprintf-js "^1.1.2"
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==
semver-compare@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc"
integrity sha1-De4hahyUGrN+nvsXiPavxf9VN/w=
semver@^6.2.0:
version "6.3.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
semver@^7.3.2:
version "7.3.4"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97"
integrity sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==
dependencies:
lru-cache "^6.0.0"
serialize-error@^7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-7.0.1.tgz#f1360b0447f61ffb483ec4157c737fab7d778e18"
integrity sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==
dependencies:
type-fest "^0.13.1"
"shapez.io-private-artifacts@github:tobspr/shapez.io-private-artifacts#abi-v99":
version "0.1.0"
resolved "git+ssh://git@github.com/tobspr/shapez.io-private-artifacts.git#3293b20be26060fd36e9f00ded9ab5d0bdf57338"
sprintf-js@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.2.tgz#da1765262bf8c0f571749f2ad6c26300207ae673"
integrity sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==
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"
sumchecker@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/sumchecker/-/sumchecker-3.0.1.tgz#6377e996795abb0b6d348e9b3e1dfb24345a8e42"
integrity sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==
dependencies:
debug "^4.1.0"
to-readable-stream@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771"
integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==
tunnel@^0.0.6:
version "0.0.6"
resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c"
integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==
type-fest@^0.13.1:
version "0.13.1"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.13.1.tgz#0172cb5bce80b0bd542ea348db50c7e21834d934"
integrity sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==
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==
url-parse-lax@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c"
integrity sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=
dependencies:
prepend-http "^2.0.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=
wrappy@1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
yallist@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
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"

View File

@ -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.io", "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();

View File

@ -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"
}
}

View 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),
});

View File

@ -7,7 +7,7 @@ function init(isDev) {
try {
console.log("Step 2: Calling need restart app");
const need_restart = railsdk.RailNeedRestartAppForCheckingEnvironment(
2001639,
2002030,
[`--rail_render_pid=${process.pid}`] //,"--rail_debug_mode",
);
console.log("Step 3: Needs restart =", need_restart);
@ -58,6 +58,22 @@ function listen() {
return data;
});
ipcMain.handle("wegame:activate-achievement", async (event, data) => {
console.log("Unlock wegame achievement", data);
var manager = railsdk.RailAchievementHelper.CreatePlayerAchievement("0");
var result = manager.MakeAchievement(data);
if (result != railsdk.RailResult.kSuccess) {
console.error("Unlock wegame achievement", data, "failed with code", result);
return false;
}
manager.AsyncStoreAchievement().then(
() => console.log("Achievements stored successfully."),
err => console.error("Failed to unlock achievement async:", err)
);
return true;
});
}
module.exports = { init, listen };

View File

@ -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"

View File

@ -9,6 +9,7 @@
* chineseVersion?: boolean,
* wegameVersion?: boolean,
* steamDemo?: boolean,
* steamIsbnVersion?: boolean,
* gogVersion?: boolean
* }}>}
*/
@ -63,6 +64,14 @@ const BUILD_VARIANTS = {
wegameVersion: true,
},
},
"standalone-steam-isbn": {
standalone: true,
steamAppId: 1318690,
electronBaseDir: "electron_steam_isbn",
buildArgs: {
steamIsbnVersion: true,
},
},
"standalone-gog": {
standalone: true,
electronBaseDir: "electron_gog",

View File

@ -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",

View File

@ -10,6 +10,7 @@ module.exports = ({
standalone = false,
chineseVersion = false,
wegameVersion = false,
steamIsbnVersion = false,
steamDemo = false,
gogVersion = false,
}) => {
@ -39,6 +40,8 @@ module.exports = ({
G_APP_ENVIRONMENT: JSON.stringify("dev"),
G_CHINA_VERSION: JSON.stringify(chineseVersion),
G_WEGAME_VERSION: JSON.stringify(wegameVersion),
G_ISBN_VERSION: JSON.stringify(wegameVersion || steamIsbnVersion),
G_STEAM_ISBN_VERSION: JSON.stringify(steamIsbnVersion),
G_GOG_VERSION: JSON.stringify(gogVersion),
G_IS_DEV: "true",
G_IS_RELEASE: "false",

View File

@ -17,6 +17,7 @@ module.exports = ({
chineseVersion = false,
wegameVersion = false,
steamIsbnVersion = false,
steamDemo = false,
gogVersion = false,
}) => {
@ -28,6 +29,8 @@ module.exports = ({
G_CHINA_VERSION: JSON.stringify(chineseVersion),
G_WEGAME_VERSION: JSON.stringify(wegameVersion),
G_ISBN_VERSION: JSON.stringify(wegameVersion || steamIsbnVersion),
G_STEAM_ISBN_VERSION: JSON.stringify(steamIsbnVersion),
G_GOG_VERSION: JSON.stringify(gogVersion),
G_IS_RELEASE: environment === "prod" ? "true" : "false",
G_IS_STANDALONE: standalone ? "true" : "false",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

View File

@ -1191,11 +1191,9 @@
background: green;
cursor: pointer !important;
pointer-events: all;
@include S(border-radius, 4px);
overflow: hidden;
& {
background: #fff uiResource("wegame_isbn_rating.jpg") center center / contain no-repeat;
background: uiResource("wegame_isbn_rating.png") center center / contain no-repeat;
}
}
}

View File

@ -1,7 +1,7 @@
<!DOCTYPE html>
<html lang="en" style="--ui-scale: 1.33;">
<head>
<title>shapez Demo - Factory Automation Game</title>
<title>图形工厂</title>
<!-- mobile stuff -->
<meta name="format-detection" content="telephone=no" />

View File

@ -1,7 +1,7 @@
<!DOCTYPE html>
<html>
<head>
<title>shapez</title>
<title>图形工厂</title>
<!-- mobile stuff -->
<meta name="format-detection" content="telephone=no" />

View File

@ -153,7 +153,7 @@ export class Application {
Loader.linkAppAfterBoot(this);
if (G_WEGAME_VERSION) {
if (G_ISBN_VERSION) {
this.stateMgr.moveToState("WegameSplashState");
}

View File

@ -21,6 +21,8 @@ export const BUILD_OPTIONS = {
APP_ENVIRONMENT: G_APP_ENVIRONMENT,
CHINA_VERSION: G_CHINA_VERSION,
WEGAME_VERSION: G_WEGAME_VERSION,
ISBN_VERSION: G_ISBN_VERSION,
STEAM_ISBN_VERSION: G_STEAM_ISBN_VERSION,
IS_DEV: G_IS_DEV,
IS_RELEASE: G_IS_RELEASE,
IS_BROWSER: G_IS_BROWSER,

View File

@ -247,6 +247,16 @@ export function formatBigNumber(num, separator = T.global.decimalSeparator) {
if (num < 1000) {
return sign + "" + num;
} else {
if (G_ISBN_VERSION) {
if (num < 1000000) {
if (num < 10000) {
return sign + String(num).replace(".0", "").replace(".", separator);
} else {
return sign + round2Digits(num / 10000.0) + T.global.suffix.thousands;
}
}
}
let leadingDigits = num;
let suffix = "";
for (let suffixIndex = 0; suffixIndex < bigNumberSuffixTranslationKeys.length; ++suffixIndex) {
@ -694,7 +704,7 @@ const romanLiteralsCache = ["0"];
* @returns {string}
*/
export function getRomanNumber(number) {
if (G_WEGAME_VERSION) {
if (G_ISBN_VERSION) {
return String(number);
}
@ -753,7 +763,7 @@ export function getRomanNumber(number) {
* Returns the appropriate logo sprite path
*/
export function getLogoSprite() {
if (G_WEGAME_VERSION) {
if (G_ISBN_VERSION) {
return "logo_wegame.png";
}

View File

@ -188,9 +188,7 @@ export class HUDInteractiveTutorial extends BaseHUDPart {
onHintChanged(hintId) {
this.elementDescription.innerHTML = T.ingame.interactiveTutorial.hints[hintId];
document.documentElement.setAttribute("data-tutorial-step", hintId);
const folder = G_WEGAME_VERSION
? "interactive_tutorial.cn.noinline"
: "interactive_tutorial.noinline";
const folder = G_ISBN_VERSION ? "interactive_tutorial.cn.noinline" : "interactive_tutorial.noinline";
this.elementGif.style.backgroundImage =
"url('" + cachebust("res/ui/" + folder + "/" + hintId + ".gif") + "')";

View File

@ -233,7 +233,7 @@ export class HUDPinnedShapes extends BaseHUDPart {
// Show small info icon
let infoDetector;
if (!G_WEGAME_VERSION) {
if (!G_ISBN_VERSION) {
const infoButton = document.createElement("button");
infoButton.classList.add("infoButton");
element.appendChild(infoButton);

View File

@ -4,7 +4,7 @@ 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);
this.element.classList.toggle("china", G_CHINA_VERSION || G_ISBN_VERSION);
parent.appendChild(this.element);
}

View File

@ -123,7 +123,7 @@ export class HUDShop extends BaseHUDPart {
container.appendChild(pinButton);
let infoDetector;
if (!G_WEGAME_VERSION) {
if (!G_ISBN_VERSION) {
const viewInfoButton = document.createElement("button");
viewInfoButton.classList.add("showInfo");
container.appendChild(viewInfoButton);

View File

@ -45,7 +45,7 @@ export class HUDWaypoints extends BaseHUDPart {
*/
createElements(parent) {
// Create the helper box on the lower right when zooming out
if (this.root.app.settings.getAllSettings().offerHints && !G_WEGAME_VERSION) {
if (this.root.app.settings.getAllSettings().offerHints && !G_ISBN_VERSION) {
this.hintElement = makeDiv(
parent,
"ingame_HUD_Waypoints_Hint",
@ -121,7 +121,7 @@ export class HUDWaypoints extends BaseHUDPart {
}
// Catch mouse and key events
if (!G_WEGAME_VERSION) {
if (!G_ISBN_VERSION) {
this.root.camera.downPreHandler.add(this.onMouseDown, this);
this.root.keyMapper
.getBinding(KEYMAPPINGS.navigation.createMarker)

View File

@ -5,7 +5,7 @@ import { WEB_STEAM_SSO_AUTHENTICATED } from "../../core/steam_sso";
import { enumHubGoalRewards } from "../tutorial_goals";
export const finalGameShape = "RuCw--Cw:----Ru--";
const chinaShapes = G_WEGAME_VERSION || G_CHINA_VERSION;
const chinaShapes = G_ISBN_VERSION || G_CHINA_VERSION;
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////

View File

@ -65,7 +65,7 @@ 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;
const chinaShapes = G_ISBN_VERSION || G_CHINA_VERSION;
const upgradesCache = {};
@ -362,7 +362,7 @@ export class RegularGameMode extends GameMode {
}
if (this.root.app.settings.getAllSettings().offerHints) {
if (!G_WEGAME_VERSION) {
if (!G_ISBN_VERSION) {
this.additionalHudParts.tutorialHints = HUDPartTutorialHints;
}
this.additionalHudParts.interactiveTutorial = HUDInteractiveTutorial;

2
src/js/globals.d.ts vendored
View File

@ -20,6 +20,8 @@ declare const G_IS_RELEASE: boolean;
declare const G_CHINA_VERSION: boolean;
declare const G_WEGAME_VERSION: boolean;
declare const G_ISBN_VERSION: boolean;
declare const G_STEAM_ISBN_VERSION: boolean;
declare const G_GOG_VERSION: boolean;
declare const shapez: any;

View File

@ -12,7 +12,7 @@ export const LANGUAGES = {
"zh-CN": {
// simplified chinese
name: "简体中文",
data: G_WEGAME_VERSION
data: G_ISBN_VERSION
? require("./built-temp/base-zh-CN-ISBN.json")
: require("./built-temp/base-zh-CN.json"),
code: "zh",

View File

@ -114,7 +114,7 @@ export class ShapezGameAnalytics extends GameAnalyticsInterface {
initialize() {
this.syncKey = null;
if (G_WEGAME_VERSION) {
if (G_ISBN_VERSION) {
return;
}
@ -221,7 +221,7 @@ export class ShapezGameAnalytics extends GameAnalyticsInterface {
* @returns {Promise<any>}
*/
sendToApi(endpoint, data) {
if (G_WEGAME_VERSION) {
if (G_ISBN_VERSION) {
return Promise.resolve();
}
@ -263,7 +263,7 @@ export class ShapezGameAnalytics extends GameAnalyticsInterface {
* @param {string} value
*/
sendGameEvent(category, value) {
if (G_WEGAME_VERSION) {
if (G_ISBN_VERSION) {
return;
}

View File

@ -33,9 +33,9 @@ export class StorageImplBrowser extends StorageInterface {
window.localStorage.setItem("storage_availability_test", "1");
window.localStorage.removeItem("storage_availability_test");
} catch (e) {
alert(
"It seems we don't have permission to write to local storage! Please update your browsers settings or use a different browser!"
);
// alert(
// "It seems we don't have permission to write to local storage! Please update your browsers settings or use a different browser!"
// );
reject(LOCAL_STORAGE_NO_WRITE_PERMISSION);
return;
}

View File

@ -0,0 +1,141 @@
/* typehints:start */
import { Application } from "../../application";
import { GameRoot } from "../../game/root";
/* typehints:end */
import { createLogger } from "../../core/logging";
import { ACHIEVEMENTS, AchievementCollection, AchievementProviderInterface } from "../achievement_provider";
const logger = createLogger("achievements/wegame");
const ACHIEVEMENT_IDS = {
[ACHIEVEMENTS.belt500Tiles]: "belt_500_tiles",
[ACHIEVEMENTS.blueprint100k]: "blueprint_100k",
[ACHIEVEMENTS.blueprint1m]: "blueprint_1m",
[ACHIEVEMENTS.completeLvl26]: "complete_lvl_26",
[ACHIEVEMENTS.cutShape]: "cut_shape",
[ACHIEVEMENTS.darkMode]: "dark_mode",
[ACHIEVEMENTS.destroy1000]: "destroy_1000",
[ACHIEVEMENTS.irrelevantShape]: "irrelevant_shape",
[ACHIEVEMENTS.level100]: "level_100",
[ACHIEVEMENTS.level50]: "level_50",
[ACHIEVEMENTS.logoBefore18]: "logo_before_18",
[ACHIEVEMENTS.mam]: "mam",
[ACHIEVEMENTS.mapMarkers15]: "map_markers_15",
[ACHIEVEMENTS.openWires]: "open_wires",
[ACHIEVEMENTS.oldLevel17]: "old_level_17",
[ACHIEVEMENTS.noBeltUpgradesUntilBp]: "no_belt_upgrades_until_bp",
[ACHIEVEMENTS.noInverseRotater]: "no_inverse_rotator", // [sic]
[ACHIEVEMENTS.paintShape]: "paint_shape",
[ACHIEVEMENTS.place5000Wires]: "place_5000_wires",
[ACHIEVEMENTS.placeBlueprint]: "place_blueprint",
[ACHIEVEMENTS.placeBp1000]: "place_bp_1000",
[ACHIEVEMENTS.play1h]: "play_1h",
[ACHIEVEMENTS.play10h]: "play_10h",
[ACHIEVEMENTS.play20h]: "play_20h",
[ACHIEVEMENTS.produceLogo]: "produce_logo",
[ACHIEVEMENTS.produceMsLogo]: "produce_ms_logo",
[ACHIEVEMENTS.produceRocket]: "produce_rocket",
[ACHIEVEMENTS.rotateShape]: "rotate_shape",
[ACHIEVEMENTS.speedrunBp30]: "speedrun_bp_30",
[ACHIEVEMENTS.speedrunBp60]: "speedrun_bp_60",
[ACHIEVEMENTS.speedrunBp120]: "speedrun_bp_120",
[ACHIEVEMENTS.stack4Layers]: "stack_4_layers",
[ACHIEVEMENTS.stackShape]: "stack_shape",
[ACHIEVEMENTS.store100Unique]: "store_100_unique",
[ACHIEVEMENTS.storeShape]: "store_shape",
[ACHIEVEMENTS.throughputBp25]: "throughput_bp_25",
[ACHIEVEMENTS.throughputBp50]: "throughput_bp_50",
[ACHIEVEMENTS.throughputLogo25]: "throughput_logo_25",
[ACHIEVEMENTS.throughputLogo50]: "throughput_logo_50",
[ACHIEVEMENTS.throughputRocket10]: "throughput_rocket_10",
[ACHIEVEMENTS.throughputRocket20]: "throughput_rocket_20",
[ACHIEVEMENTS.trash1000]: "trash_1000",
[ACHIEVEMENTS.unlockWires]: "unlock_wires",
[ACHIEVEMENTS.upgradesTier5]: "upgrades_tier_5",
[ACHIEVEMENTS.upgradesTier8]: "upgrades_tier_8",
};
export class WegameAchievementProvider extends AchievementProviderInterface {
/** @param {Application} app */
constructor(app) {
super(app);
this.initialized = false;
this.collection = new AchievementCollection(this.activate.bind(this));
if (G_IS_DEV) {
for (let key in ACHIEVEMENT_IDS) {
assert(this.collection.map.has(key), "Key not found in collection: " + key);
}
}
logger.log("Collection created with", this.collection.map.size, "achievements");
}
/** @returns {boolean} */
hasAchievements() {
return true;
}
/**
* @param {GameRoot} root
* @returns {Promise<void>}
*/
onLoad(root) {
this.root = root;
try {
this.collection = new AchievementCollection(this.activate.bind(this));
this.collection.initialize(root);
logger.log("Initialized", this.collection.map.size, "relevant achievements");
return Promise.resolve();
} catch (err) {
logger.error("Failed to initialize the collection");
return Promise.reject(err);
}
}
/** @returns {Promise<void>} */
initialize() {
if (!G_IS_STANDALONE) {
logger.warn("Wegame unavailable. Achievements won't sync.");
return Promise.resolve();
}
if (!G_WEGAME_VERSION) {
return Promise.resolve();
}
this.initialized = true;
return Promise.resolve();
}
/**
* @param {string} key
* @returns {Promise<void>}
*/
activate(key) {
let promise;
if (!G_WEGAME_VERSION) {
return Promise.resolve();
}
if (!this.initialized) {
promise = Promise.resolve();
} else {
promise = ipcRenderer.invoke("wegame:activate-achievement", ACHIEVEMENT_IDS[key]);
}
return promise
.then(() => {
logger.log("Achievement activated:", key);
})
.catch(err => {
logger.error("Failed to activate achievement:", key, err);
throw err;
});
}
}

View File

@ -4,6 +4,7 @@ import { createLogger } from "../../core/logging";
import { StorageImplElectron } from "./storage";
import { SteamAchievementProvider } from "./steam_achievement_provider";
import { PlatformWrapperInterface } from "../wrapper";
import { WegameAchievementProvider } from "./wegame_achievement_provider";
const logger = createLogger("electron-wrapper");
@ -24,7 +25,10 @@ export class PlatformWrapperImplElectron extends PlatformWrapperImplBrowser {
this.app.ticker.frameEmitted.add(this.steamOverlayFixRedrawCanvas, this);
this.app.storage = new StorageImplElectron(this);
this.app.achievementProvider = new SteamAchievementProvider(this.app);
this.app.achievementProvider = G_WEGAME_VERSION
? new WegameAchievementProvider(this.app)
: new SteamAchievementProvider(this.app);
return this.initializeAchievementProvider()
.then(() => this.initializeDlcStatus())
@ -70,7 +74,7 @@ export class PlatformWrapperImplElectron extends PlatformWrapperImplBrowser {
}
initializeDlcStatus() {
if (G_WEGAME_VERSION) {
if (G_ISBN_VERSION) {
return Promise.resolve();
}

View File

@ -140,7 +140,7 @@ function initializeSettings() {
options: Object.keys(LANGUAGES),
valueGetter: key => key,
textGetter: key => LANGUAGES[key].name,
category: G_CHINA_VERSION || G_WEGAME_VERSION ? null : enumCategories.general,
category: G_CHINA_VERSION || G_ISBN_VERSION ? null : enumCategories.general,
restartRequired: true,
changeCb: (app, id) => null,
magicValue: "auto-detect",

View File

@ -39,21 +39,21 @@ export class MainMenuState extends GameState {
}
getInnerHTML() {
const showLanguageIcon = !G_CHINA_VERSION && !G_WEGAME_VERSION;
const showLanguageIcon = !G_CHINA_VERSION && !G_ISBN_VERSION;
const showExitAppButton = G_IS_STANDALONE;
const showPuzzleDLC =
!G_WEGAME_VERSION &&
!G_ISBN_VERSION &&
(G_IS_STANDALONE || WEB_STEAM_SSO_AUTHENTICATED) &&
!G_IS_STEAM_DEMO &&
!G_GOG_VERSION;
const showWegameFooter = G_WEGAME_VERSION;
const showWegameFooter = G_ISBN_VERSION;
const hasMods = MODS.anyModsActive();
const hasSteamBridge = !G_GOG_VERSION && !G_IS_STEAM_DEMO;
const hasSteamBridge = !G_GOG_VERSION && !G_IS_STEAM_DEMO && !G_ISBN_VERSION;
let showExternalLinks = true;
if (G_IS_STANDALONE) {
if (G_WEGAME_VERSION || G_CHINA_VERSION) {
if (G_ISBN_VERSION || G_CHINA_VERSION) {
showExternalLinks = false;
}
} else {
@ -262,8 +262,8 @@ export class MainMenuState extends GameState {
抵制不良游戏,拒绝盗版游戏注意自我保护,谨防受骗上当<br>
适度游戏益脑,沉迷游戏伤身合理安排时间,享受健康生活
</div>
<div class="rating"></div>
</div>
`
: `
@ -382,7 +382,7 @@ export class MainMenuState extends GameState {
closeLoader();
this.dialogs.showWarning(
T.dialogs.importSavegameError.title,
T.dialogs.importSavegameError.text + "<br><br>" + err
T.dialogs.importSavegameError.text
);
return;
}
@ -402,7 +402,7 @@ export class MainMenuState extends GameState {
closeLoader();
this.dialogs.showWarning(
T.dialogs.importSavegameError.title,
T.dialogs.importSavegameError.text + ":<br><br>" + err
T.dialogs.importSavegameError.text
);
}
);
@ -410,7 +410,7 @@ export class MainMenuState extends GameState {
reader.addEventListener("error", error => {
this.dialogs.showWarning(
T.dialogs.importSavegameError.title,
T.dialogs.importSavegameError.text + ":<br><br>" + error
T.dialogs.importSavegameError.text
);
});
reader.readAsText(file, "utf-8");
@ -433,10 +433,7 @@ export class MainMenuState extends GameState {
this.dialogs.initializeToElement(dialogsElement);
if (payload.loadError) {
this.dialogs.showWarning(
T.dialogs.gameLoadFailure.title,
T.dialogs.gameLoadFailure.text + "<br><br>" + payload.loadError
);
this.dialogs.showWarning(T.dialogs.gameLoadFailure.title, T.dialogs.gameLoadFailure.text);
}
if (G_IS_DEV && globalConfig.debug.testPuzzleMode) {
@ -540,10 +537,12 @@ export class MainMenuState extends GameState {
.setAttribute("data-savegames", String(this.savedGames.length));
// Mods
this.trackClicks(
makeButton(outerDiv, ["modsButton", "styledButton"], T.mods.title),
this.onModsClicked
);
if (!G_STEAM_ISBN_VERSION) {
this.trackClicks(
makeButton(outerDiv, ["modsButton", "styledButton"], T.mods.title),
this.onModsClicked
);
}
buttonContainer.appendChild(outerDiv);
}
@ -715,7 +714,7 @@ export class MainMenuState extends GameState {
downloadButton.setAttribute("aria-label", "Download");
elem.appendChild(downloadButton);
if (!G_WEGAME_VERSION) {
if (!G_ISBN_VERSION) {
const renameButton = document.createElement("button");
renameButton.classList.add("styledButton", "renameGame");
renameButton.setAttribute("aria-label", "Rename Savegame");
@ -788,10 +787,7 @@ export class MainMenuState extends GameState {
})
.catch(err => {
this.dialogs.showWarning(
T.dialogs.gameLoadFailure.title,
T.dialogs.gameLoadFailure.text + "<br><br>" + err
);
this.dialogs.showWarning(T.dialogs.gameLoadFailure.title, T.dialogs.gameLoadFailure.text);
});
});
}
@ -868,7 +864,7 @@ export class MainMenuState extends GameState {
err => {
this.dialogs.showWarning(
T.dialogs.savegameDeletionError.title,
T.dialogs.savegameDeletionError.text + "<br><br>" + err
T.dialogs.savegameDeletionError.text
);
}
);

View File

@ -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();
@ -158,7 +155,7 @@ export class PreloadState extends GameState {
.then(() => this.setStatus("Initializing language", 25))
.then(() => {
if (G_CHINA_VERSION || G_WEGAME_VERSION) {
if (G_CHINA_VERSION || G_ISBN_VERSION) {
return this.app.settings.updateLanguage("zh-CN");
}
@ -222,7 +219,7 @@ export class PreloadState extends GameState {
return;
}
if (G_CHINA_VERSION || G_WEGAME_VERSION) {
if (G_CHINA_VERSION || G_ISBN_VERSION) {
return;
}
@ -289,7 +286,7 @@ export class PreloadState extends GameState {
}
update() {
if (G_CHINA_VERSION || G_WEGAME_VERSION) {
if (G_CHINA_VERSION || G_ISBN_VERSION) {
return;
}
const now = performance.now();
@ -323,7 +320,7 @@ export class PreloadState extends GameState {
setStatus(text, progress) {
logger.log("✅ " + text);
if (G_CHINA_VERSION || G_WEGAME_VERSION) {
if (G_CHINA_VERSION || G_ISBN_VERSION) {
return Promise.resolve();
}
this.currentStatus = text;
@ -346,15 +343,11 @@ export class PreloadState extends GameState {
</div>
<div class="failureInner">
<div class="errorHeader">
Failed to initialize application!
</div>
<div class="errorMessage">
${this.currentStatus} failed:<br/>
${text}
应用初始化失败
</div>
<div class="lower">
<button class="resetApp styledButton">Reset App</button>
<i>Build ${G_BUILD_VERSION} @ ${G_BUILD_COMMIT_HASH}</i>
<i>建造${G_BUILD_VERSION} @ ${G_BUILD_COMMIT_HASH}</i>
</div>
</div>
`;
@ -362,9 +355,6 @@ export class PreloadState extends GameState {
this.htmlElement.classList.add("failure");
this.htmlElement.appendChild(subElement);
const resetBtn = subElement.querySelector("button.resetApp");
this.trackClicks(resetBtn, this.showResetConfirm);
this.hintsText.remove();
}

View File

@ -31,19 +31,20 @@ export class SettingsState extends TextualGameState {
}
${
G_WEGAME_VERSION
// Disable mods for steam china
G_STEAM_ISBN_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" : ""}">
<div class="other ${G_CHINA_VERSION || G_ISBN_VERSION ? "noabout" : ""}">
${
G_CHINA_VERSION || G_WEGAME_VERSION
G_CHINA_VERSION || G_ISBN_VERSION
? ""
: `
<button class="styledButton about">${T.about.title}</button>
@ -52,7 +53,7 @@ export class SettingsState extends TextualGameState {
`
}
<div class="versionbar">
${G_WEGAME_VERSION ? "" : `<div class="buildVersion">${T.global.loading} ...</div>`}
${G_ISBN_VERSION ? "" : `<div class="buildVersion">${T.global.loading} ...</div>`}
</div>
</div>
</div>
@ -122,7 +123,7 @@ export class SettingsState extends TextualGameState {
onEnter(payload) {
this.renderBuildText();
if (!G_CHINA_VERSION && !G_WEGAME_VERSION) {
if (!G_CHINA_VERSION && !G_ISBN_VERSION) {
this.trackClicks(this.htmlElement.querySelector(".about"), this.onAboutClicked, {
preventDefault: false,
});

View File

@ -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

View File

@ -1,11 +1,10 @@
steamPage:
shortText: “唯一能限制您的,只有您的想象力!” 《异形工厂》Shapez.io
是一款在无限拓展的地图上,通过建造各类工厂设施,来自动化生产与组合出愈加复杂图形的游戏。
discordLinkShort: 官方 Discord 服务器
shortText: “唯一能限制您的,只有您的想象力!” 《图形工厂》 是一款在无限拓展的地图上,通过建造各类工厂设施,来自动化生产与组合出愈加复杂图形的游戏。
discordLinkShort: 官方讨论区
intro: |-
“奇形怪状,放飞想象!”
“自动生产,尽情创造!”
异形工厂》Shapez.io是一款能让您尽情发挥创造力充分享受思维乐趣的IO游戏。
图形工厂》是一款能让您尽情发挥创造力,充分享受思维乐趣的益智游戏。
游戏很轻松,只需建造工厂,布好设施,无需操作即能自动创造出各种各样的几何图形。
挑战很烧脑,随着等级提升,需要创造的图形将会越来越复杂,同时您还需要在无限扩展的地图中持续扩建优化您的工厂。
以为这就是全部了吗? 不!图形的生产需求将会指数性增长,持续的扩大规模和熵增带来的无序,将会是令人头痛的问题!
@ -13,8 +12,8 @@ steamPage:
然后,还有吗? 当然,唯有思维,方能无限。
欢迎免费体验试玩版:“让您的想象力插上翅膀!”
和最聪明的玩家一起挑战,请访问 Steam 游戏商城购买《异形工厂》Shapez.io的完整版,
what_others_say: 来看看玩家们对《异形工厂》Shapez.io的评价
和最聪明的玩家一起挑战,请购买《图形工厂》的完整版,
what_others_say: 来看看玩家们对《图形工厂》的评价
nothernlion_comment: 非常棒的有游戏,我的游戏过程充满乐趣,不觉时间飞逝。
notch_comment: 哦,天哪!我真得该去睡了!但我想我刚刚搞定如何在游戏里面制造一台电脑出来。
steam_review_comment: 这是一个不知不觉偷走你时间,但你并不会想要追回的游戏。非常烧脑的挑战,让我这样的完美主义者停不下来,总是希望可以再高效一些。
@ -24,7 +23,7 @@ global:
thousandsDivider: ","
decimalSeparator: .
suffix:
thousands:
thousands:
millions: 百万
billions: 亿万
trillions:
@ -50,24 +49,24 @@ global:
shift: SHIFT键
space: 空格键
loggingIn: 登录
loadingResources: Downloading additional resources (<percentage> %)
loadingResources: 正在下载资源(<percentage> %
discount: -<percentage>%
discountSummerSale: SPECIAL PROMOTION! Offer ends 7 July
discountSummerSale: 特别促销优惠将于7月7日结束
demoBanners:
title: 试玩版
intro: 购买完整版以解锁所有游戏内容!
playtimeDisclaimer: The full version contains more than <strong>20 hours of content</strong>.
playerCount: <playerCount> players like you are currently playing shapez on Steam
untilEndOfDemo: Until end of demo
playtimeDisclaimerDownload: You can continue your savegame in the full version!
Click <strong>here</strong> to download your savegame.
titleV2: "Play the full version now for:"
playtimeDisclaimer: 完整版本包括超过<strong>20小时游戏内容</strong>。
playerCount: 目前有超过<playerCount>玩家正在一起玩图形工厂
untilEndOfDemo: 直到DEMO结束
playtimeDisclaimerDownload: 你可以在完成版中继续游戏进度!
点击<strong>这里</strong>下载你的进度。
titleV2: "游玩完整版本:"
mainMenu:
play: 开始游戏
changelog: 更新日志
importSavegame: 读取存档
openSourceHint: 本游戏已开源!
discordLink: 官方Discord服务器
discordLink: 官方讨论区
helpTranslate: 帮助我们翻译!
browserWarning: 很抱歉, 本游戏在当前浏览器上可能运行缓慢! 使用 谷歌浏览器 或者购买完整版以得到更好的体验。
savegameLevel: 第<x>关
@ -75,22 +74,21 @@ mainMenu:
continue: 继续游戏
newGame: 新游戏
madeBy: 作者:<author-link>
subreddit: Reddit
subreddit: 讨论区
savegameUnnamed: 存档未命名
puzzleMode: 谜题模式
back: 返回
puzzleDlcText: 新增谜题模式将带给您更多的游戏乐趣!
puzzleDlcWishlist: 添加心愿单!
puzzleDlcViewNow: View Dlc
puzzleDlcViewNow: 查看资料片!
mods:
title: Active Mods
warningPuzzleDLC: Playing the Puzzle DLC is not possible with mods. Please
disable all mods to play the DLC.
playingFullVersion: You are now playing the full version!
logout: Logout
noActiveSavegames: No active savegames found - Click play to start a new game!
playFullVersionV2: Bough shapez on Steam? Play the full version in your Browser!
playFullVersionStandalone: You can now also play the full version in your Browser!
title: 激活模组
warningPuzzleDLC: DLC谜题挑战者与模组不兼容。请屏蔽所有模组再游玩DLC。
playingFullVersion: 你现在正在游玩完整版本游戏!
logout: 登出
noActiveSavegames: 未找到游戏存档 - 点击开始游戏开始一局新游戏!
playFullVersionV2: 在浏览器中也能游玩完整版本!
playFullVersionStandalone: 在浏览器中也能游玩完整版本!
dialogs:
buttons:
ok: 确认
@ -99,7 +97,7 @@ dialogs:
later: 以后
restart: 重新开始
reset: 重置
getStandalone: 获取完整版
getStandalone: 获取完整版
deleteGame: 我没疯!我知道我在做什么!
viewUpdate: 查看更新
showUpgrades: 显示设施升级
@ -109,19 +107,19 @@ dialogs:
playOffline: 离线游戏
importSavegameError:
title: 读取错误
text: 未能读取您的存档
text: 未能读取您的存档
importSavegameSuccess:
title: 读取成功
text: 存档被成功读取
text: 存档被成功读取
gameLoadFailure:
title: 存档损坏
text: 未能读取您的存档
text: 未能读取您的存档
confirmSavegameDelete:
title: 确认删除
text: 您确定要删除这个游戏吗?<br><br> '<savegameName>' 等级 <savegameLevel><br><br> 该操作无法回退!
text: 您确定要删除这个游戏吗?<br><br> "<savegameName>" 等级 <savegameLevel><br><br> 该操作无法回退!
savegameDeletionError:
title: 删除失败
text: 未能删除您的存档
text: 未能删除您的存档
restartRequired:
title: 需要重启游戏
text: 您需要重启游戏以应用变更的设置。
@ -179,24 +177,24 @@ 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: 谜题未能载入
desc: 谜题未能载入
submitPuzzle:
title: 提交谜题
descName: 为您的谜题命名
descName: 为您的谜题命名
descIcon: 请输入唯一的短代码,它将作为您的谜题图标显示(您可以在<link>这里</link>生成,或者从以下随机推荐的图形中选择一个):
placeholderName: 谜题标题
puzzleResizeBadBuildings:
@ -204,16 +202,16 @@ dialogs:
desc: 由于某些设施将会超出区域范围,因此您无法将区域变得更小。
puzzleLoadError:
title: 谜题出错!
desc: 谜题未能载入
desc: 谜题未能载入
offlineMode:
title: 离线模式
desc: 无法访问服务器,所以游戏以离线模式进行。请确认您的互联网访问正常。
puzzleDownloadError:
title: 下载出错!
desc: 无法下载谜题
desc: 无法下载谜题
puzzleSubmitError:
title: 提交出错!
desc: 无法提交谜题
desc: 无法提交谜题
puzzleSubmitOk:
title: 谜题成功发布!
desc: 恭喜!您的谜题已经成功发布,其他玩家已经可以玩到。您可以在“我的谜题”中找到自己已发布的谜题。
@ -237,7 +235,7 @@ dialogs:
desc: 此谜已被标记!
puzzleReportError:
title: 上报失败
desc: 无法处理您的上报
desc: 无法处理您的上报
puzzleLoadShortKey:
title: 输入短代码
desc: 输入谜题的短代码并载入。
@ -245,25 +243,19 @@ dialogs:
title: 删除谜题吗?
desc: 您是否确认删除 '<title>'?删除谜题后将无法恢复!
modsDifference:
title: Mod Warning
desc: The currently installed mods differ from the mods the savegame was created
with. This might cause the savegame to break or not load at all. Are
you sure you want to continue?
missingMods: Missing Mods
newMods: Newly installed Mods
title: 模组警告
desc: 当前安装的模组与存档中的模组不同。这可能会导致存档无法读取或损坏。您确定要继续吗?
missingMods: 模组缺失
newMods: 最新安装的模组
resourceLoadFailed:
title: Resources failed to load
demoLinkText: shapez demo on Steam
descWeb: "One ore more resources could not be loaded. Make sure you have a
stable internet connection and try again. If this still doesn't
work, make sure to also disable any browser extensions (including
adblockers).<br><br> As an alternative, you can also play the
<demoOnSteamLinkText>. <br><br> Error Message:"
descSteamDemo: "One ore more resources could not be loaded. Try restarting the
game - If that does not help, try reinstalling and verifying the
game files via Steam. <br><br> Error Message:"
title: 资源加载错误
demoLinkText: DEMO
descWeb:
"一个或更多资源无法加载。请确保您的网络处于正常连接状态。如果仍旧出现问题,请确保已关闭所有浏览器插件(包括屏蔽广告插件)。<br><br>
作为一种选择,您也可以玩<demoOnSteamLinkText>。 <br><br>错误信息:"
descSteamDemo: "一个或更多资源无法加载。可以试着重启游戏 - 如果依旧不行,请试下重新安装或验证完整性。 <br><br>错误信息:"
steamSsoError:
title: Full Version Logout
title: 登出完成版本
desc: You have been logged out from the Full Browser Version since either your
network connection is unstable or you are playing on another
device.<br><br> Please make sure you don't have shapez open in any
@ -358,7 +350,9 @@ ingame:
interactiveTutorial:
title: 新手教程
hints:
1_1_extractor: 在<strong>圆形</strong>上放置一个<strong>开采器</strong>来获取圆形!<br><br>提示:<strong>按下鼠标左键</strong>选中<strong>开采器</strong>
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>
@ -400,7 +394,7 @@ ingame:
watermark:
title: 试玩版
desc: 点击这里了解完整版内容
get_on_steam: 在Steam商城购买
get_on_steam: 购买完整版!
standaloneAdvantages:
no_thanks: 不需要,谢谢
points:
@ -426,11 +420,11 @@ ingame:
title: 成就
desc: 挑战全成就解锁!
mods:
title: Modding support!
desc: Over 80 mods available!
titleV2: "Get the full version now on Steam to unlock:"
titleExpiredV2: Demo completed!
titleEnjoyingDemo: Enjoy the demo?
title: 支持模组!
desc: 超过80个可用模组
titleV2: "获取完整版解锁:"
titleExpiredV2: DEMO已通关
titleEnjoyingDemo: DEMO是否让您满意
puzzleEditorSettings:
zoneTitle: 区域
zoneWidth: 宽度
@ -452,7 +446,7 @@ ingame:
- 6.谜题发布后,<strong>所有设施都将被拆除</strong>,除了<strong>常量生成器</strong>和<strong>目标接收器</strong>。然后,等着其他玩家对您创造的谜题发起挑战吧!
puzzleCompletion:
title: 谜题挑战成功!
titleLike: 喜欢此谜题的话,请为它点赞
titleLike: 喜欢此谜题的话,请为它点赞
titleRating: 您觉得此谜题难度如何?
titleRatingDesc: 您的评分将帮助作者在未来创作出更好的谜题!
continueBtn: 继续游戏
@ -542,16 +536,16 @@ buildings:
deliver: 交付
toUnlock: 解锁
levelShortcut: 关卡
endOfDemo: 试玩版结束
endOfDemo: 试玩版结束
wire:
default:
name: 电线
description: 可用来传输<strong>信号<strong>信号可以是物品颜色或者开关值0或1
不同颜色的<strong>电线</strong>不会互相连接
不同颜色的<strong>电线</strong>不会互相连接
second:
name: 电线
description: 可用来传输<strong>信号<strong>信号可以是物品颜色或者开关值0或1
不同颜色的<strong>电线</strong>不会互相连接
不同颜色的<strong>电线</strong>不会互相连接
balancer:
default:
name: 平衡器
@ -640,7 +634,7 @@ buildings:
description: 模拟将右侧<strong>图形</strong>叠在左侧<strong>图形</strong>上。
painter:
name: 模拟上色器
description: 模拟使用右侧输入的<strong>颜色</strong>给底部输入的<strong>图形</strong>上色
description: 模拟使用右侧输入的<strong>颜色</strong>给底部输入的<strong>图形</strong>上色
item_producer:
default:
name: 物品生成器
@ -750,7 +744,7 @@ storyRewards:
reward_logic_gates:
title: 逻辑门
desc: 您解锁了<strong>逻辑门</strong>!它们是个好东西!<br>
您可以用它们来进行'与,或,非,异或'操作。<br><br>作为奖励,我还给您解锁了<strong>晶体管</strong>
您可以用它们来进行"与,或,非,异或"操作。<br><br>作为奖励,我还给您解锁了<strong>晶体管</strong>
reward_virtual_processing:
title: 模拟处理器
desc: 我刚刚给了一大堆新设施,让您可以<strong>模拟形状的处理过程</strong><br>
@ -769,7 +763,7 @@ storyRewards:
您也可以输入开关值1 / 0信号来激活或者禁用它。
reward_demo_end:
title: 试玩结束
desc: 恭喜!您已经通关了试玩版本! <br>更多挑战,请至Steam商城购买完整版!谢谢支持!
desc: 恭喜!您已经通关了试玩版本! <br>更多挑战,请购买完整版!谢谢支持!
settings:
title: 设置
categories:
@ -870,10 +864,10 @@ settings:
description: 每一类设施都会记住各自上一次的旋转方向。如果您经常在不同设施类型之间切换,这个设置会让游戏操控更加便捷。
soundVolume:
title: 音效音量
description: 设置音效的音量
description: 设置音效的音量
musicVolume:
title: 音乐音量
description: 设置音乐的音量
description: 设置音乐的音量
lowQualityMapResources:
title: 低质量地图资源
description: 放大时简化地图上资源的渲染以提高性能。开启甚至会让画面看起来更干净,低配置电脑玩家建议开启!
@ -921,7 +915,7 @@ keybindings:
massSelect: 批量选择
buildings: 设施快捷键
placementModifiers: 放置设施修饰键
mods: Provided by Mods
mods: 由模组提供
mappings:
confirm: 确认
back: 返回
@ -947,7 +941,7 @@ keybindings:
painter: 上色器
trash: 垃圾桶
rotateWhilePlacing: 顺时针旋转
rotateInverseModifier: "修饰键: 改为逆时针旋转"
rotateInverseModifier: "修饰键: 改为逆时针旋转"
cycleBuildingVariants: 切换所选择设施变体
confirmMassDelete: 确认批量删除
cycleBuildings: 切换所选择设施
@ -962,7 +956,7 @@ keybindings:
exportScreenshot: 导出截图
mapMoveFaster: 快速移动
lockBeltDirection: 启用传送带规划
switchDirectionLockSide: 规划器:换边
switchDirectionLockSide: 规划器:换边
pipette: 吸取器
menuClose: 关闭菜单
switchLayers: 切换层
@ -981,7 +975,7 @@ keybindings:
analyzer: 图形分析器
comparator: 比较器
item_producer: 物品生产器 (沙盒模式)
copyWireValue: 电线:复制指定电线上的值
copyWireValue: 电线:复制指定电线上的值
rotateToUp: 向上旋转
rotateToDown: 向下旋转
rotateToRight: 向右旋转
@ -990,20 +984,14 @@ keybindings:
goal_acceptor: 目标接收器
block: 方块
massSelectClear: 清除传送带
showShapeTooltip: 显示图形输出提示
showShapeTooltip: 显示图形输出提示
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>
这个游戏的开发获得了 Discord 社区内热情玩家的巨大支持。诚挚邀请您加入我们的 <a href="<discordlink>" target="_blank">Discord 服务器</a><br><br>
本游戏的音乐由 <a href="https://soundcloud.com/pettersumelius" target="_blank">Peppsen</a> 制作——他是个很棒的伙伴。<br><br>
最后,我想感谢我最好的朋友 <a href="https://github.com/niklas-dahl" target="_blank">Niklas</a> ——如果没有他的《异星工厂》factorio带给我的体验和启发《异形工厂》shapez.io将不会存在。
body: |-
本游戏由托比亚斯开发,并且已经开源。<br><br>
这个游戏的开发获得了热情玩家的巨大支持。非常感谢!<br><br>
本游戏的音乐由佩普森制作——他是个很棒的伙伴。<br><br>
最后,我想感谢我最好的朋友尼可拉斯——如果没有他的《异星工厂》带给我的体验和启发,《图形工厂》将不会存在。
changelog:
title: 版本日志
demo:
@ -1067,7 +1055,7 @@ tips:
- 这个游戏有很多设置可以提高游戏效率,请一定要了解一下!
- 中心基地有个指向它所在方向的小指南指针!
- 想清理传送带,可剪切那块区域然后将其在相同位置粘贴。
- 按F4显示FPS
- 按F4显示帧数
- 按两次F4显示您鼠标和镜头所在的块。
- 您可以点击被固定在屏幕左侧的图形来解除固定。
- 您可以点击被固定在屏幕左侧的图形来解除固定。
@ -1081,7 +1069,7 @@ puzzleMenu:
validatingPuzzle: 验证谜题
submittingPuzzle: 提交谜题
noPuzzles: 暂无满足此部分条件的谜题。
dlcHint: 如已购买DLC请在您的Steam库中右键点击异形工厂然后选择属性-DLC
dlcHint: 如已购买资料片,请在您的游戏库中右键点击图形工厂,然后选择属性-资料片
categories:
levels: 关卡
new: 最新
@ -1127,7 +1115,7 @@ puzzleMenu:
autoComplete: 您的谜题已自动完成!请确认您的常量生成器没有直接向您的目标接收器进行传送。
backendErrors:
ratelimit: 您的操作太频繁了。请稍等。
invalid-api-key: 与后台通信失败,请尝试更新或重新启动游戏(无效的Api密钥)。
invalid-api-key: 与后台通信失败,请尝试更新或重新启动游戏(无效的密钥)。
unauthorized: 与后台通信失败,请尝试更新或重新启动游戏(未经授权)。
bad-token: 与后台通信失败,请尝试更新或重新启动游戏(令牌错误)。
bad-id: 谜题标识符无效。
@ -1145,24 +1133,19 @@ backendErrors:
bad-payload: 此请求包含无效数据。
bad-building-placement: 您的谜题包含放置错误的设施。
timeout: 请求超时。
too-many-likes-already: 您的谜题已经得到了许多玩家的赞赏。如果您仍然希望删除它,请联系support@shapez.io!
too-many-likes-already: 您的谜题已经得到了许多玩家的赞赏。如果您仍然希望删除它,请联系客服!
no-permission: 您没有执行此操作的权限。
mods:
title: Mods
author: Author
version: Version
modWebsite: Website
openFolder: Open Mods Folder
folderOnlyStandalone: Opening the mod folder is only possible when running the standalone.
browseMods: Browse Mods
modsInfo: To install and manage mods, copy them to the mods folder within the
game directory. You can also use the 'Open Mods Folder' button on the
top right.
noModSupport: You need the standalone version on Steam to install mods.
title: 模组
author: 作者
version: 版本
modWebsite: 网站
openFolder: 打开模组文件夹
folderOnlyStandalone: 只有在运行完整版时才能打开模组文件夹
browseMods: 浏览模组
modsInfo: 要安装和管理模组,请把模组文件复制到游戏模组文件夹中。使用右上角的“打开模组文件夹”按钮可快速。
noModSupport: 你需要完整版才能安装模组。
togglingComingSoon:
title: Coming Soon
description: Enabling or disabling mods is currently only possible by copying
the mod file from or to the mods/ folder. However, being able to
toggle them here is planned for a future update!
browserNoSupport: Due to browser restrictions it is currently only possible to
install mods in the Steam version - Sorry!
title: 即将开放
description: 当前只能通过复制、移除模组文件才能启用/安装模组。不久的将来,可以通过界面来启用/安装模组,敬请期待!
browserNoSupport: 由于浏览器限制,目前网页版无法使用模组 - 非常抱歉!