mirror of
https://github.com/tobspr/shapez.io.git
synced 2025-06-13 13:04:03 +00:00
Merge https://github.com/tobspr/shapez.io into modloader
This commit is contained in:
commit
4617fe01ca
5
.gitignore
vendored
5
.gitignore
vendored
@ -46,7 +46,12 @@ res_built
|
|||||||
|
|
||||||
gulp/runnable-texturepacker.jar
|
gulp/runnable-texturepacker.jar
|
||||||
tmp_standalone_files
|
tmp_standalone_files
|
||||||
|
tmp_standalone_files_china
|
||||||
|
|
||||||
# Local config
|
# Local config
|
||||||
config.local.js
|
config.local.js
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
|
# Editor artifacts
|
||||||
|
*.*.swp
|
||||||
|
*.*.swo
|
||||||
|
@ -6,6 +6,7 @@ const url = require("url");
|
|||||||
const childProcess = require("child_process");
|
const childProcess = require("child_process");
|
||||||
const { ipcMain, shell } = require("electron");
|
const { ipcMain, shell } = require("electron");
|
||||||
const fs = require("fs");
|
const fs = require("fs");
|
||||||
|
const steam = require("./steam");
|
||||||
const isDev = process.argv.indexOf("--dev") >= 0;
|
const isDev = process.argv.indexOf("--dev") >= 0;
|
||||||
const isLocal = process.argv.indexOf("--local") >= 0;
|
const isLocal = process.argv.indexOf("--local") >= 0;
|
||||||
|
|
||||||
@ -290,3 +291,5 @@ const emitOpenedWithFile = path => {
|
|||||||
const content = fs.readFileSync(path, "utf-8");
|
const content = fs.readFileSync(path, "utf-8");
|
||||||
ipcMain.emit("opened-with-file", path, content);
|
ipcMain.emit("opened-with-file", path, content);
|
||||||
};
|
};
|
||||||
|
steam.init(isDev);
|
||||||
|
steam.listen();
|
||||||
|
@ -10,7 +10,9 @@
|
|||||||
"start": "electron --disable-direct-composition --in-process-gpu ."
|
"start": "electron --disable-direct-composition --in-process-gpu ."
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"electron": "10.1.3"
|
"electron": "11.3.0"
|
||||||
},
|
},
|
||||||
"dependencies": {}
|
"optionalDependencies": {
|
||||||
|
"shapez.io-private-artifacts": "github:tobspr/shapez.io-private-artifacts#abi-v85"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
73
electron/steam.js
Normal file
73
electron/steam.js
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
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
|
||||||
|
// throw err;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (!greenworks || !initialized) {
|
||||||
|
console.log("Ignoring Steam IPC events");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ipcMain.handle("steam:get-achievement-names", getAchievementNames);
|
||||||
|
ipcMain.handle("steam:activate-achievement", activateAchievement);
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
};
|
@ -1 +1 @@
|
|||||||
1134480
|
1318690
|
||||||
|
1149
electron/yarn.lock
1149
electron/yarn.lock
File diff suppressed because it is too large
Load Diff
@ -50,6 +50,9 @@ css.gulptasksCSS($, gulp, buildFolder, browserSync);
|
|||||||
const sounds = require("./sounds");
|
const sounds = require("./sounds");
|
||||||
sounds.gulptasksSounds($, gulp, buildFolder);
|
sounds.gulptasksSounds($, gulp, buildFolder);
|
||||||
|
|
||||||
|
const localConfig = require("./local-config");
|
||||||
|
localConfig.gulptasksLocalConfig($, gulp);
|
||||||
|
|
||||||
const js = require("./js");
|
const js = require("./js");
|
||||||
js.gulptasksJS($, gulp, buildFolder, browserSync);
|
js.gulptasksJS($, gulp, buildFolder, browserSync);
|
||||||
|
|
||||||
@ -136,7 +139,7 @@ gulp.task("main.webserver", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
function serve({ standalone }) {
|
function serve({ standalone, chineseVersion = false }) {
|
||||||
browserSync.init({
|
browserSync.init({
|
||||||
server: buildFolder,
|
server: buildFolder,
|
||||||
port: 3005,
|
port: 3005,
|
||||||
@ -200,7 +203,11 @@ function serve({ standalone }) {
|
|||||||
if (standalone) {
|
if (standalone) {
|
||||||
gulp.series("js.standalone-dev.watch")(() => true);
|
gulp.series("js.standalone-dev.watch")(() => true);
|
||||||
} else {
|
} else {
|
||||||
gulp.series("js.dev.watch")(() => true);
|
if (chineseVersion) {
|
||||||
|
gulp.series("china.js.dev.watch")(() => true);
|
||||||
|
} else {
|
||||||
|
gulp.series("js.dev.watch")(() => true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -221,6 +228,7 @@ gulp.task(
|
|||||||
gulp.series(
|
gulp.series(
|
||||||
"utils.cleanup",
|
"utils.cleanup",
|
||||||
"utils.copyAdditionalBuildFiles",
|
"utils.copyAdditionalBuildFiles",
|
||||||
|
"localConfig.findOrCreate",
|
||||||
"imgres.buildAtlas",
|
"imgres.buildAtlas",
|
||||||
"imgres.atlasToJson",
|
"imgres.atlasToJson",
|
||||||
"imgres.atlas",
|
"imgres.atlas",
|
||||||
@ -238,6 +246,7 @@ gulp.task(
|
|||||||
"build.standalone.dev",
|
"build.standalone.dev",
|
||||||
gulp.series(
|
gulp.series(
|
||||||
"utils.cleanup",
|
"utils.cleanup",
|
||||||
|
"localConfig.findOrCreate",
|
||||||
"imgres.buildAtlas",
|
"imgres.buildAtlas",
|
||||||
"imgres.atlasToJson",
|
"imgres.atlasToJson",
|
||||||
"imgres.atlas",
|
"imgres.atlas",
|
||||||
@ -284,30 +293,28 @@ gulp.task(
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Builds everything (standalone-prod)
|
// Builds everything (standalone-prod)
|
||||||
gulp.task(
|
|
||||||
"step.standalone-prod.code",
|
|
||||||
gulp.series("sounds.fullbuildHQ", "translations.fullBuild", "js.standalone-prod")
|
|
||||||
);
|
|
||||||
gulp.task("step.standalone-prod.mainbuild", gulp.parallel("step.baseResources", "step.standalone-prod.code"));
|
|
||||||
gulp.task(
|
|
||||||
"step.standalone-prod.all",
|
|
||||||
gulp.series("step.standalone-prod.mainbuild", "css.prod-standalone", "html.standalone-prod")
|
|
||||||
);
|
|
||||||
gulp.task(
|
|
||||||
"build.standalone-prod",
|
|
||||||
gulp.series("utils.cleanup", "step.standalone-prod.all", "step.postbuild")
|
|
||||||
);
|
|
||||||
|
|
||||||
// OS X build and release upload
|
for (const prefix of ["", "china."]) {
|
||||||
gulp.task(
|
gulp.task(
|
||||||
"build.darwin64-prod",
|
prefix + "step.standalone-prod.code",
|
||||||
gulp.series(
|
gulp.series("sounds.fullbuildHQ", "translations.fullBuild", prefix + "js.standalone-prod")
|
||||||
"build.standalone-prod",
|
);
|
||||||
"standalone.prepare",
|
|
||||||
"standalone.package.prod.darwin64",
|
gulp.task(
|
||||||
"standalone.uploadRelease.darwin64"
|
prefix + "step.standalone-prod.mainbuild",
|
||||||
)
|
gulp.parallel("step.baseResources", prefix + "step.standalone-prod.code")
|
||||||
);
|
);
|
||||||
|
|
||||||
|
gulp.task(
|
||||||
|
prefix + "step.standalone-prod.all",
|
||||||
|
gulp.series(prefix + "step.standalone-prod.mainbuild", "css.prod-standalone", "html.standalone-prod")
|
||||||
|
);
|
||||||
|
|
||||||
|
gulp.task(
|
||||||
|
prefix + "build.standalone-prod",
|
||||||
|
gulp.series("utils.cleanup", prefix + "step.standalone-prod.all", "step.postbuild")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Deploying!
|
// Deploying!
|
||||||
gulp.task(
|
gulp.task(
|
||||||
@ -320,7 +327,12 @@ gulp.task(
|
|||||||
);
|
);
|
||||||
gulp.task("main.deploy.prod", gulp.series("utils.requireCleanWorkingTree", "build.prod", "ftp.upload.prod"));
|
gulp.task("main.deploy.prod", gulp.series("utils.requireCleanWorkingTree", "build.prod", "ftp.upload.prod"));
|
||||||
gulp.task("main.deploy.all", gulp.series("main.deploy.staging", "main.deploy.prod"));
|
gulp.task("main.deploy.all", gulp.series("main.deploy.staging", "main.deploy.prod"));
|
||||||
gulp.task("main.standalone", gulp.series("build.standalone-prod", "standalone.package.prod"));
|
gulp.task("regular.main.standalone", gulp.series("build.standalone-prod", "standalone.package.prod"));
|
||||||
|
gulp.task(
|
||||||
|
"china.main.standalone",
|
||||||
|
gulp.series("china.build.standalone-prod", "china.standalone.package.prod")
|
||||||
|
);
|
||||||
|
gulp.task("standalone.all", gulp.series("regular.main.standalone", "china.main.standalone"));
|
||||||
|
|
||||||
// Live-development
|
// Live-development
|
||||||
gulp.task(
|
gulp.task(
|
||||||
@ -331,5 +343,9 @@ gulp.task(
|
|||||||
"main.serveStandalone",
|
"main.serveStandalone",
|
||||||
gulp.series("build.standalone.dev", () => serve({ standalone: true }))
|
gulp.series("build.standalone.dev", () => serve({ standalone: true }))
|
||||||
);
|
);
|
||||||
|
gulp.task(
|
||||||
|
"china.main.serveDev",
|
||||||
|
gulp.series("build.dev", () => serve({ standalone: false, chineseVersion: true }))
|
||||||
|
);
|
||||||
|
|
||||||
gulp.task("default", gulp.series("main.serveDev"));
|
gulp.task("default", gulp.series("main.serveDev"));
|
||||||
|
47
gulp/js.js
47
gulp/js.js
@ -29,6 +29,36 @@ function gulptasksJS($, gulp, buildFolder, browserSync) {
|
|||||||
.pipe(gulp.dest(buildFolder));
|
.pipe(gulp.dest(buildFolder));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//// DEV CHINA
|
||||||
|
|
||||||
|
gulp.task("china.js.dev.watch", () => {
|
||||||
|
return gulp
|
||||||
|
.src("../src/js/main.js")
|
||||||
|
.pipe(
|
||||||
|
$.webpackStream(
|
||||||
|
requireUncached("./webpack.config.js")({
|
||||||
|
watch: true,
|
||||||
|
chineseVersion: true,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.pipe(gulp.dest(buildFolder))
|
||||||
|
.pipe(browserSync.stream());
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task("china.js.dev", () => {
|
||||||
|
return gulp
|
||||||
|
.src("../src/js/main.js")
|
||||||
|
.pipe(
|
||||||
|
$.webpackStream(
|
||||||
|
requireUncached("./webpack.config.js")({
|
||||||
|
chineseVersion: true,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.pipe(gulp.dest(buildFolder));
|
||||||
|
});
|
||||||
|
|
||||||
//// STAGING
|
//// STAGING
|
||||||
|
|
||||||
gulp.task("js.staging.transpiled", () => {
|
gulp.task("js.staging.transpiled", () => {
|
||||||
@ -161,6 +191,23 @@ function gulptasksJS($, gulp, buildFolder, browserSync) {
|
|||||||
)
|
)
|
||||||
.pipe(gulp.dest(buildFolder));
|
.pipe(gulp.dest(buildFolder));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
gulp.task("china.js.standalone-prod", () => {
|
||||||
|
return gulp
|
||||||
|
.src("../src/js/main.js")
|
||||||
|
.pipe(
|
||||||
|
$.webpackStream(
|
||||||
|
requireUncached("./webpack.production.config.js")({
|
||||||
|
enableAssert: false,
|
||||||
|
environment: "prod",
|
||||||
|
es6: true,
|
||||||
|
standalone: true,
|
||||||
|
chineseVersion: true,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.pipe(gulp.dest(buildFolder));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
18
gulp/local-config.js
Normal file
18
gulp/local-config.js
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
const path = require("path");
|
||||||
|
const fs = require("fs");
|
||||||
|
const fse = require("fs-extra");
|
||||||
|
|
||||||
|
const configTemplatePath = path.join(__dirname, "../src/js/core/config.local.template.js");
|
||||||
|
const configPath = path.join(__dirname, "../src/js/core/config.local.js");
|
||||||
|
|
||||||
|
function gulptasksLocalConfig($, gulp) {
|
||||||
|
gulp.task("localConfig.findOrCreate", cb => {
|
||||||
|
if (!fs.existsSync(configPath)) {
|
||||||
|
fse.copySync(configTemplatePath, configPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
cb();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { gulptasksLocalConfig };
|
@ -34,6 +34,7 @@
|
|||||||
"fastdom": "^1.0.9",
|
"fastdom": "^1.0.9",
|
||||||
"flatted": "^2.0.1",
|
"flatted": "^2.0.1",
|
||||||
"fs-extra": "^8.1.0",
|
"fs-extra": "^8.1.0",
|
||||||
|
"gifsicle": "^5.2.0",
|
||||||
"gulp-audiosprite": "^1.1.0",
|
"gulp-audiosprite": "^1.1.0",
|
||||||
"howler": "^2.1.2",
|
"howler": "^2.1.2",
|
||||||
"html-loader": "^0.5.5",
|
"html-loader": "^0.5.5",
|
||||||
@ -61,7 +62,8 @@
|
|||||||
"webpack-plugin-replace": "^1.1.1",
|
"webpack-plugin-replace": "^1.1.1",
|
||||||
"webpack-strip-block": "^0.2.0",
|
"webpack-strip-block": "^0.2.0",
|
||||||
"whatwg-fetch": "^3.0.0",
|
"whatwg-fetch": "^3.0.0",
|
||||||
"worker-loader": "^2.0.0"
|
"worker-loader": "^2.0.0",
|
||||||
|
"yaml": "^1.10.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/plugin-proposal-class-properties": "^7.13.0",
|
"@babel/plugin-proposal-class-properties": "^7.13.0",
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
require("colors");
|
require("colors");
|
||||||
const packager = require("electron-packager");
|
const packager = require("electron-packager");
|
||||||
|
const pj = require("../electron/package.json");
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
const { getVersion } = require("./buildutils");
|
const { getVersion } = require("./buildutils");
|
||||||
const fs = require("fs");
|
const fs = require("fs");
|
||||||
@ -9,66 +10,69 @@ const execSync = require("child_process").execSync;
|
|||||||
|
|
||||||
function gulptasksStandalone($, gulp) {
|
function gulptasksStandalone($, gulp) {
|
||||||
const electronBaseDir = path.join(__dirname, "..", "electron");
|
const electronBaseDir = path.join(__dirname, "..", "electron");
|
||||||
|
const targets = [
|
||||||
|
{
|
||||||
|
tempDestDir: path.join(__dirname, "..", "tmp_standalone_files"),
|
||||||
|
suffix: "",
|
||||||
|
taskPrefix: ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tempDestDir: path.join(__dirname, "..", "tmp_standalone_files_china"),
|
||||||
|
suffix: "china",
|
||||||
|
taskPrefix: "china.",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
const tempDestDir = path.join(__dirname, "..", "tmp_standalone_files");
|
for (const { tempDestDir, suffix, taskPrefix } of targets) {
|
||||||
const tempDestBuildDir = path.join(tempDestDir, "built");
|
const tempDestBuildDir = path.join(tempDestDir, "built");
|
||||||
|
|
||||||
gulp.task("standalone.prepare.cleanup", () => {
|
gulp.task(taskPrefix + "standalone.prepare.cleanup", () => {
|
||||||
return gulp.src(tempDestDir, { read: false, allowEmpty: true }).pipe($.clean({ force: true }));
|
return gulp.src(tempDestDir, { read: false, allowEmpty: true })
|
||||||
});
|
.pipe($.clean({ force: true }));
|
||||||
|
});
|
||||||
|
|
||||||
gulp.task("standalone.prepare.copyPrefab", () => {
|
gulp.task(taskPrefix + "standalone.prepare.copyPrefab", () => {
|
||||||
// const requiredFiles = $.glob.sync("../electron/");
|
const requiredFiles = [
|
||||||
const requiredFiles = [
|
path.join(electronBaseDir, "node_modules", "**", "*.*"),
|
||||||
path.join(electronBaseDir, "lib", "**", "*.node"),
|
path.join(electronBaseDir, "node_modules", "**", ".*"),
|
||||||
path.join(electronBaseDir, "node_modules", "**", "*.*"),
|
path.join(electronBaseDir, "steam_appid.txt"),
|
||||||
path.join(electronBaseDir, "node_modules", "**", ".*"),
|
path.join(electronBaseDir, "favicon*"),
|
||||||
path.join(electronBaseDir, "favicon*"),
|
|
||||||
|
|
||||||
// fails on platforms which support symlinks
|
// fails on platforms which support symlinks
|
||||||
// https://github.com/gulpjs/gulp/issues/1427
|
// https://github.com/gulpjs/gulp/issues/1427
|
||||||
// path.join(electronBaseDir, "node_modules", "**", "*"),
|
// path.join(electronBaseDir, "node_modules", "**", "*"),
|
||||||
];
|
];
|
||||||
return gulp.src(requiredFiles, { base: electronBaseDir }).pipe(gulp.dest(tempDestBuildDir));
|
return gulp.src(requiredFiles, { base: electronBaseDir })
|
||||||
});
|
.pipe(gulp.dest(tempDestBuildDir));
|
||||||
|
});
|
||||||
|
|
||||||
gulp.task("standalone.prepare.writePackageJson", cb => {
|
gulp.task(taskPrefix + "standalone.prepare.writePackageJson", cb => {
|
||||||
fs.writeFileSync(
|
const packageJsonString = JSON.stringify({
|
||||||
path.join(tempDestBuildDir, "package.json"),
|
scripts: {
|
||||||
JSON.stringify(
|
start: pj.scripts.start
|
||||||
{
|
|
||||||
devDependencies: {
|
|
||||||
electron: "6.1.12",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
null,
|
devDependencies: pj.devDependencies,
|
||||||
4
|
optionalDependencies: pj.optionalDependencies
|
||||||
)
|
}, null, 4);
|
||||||
);
|
|
||||||
cb();
|
|
||||||
});
|
|
||||||
|
|
||||||
gulp.task("standalone.prepareVDF", cb => {
|
fs.writeFileSync(path.join(tempDestBuildDir, "package.json"), packageJsonString);
|
||||||
const hash = buildutils.getRevision();
|
|
||||||
|
|
||||||
const steampipeDir = path.join(__dirname, "steampipe", "scripts");
|
cb();
|
||||||
const templateContents = fs
|
});
|
||||||
.readFileSync(path.join(steampipeDir, "app.vdf.template"), { encoding: "utf-8" })
|
|
||||||
.toString();
|
|
||||||
|
|
||||||
const convertedContents = templateContents.replace("$DESC$", "Commit " + hash);
|
gulp.task(taskPrefix + "standalone.prepareVDF", cb => {
|
||||||
fs.writeFileSync(path.join(steampipeDir, "app.vdf"), convertedContents);
|
const hash = buildutils.getRevision();
|
||||||
|
|
||||||
cb();
|
const steampipeDir = path.join(__dirname, "steampipe", "scripts");
|
||||||
});
|
const templateContents = fs
|
||||||
|
.readFileSync(path.join(steampipeDir, "app.vdf.template"), { encoding: "utf-8" })
|
||||||
|
.toString();
|
||||||
|
|
||||||
gulp.task("standalone.prepare.minifyCode", () => {
|
const convertedContents = templateContents.replace("$DESC$", "Commit " + hash);
|
||||||
return gulp.src(path.join(electronBaseDir, "*.js")).pipe(gulp.dest(tempDestBuildDir));
|
fs.writeFileSync(path.join(steampipeDir, "app.vdf"), convertedContents);
|
||||||
});
|
|
||||||
|
|
||||||
gulp.task("standalone.prepare.copyGamefiles", () => {
|
cb();
|
||||||
return gulp.src("../build/**/*.*", { base: "../build" }).pipe(gulp.dest(tempDestBuildDir));
|
});
|
||||||
});
|
|
||||||
|
|
||||||
gulp.task("standalone.killRunningInstances", cb => {
|
gulp.task("standalone.killRunningInstances", cb => {
|
||||||
try {
|
try {
|
||||||
@ -171,73 +175,125 @@ function gulptasksStandalone($, gulp) {
|
|||||||
"For more information, see " +
|
"For more information, see " +
|
||||||
"https://github.com/electron/electron-packager/issues/71".underline
|
"https://github.com/electron/electron-packager/issues/71".underline
|
||||||
);
|
);
|
||||||
|
gulp.task(taskPrefix + "standalone.prepare.minifyCode", () => {
|
||||||
|
return gulp.src(path.join(electronBaseDir, "*.js")).pipe(gulp.dest(tempDestBuildDir));
|
||||||
|
});
|
||||||
|
|
||||||
// Clear up framework folders
|
gulp.task(taskPrefix + "standalone.prepare.copyGamefiles", () => {
|
||||||
fs.writeFileSync(
|
return gulp.src("../build/**/*.*", { base: "../build" })
|
||||||
path.join(appPath, "play.sh"),
|
.pipe(gulp.dest(tempDestBuildDir));
|
||||||
'#!/usr/bin/env bash\n./shapez.io-standalone.app/Contents/MacOS/shapezio --no-sandbox "$@"\n'
|
});
|
||||||
);
|
|
||||||
fs.chmodSync(path.join(appPath, "play.sh"), 0o775);
|
|
||||||
fs.chmodSync(
|
|
||||||
path.join(appPath, "shapez.io-standalone.app", "Contents", "MacOS", "shapezio"),
|
|
||||||
0o775
|
|
||||||
);
|
|
||||||
|
|
||||||
const finalPath = path.join(appPath, "shapez.io-standalone.app");
|
gulp.task(taskPrefix + "standalone.killRunningInstances", cb => {
|
||||||
|
try {
|
||||||
const frameworksDir = path.join(finalPath, "Contents", "Frameworks");
|
execSync("taskkill /F /IM shapezio.exe");
|
||||||
const frameworkFolders = fs
|
} catch (ex) {
|
||||||
.readdirSync(frameworksDir)
|
console.warn("Failed to kill running instances, maybe none are up.");
|
||||||
.filter(fname => fname.endsWith(".framework"));
|
|
||||||
|
|
||||||
for (let i = 0; i < frameworkFolders.length; ++i) {
|
|
||||||
const folderName = frameworkFolders[i];
|
|
||||||
const frameworkFolder = path.join(frameworksDir, folderName);
|
|
||||||
console.log(" -> ", frameworkFolder);
|
|
||||||
|
|
||||||
const filesToDelete = fs
|
|
||||||
.readdirSync(frameworkFolder)
|
|
||||||
.filter(fname => fname.toLowerCase() !== "versions");
|
|
||||||
filesToDelete.forEach(fname => {
|
|
||||||
console.log(" -> Deleting", fname);
|
|
||||||
fs.unlinkSync(path.join(frameworkFolder, fname));
|
|
||||||
});
|
|
||||||
|
|
||||||
const frameworkSourceDir = path.join(frameworkFolder, "Versions", "A");
|
|
||||||
fse.copySync(frameworkSourceDir, frameworkFolder);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
cb();
|
|
||||||
},
|
|
||||||
err => {
|
|
||||||
console.error("Packaging error:", err);
|
|
||||||
cb();
|
|
||||||
}
|
}
|
||||||
|
cb();
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task(
|
||||||
|
taskPrefix + "standalone.prepare",
|
||||||
|
gulp.series(
|
||||||
|
taskPrefix + "standalone.killRunningInstances",
|
||||||
|
taskPrefix + "standalone.prepare.cleanup",
|
||||||
|
taskPrefix + "standalone.prepare.copyPrefab",
|
||||||
|
taskPrefix + "standalone.prepare.writePackageJson",
|
||||||
|
taskPrefix + "standalone.prepare.minifyCode",
|
||||||
|
taskPrefix + "standalone.prepare.copyGamefiles"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {'win32'|'linux'} platform
|
||||||
|
* @param {'x64'|'ia32'} arch
|
||||||
|
* @param {function():void} cb
|
||||||
|
*/
|
||||||
|
function packageStandalone(platform, arch, cb) {
|
||||||
|
const tomlFile = fs.readFileSync(path.join(__dirname, ".itch.toml"));
|
||||||
|
const privateArtifactsPath = "node_modules/shapez.io-private-artifacts";
|
||||||
|
|
||||||
|
let asar;
|
||||||
|
if (fs.existsSync(path.join(tempDestBuildDir, privateArtifactsPath))) {
|
||||||
|
asar = { unpackDir: privateArtifactsPath };
|
||||||
|
} else {
|
||||||
|
asar = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
packager({
|
||||||
|
dir: tempDestBuildDir,
|
||||||
|
appCopyright: "Tobias Springer",
|
||||||
|
appVersion: getVersion(),
|
||||||
|
buildVersion: "1.0.0",
|
||||||
|
arch,
|
||||||
|
platform,
|
||||||
|
asar: asar,
|
||||||
|
executableName: "shapezio",
|
||||||
|
icon: path.join(electronBaseDir, "favicon"),
|
||||||
|
name: "shapez.io-standalone" + suffix,
|
||||||
|
out: tempDestDir,
|
||||||
|
overwrite: true,
|
||||||
|
appBundleId: "io.shapez.standalone",
|
||||||
|
appCategoryType: "public.app-category.games",
|
||||||
|
}).then(
|
||||||
|
appPaths => {
|
||||||
|
console.log("Packages created:", appPaths);
|
||||||
|
appPaths.forEach(appPath => {
|
||||||
|
if (!fs.existsSync(appPath)) {
|
||||||
|
console.error("Bad app path gotten:", appPath);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(appPath, "LICENSE"),
|
||||||
|
fs.readFileSync(path.join(__dirname, "..", "LICENSE"))
|
||||||
|
);
|
||||||
|
|
||||||
|
fse.copySync(
|
||||||
|
path.join(tempDestBuildDir, "steam_appid.txt"),
|
||||||
|
path.join(appPath, "steam_appid.txt")
|
||||||
|
);
|
||||||
|
|
||||||
|
fs.writeFileSync(path.join(appPath, ".itch.toml"), tomlFile);
|
||||||
|
|
||||||
|
if (platform === "linux") {
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(appPath, "play.sh"),
|
||||||
|
'#!/usr/bin/env bash\n./shapezio --no-sandbox "$@"\n'
|
||||||
|
);
|
||||||
|
fs.chmodSync(path.join(appPath, "play.sh"), 0o775);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
cb();
|
||||||
|
},
|
||||||
|
err => {
|
||||||
|
console.error("Packaging error:", err);
|
||||||
|
cb();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
gulp.task(taskPrefix + "standalone.package.prod.win64", cb =>
|
||||||
|
packageStandalone("win32", "x64", cb)
|
||||||
|
);
|
||||||
|
gulp.task(taskPrefix + "standalone.package.prod.linux64", cb =>
|
||||||
|
packageStandalone("linux", "x64", cb)
|
||||||
|
);
|
||||||
|
|
||||||
|
gulp.task(
|
||||||
|
taskPrefix + "standalone.package.prod",
|
||||||
|
gulp.series(
|
||||||
|
taskPrefix + "standalone.prepare",
|
||||||
|
gulp.parallel(
|
||||||
|
taskPrefix + "standalone.package.prod.win64",
|
||||||
|
taskPrefix + "standalone.package.prod.linux64"
|
||||||
|
)
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
gulp.task("standalone.package.prod.win64", cb => packageStandalone("win32", "x64", cb));
|
|
||||||
gulp.task("standalone.package.prod.win32", cb => packageStandalone("win32", "ia32", cb));
|
|
||||||
gulp.task("standalone.package.prod.linux64", cb => packageStandalone("linux", "x64", cb));
|
|
||||||
gulp.task("standalone.package.prod.linux32", cb => packageStandalone("linux", "ia32", cb));
|
|
||||||
gulp.task("standalone.package.prod.darwin64", cb => packageStandalone("darwin", "x64", cb));
|
|
||||||
gulp.task("standalone.package.prod.darwin64.unsigned", cb =>
|
|
||||||
packageStandalone("darwin", "x64", cb, false)
|
|
||||||
);
|
|
||||||
|
|
||||||
gulp.task(
|
|
||||||
"standalone.package.prod",
|
|
||||||
gulp.series(
|
|
||||||
"standalone.prepare",
|
|
||||||
gulp.parallel(
|
|
||||||
"standalone.package.prod.win64",
|
|
||||||
"standalone.package.prod.linux64",
|
|
||||||
"standalone.package.prod.darwin64"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { gulptasksStandalone };
|
module.exports = { gulptasksStandalone };
|
||||||
|
@ -2,14 +2,16 @@
|
|||||||
{
|
{
|
||||||
"appid" "1318690"
|
"appid" "1318690"
|
||||||
"desc" "$DESC$"
|
"desc" "$DESC$"
|
||||||
"buildoutput" "C:\work\shapez\shapez.io\gulp\steampipe\steamtemp"
|
"buildoutput" "C:\work\shapez.io\gulp\steampipe\steamtemp"
|
||||||
"contentroot" ""
|
"contentroot" ""
|
||||||
"setlive" ""
|
"setlive" ""
|
||||||
"preview" "0"
|
"preview" "0"
|
||||||
"local" ""
|
"local" ""
|
||||||
"depots"
|
"depots"
|
||||||
{
|
{
|
||||||
"1318691" "C:\work\shapez\shapez.io\gulp\steampipe\scripts\windows.vdf"
|
"1318691" "C:\work\shapez.io\gulp\steampipe\scripts\windows.vdf"
|
||||||
"1318692" "C:\work\shapez\shapez.io\gulp\steampipe\scripts\linux.vdf"
|
"1318694" "C:\work\shapez.io\gulp\steampipe\scripts\china-windows.vdf"
|
||||||
|
"1318692" "C:\work\shapez.io\gulp\steampipe\scripts\linux.vdf"
|
||||||
|
"1318695" "C:\work\shapez.io\gulp\steampipe\scripts\china-linux.vdf"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
12
gulp/steampipe/scripts/china-linux.vdf
Normal file
12
gulp/steampipe/scripts/china-linux.vdf
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
"DepotBuildConfig"
|
||||||
|
{
|
||||||
|
"DepotID" "1318695"
|
||||||
|
"contentroot" "C:\work\shapez.io\tmp_standalone_files_china\shapez.io-standalonechina-linux-x64"
|
||||||
|
"FileMapping"
|
||||||
|
{
|
||||||
|
"LocalPath" "*"
|
||||||
|
"DepotPath" "."
|
||||||
|
"recursive" "1"
|
||||||
|
}
|
||||||
|
"FileExclusion" "*.pdb"
|
||||||
|
}
|
12
gulp/steampipe/scripts/china-windows.vdf
Normal file
12
gulp/steampipe/scripts/china-windows.vdf
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
"DepotBuildConfig"
|
||||||
|
{
|
||||||
|
"DepotID" "1318694"
|
||||||
|
"contentroot" "C:\work\shapez.io\tmp_standalone_files_china\shapez.io-standalonechina-win32-x64"
|
||||||
|
"FileMapping"
|
||||||
|
{
|
||||||
|
"LocalPath" "*"
|
||||||
|
"DepotPath" "."
|
||||||
|
"recursive" "1"
|
||||||
|
}
|
||||||
|
"FileExclusion" "*.pdb"
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
"DepotBuildConfig"
|
"DepotBuildConfig"
|
||||||
{
|
{
|
||||||
"DepotID" "1318692"
|
"DepotID" "1318692"
|
||||||
"contentroot" "C:\work\shapez\shapez.io\tmp_standalone_files\shapez.io-standalone-linux-x64"
|
"contentroot" "C:\work\shapez.io\tmp_standalone_files\shapez.io-standalone-linux-x64"
|
||||||
"FileMapping"
|
"FileMapping"
|
||||||
{
|
{
|
||||||
"LocalPath" "*"
|
"LocalPath" "*"
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"DepotBuildConfig"
|
"DepotBuildConfig"
|
||||||
{
|
{
|
||||||
"DepotID" "1318691"
|
"DepotID" "1318691"
|
||||||
"contentroot" "C:\work\shapez\shapez.io\tmp_standalone_files\shapez.io-standalone-win32-x64"
|
"contentroot" "C:\work\shapez.io\tmp_standalone_files\shapez.io-standalone-win32-x64"
|
||||||
"FileMapping"
|
"FileMapping"
|
||||||
{
|
{
|
||||||
"LocalPath" "*"
|
"LocalPath" "*"
|
||||||
|
@ -25,6 +25,7 @@ function gulptasksTranslations($, gulp) {
|
|||||||
files
|
files
|
||||||
.filter(name => name.endsWith(".yaml"))
|
.filter(name => name.endsWith(".yaml"))
|
||||||
.forEach(fname => {
|
.forEach(fname => {
|
||||||
|
console.log("Loading", fname);
|
||||||
const languageName = fname.replace(".yaml", "");
|
const languageName = fname.replace(".yaml", "");
|
||||||
const abspath = path.join(translationsSourceDir, fname);
|
const abspath = path.join(translationsSourceDir, fname);
|
||||||
|
|
||||||
@ -40,39 +41,13 @@ function gulptasksTranslations($, gulp) {
|
|||||||
|
|
||||||
${storePage.intro.replace(/\n/gi, "\n\n")}
|
${storePage.intro.replace(/\n/gi, "\n\n")}
|
||||||
|
|
||||||
[h2]${storePage.title_advantages}[/h2]
|
[h2]${storePage.what_others_say}[/h2]
|
||||||
|
|
||||||
[list]
|
[list]
|
||||||
${storePage.advantages
|
[*] [i]${storePage.northernlion_comment}[/i] [b]- Northernlion, YouTube[/b]
|
||||||
.map(x => "[*] " + x.replace(/<b>/, "[b]").replace(/<\/b>/, "[/b]"))
|
[*] [i]${storePage.notch_comment}[/i] [b]- Notch[/b]
|
||||||
.join("\n")}
|
[*] [i]${storePage.steam_review_comment}[/i] [b]- Steam User[/b]
|
||||||
[/list]
|
[/list]
|
||||||
|
|
||||||
[h2]${storePage.title_future}[/h2]
|
|
||||||
|
|
||||||
[list]
|
|
||||||
${storePage.planned
|
|
||||||
.map(x => "[*] " + x.replace(/<b>/, "[b]").replace(/<\/b>/, "[/b]"))
|
|
||||||
.join("\n")}
|
|
||||||
[/list]
|
|
||||||
|
|
||||||
[h2]${storePage.title_open_source}[/h2]
|
|
||||||
|
|
||||||
${storePage.text_open_source.replace(/\n/gi, "\n\n")}
|
|
||||||
|
|
||||||
[h2]${storePage.title_links}[/h2]
|
|
||||||
|
|
||||||
[list]
|
|
||||||
[*] [url=https://discord.com/invite/HN7EVzV]${storePage.links.discord}[/url]
|
|
||||||
[*] [url=https://trello.com/b/ISQncpJP/shapezio]${storePage.links.roadmap}[/url]
|
|
||||||
[*] [url=https://www.reddit.com/r/shapezio]${storePage.links.subreddit}[/url]
|
|
||||||
[*] [url=https://github.com/tobspr/shapez.io]${storePage.links.source_code}[/url]
|
|
||||||
[*] [url=https://github.com/tobspr/shapez.io/blob/master/translations/README.md]${
|
|
||||||
storePage.links.translate
|
|
||||||
}[/url]
|
|
||||||
[/list]
|
|
||||||
|
|
||||||
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
fs.writeFileSync(destpath, trim(content.replace(/(\n[ \t\r]*)/gi, "\n")), {
|
fs.writeFileSync(destpath, trim(content.replace(/(\n[ \t\r]*)/gi, "\n")), {
|
||||||
|
@ -6,7 +6,7 @@ const { getRevision, getVersion, getAllResourceImages } = require("./buildutils"
|
|||||||
const lzString = require("lz-string");
|
const lzString = require("lz-string");
|
||||||
const CircularDependencyPlugin = require("circular-dependency-plugin");
|
const CircularDependencyPlugin = require("circular-dependency-plugin");
|
||||||
|
|
||||||
module.exports = ({ watch = false, standalone = false }) => {
|
module.exports = ({ watch = false, standalone = false, chineseVersion = false }) => {
|
||||||
return {
|
return {
|
||||||
mode: "development",
|
mode: "development",
|
||||||
devtool: "cheap-source-map",
|
devtool: "cheap-source-map",
|
||||||
@ -34,6 +34,7 @@ module.exports = ({ watch = false, standalone = false }) => {
|
|||||||
G_TRACKING_ENDPOINT: JSON.stringify(
|
G_TRACKING_ENDPOINT: JSON.stringify(
|
||||||
lzString.compressToEncodedURIComponent("http://localhost:10005/v1")
|
lzString.compressToEncodedURIComponent("http://localhost:10005/v1")
|
||||||
),
|
),
|
||||||
|
G_CHINA_VERSION: JSON.stringify(chineseVersion),
|
||||||
G_IS_DEV: "true",
|
G_IS_DEV: "true",
|
||||||
G_IS_RELEASE: "false",
|
G_IS_RELEASE: "false",
|
||||||
G_IS_MOBILE_APP: "false",
|
G_IS_MOBILE_APP: "false",
|
||||||
|
@ -16,12 +16,15 @@ module.exports = ({
|
|||||||
standalone = false,
|
standalone = false,
|
||||||
isBrowser = true,
|
isBrowser = true,
|
||||||
mobileApp = false,
|
mobileApp = false,
|
||||||
|
chineseVersion = false,
|
||||||
}) => {
|
}) => {
|
||||||
const globalDefs = {
|
const globalDefs = {
|
||||||
assert: enableAssert ? "window.assert" : "false && window.assert",
|
assert: enableAssert ? "window.assert" : "false && window.assert",
|
||||||
assertAlways: "window.assert",
|
assertAlways: "window.assert",
|
||||||
abstract: "window.assert(false, 'abstract method called');",
|
abstract: "window.assert(false, 'abstract method called');",
|
||||||
G_IS_DEV: "false",
|
G_IS_DEV: "false",
|
||||||
|
|
||||||
|
G_CHINA_VERSION: JSON.stringify(chineseVersion),
|
||||||
G_IS_RELEASE: environment === "prod" ? "true" : "false",
|
G_IS_RELEASE: environment === "prod" ? "true" : "false",
|
||||||
G_IS_STANDALONE: standalone ? "true" : "false",
|
G_IS_STANDALONE: standalone ? "true" : "false",
|
||||||
G_IS_BROWSER: isBrowser ? "true" : "false",
|
G_IS_BROWSER: isBrowser ? "true" : "false",
|
||||||
|
4473
gulp/yarn.lock
4473
gulp/yarn.lock
File diff suppressed because it is too large
Load Diff
@ -8,6 +8,7 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "cd gulp && yarn gulp main.serveDev",
|
"dev": "cd gulp && yarn gulp main.serveDev",
|
||||||
|
"devStandalone": "cd gulp && yarn gulp main.serveStandalone",
|
||||||
"tslint": "cd src/js && tsc",
|
"tslint": "cd src/js && tsc",
|
||||||
"lint": "eslint src/js",
|
"lint": "eslint src/js",
|
||||||
"prettier-all": "prettier --write src/**/*.* && prettier --write gulp/**/*.* && prettier --write electron/**/*.*",
|
"prettier-all": "prettier --write src/**/*.* && prettier --write gulp/**/*.* && prettier --write electron/**/*.*",
|
||||||
|
BIN
res/logo_cn.png
Normal file
BIN
res/logo_cn.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 34 KiB |
BIN
res/ui/changelog_skins/achievements.noinline.png
Normal file
BIN
res/ui/changelog_skins/achievements.noinline.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 479 KiB |
Binary file not shown.
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 24 KiB |
BIN
res/ui/icons/advantage_achievements.png
Normal file
BIN
res/ui/icons/advantage_achievements.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.4 KiB |
18
src/css/changelog_skins.scss
Normal file
18
src/css/changelog_skins.scss
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
[data-changelog-skin="achievements"] {
|
||||||
|
background: #f8f8f8;
|
||||||
|
|
||||||
|
@include DarkThemeOverride {
|
||||||
|
background: rgba(0, 10, 20, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@include S(border-radius, 5px);
|
||||||
|
&::before {
|
||||||
|
content: " ";
|
||||||
|
width: 100%;
|
||||||
|
display: block;
|
||||||
|
background: uiResource("changelog_skins/achievements.noinline.png") center center / cover no-repeat !important;
|
||||||
|
@include S(height, 80px);
|
||||||
|
@include S(border-radius, 5px);
|
||||||
|
@include S(margin-bottom, 5px);
|
||||||
|
}
|
||||||
|
}
|
@ -165,5 +165,15 @@
|
|||||||
color: #e72d2d;
|
color: #e72d2d;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.achievements {
|
||||||
|
& {
|
||||||
|
/* @load-async */
|
||||||
|
background-image: uiResource("res/ui/icons/advantage_achievements.png");
|
||||||
|
}
|
||||||
|
> strong {
|
||||||
|
color: #ffac0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
@import "application_error";
|
@import "application_error";
|
||||||
@import "textual_game_state";
|
@import "textual_game_state";
|
||||||
@import "adinplay";
|
@import "adinplay";
|
||||||
|
@import "changelog_skins";
|
||||||
|
|
||||||
@import "states/preload";
|
@import "states/preload";
|
||||||
@import "states/main_menu";
|
@import "states/main_menu";
|
||||||
@ -56,8 +57,8 @@
|
|||||||
@import "ingame_hud/cat_memes";
|
@import "ingame_hud/cat_memes";
|
||||||
|
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
$elements:
|
$elements:
|
||||||
// Base
|
// Base
|
||||||
ingame_Canvas,
|
ingame_Canvas,
|
||||||
ingame_VignetteOverlay,
|
ingame_VignetteOverlay,
|
||||||
|
|
||||||
@ -119,11 +120,3 @@ body.uiHidden {
|
|||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
body.modalDialogActive,
|
|
||||||
body.externalAdOpen,
|
|
||||||
body.ingameDialogOpen {
|
|
||||||
> *:not(.ingameDialog):not(.modalDialogParent):not(.loadingDialog):not(.gameLoadingOverlay):not(#ingame_HUD_ModalDialogs):not(.noBlur) {
|
|
||||||
// filter: blur(5px) !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -145,12 +145,10 @@
|
|||||||
pointer-events: all;
|
pointer-events: all;
|
||||||
transition: all 0.12s ease-in;
|
transition: all 0.12s ease-in;
|
||||||
transition-property: opacity, transform;
|
transition-property: opacity, transform;
|
||||||
transform: skewX(-0.5deg);
|
|
||||||
|
|
||||||
@include S(border-radius, $globalBorderRadius);
|
@include S(border-radius, $globalBorderRadius);
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
transform: scale(1.02);
|
|
||||||
opacity: 0.9;
|
opacity: 0.9;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -184,7 +182,7 @@
|
|||||||
.updateLabel {
|
.updateLabel {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
transform: translateX(50%) rotate(-5deg);
|
transform: translateX(50%) rotate(-5deg);
|
||||||
color: rgb(231, 78, 58);
|
color: rgb(133, 58, 231);
|
||||||
@include Heading;
|
@include Heading;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
@include S(right, 40px);
|
@include S(right, 40px);
|
||||||
@ -484,6 +482,10 @@
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
@include S(grid-gap, 4px);
|
@include S(grid-gap, 4px);
|
||||||
|
|
||||||
|
&.china {
|
||||||
|
grid-template-columns: auto auto 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
.author {
|
.author {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
padding: 10px;
|
padding: 10px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
background: #eef1f4;
|
background: #eef1f4;
|
||||||
|
@include S(border-radius, 3px);
|
||||||
|
|
||||||
@include DarkThemeOverride {
|
@include DarkThemeOverride {
|
||||||
background: #424242;
|
background: #424242;
|
||||||
|
@ -11,6 +11,7 @@ import { getPlatformName, waitNextFrame } from "./core/utils";
|
|||||||
import { Vector } from "./core/vector";
|
import { Vector } from "./core/vector";
|
||||||
import { AdProviderInterface } from "./platform/ad_provider";
|
import { AdProviderInterface } from "./platform/ad_provider";
|
||||||
import { NoAdProvider } from "./platform/ad_providers/no_ad_provider";
|
import { NoAdProvider } from "./platform/ad_providers/no_ad_provider";
|
||||||
|
import { NoAchievementProvider } from "./platform/browser/no_achievement_provider";
|
||||||
import { AnalyticsInterface } from "./platform/analytics";
|
import { AnalyticsInterface } from "./platform/analytics";
|
||||||
import { GoogleAnalyticsImpl } from "./platform/browser/google_analytics";
|
import { GoogleAnalyticsImpl } from "./platform/browser/google_analytics";
|
||||||
import { SoundImplBrowser } from "./platform/browser/sound";
|
import { SoundImplBrowser } from "./platform/browser/sound";
|
||||||
@ -29,6 +30,7 @@ import { PreloadState } from "./states/preload";
|
|||||||
import { SettingsState } from "./states/settings";
|
import { SettingsState } from "./states/settings";
|
||||||
import { ShapezGameAnalytics } from "./platform/browser/game_analytics";
|
import { ShapezGameAnalytics } from "./platform/browser/game_analytics";
|
||||||
import { RestrictionManager } from "./core/restriction_manager";
|
import { RestrictionManager } from "./core/restriction_manager";
|
||||||
|
import { AchievementProviderInterface } from "./platform/achievement_provider";
|
||||||
|
|
||||||
const logger = createLogger("application");
|
const logger = createLogger("application");
|
||||||
|
|
||||||
@ -91,6 +93,9 @@ export class Application {
|
|||||||
/** @type {PlatformWrapperInterface} */
|
/** @type {PlatformWrapperInterface} */
|
||||||
this.platformWrapper = null;
|
this.platformWrapper = null;
|
||||||
|
|
||||||
|
/** @type {AchievementProviderInterface} */
|
||||||
|
this.achievementProvider = null;
|
||||||
|
|
||||||
/** @type {AdProviderInterface} */
|
/** @type {AdProviderInterface} */
|
||||||
this.adProvider = null;
|
this.adProvider = null;
|
||||||
|
|
||||||
@ -143,6 +148,7 @@ export class Application {
|
|||||||
this.sound = new SoundImplBrowser(this);
|
this.sound = new SoundImplBrowser(this);
|
||||||
this.analytics = new GoogleAnalyticsImpl(this);
|
this.analytics = new GoogleAnalyticsImpl(this);
|
||||||
this.gameAnalytics = new ShapezGameAnalytics(this);
|
this.gameAnalytics = new ShapezGameAnalytics(this);
|
||||||
|
this.achievementProvider = new NoAchievementProvider(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -3,11 +3,14 @@
|
|||||||
*/
|
*/
|
||||||
export const CHANGELOG = [
|
export const CHANGELOG = [
|
||||||
{
|
{
|
||||||
version: "1.2.3",
|
version: "1.3.0",
|
||||||
date: "unreleased",
|
date: "12.03.2020",
|
||||||
|
skin: "achievements",
|
||||||
entries: [
|
entries: [
|
||||||
|
"There are now <strong>45 Steam Achievements!</strong>",
|
||||||
"Fixed constant signals being editable from the regular layer",
|
"Fixed constant signals being editable from the regular layer",
|
||||||
"Fixed items still overlapping sometimes between buildings and belts",
|
"Fixed items still overlapping sometimes between buildings and belts",
|
||||||
|
"Updated translations (Thanks to all contributors!)",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -13,7 +13,7 @@ import { cachebust } from "./cachebust";
|
|||||||
const logger = createLogger("background_loader");
|
const logger = createLogger("background_loader");
|
||||||
|
|
||||||
const essentialMainMenuSprites = [
|
const essentialMainMenuSprites = [
|
||||||
"logo.png",
|
G_CHINA_VERSION ? "logo_cn.png" : "logo.png",
|
||||||
...G_ALL_UI_IMAGES.filter(src => src.startsWith("ui/") && src.indexOf(".gif") < 0),
|
...G_ALL_UI_IMAGES.filter(src => src.startsWith("ui/") && src.indexOf(".gif") < 0),
|
||||||
];
|
];
|
||||||
const essentialMainMenuSounds = [
|
const essentialMainMenuSounds = [
|
||||||
|
@ -40,6 +40,9 @@ export const globalConfig = {
|
|||||||
assetsSharpness: 1.5,
|
assetsSharpness: 1.5,
|
||||||
shapesSharpness: 1.4,
|
shapesSharpness: 1.4,
|
||||||
|
|
||||||
|
// Achievements
|
||||||
|
achievementSliceDuration: 10, // Seconds
|
||||||
|
|
||||||
// Production analytics
|
// Production analytics
|
||||||
statisticsGraphDpi: 2.5,
|
statisticsGraphDpi: 2.5,
|
||||||
statisticsGraphSlices: 100,
|
statisticsGraphSlices: 100,
|
||||||
|
@ -59,6 +59,9 @@ export default {
|
|||||||
// Enables ads in the local build (normally they are deactivated there)
|
// Enables ads in the local build (normally they are deactivated there)
|
||||||
// testAds: true,
|
// testAds: true,
|
||||||
// -----------------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------------
|
||||||
|
// Allows unlocked achievements to be logged to console in the local build
|
||||||
|
// testAchievements: true,
|
||||||
|
// -----------------------------------------------------------------------------------
|
||||||
// Disables the automatic switch to an overview when zooming out
|
// Disables the automatic switch to an overview when zooming out
|
||||||
// disableMapOverview: true,
|
// disableMapOverview: true,
|
||||||
// -----------------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------------
|
148
src/js/game/achievement_proxy.js
Normal file
148
src/js/game/achievement_proxy.js
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
/* typehints:start */
|
||||||
|
import { Entity } from "./entity";
|
||||||
|
import { GameRoot } from "./root";
|
||||||
|
/* typehints:end */
|
||||||
|
|
||||||
|
import { globalConfig } from "../core/config";
|
||||||
|
import { createLogger } from "../core/logging";
|
||||||
|
import { ACHIEVEMENTS } from "../platform/achievement_provider";
|
||||||
|
import { getBuildingDataFromCode } from "./building_codes";
|
||||||
|
|
||||||
|
const logger = createLogger("achievement_proxy");
|
||||||
|
|
||||||
|
const ROTATER = "rotater";
|
||||||
|
const DEFAULT = "default";
|
||||||
|
|
||||||
|
export class AchievementProxy {
|
||||||
|
/** @param {GameRoot} root */
|
||||||
|
constructor(root) {
|
||||||
|
this.root = root;
|
||||||
|
this.provider = this.root.app.achievementProvider;
|
||||||
|
this.disabled = true;
|
||||||
|
|
||||||
|
if (G_IS_DEV && globalConfig.debug.testAchievements) {
|
||||||
|
// still enable the proxy
|
||||||
|
} else if (!this.provider.hasAchievements()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.sliceTime = 0;
|
||||||
|
|
||||||
|
this.root.signals.postLoadHook.add(this.onLoad, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
onLoad() {
|
||||||
|
this.provider
|
||||||
|
.onLoad(this.root)
|
||||||
|
.then(() => {
|
||||||
|
this.disabled = false;
|
||||||
|
logger.log("Recieving achievement signals");
|
||||||
|
this.initialize();
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
this.disabled = true;
|
||||||
|
logger.error("Ignoring achievement signals", err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize() {
|
||||||
|
this.root.signals.achievementCheck.dispatch(ACHIEVEMENTS.darkMode);
|
||||||
|
|
||||||
|
if (this.has(ACHIEVEMENTS.mam)) {
|
||||||
|
this.root.signals.entityAdded.add(this.onMamFailure, this);
|
||||||
|
this.root.signals.entityDestroyed.add(this.onMamFailure, this);
|
||||||
|
this.root.signals.storyGoalCompleted.add(this.onStoryGoalCompleted, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.has(ACHIEVEMENTS.noInverseRotater)) {
|
||||||
|
this.root.signals.entityAdded.add(this.onEntityAdded, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.startSlice();
|
||||||
|
}
|
||||||
|
|
||||||
|
startSlice() {
|
||||||
|
this.sliceTime = this.root.time.now();
|
||||||
|
|
||||||
|
this.root.signals.bulkAchievementCheck.dispatch(
|
||||||
|
ACHIEVEMENTS.storeShape,
|
||||||
|
this.sliceTime,
|
||||||
|
ACHIEVEMENTS.throughputBp25,
|
||||||
|
this.sliceTime,
|
||||||
|
ACHIEVEMENTS.throughputBp50,
|
||||||
|
this.sliceTime,
|
||||||
|
ACHIEVEMENTS.throughputLogo25,
|
||||||
|
this.sliceTime,
|
||||||
|
ACHIEVEMENTS.throughputLogo50,
|
||||||
|
this.sliceTime,
|
||||||
|
ACHIEVEMENTS.throughputRocket10,
|
||||||
|
this.sliceTime,
|
||||||
|
ACHIEVEMENTS.throughputRocket20,
|
||||||
|
this.sliceTime,
|
||||||
|
ACHIEVEMENTS.play1h,
|
||||||
|
this.sliceTime,
|
||||||
|
ACHIEVEMENTS.play10h,
|
||||||
|
this.sliceTime,
|
||||||
|
ACHIEVEMENTS.play20h,
|
||||||
|
this.sliceTime
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
update() {
|
||||||
|
if (this.disabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.root.time.now() - this.sliceTime > globalConfig.achievementSliceDuration) {
|
||||||
|
this.startSlice();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} key
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
has(key) {
|
||||||
|
if (!this.provider.collection) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return this.provider.collection.map.has(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @param {Entity} entity */
|
||||||
|
onEntityAdded(entity) {
|
||||||
|
if (!entity.components.StaticMapEntity) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const building = getBuildingDataFromCode(entity.components.StaticMapEntity.code);
|
||||||
|
|
||||||
|
if (building.metaInstance.id !== ROTATER) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (building.variant === DEFAULT) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.root.savegame.currentData.stats.usedInverseRotater = true;
|
||||||
|
this.root.signals.entityAdded.remove(this.onEntityAdded);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @param {number} level */
|
||||||
|
onStoryGoalCompleted(level) {
|
||||||
|
if (level > 26) {
|
||||||
|
this.root.signals.entityAdded.add(this.onMamFailure, this);
|
||||||
|
this.root.signals.entityDestroyed.add(this.onMamFailure, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.root.signals.achievementCheck.dispatch(ACHIEVEMENTS.mam);
|
||||||
|
|
||||||
|
// reset on every level
|
||||||
|
this.root.savegame.currentData.stats.failedMam = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
onMamFailure() {
|
||||||
|
this.root.savegame.currentData.stats.failedMam = true;
|
||||||
|
}
|
||||||
|
}
|
@ -3,6 +3,7 @@ import { DrawParameters } from "../core/draw_parameters";
|
|||||||
import { findNiceIntegerValue } from "../core/utils";
|
import { findNiceIntegerValue } from "../core/utils";
|
||||||
import { Vector } from "../core/vector";
|
import { Vector } from "../core/vector";
|
||||||
import { Entity } from "./entity";
|
import { Entity } from "./entity";
|
||||||
|
import { ACHIEVEMENTS } from "../platform/achievement_provider";
|
||||||
import { GameRoot } from "./root";
|
import { GameRoot } from "./root";
|
||||||
|
|
||||||
export class Blueprint {
|
export class Blueprint {
|
||||||
@ -148,7 +149,7 @@ export class Blueprint {
|
|||||||
*/
|
*/
|
||||||
tryPlace(root, tile) {
|
tryPlace(root, tile) {
|
||||||
return root.logic.performBulkOperation(() => {
|
return root.logic.performBulkOperation(() => {
|
||||||
let anyPlaced = false;
|
let count = 0;
|
||||||
for (let i = 0; i < this.entities.length; ++i) {
|
for (let i = 0; i < this.entities.length; ++i) {
|
||||||
const entity = this.entities[i];
|
const entity = this.entities[i];
|
||||||
if (!root.logic.checkCanPlaceEntity(entity, tile, true)) {
|
if (!root.logic.checkCanPlaceEntity(entity, tile, true)) {
|
||||||
@ -160,9 +161,17 @@ export class Blueprint {
|
|||||||
root.logic.freeEntityAreaBeforeBuild(clone);
|
root.logic.freeEntityAreaBeforeBuild(clone);
|
||||||
root.map.placeStaticEntity(clone);
|
root.map.placeStaticEntity(clone);
|
||||||
root.entityMgr.registerEntity(clone, null, true);
|
root.entityMgr.registerEntity(clone, null, true);
|
||||||
anyPlaced = true;
|
count++;
|
||||||
}
|
}
|
||||||
return anyPlaced;
|
|
||||||
|
root.signals.bulkAchievementCheck.dispatch(
|
||||||
|
ACHIEVEMENTS.placeBlueprint,
|
||||||
|
count,
|
||||||
|
ACHIEVEMENTS.placeBp1000,
|
||||||
|
count
|
||||||
|
);
|
||||||
|
|
||||||
|
return count !== 0;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { generateMatrixRotations } from "../../core/utils";
|
import { generateMatrixRotations } from "../../core/utils";
|
||||||
import { enumDirection, Vector } from "../../core/vector";
|
import { enumDirection, Vector } from "../../core/vector";
|
||||||
|
import { ACHIEVEMENTS } from "../../platform/achievement_provider";
|
||||||
import { ItemAcceptorComponent } from "../components/item_acceptor";
|
import { ItemAcceptorComponent } from "../components/item_acceptor";
|
||||||
import { enumItemProcessorTypes, ItemProcessorComponent } from "../components/item_processor";
|
import { enumItemProcessorTypes, ItemProcessorComponent } from "../components/item_processor";
|
||||||
import { Entity } from "../entity";
|
import { Entity } from "../entity";
|
||||||
@ -93,6 +94,25 @@ export class MetaTrashBuilding extends MetaBuilding {
|
|||||||
return MetaTrashBuilding.renderPins[variant]();
|
return MetaTrashBuilding.renderPins[variant]();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addAchievementReceiver(entity) {
|
||||||
|
if (!entity.root) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const itemProcessor = entity.components.ItemProcessor;
|
||||||
|
const tryTakeItem = itemProcessor.tryTakeItem.bind(itemProcessor);
|
||||||
|
|
||||||
|
itemProcessor.tryTakeItem = () => {
|
||||||
|
const taken = tryTakeItem(...arguments);
|
||||||
|
|
||||||
|
if (taken) {
|
||||||
|
entity.root.signals.achievementCheck.dispatch(ACHIEVEMENTS.trash1000, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return taken;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates the entity at the given location
|
* Creates the entity at the given location
|
||||||
* @param {Entity} entity
|
* @param {Entity} entity
|
||||||
@ -107,7 +127,7 @@ export class MetaTrashBuilding extends MetaBuilding {
|
|||||||
* @param {string} variant
|
* @param {string} variant
|
||||||
*/
|
*/
|
||||||
updateVariants(entity, rotationVariant, variant) {
|
updateVariants(entity, rotationVariant, variant) {
|
||||||
MetaTrashBuilding.componentVariations[variant](entity, rotationVariant);
|
MetaTrashBuilding.componentVariations[variant].bind(this)(entity, rotationVariant);
|
||||||
}
|
}
|
||||||
|
|
||||||
static setupEntityComponents = [
|
static setupEntityComponents = [
|
||||||
@ -134,6 +154,10 @@ export class MetaTrashBuilding extends MetaBuilding {
|
|||||||
processorType: enumItemProcessorTypes.trash,
|
processorType: enumItemProcessorTypes.trash,
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
|
function (entity) {
|
||||||
|
// @ts-ignore
|
||||||
|
this.addAchievementReceiver(entity);
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
static overlayMatrices = {
|
static overlayMatrices = {
|
||||||
|
@ -36,6 +36,7 @@ import { RegularGameMode } from "./modes/regular";
|
|||||||
import { ProductionAnalytics } from "./production_analytics";
|
import { ProductionAnalytics } from "./production_analytics";
|
||||||
import { GameRoot } from "./root";
|
import { GameRoot } from "./root";
|
||||||
import { ShapeDefinitionManager } from "./shape_definition_manager";
|
import { ShapeDefinitionManager } from "./shape_definition_manager";
|
||||||
|
import { AchievementProxy } from "./achievement_proxy";
|
||||||
import { SoundProxy } from "./sound_proxy";
|
import { SoundProxy } from "./sound_proxy";
|
||||||
import { GameTime } from "./time/game_time";
|
import { GameTime } from "./time/game_time";
|
||||||
|
|
||||||
@ -114,6 +115,7 @@ export class GameCore {
|
|||||||
root.logic = new GameLogic(root);
|
root.logic = new GameLogic(root);
|
||||||
root.hud = new GameHUD(root);
|
root.hud = new GameHUD(root);
|
||||||
root.time = new GameTime(root);
|
root.time = new GameTime(root);
|
||||||
|
root.achievementProxy = new AchievementProxy(root);
|
||||||
root.automaticSave = new AutomaticSave(root);
|
root.automaticSave = new AutomaticSave(root);
|
||||||
root.soundProxy = new SoundProxy(root);
|
root.soundProxy = new SoundProxy(root);
|
||||||
|
|
||||||
@ -164,6 +166,9 @@ export class GameCore {
|
|||||||
|
|
||||||
// Update analytics
|
// Update analytics
|
||||||
root.productionAnalytics.update();
|
root.productionAnalytics.update();
|
||||||
|
|
||||||
|
// Check achievements
|
||||||
|
root.achievementProxy.update();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -294,6 +299,9 @@ export class GameCore {
|
|||||||
|
|
||||||
// Update analytics
|
// Update analytics
|
||||||
root.productionAnalytics.update();
|
root.productionAnalytics.update();
|
||||||
|
|
||||||
|
// Check achievements
|
||||||
|
root.achievementProxy.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update automatic save after everything finished
|
// Update automatic save after everything finished
|
||||||
|
@ -169,7 +169,7 @@ export class HubGoals extends BasicSerializableObject {
|
|||||||
getCurrentGoalDelivered() {
|
getCurrentGoalDelivered() {
|
||||||
if (this.currentGoal.throughputOnly) {
|
if (this.currentGoal.throughputOnly) {
|
||||||
return (
|
return (
|
||||||
this.root.productionAnalytics.getCurrentShapeRate(
|
this.root.productionAnalytics.getCurrentShapeRateRaw(
|
||||||
enumAnalyticsDataSource.delivered,
|
enumAnalyticsDataSource.delivered,
|
||||||
this.currentGoal.definition
|
this.currentGoal.definition
|
||||||
) / globalConfig.analyticsSliceDurationSeconds
|
) / globalConfig.analyticsSliceDurationSeconds
|
||||||
|
@ -8,6 +8,7 @@ import { globalConfig } from "../../../core/config";
|
|||||||
import { makeDiv, formatBigNumber, formatBigNumberFull } from "../../../core/utils";
|
import { makeDiv, formatBigNumber, formatBigNumberFull } from "../../../core/utils";
|
||||||
import { DynamicDomAttach } from "../dynamic_dom_attach";
|
import { DynamicDomAttach } from "../dynamic_dom_attach";
|
||||||
import { createLogger } from "../../../core/logging";
|
import { createLogger } from "../../../core/logging";
|
||||||
|
import { ACHIEVEMENTS } from "../../../platform/achievement_provider";
|
||||||
import { enumMouseButton } from "../../camera";
|
import { enumMouseButton } from "../../camera";
|
||||||
import { T } from "../../../translations";
|
import { T } from "../../../translations";
|
||||||
import { KEYMAPPINGS } from "../../key_action_mapper";
|
import { KEYMAPPINGS } from "../../key_action_mapper";
|
||||||
@ -100,6 +101,7 @@ export class HUDMassSelector extends BaseHUDPart {
|
|||||||
*/
|
*/
|
||||||
const mapUidToEntity = this.root.entityMgr.getFrozenUidSearchMap();
|
const mapUidToEntity = this.root.entityMgr.getFrozenUidSearchMap();
|
||||||
|
|
||||||
|
let count = 0;
|
||||||
this.root.logic.performBulkOperation(() => {
|
this.root.logic.performBulkOperation(() => {
|
||||||
for (let i = 0; i < entityUids.length; ++i) {
|
for (let i = 0; i < entityUids.length; ++i) {
|
||||||
const uid = entityUids[i];
|
const uid = entityUids[i];
|
||||||
@ -111,8 +113,12 @@ export class HUDMassSelector extends BaseHUDPart {
|
|||||||
|
|
||||||
if (!this.root.logic.tryDeleteBuilding(entity)) {
|
if (!this.root.logic.tryDeleteBuilding(entity)) {
|
||||||
logger.error("Error in mass delete, could not remove building");
|
logger.error("Error in mass delete, could not remove building");
|
||||||
|
} else {
|
||||||
|
count++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.root.signals.achievementCheck.dispatch(ACHIEVEMENTS.destroy1000, count);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Clear uids later
|
// Clear uids later
|
||||||
|
@ -273,7 +273,7 @@ export class HUDPinnedShapes extends BaseHUDPart {
|
|||||||
|
|
||||||
if (handle.throughputOnly) {
|
if (handle.throughputOnly) {
|
||||||
currentValue =
|
currentValue =
|
||||||
this.root.productionAnalytics.getCurrentShapeRate(
|
this.root.productionAnalytics.getCurrentShapeRateRaw(
|
||||||
enumAnalyticsDataSource.delivered,
|
enumAnalyticsDataSource.delivered,
|
||||||
handle.definition
|
handle.definition
|
||||||
) / globalConfig.analyticsSliceDurationSeconds;
|
) / globalConfig.analyticsSliceDurationSeconds;
|
||||||
|
@ -5,7 +5,7 @@ import { T } from "../../../translations";
|
|||||||
import { BaseHUDPart } from "../base_hud_part";
|
import { BaseHUDPart } from "../base_hud_part";
|
||||||
import { DynamicDomAttach } from "../dynamic_dom_attach";
|
import { DynamicDomAttach } from "../dynamic_dom_attach";
|
||||||
|
|
||||||
const showIntervalSeconds = 30 * 60;
|
const showIntervalSeconds = 9 * 60;
|
||||||
|
|
||||||
export class HUDStandaloneAdvantages extends BaseHUDPart {
|
export class HUDStandaloneAdvantages extends BaseHUDPart {
|
||||||
createElements(parent) {
|
createElements(parent) {
|
||||||
@ -25,7 +25,7 @@ export class HUDStandaloneAdvantages extends BaseHUDPart {
|
|||||||
([key, trans]) => `
|
([key, trans]) => `
|
||||||
<div class="point ${key}">
|
<div class="point ${key}">
|
||||||
<strong>${trans.title}</strong>
|
<strong>${trans.title}</strong>
|
||||||
<p>${trans.desc}</p>
|
<p>${trans.desc}</p>
|
||||||
</div>`
|
</div>`
|
||||||
)
|
)
|
||||||
.join("")}
|
.join("")}
|
||||||
@ -60,7 +60,7 @@ export class HUDStandaloneAdvantages extends BaseHUDPart {
|
|||||||
this.inputReciever = new InputReceiver("standalone-advantages");
|
this.inputReciever = new InputReceiver("standalone-advantages");
|
||||||
this.close();
|
this.close();
|
||||||
|
|
||||||
this.lastShown = this.root.gameIsFresh ? this.root.time.now() : 0;
|
this.lastShown = -1e10;
|
||||||
}
|
}
|
||||||
|
|
||||||
show() {
|
show() {
|
||||||
|
@ -209,7 +209,9 @@ export class HUDStatistics extends BaseHUDPart {
|
|||||||
}
|
}
|
||||||
case enumAnalyticsDataSource.produced:
|
case enumAnalyticsDataSource.produced:
|
||||||
case enumAnalyticsDataSource.delivered: {
|
case enumAnalyticsDataSource.delivered: {
|
||||||
entries = Object.entries(this.root.productionAnalytics.getCurrentShapeRates(this.dataSource));
|
entries = Object.entries(
|
||||||
|
this.root.productionAnalytics.getCurrentShapeRatesRaw(this.dataSource)
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -100,7 +100,7 @@ export class HUDShapeStatisticsHandle {
|
|||||||
case enumAnalyticsDataSource.delivered:
|
case enumAnalyticsDataSource.delivered:
|
||||||
case enumAnalyticsDataSource.produced: {
|
case enumAnalyticsDataSource.produced: {
|
||||||
let rate =
|
let rate =
|
||||||
this.root.productionAnalytics.getCurrentShapeRate(dataSource, this.definition) /
|
this.root.productionAnalytics.getCurrentShapeRateRaw(dataSource, this.definition) /
|
||||||
globalConfig.analyticsSliceDurationSeconds;
|
globalConfig.analyticsSliceDurationSeconds;
|
||||||
|
|
||||||
this.counter.innerText = T.ingame.statistics.shapesDisplayUnits[unit].replace(
|
this.counter.innerText = T.ingame.statistics.shapesDisplayUnits[unit].replace(
|
||||||
|
@ -15,6 +15,7 @@ import {
|
|||||||
removeAllChildren,
|
removeAllChildren,
|
||||||
} from "../../../core/utils";
|
} from "../../../core/utils";
|
||||||
import { Vector } from "../../../core/vector";
|
import { Vector } from "../../../core/vector";
|
||||||
|
import { ACHIEVEMENTS } from "../../../platform/achievement_provider";
|
||||||
import { T } from "../../../translations";
|
import { T } from "../../../translations";
|
||||||
import { BaseItem } from "../../base_item";
|
import { BaseItem } from "../../base_item";
|
||||||
import { MetaHubBuilding } from "../../buildings/hub";
|
import { MetaHubBuilding } from "../../buildings/hub";
|
||||||
@ -352,6 +353,10 @@ export class HUDWaypoints extends BaseHUDPart {
|
|||||||
T.ingame.waypoints.creationSuccessNotification,
|
T.ingame.waypoints.creationSuccessNotification,
|
||||||
enumNotificationType.success
|
enumNotificationType.success
|
||||||
);
|
);
|
||||||
|
this.root.signals.achievementCheck.dispatch(
|
||||||
|
ACHIEVEMENTS.mapMarkers15,
|
||||||
|
this.waypoints.length - 1 // Disregard HUB
|
||||||
|
);
|
||||||
|
|
||||||
// Re-render the list and thus add it
|
// Re-render the list and thus add it
|
||||||
this.rerenderWaypointList();
|
this.rerenderWaypointList();
|
||||||
|
@ -83,7 +83,7 @@ export class ProductionAnalytics extends BasicSerializableObject {
|
|||||||
* @param {enumAnalyticsDataSource} dataSource
|
* @param {enumAnalyticsDataSource} dataSource
|
||||||
* @param {ShapeDefinition} definition
|
* @param {ShapeDefinition} definition
|
||||||
*/
|
*/
|
||||||
getCurrentShapeRate(dataSource, definition) {
|
getCurrentShapeRateRaw(dataSource, definition) {
|
||||||
const slices = this.history[dataSource];
|
const slices = this.history[dataSource];
|
||||||
return slices[slices.length - 2][definition.getHash()] || 0;
|
return slices[slices.length - 2][definition.getHash()] || 0;
|
||||||
}
|
}
|
||||||
@ -108,7 +108,7 @@ export class ProductionAnalytics extends BasicSerializableObject {
|
|||||||
* Returns the rates of all shapes
|
* Returns the rates of all shapes
|
||||||
* @param {enumAnalyticsDataSource} dataSource
|
* @param {enumAnalyticsDataSource} dataSource
|
||||||
*/
|
*/
|
||||||
getCurrentShapeRates(dataSource) {
|
getCurrentShapeRatesRaw(dataSource) {
|
||||||
const slices = this.history[dataSource];
|
const slices = this.history[dataSource];
|
||||||
|
|
||||||
// First, copy current slice
|
// First, copy current slice
|
||||||
|
@ -8,6 +8,7 @@ import { createLogger } from "../core/logging";
|
|||||||
import { GameTime } from "./time/game_time";
|
import { GameTime } from "./time/game_time";
|
||||||
import { EntityManager } from "./entity_manager";
|
import { EntityManager } from "./entity_manager";
|
||||||
import { GameSystemManager } from "./game_system_manager";
|
import { GameSystemManager } from "./game_system_manager";
|
||||||
|
import { AchievementProxy } from "./achievement_proxy";
|
||||||
import { GameHUD } from "./hud/hud";
|
import { GameHUD } from "./hud/hud";
|
||||||
import { MapView } from "./map_view";
|
import { MapView } from "./map_view";
|
||||||
import { Camera } from "./camera";
|
import { Camera } from "./camera";
|
||||||
@ -119,6 +120,9 @@ export class GameRoot {
|
|||||||
/** @type {SoundProxy} */
|
/** @type {SoundProxy} */
|
||||||
this.soundProxy = null;
|
this.soundProxy = null;
|
||||||
|
|
||||||
|
/** @type {AchievementProxy} */
|
||||||
|
this.achievementProxy = null;
|
||||||
|
|
||||||
/** @type {ShapeDefinitionManager} */
|
/** @type {ShapeDefinitionManager} */
|
||||||
this.shapeDefinitionMgr = null;
|
this.shapeDefinitionMgr = null;
|
||||||
|
|
||||||
@ -175,6 +179,10 @@ export class GameRoot {
|
|||||||
// Called before actually placing an entity, use to perform additional logic
|
// Called before actually placing an entity, use to perform additional logic
|
||||||
// for freeing space before actually placing.
|
// for freeing space before actually placing.
|
||||||
freeEntityAreaBeforeBuild: /** @type {TypedSignal<[Entity]>} */ (new Signal()),
|
freeEntityAreaBeforeBuild: /** @type {TypedSignal<[Entity]>} */ (new Signal()),
|
||||||
|
|
||||||
|
// Called with an achievement key and necessary args to validate it can be unlocked.
|
||||||
|
achievementCheck: /** @type {TypedSignal<[string, *]>} */ (new Signal()),
|
||||||
|
bulkAchievementCheck: /** @type {TypedSignal<(string|any)[]>} */ (new Signal()),
|
||||||
};
|
};
|
||||||
|
|
||||||
// RNG's
|
// RNG's
|
||||||
|
@ -4,6 +4,7 @@ import { enumColors } from "./colors";
|
|||||||
import { ShapeItem } from "./items/shape_item";
|
import { ShapeItem } from "./items/shape_item";
|
||||||
import { GameRoot } from "./root";
|
import { GameRoot } from "./root";
|
||||||
import { enumSubShape, ShapeDefinition } from "./shape_definition";
|
import { enumSubShape, ShapeDefinition } from "./shape_definition";
|
||||||
|
import { ACHIEVEMENTS } from "../platform/achievement_provider";
|
||||||
|
|
||||||
const logger = createLogger("shape_definition_manager");
|
const logger = createLogger("shape_definition_manager");
|
||||||
|
|
||||||
@ -96,6 +97,8 @@ export class ShapeDefinitionManager extends BasicSerializableObject {
|
|||||||
const rightSide = definition.cloneFilteredByQuadrants([2, 3]);
|
const rightSide = definition.cloneFilteredByQuadrants([2, 3]);
|
||||||
const leftSide = definition.cloneFilteredByQuadrants([0, 1]);
|
const leftSide = definition.cloneFilteredByQuadrants([0, 1]);
|
||||||
|
|
||||||
|
this.root.signals.achievementCheck.dispatch(ACHIEVEMENTS.cutShape);
|
||||||
|
|
||||||
return /** @type {[ShapeDefinition, ShapeDefinition]} */ (this.operationCache[key] = [
|
return /** @type {[ShapeDefinition, ShapeDefinition]} */ (this.operationCache[key] = [
|
||||||
this.registerOrReturnHandle(rightSide),
|
this.registerOrReturnHandle(rightSide),
|
||||||
this.registerOrReturnHandle(leftSide),
|
this.registerOrReturnHandle(leftSide),
|
||||||
@ -137,6 +140,8 @@ export class ShapeDefinitionManager extends BasicSerializableObject {
|
|||||||
|
|
||||||
const rotated = definition.cloneRotateCW();
|
const rotated = definition.cloneRotateCW();
|
||||||
|
|
||||||
|
this.root.signals.achievementCheck.dispatch(ACHIEVEMENTS.rotateShape);
|
||||||
|
|
||||||
return /** @type {ShapeDefinition} */ (this.operationCache[key] = this.registerOrReturnHandle(
|
return /** @type {ShapeDefinition} */ (this.operationCache[key] = this.registerOrReturnHandle(
|
||||||
rotated
|
rotated
|
||||||
));
|
));
|
||||||
@ -189,6 +194,9 @@ export class ShapeDefinitionManager extends BasicSerializableObject {
|
|||||||
if (this.operationCache[key]) {
|
if (this.operationCache[key]) {
|
||||||
return /** @type {ShapeDefinition} */ (this.operationCache[key]);
|
return /** @type {ShapeDefinition} */ (this.operationCache[key]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.root.signals.achievementCheck.dispatch(ACHIEVEMENTS.stackShape);
|
||||||
|
|
||||||
const stacked = lowerDefinition.cloneAndStackWith(upperDefinition);
|
const stacked = lowerDefinition.cloneAndStackWith(upperDefinition);
|
||||||
return /** @type {ShapeDefinition} */ (this.operationCache[key] = this.registerOrReturnHandle(
|
return /** @type {ShapeDefinition} */ (this.operationCache[key] = this.registerOrReturnHandle(
|
||||||
stacked
|
stacked
|
||||||
@ -206,6 +214,9 @@ export class ShapeDefinitionManager extends BasicSerializableObject {
|
|||||||
if (this.operationCache[key]) {
|
if (this.operationCache[key]) {
|
||||||
return /** @type {ShapeDefinition} */ (this.operationCache[key]);
|
return /** @type {ShapeDefinition} */ (this.operationCache[key]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.root.signals.achievementCheck.dispatch(ACHIEVEMENTS.paintShape);
|
||||||
|
|
||||||
const colorized = definition.cloneAndPaintWith(color);
|
const colorized = definition.cloneAndPaintWith(color);
|
||||||
return /** @type {ShapeDefinition} */ (this.operationCache[key] = this.registerOrReturnHandle(
|
return /** @type {ShapeDefinition} */ (this.operationCache[key] = this.registerOrReturnHandle(
|
||||||
colorized
|
colorized
|
||||||
|
@ -4,6 +4,7 @@ import { createLogger } from "../../core/logging";
|
|||||||
import { Rectangle } from "../../core/rectangle";
|
import { Rectangle } from "../../core/rectangle";
|
||||||
import { StaleAreaDetector } from "../../core/stale_area_detector";
|
import { StaleAreaDetector } from "../../core/stale_area_detector";
|
||||||
import { enumDirection, enumDirectionToVector } from "../../core/vector";
|
import { enumDirection, enumDirectionToVector } from "../../core/vector";
|
||||||
|
import { ACHIEVEMENTS } from "../../platform/achievement_provider";
|
||||||
import { BaseItem } from "../base_item";
|
import { BaseItem } from "../base_item";
|
||||||
import { BeltComponent } from "../components/belt";
|
import { BeltComponent } from "../components/belt";
|
||||||
import { ItemAcceptorComponent } from "../components/item_acceptor";
|
import { ItemAcceptorComponent } from "../components/item_acceptor";
|
||||||
|
@ -13,6 +13,7 @@ import {
|
|||||||
enumInvertedDirections,
|
enumInvertedDirections,
|
||||||
Vector,
|
Vector,
|
||||||
} from "../../core/vector";
|
} from "../../core/vector";
|
||||||
|
import { ACHIEVEMENTS } from "../../platform/achievement_provider";
|
||||||
import { BaseItem } from "../base_item";
|
import { BaseItem } from "../base_item";
|
||||||
import { MetaWireBuilding } from "../buildings/wire";
|
import { MetaWireBuilding } from "../buildings/wire";
|
||||||
import { getCodeFromBuildingData } from "../building_codes";
|
import { getCodeFromBuildingData } from "../building_codes";
|
||||||
@ -702,6 +703,8 @@ export class WireSystem extends GameSystemWithFilter {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.root.signals.achievementCheck.dispatch(ACHIEVEMENTS.place5000Wires, entity);
|
||||||
|
|
||||||
// Invalidate affected area
|
// Invalidate affected area
|
||||||
const originalRect = staticComp.getTileSpaceBounds();
|
const originalRect = staticComp.getTileSpaceBounds();
|
||||||
const affectedArea = originalRect.expandedInAllDirections(1);
|
const affectedArea = originalRect.expandedInAllDirections(1);
|
||||||
|
2
src/js/globals.d.ts
vendored
2
src/js/globals.d.ts
vendored
@ -19,6 +19,8 @@ declare const G_BUILD_VERSION: string;
|
|||||||
declare const G_ALL_UI_IMAGES: Array<string>;
|
declare const G_ALL_UI_IMAGES: Array<string>;
|
||||||
declare const G_IS_RELEASE: boolean;
|
declare const G_IS_RELEASE: boolean;
|
||||||
|
|
||||||
|
declare const G_CHINA_VERSION : boolean;
|
||||||
|
|
||||||
// Polyfills
|
// Polyfills
|
||||||
declare interface String {
|
declare interface String {
|
||||||
replaceAll(search: string, replacement: string): string;
|
replaceAll(search: string, replacement: string): string;
|
||||||
|
634
src/js/platform/achievement_provider.js
Normal file
634
src/js/platform/achievement_provider.js
Normal file
@ -0,0 +1,634 @@
|
|||||||
|
/* typehints:start */
|
||||||
|
import { Application } from "../application";
|
||||||
|
import { Entity } from "../game/entity";
|
||||||
|
import { GameRoot } from "../game/root";
|
||||||
|
import { ShapeDefinition } from "../game/shape_definition";
|
||||||
|
import { THEMES } from "../game/theme";
|
||||||
|
/* typehints:end */
|
||||||
|
|
||||||
|
import { enumAnalyticsDataSource } from "../game/production_analytics";
|
||||||
|
import { ShapeItem } from "../game/items/shape_item";
|
||||||
|
import { globalConfig } from "../core/config";
|
||||||
|
|
||||||
|
export const ACHIEVEMENTS = {
|
||||||
|
belt500Tiles: "belt500Tiles",
|
||||||
|
blueprint100k: "blueprint100k",
|
||||||
|
blueprint1m: "blueprint1m",
|
||||||
|
completeLvl26: "completeLvl26",
|
||||||
|
cutShape: "cutShape",
|
||||||
|
darkMode: "darkMode",
|
||||||
|
destroy1000: "destroy1000",
|
||||||
|
irrelevantShape: "irrelevantShape",
|
||||||
|
level100: "level100",
|
||||||
|
level50: "level50",
|
||||||
|
logoBefore18: "logoBefore18",
|
||||||
|
mam: "mam",
|
||||||
|
mapMarkers15: "mapMarkers15",
|
||||||
|
noBeltUpgradesUntilBp: "noBeltUpgradesUntilBp",
|
||||||
|
noInverseRotater: "noInverseRotater",
|
||||||
|
oldLevel17: "oldLevel17",
|
||||||
|
openWires: "openWires",
|
||||||
|
paintShape: "paintShape",
|
||||||
|
place5000Wires: "place5000Wires",
|
||||||
|
placeBlueprint: "placeBlueprint",
|
||||||
|
placeBp1000: "placeBp1000",
|
||||||
|
play1h: "play1h",
|
||||||
|
play10h: "play10h",
|
||||||
|
play20h: "play20h",
|
||||||
|
produceLogo: "produceLogo",
|
||||||
|
produceMsLogo: "produceMsLogo",
|
||||||
|
produceRocket: "produceRocket",
|
||||||
|
rotateShape: "rotateShape",
|
||||||
|
speedrunBp30: "speedrunBp30",
|
||||||
|
speedrunBp60: "speedrunBp60",
|
||||||
|
speedrunBp120: "speedrunBp120",
|
||||||
|
stack4Layers: "stack4Layers",
|
||||||
|
stackShape: "stackShape",
|
||||||
|
store100Unique: "store100Unique",
|
||||||
|
storeShape: "storeShape",
|
||||||
|
throughputBp25: "throughputBp25",
|
||||||
|
throughputBp50: "throughputBp50",
|
||||||
|
throughputLogo25: "throughputLogo25",
|
||||||
|
throughputLogo50: "throughputLogo50",
|
||||||
|
throughputRocket10: "throughputRocket10",
|
||||||
|
throughputRocket20: "throughputRocket20",
|
||||||
|
trash1000: "trash1000",
|
||||||
|
unlockWires: "unlockWires",
|
||||||
|
upgradesTier5: "upgradesTier5",
|
||||||
|
upgradesTier8: "upgradesTier8",
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @type {keyof typeof THEMES} */
|
||||||
|
const DARK_MODE = "dark";
|
||||||
|
|
||||||
|
const HOUR_1 = 3600; // Seconds
|
||||||
|
const HOUR_10 = HOUR_1 * 10;
|
||||||
|
const HOUR_20 = HOUR_1 * 20;
|
||||||
|
const ITEM_SHAPE = ShapeItem.getId();
|
||||||
|
const MINUTE_30 = 1800; // Seconds
|
||||||
|
const MINUTE_60 = MINUTE_30 * 2;
|
||||||
|
const MINUTE_120 = MINUTE_30 * 4;
|
||||||
|
const ROTATER_CCW_CODE = 12;
|
||||||
|
const ROTATER_180_CODE = 13;
|
||||||
|
const SHAPE_BP = "CbCbCbRb:CwCwCwCw";
|
||||||
|
const SHAPE_LOGO = "RuCw--Cw:----Ru--";
|
||||||
|
const SHAPE_MS_LOGO = "RgRyRbRr";
|
||||||
|
const SHAPE_OLD_LEVEL_17 = "WrRgWrRg:CwCrCwCr:SgSgSgSg";
|
||||||
|
const SHAPE_ROCKET = "CbCuCbCu:Sr------:--CrSrCr:CwCwCwCw";
|
||||||
|
|
||||||
|
/** @type {Layer} */
|
||||||
|
const WIRE_LAYER = "wires";
|
||||||
|
|
||||||
|
export class AchievementProviderInterface {
|
||||||
|
/* typehints:start */
|
||||||
|
collection = /** @type {AchievementCollection|undefined} */ (null);
|
||||||
|
/* typehints:end */
|
||||||
|
|
||||||
|
/** @param {Application} app */
|
||||||
|
constructor(app) {
|
||||||
|
this.app = app;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the achievement provider.
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
initialize() {
|
||||||
|
abstract;
|
||||||
|
return Promise.reject();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opportunity to do additional initialization work with the GameRoot.
|
||||||
|
* @param {GameRoot} root
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
onLoad(root) {
|
||||||
|
abstract;
|
||||||
|
return Promise.reject();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @returns {boolean} */
|
||||||
|
hasLoaded() {
|
||||||
|
abstract;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call to activate an achievement with the provider
|
||||||
|
* @param {string} key - Maps to an Achievement
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
activate(key) {
|
||||||
|
abstract;
|
||||||
|
return Promise.reject();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if achievements are supported in the current build
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
hasAchievements() {
|
||||||
|
abstract;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Achievement {
|
||||||
|
/** @param {string} key - An ACHIEVEMENTS key */
|
||||||
|
constructor(key) {
|
||||||
|
this.key = key;
|
||||||
|
this.activate = null;
|
||||||
|
this.activatePromise = null;
|
||||||
|
this.receiver = null;
|
||||||
|
this.signal = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {}
|
||||||
|
|
||||||
|
isValid() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
unlock() {
|
||||||
|
if (!this.activatePromise) {
|
||||||
|
this.activatePromise = this.activate(this.key);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.activatePromise;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AchievementCollection {
|
||||||
|
/**
|
||||||
|
* @param {function} activate - Resolves when provider activation is complete
|
||||||
|
*/
|
||||||
|
constructor(activate) {
|
||||||
|
this.map = new Map();
|
||||||
|
this.activate = activate;
|
||||||
|
|
||||||
|
this.add(ACHIEVEMENTS.belt500Tiles, {
|
||||||
|
isValid: this.isBelt500TilesValid,
|
||||||
|
signal: "entityAdded",
|
||||||
|
});
|
||||||
|
this.add(ACHIEVEMENTS.blueprint100k, {
|
||||||
|
isValid: this.isBlueprint100kValid,
|
||||||
|
signal: "shapeDelivered",
|
||||||
|
});
|
||||||
|
this.add(ACHIEVEMENTS.blueprint1m, {
|
||||||
|
isValid: this.isBlueprint1mValid,
|
||||||
|
signal: "shapeDelivered",
|
||||||
|
});
|
||||||
|
this.add(ACHIEVEMENTS.completeLvl26, this.createLevelOptions(26));
|
||||||
|
this.add(ACHIEVEMENTS.cutShape);
|
||||||
|
this.add(ACHIEVEMENTS.darkMode, {
|
||||||
|
isValid: this.isDarkModeValid,
|
||||||
|
});
|
||||||
|
this.add(ACHIEVEMENTS.destroy1000, {
|
||||||
|
isValid: this.isDestroy1000Valid,
|
||||||
|
});
|
||||||
|
this.add(ACHIEVEMENTS.irrelevantShape, {
|
||||||
|
isValid: this.isIrrelevantShapeValid,
|
||||||
|
signal: "shapeDelivered",
|
||||||
|
});
|
||||||
|
this.add(ACHIEVEMENTS.level100, this.createLevelOptions(100));
|
||||||
|
this.add(ACHIEVEMENTS.level50, this.createLevelOptions(50));
|
||||||
|
this.add(ACHIEVEMENTS.logoBefore18, {
|
||||||
|
isValid: this.isLogoBefore18Valid,
|
||||||
|
signal: "itemProduced",
|
||||||
|
});
|
||||||
|
this.add(ACHIEVEMENTS.mam, {
|
||||||
|
isValid: this.isMamValid,
|
||||||
|
});
|
||||||
|
this.add(ACHIEVEMENTS.mapMarkers15, {
|
||||||
|
isValid: this.isMapMarkers15Valid,
|
||||||
|
});
|
||||||
|
this.add(ACHIEVEMENTS.noBeltUpgradesUntilBp, {
|
||||||
|
isValid: this.isNoBeltUpgradesUntilBpValid,
|
||||||
|
signal: "storyGoalCompleted",
|
||||||
|
});
|
||||||
|
this.add(ACHIEVEMENTS.noInverseRotater, {
|
||||||
|
init: this.initNoInverseRotater,
|
||||||
|
isValid: this.isNoInverseRotaterValid,
|
||||||
|
signal: "storyGoalCompleted",
|
||||||
|
});
|
||||||
|
this.add(ACHIEVEMENTS.oldLevel17, this.createShapeOptions(SHAPE_OLD_LEVEL_17));
|
||||||
|
this.add(ACHIEVEMENTS.openWires, {
|
||||||
|
isValid: this.isOpenWiresValid,
|
||||||
|
signal: "editModeChanged",
|
||||||
|
});
|
||||||
|
this.add(ACHIEVEMENTS.paintShape);
|
||||||
|
this.add(ACHIEVEMENTS.place5000Wires, {
|
||||||
|
isValid: this.isPlace5000WiresValid,
|
||||||
|
});
|
||||||
|
this.add(ACHIEVEMENTS.placeBlueprint, {
|
||||||
|
isValid: this.isPlaceBlueprintValid,
|
||||||
|
});
|
||||||
|
this.add(ACHIEVEMENTS.placeBp1000, {
|
||||||
|
isValid: this.isPlaceBp1000Valid,
|
||||||
|
});
|
||||||
|
this.add(ACHIEVEMENTS.play1h, this.createTimeOptions(HOUR_1));
|
||||||
|
this.add(ACHIEVEMENTS.play10h, this.createTimeOptions(HOUR_10));
|
||||||
|
this.add(ACHIEVEMENTS.play20h, this.createTimeOptions(HOUR_20));
|
||||||
|
this.add(ACHIEVEMENTS.produceLogo, this.createShapeOptions(SHAPE_LOGO));
|
||||||
|
this.add(ACHIEVEMENTS.produceRocket, this.createShapeOptions(SHAPE_ROCKET));
|
||||||
|
this.add(ACHIEVEMENTS.produceMsLogo, this.createShapeOptions(SHAPE_MS_LOGO));
|
||||||
|
this.add(ACHIEVEMENTS.rotateShape);
|
||||||
|
this.add(ACHIEVEMENTS.speedrunBp30, this.createSpeedOptions(12, MINUTE_30));
|
||||||
|
this.add(ACHIEVEMENTS.speedrunBp60, this.createSpeedOptions(12, MINUTE_60));
|
||||||
|
this.add(ACHIEVEMENTS.speedrunBp120, this.createSpeedOptions(12, MINUTE_120));
|
||||||
|
this.add(ACHIEVEMENTS.stack4Layers, {
|
||||||
|
isValid: this.isStack4LayersValid,
|
||||||
|
signal: "itemProduced",
|
||||||
|
});
|
||||||
|
this.add(ACHIEVEMENTS.stackShape);
|
||||||
|
this.add(ACHIEVEMENTS.store100Unique, {
|
||||||
|
isValid: this.isStore100UniqueValid,
|
||||||
|
signal: "shapeDelivered",
|
||||||
|
});
|
||||||
|
this.add(ACHIEVEMENTS.storeShape, {
|
||||||
|
isValid: this.isStoreShapeValid,
|
||||||
|
});
|
||||||
|
this.add(ACHIEVEMENTS.throughputBp25, this.createRateOptions(SHAPE_BP, 25));
|
||||||
|
this.add(ACHIEVEMENTS.throughputBp50, this.createRateOptions(SHAPE_BP, 50));
|
||||||
|
this.add(ACHIEVEMENTS.throughputLogo25, this.createRateOptions(SHAPE_LOGO, 25));
|
||||||
|
this.add(ACHIEVEMENTS.throughputLogo50, this.createRateOptions(SHAPE_LOGO, 50));
|
||||||
|
this.add(ACHIEVEMENTS.throughputRocket10, this.createRateOptions(SHAPE_ROCKET, 25));
|
||||||
|
this.add(ACHIEVEMENTS.throughputRocket20, this.createRateOptions(SHAPE_ROCKET, 50));
|
||||||
|
this.add(ACHIEVEMENTS.trash1000, {
|
||||||
|
init: this.initTrash1000,
|
||||||
|
isValid: this.isTrash1000Valid,
|
||||||
|
});
|
||||||
|
this.add(ACHIEVEMENTS.unlockWires, this.createLevelOptions(20));
|
||||||
|
this.add(ACHIEVEMENTS.upgradesTier5, this.createUpgradeOptions(5));
|
||||||
|
this.add(ACHIEVEMENTS.upgradesTier8, this.createUpgradeOptions(8));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @param {GameRoot} root */
|
||||||
|
initialize(root) {
|
||||||
|
this.root = root;
|
||||||
|
this.root.signals.achievementCheck.add(this.unlock, this);
|
||||||
|
this.root.signals.bulkAchievementCheck.add(this.bulkUnlock, this);
|
||||||
|
|
||||||
|
for (let [key, achievement] of this.map.entries()) {
|
||||||
|
if (achievement.init) {
|
||||||
|
achievement.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (achievement.signal) {
|
||||||
|
achievement.receiver = this.unlock.bind(this, key);
|
||||||
|
this.root.signals[achievement.signal].add(achievement.receiver);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.hasDefaultReceivers()) {
|
||||||
|
this.root.signals.achievementCheck.remove(this.unlock);
|
||||||
|
this.root.signals.bulkAchievementCheck.remove(this.bulkUnlock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} key - Maps to an Achievement
|
||||||
|
* @param {object} [options]
|
||||||
|
* @param {function} [options.init]
|
||||||
|
* @param {function} [options.isValid]
|
||||||
|
* @param {string} [options.signal]
|
||||||
|
*/
|
||||||
|
add(key, options = {}) {
|
||||||
|
if (G_IS_DEV) {
|
||||||
|
assert(ACHIEVEMENTS[key], "Achievement key not found: ", key);
|
||||||
|
}
|
||||||
|
|
||||||
|
const achievement = new Achievement(key);
|
||||||
|
|
||||||
|
achievement.activate = this.activate;
|
||||||
|
|
||||||
|
if (options.init) {
|
||||||
|
achievement.init = options.init.bind(this, achievement);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.isValid) {
|
||||||
|
achievement.isValid = options.isValid.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.signal) {
|
||||||
|
achievement.signal = options.signal;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.map.set(key, achievement);
|
||||||
|
}
|
||||||
|
|
||||||
|
bulkUnlock() {
|
||||||
|
for (let i = 0; i < arguments.length; i += 2) {
|
||||||
|
this.unlock(arguments[i], arguments[i + 1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} key - Maps to an Achievement
|
||||||
|
* @param {?*} data - Data received from signal dispatches for validation
|
||||||
|
*/
|
||||||
|
unlock(key, data) {
|
||||||
|
if (!this.map.has(key)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const achievement = this.map.get(key);
|
||||||
|
|
||||||
|
if (!achievement.isValid(data)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
achievement
|
||||||
|
.unlock()
|
||||||
|
.then(() => {
|
||||||
|
this.onActivate(null, key);
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
this.onActivate(err, key);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cleans up after achievement activation attempt with the provider. Could
|
||||||
|
* utilize err to retry some number of times if needed.
|
||||||
|
* @param {?Error} err - Error is null if activation was successful
|
||||||
|
* @param {string} key - Maps to an Achievement
|
||||||
|
*/
|
||||||
|
onActivate(err, key) {
|
||||||
|
this.remove(key);
|
||||||
|
|
||||||
|
if (!this.hasDefaultReceivers()) {
|
||||||
|
this.root.signals.achievementCheck.remove(this.unlock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @param {string} key - Maps to an Achievement */
|
||||||
|
remove(key) {
|
||||||
|
const achievement = this.map.get(key);
|
||||||
|
if (achievement) {
|
||||||
|
if (achievement.receiver) {
|
||||||
|
this.root.signals[achievement.signal].remove(achievement.receiver);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.map.delete(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the collection-level achievementCheck receivers are still
|
||||||
|
* necessary.
|
||||||
|
*/
|
||||||
|
hasDefaultReceivers() {
|
||||||
|
if (!this.map.size) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let achievement of this.map.values()) {
|
||||||
|
if (!achievement.signal) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Remaining methods exist to extend Achievement instances within the
|
||||||
|
* collection.
|
||||||
|
*/
|
||||||
|
|
||||||
|
hasAllUpgradesAtLeastAtTier(tier) {
|
||||||
|
const upgrades = this.root.gameMode.getUpgrades();
|
||||||
|
|
||||||
|
for (let upgradeId in upgrades) {
|
||||||
|
if (this.root.hubGoals.getUpgradeLevel(upgradeId) < tier - 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {ShapeItem} item
|
||||||
|
* @param {string} shape
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
isShape(item, shape) {
|
||||||
|
return item.getItemType() === ITEM_SHAPE && item.definition.getHash() === shape;
|
||||||
|
}
|
||||||
|
|
||||||
|
createLevelOptions(level) {
|
||||||
|
return {
|
||||||
|
isValid: currentLevel => currentLevel >= level,
|
||||||
|
signal: "storyGoalCompleted",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
createRateOptions(shape, rate) {
|
||||||
|
return {
|
||||||
|
isValid: () => {
|
||||||
|
return (
|
||||||
|
this.root.productionAnalytics.getCurrentShapeRateRaw(
|
||||||
|
enumAnalyticsDataSource.delivered,
|
||||||
|
this.root.shapeDefinitionMgr.getShapeFromShortKey(shape)
|
||||||
|
) /
|
||||||
|
globalConfig.analyticsSliceDurationSeconds >=
|
||||||
|
rate
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
createShapeOptions(shape) {
|
||||||
|
return {
|
||||||
|
isValid: item => this.isShape(item, shape),
|
||||||
|
signal: "itemProduced",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
createSpeedOptions(level, time) {
|
||||||
|
return {
|
||||||
|
isValid: currentLevel => currentLevel >= level && this.root.time.now() < time,
|
||||||
|
signal: "storyGoalCompleted",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
createTimeOptions(duration) {
|
||||||
|
return {
|
||||||
|
isValid: () => this.root.time.now() >= duration,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
createUpgradeOptions(tier) {
|
||||||
|
return {
|
||||||
|
isValid: () => this.hasAllUpgradesAtLeastAtTier(tier),
|
||||||
|
signal: "upgradePurchased",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @param {Entity} entity @returns {boolean} */
|
||||||
|
isBelt500TilesValid(entity) {
|
||||||
|
return entity.components.Belt && entity.components.Belt.assignedPath.totalLength >= 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @param {ShapeDefinition} definition @returns {boolean} */
|
||||||
|
isBlueprint100kValid(definition) {
|
||||||
|
return definition.cachedHash === SHAPE_BP && this.root.hubGoals.storedShapes[SHAPE_BP] >= 100000;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @param {ShapeDefinition} definition @returns {boolean} */
|
||||||
|
isBlueprint1mValid(definition) {
|
||||||
|
return definition.cachedHash === SHAPE_BP && this.root.hubGoals.storedShapes[SHAPE_BP] >= 1000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @returns {boolean} */
|
||||||
|
isDarkModeValid() {
|
||||||
|
return this.root.app.settings.currentData.settings.theme === DARK_MODE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @param {number} count @returns {boolean} */
|
||||||
|
isDestroy1000Valid(count) {
|
||||||
|
return count >= 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @param {ShapeDefinition} definition @returns {boolean} */
|
||||||
|
isIrrelevantShapeValid(definition) {
|
||||||
|
if (definition.cachedHash === this.root.hubGoals.currentGoal.definition.cachedHash) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (definition.cachedHash === this.root.gameMode.getBlueprintShapeKey()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const upgrades = this.root.gameMode.getUpgrades();
|
||||||
|
for (let upgradeId in upgrades) {
|
||||||
|
for (const tier in upgrades[upgradeId]) {
|
||||||
|
const requiredShapes = upgrades[upgradeId][tier].required;
|
||||||
|
for (let i = 0; i < requiredShapes.length; i++) {
|
||||||
|
if (definition.cachedHash === requiredShapes[i].shape) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @param {ShapeItem} item @returns {boolean} */
|
||||||
|
isLogoBefore18Valid(item) {
|
||||||
|
return this.root.hubGoals.level < 18 && this.isShape(item, SHAPE_LOGO);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @params {number} level @returns {boolean} */
|
||||||
|
isMamValid() {
|
||||||
|
return this.root.hubGoals.level > 27 && !this.root.savegame.currentData.stats.failedMam;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @param {number} count @returns {boolean} */
|
||||||
|
isMapMarkers15Valid(count) {
|
||||||
|
return count >= 15;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {number} level
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
isNoBeltUpgradesUntilBpValid(level) {
|
||||||
|
return level >= 12 && this.root.hubGoals.upgradeLevels.belt === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
initNoInverseRotater() {
|
||||||
|
if (this.root.savegame.currentData.stats.usedInverseRotater === true) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const entities = this.root.entityMgr.componentToEntity.StaticMapEntity;
|
||||||
|
|
||||||
|
let usedInverseRotater = false;
|
||||||
|
for (var i = 0; i < entities.length; i++) {
|
||||||
|
const entity = entities[i].components.StaticMapEntity;
|
||||||
|
|
||||||
|
if (entity.code === ROTATER_CCW_CODE || entity.code === ROTATER_180_CODE) {
|
||||||
|
usedInverseRotater = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.root.savegame.currentData.stats.usedInverseRotater = usedInverseRotater;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @param {number} level @returns {boolean} */
|
||||||
|
isNoInverseRotaterValid(level) {
|
||||||
|
return level >= 14 && !this.root.savegame.currentData.stats.usedInverseRotater;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @param {string} currentLayer @returns {boolean} */
|
||||||
|
isOpenWiresValid(currentLayer) {
|
||||||
|
return currentLayer === WIRE_LAYER;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @param {Entity} entity @returns {boolean} */
|
||||||
|
isPlace5000WiresValid(entity) {
|
||||||
|
return (
|
||||||
|
entity.components.Wire &&
|
||||||
|
entity.registered &&
|
||||||
|
entity.root.entityMgr.componentToEntity.Wire.length >= 5000
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @param {number} count @returns {boolean} */
|
||||||
|
isPlaceBlueprintValid(count) {
|
||||||
|
return count != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @param {number} count @returns {boolean} */
|
||||||
|
isPlaceBp1000Valid(count) {
|
||||||
|
return count >= 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @param {ShapeItem} item @returns {boolean} */
|
||||||
|
isStack4LayersValid(item) {
|
||||||
|
return item.getItemType() === ITEM_SHAPE && item.definition.layers.length === 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @returns {boolean} */
|
||||||
|
isStore100UniqueValid() {
|
||||||
|
return Object.keys(this.root.hubGoals.storedShapes).length >= 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @returns {boolean} */
|
||||||
|
isStoreShapeValid() {
|
||||||
|
const entities = this.root.systemMgr.systems.storage.allEntities;
|
||||||
|
|
||||||
|
if (entities.length === 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < entities.length; i++) {
|
||||||
|
if (entities[i].components.Storage.storedCount > 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
initTrash1000() {
|
||||||
|
if (Number(this.root.savegame.currentData.stats.trashedCount)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.root.savegame.currentData.stats.trashedCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @params {number} count @returns {boolean} */
|
||||||
|
isTrash1000Valid(count) {
|
||||||
|
this.root.savegame.currentData.stats.trashedCount += count;
|
||||||
|
|
||||||
|
return this.root.savegame.currentData.stats.trashedCount >= 1000;
|
||||||
|
}
|
||||||
|
}
|
23
src/js/platform/browser/no_achievement_provider.js
Normal file
23
src/js/platform/browser/no_achievement_provider.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { AchievementProviderInterface } from "../achievement_provider";
|
||||||
|
|
||||||
|
export class NoAchievementProvider extends AchievementProviderInterface {
|
||||||
|
hasAchievements() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
hasLoaded() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize() {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
onLoad() {
|
||||||
|
return Promise.reject(new Error("No achievements to load"));
|
||||||
|
}
|
||||||
|
|
||||||
|
activate() {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
}
|
@ -4,7 +4,9 @@ import { queryParamOptions } from "../../core/query_parameters";
|
|||||||
import { clamp } from "../../core/utils";
|
import { clamp } from "../../core/utils";
|
||||||
import { GamedistributionAdProvider } from "../ad_providers/gamedistribution";
|
import { GamedistributionAdProvider } from "../ad_providers/gamedistribution";
|
||||||
import { NoAdProvider } from "../ad_providers/no_ad_provider";
|
import { NoAdProvider } from "../ad_providers/no_ad_provider";
|
||||||
|
import { SteamAchievementProvider } from "../electron/steam_achievement_provider";
|
||||||
import { PlatformWrapperInterface } from "../wrapper";
|
import { PlatformWrapperInterface } from "../wrapper";
|
||||||
|
import { NoAchievementProvider } from "./no_achievement_provider";
|
||||||
import { StorageImplBrowser } from "./storage";
|
import { StorageImplBrowser } from "./storage";
|
||||||
import { StorageImplBrowserIndexedDB } from "./storage_indexed_db";
|
import { StorageImplBrowserIndexedDB } from "./storage_indexed_db";
|
||||||
|
|
||||||
@ -71,6 +73,7 @@ export class PlatformWrapperImplBrowser extends PlatformWrapperInterface {
|
|||||||
|
|
||||||
return this.detectStorageImplementation()
|
return this.detectStorageImplementation()
|
||||||
.then(() => this.initializeAdProvider())
|
.then(() => this.initializeAdProvider())
|
||||||
|
.then(() => this.initializeAchievementProvider())
|
||||||
.then(() => super.initialize());
|
.then(() => super.initialize());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,6 +199,20 @@ export class PlatformWrapperImplBrowser extends PlatformWrapperInterface {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
initializeAchievementProvider() {
|
||||||
|
if (G_IS_DEV && globalConfig.debug.testAchievements) {
|
||||||
|
this.app.achievementProvider = new SteamAchievementProvider(this.app);
|
||||||
|
|
||||||
|
return this.app.achievementProvider.initialize().catch(err => {
|
||||||
|
logger.error("Failed to initialize achievement provider, disabling:", err);
|
||||||
|
|
||||||
|
this.app.achievementProvider = new NoAchievementProvider(this.app);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.app.achievementProvider.initialize();
|
||||||
|
}
|
||||||
|
|
||||||
exitApp() {
|
exitApp() {
|
||||||
// Can not exit app
|
// Can not exit app
|
||||||
}
|
}
|
||||||
|
148
src/js/platform/electron/steam_achievement_provider.js
Normal file
148
src/js/platform/electron/steam_achievement_provider.js
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
/* typehints:start */
|
||||||
|
import { Application } from "../../application";
|
||||||
|
import { GameRoot } from "../../game/root";
|
||||||
|
/* typehints:end */
|
||||||
|
|
||||||
|
import { createLogger } from "../../core/logging";
|
||||||
|
import { getIPCRenderer } from "../../core/utils";
|
||||||
|
import {
|
||||||
|
ACHIEVEMENTS,
|
||||||
|
AchievementCollection,
|
||||||
|
AchievementProviderInterface
|
||||||
|
} from "../achievement_provider";
|
||||||
|
|
||||||
|
const logger = createLogger("achievements/steam");
|
||||||
|
|
||||||
|
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 SteamAchievementProvider 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("Steam unavailable. Achievements won't sync.");
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.ipc = getIPCRenderer();
|
||||||
|
|
||||||
|
return this.ipc.invoke("steam:is-initialized")
|
||||||
|
.then(initialized => {
|
||||||
|
this.initialized = initialized;
|
||||||
|
|
||||||
|
if (!this.initialized) {
|
||||||
|
logger.warn("Steam failed to intialize. Achievements won't sync.");
|
||||||
|
} else {
|
||||||
|
logger.log("Steam achievement provider initialized");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} key
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
activate(key) {
|
||||||
|
let promise;
|
||||||
|
|
||||||
|
if (!this.initialized) {
|
||||||
|
promise = Promise.resolve();
|
||||||
|
} else {
|
||||||
|
promise = this.ipc.invoke("steam: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;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -1,15 +1,34 @@
|
|||||||
|
import { NoAchievementProvider } from "../browser/no_achievement_provider";
|
||||||
import { PlatformWrapperImplBrowser } from "../browser/wrapper";
|
import { PlatformWrapperImplBrowser } from "../browser/wrapper";
|
||||||
import { getIPCRenderer } from "../../core/utils";
|
import { getIPCRenderer } from "../../core/utils";
|
||||||
import { createLogger } from "../../core/logging";
|
import { createLogger } from "../../core/logging";
|
||||||
import { StorageImplElectron } from "./storage";
|
import { StorageImplElectron } from "./storage";
|
||||||
|
import { SteamAchievementProvider } from "./steam_achievement_provider";
|
||||||
import { PlatformWrapperInterface } from "../wrapper";
|
import { PlatformWrapperInterface } from "../wrapper";
|
||||||
|
|
||||||
const logger = createLogger("electron-wrapper");
|
const logger = createLogger("electron-wrapper");
|
||||||
|
|
||||||
export class PlatformWrapperImplElectron extends PlatformWrapperImplBrowser {
|
export class PlatformWrapperImplElectron extends PlatformWrapperImplBrowser {
|
||||||
initialize() {
|
initialize() {
|
||||||
|
this.steamOverlayCanvasFix = document.createElement("canvas");
|
||||||
|
this.steamOverlayCanvasFix.width = 1;
|
||||||
|
this.steamOverlayCanvasFix.height = 1;
|
||||||
|
this.steamOverlayCanvasFix.id = "steamOverlayCanvasFix";
|
||||||
|
|
||||||
|
this.steamOverlayContextFix = this.steamOverlayCanvasFix.getContext("2d");
|
||||||
|
document.documentElement.appendChild(this.steamOverlayCanvasFix);
|
||||||
|
|
||||||
|
this.app.ticker.frameEmitted.add(this.steamOverlayFixRedrawCanvas, this);
|
||||||
|
|
||||||
this.app.storage = new StorageImplElectron(this);
|
this.app.storage = new StorageImplElectron(this);
|
||||||
return PlatformWrapperInterface.prototype.initialize.call(this);
|
this.app.achievementProvider = new SteamAchievementProvider(this.app);
|
||||||
|
|
||||||
|
return this.initializeAchievementProvider()
|
||||||
|
.then(() => PlatformWrapperInterface.prototype.initialize.call(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
steamOverlayFixRedrawCanvas() {
|
||||||
|
this.steamOverlayContextFix.clearRect(0, 0, 1, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
getId() {
|
getId() {
|
||||||
@ -38,6 +57,15 @@ export class PlatformWrapperImplElectron extends PlatformWrapperImplBrowser {
|
|||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
initializeAchievementProvider() {
|
||||||
|
return this.app.achievementProvider.initialize()
|
||||||
|
.catch(err => {
|
||||||
|
logger.error("Failed to initialize achievement provider, disabling:", err);
|
||||||
|
|
||||||
|
this.app.achievementProvider = new NoAchievementProvider(this.app);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
getSupportsFullscreen() {
|
getSupportsFullscreen() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ import { SavegameInterface_V1004 } from "./schemas/1004";
|
|||||||
import { SavegameInterface_V1005 } from "./schemas/1005";
|
import { SavegameInterface_V1005 } from "./schemas/1005";
|
||||||
import { SavegameInterface_V1006 } from "./schemas/1006";
|
import { SavegameInterface_V1006 } from "./schemas/1006";
|
||||||
import { SavegameInterface_V1007 } from "./schemas/1007";
|
import { SavegameInterface_V1007 } from "./schemas/1007";
|
||||||
|
import { SavegameInterface_V1008 } from "./schemas/1008";
|
||||||
import { SavegameInterface_ML01 } from "./schemas/ML01";
|
import { SavegameInterface_ML01 } from "./schemas/ML01";
|
||||||
import { RegularGameMode } from "../game/modes/regular";
|
import { RegularGameMode } from "../game/modes/regular";
|
||||||
|
|
||||||
@ -70,7 +71,11 @@ export class Savegame extends ReadWriteProxy {
|
|||||||
return {
|
return {
|
||||||
version: this.getCurrentVersion(),
|
version: this.getCurrentVersion(),
|
||||||
dump: null,
|
dump: null,
|
||||||
stats: {},
|
stats: {
|
||||||
|
failedMam: false,
|
||||||
|
trashedCount: 0,
|
||||||
|
usedInverseRotater: false,
|
||||||
|
},
|
||||||
lastUpdate: Date.now(),
|
lastUpdate: Date.now(),
|
||||||
gamemode: RegularGameMode.getId(),
|
gamemode: RegularGameMode.getId(),
|
||||||
};
|
};
|
||||||
@ -121,7 +126,12 @@ export class Savegame extends ReadWriteProxy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (data.version === 1007) {
|
if (data.version === 1007) {
|
||||||
SavegameInterface_ML01.migrate1007toML01(data);
|
SavegameInterface_V1008.migrate1007to1008(data);
|
||||||
|
data.version = 1008;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.version === 1008) {
|
||||||
|
SavegameInterface_ML01.migrate1008toML01(data);
|
||||||
data.version = "ML01";
|
data.version = "ML01";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ import { SavegameInterface_V1004 } from "./schemas/1004";
|
|||||||
import { SavegameInterface_V1005 } from "./schemas/1005";
|
import { SavegameInterface_V1005 } from "./schemas/1005";
|
||||||
import { SavegameInterface_V1006 } from "./schemas/1006";
|
import { SavegameInterface_V1006 } from "./schemas/1006";
|
||||||
import { SavegameInterface_V1007 } from "./schemas/1007";
|
import { SavegameInterface_V1007 } from "./schemas/1007";
|
||||||
|
import { SavegameInterface_V1008 } from "./schemas/1008";
|
||||||
import { SavegameInterface_ML01 } from "./schemas/ML01";
|
import { SavegameInterface_ML01 } from "./schemas/ML01";
|
||||||
|
|
||||||
/** @type {Object.<any, typeof BaseSavegameInterface>} */
|
/** @type {Object.<any, typeof BaseSavegameInterface>} */
|
||||||
@ -20,6 +21,7 @@ export const savegameInterfaces = {
|
|||||||
1005: SavegameInterface_V1005,
|
1005: SavegameInterface_V1005,
|
||||||
1006: SavegameInterface_V1006,
|
1006: SavegameInterface_V1006,
|
||||||
1007: SavegameInterface_V1007,
|
1007: SavegameInterface_V1007,
|
||||||
|
1008: SavegameInterface_V1008,
|
||||||
ML01: SavegameInterface_ML01,
|
ML01: SavegameInterface_ML01,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
/**
|
/**
|
||||||
* @typedef {{}} SavegameStats
|
* @typedef {import("../game/entity").Entity} Entity
|
||||||
|
*
|
||||||
|
* @typedef {{
|
||||||
|
* failedMam: boolean,
|
||||||
|
* trashedCount: number,
|
||||||
|
* usedInverseRotater: boolean
|
||||||
|
* }} SavegameStats
|
||||||
*
|
*
|
||||||
* @typedef {{
|
* @typedef {{
|
||||||
* camera: any,
|
* camera: any,
|
||||||
|
32
src/js/savegame/schemas/1008.js
Normal file
32
src/js/savegame/schemas/1008.js
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import { createLogger } from "../../core/logging.js";
|
||||||
|
import { SavegameInterface_V1007 } from "./1007.js";
|
||||||
|
|
||||||
|
const schema = require("./1008.json");
|
||||||
|
const logger = createLogger("savegame_interface/1008");
|
||||||
|
|
||||||
|
export class SavegameInterface_V1008 extends SavegameInterface_V1007 {
|
||||||
|
getVersion() {
|
||||||
|
return 1008;
|
||||||
|
}
|
||||||
|
|
||||||
|
getSchemaUncached() {
|
||||||
|
return schema;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {import("../savegame_typedefs.js").SavegameData} data
|
||||||
|
*/
|
||||||
|
static migrate1007to1008(data) {
|
||||||
|
logger.log("Migrating 1007 to 1008");
|
||||||
|
const dump = data.dump;
|
||||||
|
if (!dump) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.assign(data.stats, {
|
||||||
|
failedMam: true,
|
||||||
|
trashedCount: 0,
|
||||||
|
usedInverseRotater: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
5
src/js/savegame/schemas/1008.json
Normal file
5
src/js/savegame/schemas/1008.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"required": [],
|
||||||
|
"additionalProperties": true
|
||||||
|
}
|
@ -15,7 +15,9 @@ export class AboutState extends TextualGameState {
|
|||||||
getMainContentHTML() {
|
getMainContentHTML() {
|
||||||
return `
|
return `
|
||||||
<div class="head">
|
<div class="head">
|
||||||
<img src="${cachebust("res/logo.png")}" alt="shapez.io Logo">
|
<img src="${cachebust(
|
||||||
|
G_CHINA_VERSION ? "res/logo_cn.png" : "res/logo.png"
|
||||||
|
)}" alt="shapez.io Logo">
|
||||||
</div>
|
</div>
|
||||||
<div class="text">
|
<div class="text">
|
||||||
${T.about.body
|
${T.about.body
|
||||||
|
@ -19,7 +19,7 @@ export class ChangelogState extends TextualGameState {
|
|||||||
for (let i = 0; i < entries.length; ++i) {
|
for (let i = 0; i < entries.length; ++i) {
|
||||||
const entry = entries[i];
|
const entry = entries[i];
|
||||||
html += `
|
html += `
|
||||||
<div class="entry">
|
<div class="entry" data-changelog-skin="${entry.skin || "default"}">
|
||||||
<span class="version">${entry.version}</span>
|
<span class="version">${entry.version}</span>
|
||||||
<span class="date">${entry.date}</span>
|
<span class="date">${entry.date}</span>
|
||||||
<ul class="changes">
|
<ul class="changes">
|
||||||
|
@ -42,7 +42,12 @@ export class MainMenuState extends GameState {
|
|||||||
|
|
||||||
return `
|
return `
|
||||||
<div class="topButtons">
|
<div class="topButtons">
|
||||||
<button class="languageChoose" data-languageicon="${this.app.settings.getLanguage()}"></button>
|
${
|
||||||
|
G_CHINA_VERSION
|
||||||
|
? ""
|
||||||
|
: `<button class="languageChoose" data-languageicon="${this.app.settings.getLanguage()}"></button>`
|
||||||
|
}
|
||||||
|
|
||||||
<button class="settingsButton"></button>
|
<button class="settingsButton"></button>
|
||||||
${this.getExtraTopButtons()}
|
${this.getExtraTopButtons()}
|
||||||
${
|
${
|
||||||
@ -59,8 +64,10 @@ export class MainMenuState extends GameState {
|
|||||||
</video>
|
</video>
|
||||||
|
|
||||||
<div class="logo">
|
<div class="logo">
|
||||||
<img src="${cachebust("res/logo.png")}" alt="shapez.io Logo">
|
<img src="${cachebust(
|
||||||
<span class="updateLabel">v${G_BUILD_VERSION}</span>
|
G_CHINA_VERSION ? "res/logo_cn.png" : "res/logo.png"
|
||||||
|
)}" alt="shapez.io Logo">
|
||||||
|
<span class="updateLabel">v${G_BUILD_VERSION} - Achievements!</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mainWrapper ${showDemoBadges ? "demo" : "noDemo"}">
|
<div class="mainWrapper ${showDemoBadges ? "demo" : "noDemo"}">
|
||||||
@ -78,11 +85,17 @@ export class MainMenuState extends GameState {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="footer">
|
<div class="footer ${G_CHINA_VERSION ? "china" : ""}">
|
||||||
|
|
||||||
|
${
|
||||||
|
G_CHINA_VERSION
|
||||||
|
? ""
|
||||||
|
: `
|
||||||
<a class="githubLink boxLink" target="_blank">
|
<a class="githubLink boxLink" target="_blank">
|
||||||
${T.mainMenu.openSourceHint}
|
${T.mainMenu.openSourceHint}
|
||||||
<span class="thirdpartyLogo githubLogo"></span>
|
<span class="thirdpartyLogo githubLogo"></span>
|
||||||
</a>
|
</a>`
|
||||||
|
}
|
||||||
|
|
||||||
<a class="discordLink boxLink" target="_blank">
|
<a class="discordLink boxLink" target="_blank">
|
||||||
${T.mainMenu.discordLink}
|
${T.mainMenu.discordLink}
|
||||||
@ -90,11 +103,11 @@ export class MainMenuState extends GameState {
|
|||||||
</a>
|
</a>
|
||||||
|
|
||||||
<div class="sidelinks">
|
<div class="sidelinks">
|
||||||
<a class="redditLink">${T.mainMenu.subreddit}</a>
|
${G_CHINA_VERSION ? "" : `<a class="redditLink">${T.mainMenu.subreddit}</a>`}
|
||||||
|
|
||||||
<a class="changelog">${T.changelog.title}</a>
|
${G_CHINA_VERSION ? "" : `<a class="changelog">${T.changelog.title}</a>`}
|
||||||
|
|
||||||
<a class="helpTranslate">${T.mainMenu.helpTranslate}</a>
|
${G_CHINA_VERSION ? "" : `<a class="helpTranslate">${T.mainMenu.helpTranslate}</a>`}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="author">${T.mainMenu.madeBy.replace(
|
<div class="author">${T.mainMenu.madeBy.replace(
|
||||||
@ -226,10 +239,13 @@ export class MainMenuState extends GameState {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
this.trackClicks(qs(".settingsButton"), this.onSettingsButtonClicked);
|
this.trackClicks(qs(".settingsButton"), this.onSettingsButtonClicked);
|
||||||
this.trackClicks(qs(".changelog"), this.onChangelogClicked);
|
|
||||||
this.trackClicks(qs(".redditLink"), this.onRedditClicked);
|
if (!G_CHINA_VERSION) {
|
||||||
this.trackClicks(qs(".languageChoose"), this.onLanguageChooseClicked);
|
this.trackClicks(qs(".languageChoose"), this.onLanguageChooseClicked);
|
||||||
this.trackClicks(qs(".helpTranslate"), this.onTranslationHelpLinkClicked);
|
this.trackClicks(qs(".redditLink"), this.onRedditClicked);
|
||||||
|
this.trackClicks(qs(".changelog"), this.onChangelogClicked);
|
||||||
|
this.trackClicks(qs(".helpTranslate"), this.onTranslationHelpLinkClicked);
|
||||||
|
}
|
||||||
|
|
||||||
if (G_IS_STANDALONE) {
|
if (G_IS_STANDALONE) {
|
||||||
this.trackClicks(qs(".exitAppButton"), this.onExitAppButtonClicked);
|
this.trackClicks(qs(".exitAppButton"), this.onExitAppButtonClicked);
|
||||||
@ -251,11 +267,13 @@ export class MainMenuState extends GameState {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const githubLink = this.htmlElement.querySelector(".githubLink");
|
const githubLink = this.htmlElement.querySelector(".githubLink");
|
||||||
this.trackClicks(
|
if (githubLink) {
|
||||||
githubLink,
|
this.trackClicks(
|
||||||
() => this.app.platformWrapper.openExternalLink(THIRDPARTY_URLS.github),
|
githubLink,
|
||||||
{ preventClick: true }
|
() => this.app.platformWrapper.openExternalLink(THIRDPARTY_URLS.github),
|
||||||
);
|
{ preventClick: true }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const producerLink = this.htmlElement.querySelector(".producerLink");
|
const producerLink = this.htmlElement.querySelector(".producerLink");
|
||||||
this.trackClicks(
|
this.trackClicks(
|
||||||
|
@ -9,17 +9,19 @@ export class MobileWarningState extends GameState {
|
|||||||
|
|
||||||
getInnerHTML() {
|
getInnerHTML() {
|
||||||
return `
|
return `
|
||||||
|
|
||||||
<img class="logo" src="${cachebust("res/logo.png")}" alt="shapez.io Logo">
|
<img class="logo" src="${cachebust(
|
||||||
|
G_CHINA_VERSION ? "res/logo_cn.png" : "res/logo.png"
|
||||||
|
)}" alt="shapez.io Logo">
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
I'm sorry, but shapez.io is not available on mobile devices yet!
|
I'm sorry, but shapez.io is not available on mobile devices yet!
|
||||||
There is also no estimate when this will change, but feel to make a contribution! It's
|
There is also no estimate when this will change, but feel to make a contribution! It's
|
||||||
<a href="https://github.com/tobspr/shapez.io" target="_blank">open source</a>!</p>
|
<a href="https://github.com/tobspr/shapez.io" target="_blank">open source</a>!</p>
|
||||||
|
|
||||||
<p>If you want to play on your computer, you can also get the standalone on Steam:</p>
|
<p>If you want to play on your computer, you can also get the standalone on Steam:</p>
|
||||||
|
|
||||||
|
|
||||||
<a href="${
|
<a href="${
|
||||||
THIRDPARTY_URLS.standaloneStorePage + "?ref=mobile"
|
THIRDPARTY_URLS.standaloneStorePage + "?ref=mobile"
|
||||||
}" class="standaloneLink" target="_blank">Get the shapez.io standalone!</a>
|
}" class="standaloneLink" target="_blank">Get the shapez.io standalone!</a>
|
||||||
|
@ -3,7 +3,6 @@ import { cachebust } from "../core/cachebust";
|
|||||||
import { globalConfig } from "../core/config";
|
import { globalConfig } from "../core/config";
|
||||||
import { GameState } from "../core/game_state";
|
import { GameState } from "../core/game_state";
|
||||||
import { createLogger } from "../core/logging";
|
import { createLogger } from "../core/logging";
|
||||||
import { findNiceValue } from "../core/utils";
|
|
||||||
import { getRandomHint } from "../game/hints";
|
import { getRandomHint } from "../game/hints";
|
||||||
import { HUDModalDialogs } from "../game/hud/parts/modal_dialogs";
|
import { HUDModalDialogs } from "../game/hud/parts/modal_dialogs";
|
||||||
import { PlatformWrapperImplBrowser } from "../platform/browser/wrapper";
|
import { PlatformWrapperImplBrowser } from "../platform/browser/wrapper";
|
||||||
@ -20,7 +19,7 @@ export class PreloadState extends GameState {
|
|||||||
return `
|
return `
|
||||||
<div class="loadingImage"></div>
|
<div class="loadingImage"></div>
|
||||||
<div class="loadingStatus">
|
<div class="loadingStatus">
|
||||||
<span class="desc">Booting</span>
|
<span class="desc">${G_CHINA_VERSION ? "加载中" : "Booting"}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<span class="prefab_GameHint"></span>
|
<span class="prefab_GameHint"></span>
|
||||||
@ -115,6 +114,10 @@ export class PreloadState extends GameState {
|
|||||||
|
|
||||||
.then(() => this.setStatus("Initializing language"))
|
.then(() => this.setStatus("Initializing language"))
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
if (G_CHINA_VERSION) {
|
||||||
|
return this.app.settings.updateLanguage("zh-CN");
|
||||||
|
}
|
||||||
|
|
||||||
if (this.app.settings.getLanguage() === "auto-detect") {
|
if (this.app.settings.getLanguage() === "auto-detect") {
|
||||||
const language = autoDetectLanguageId();
|
const language = autoDetectLanguageId();
|
||||||
logger.log("Setting language to", language);
|
logger.log("Setting language to", language);
|
||||||
@ -163,6 +166,10 @@ export class PreloadState extends GameState {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (G_CHINA_VERSION) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
return this.app.storage
|
return this.app.storage
|
||||||
.readFileAsync("lastversion.bin")
|
.readFileAsync("lastversion.bin")
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
@ -192,7 +199,9 @@ export class PreloadState extends GameState {
|
|||||||
for (let i = 0; i < changelogEntries.length; ++i) {
|
for (let i = 0; i < changelogEntries.length; ++i) {
|
||||||
const entry = changelogEntries[i];
|
const entry = changelogEntries[i];
|
||||||
dialogHtml += `
|
dialogHtml += `
|
||||||
<div class="changelogDialogEntry">
|
<div class="changelogDialogEntry" data-changelog-skin="${
|
||||||
|
entry.skin || "default"
|
||||||
|
}">
|
||||||
<span class="version">${entry.version}</span>
|
<span class="version">${entry.version}</span>
|
||||||
<span class="date">${entry.date}</span>
|
<span class="date">${entry.date}</span>
|
||||||
<ul class="changes">
|
<ul class="changes">
|
||||||
@ -220,6 +229,9 @@ export class PreloadState extends GameState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
update() {
|
update() {
|
||||||
|
if (G_CHINA_VERSION) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const now = performance.now();
|
const now = performance.now();
|
||||||
if (now - this.lastHintShown > this.nextHintDuration) {
|
if (now - this.lastHintShown > this.nextHintDuration) {
|
||||||
this.lastHintShown = now;
|
this.lastHintShown = now;
|
||||||
@ -250,6 +262,9 @@ export class PreloadState extends GameState {
|
|||||||
*/
|
*/
|
||||||
setStatus(text) {
|
setStatus(text) {
|
||||||
logger.log("✅ " + text);
|
logger.log("✅ " + text);
|
||||||
|
if (G_CHINA_VERSION) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
this.currentStatus = text;
|
this.currentStatus = text;
|
||||||
this.statusText.innerText = text;
|
this.statusText.innerText = text;
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
@ -265,7 +280,9 @@ export class PreloadState extends GameState {
|
|||||||
|
|
||||||
subElement.innerHTML = `
|
subElement.innerHTML = `
|
||||||
<div class="logo">
|
<div class="logo">
|
||||||
<img src="${cachebust("res/logo.png")}" alt="Shapez.io Logo">
|
<img src="${cachebust(
|
||||||
|
G_CHINA_VERSION ? "res/logo_cn.png" : "res/logo.png"
|
||||||
|
)}" alt="Shapez.io Logo">
|
||||||
</div>
|
</div>
|
||||||
<div class="failureInner">
|
<div class="failureInner">
|
||||||
<div class="errorHeader">
|
<div class="errorHeader">
|
||||||
|
@ -63,6 +63,10 @@ export class SettingsState extends TextualGameState {
|
|||||||
for (let i = 0; i < allApplicationSettings().length; ++i) {
|
for (let i = 0; i < allApplicationSettings().length; ++i) {
|
||||||
const setting = allApplicationSettings()[i];
|
const setting = allApplicationSettings()[i];
|
||||||
|
|
||||||
|
if (G_CHINA_VERSION && setting.id === "language") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
categoriesHTML[setting.categoryId] += setting.getHtml(this.app);
|
categoriesHTML[setting.categoryId] += setting.getHtml(this.app);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,13 +94,15 @@ export class SettingsState extends TextualGameState {
|
|||||||
onEnter(payload) {
|
onEnter(payload) {
|
||||||
this.renderBuildText();
|
this.renderBuildText();
|
||||||
|
|
||||||
for (let i = 0; i < SettingsState.trackClicks.length; i++) {
|
if (!G_CHINA_VERSION) {
|
||||||
const trackClick = SettingsState.trackClicks[i];
|
for (let i = 0; i < SettingsState.trackClicks.length; i++) {
|
||||||
this.trackClicks(
|
const trackClick = SettingsState.trackClicks[i];
|
||||||
this.htmlElement.querySelector(trackClick.htmlElement),
|
this.trackClicks(
|
||||||
trackClick.action(this),
|
this.htmlElement.querySelector(trackClick.htmlElement),
|
||||||
trackClick.options
|
trackClick.action(this),
|
||||||
);
|
trackClick.options
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const keybindingsButton = this.htmlElement.querySelector(".editKeybindings");
|
const keybindingsButton = this.htmlElement.querySelector(".editKeybindings");
|
||||||
@ -132,6 +138,10 @@ export class SettingsState extends TextualGameState {
|
|||||||
|
|
||||||
initSettings() {
|
initSettings() {
|
||||||
allApplicationSettings().forEach(setting => {
|
allApplicationSettings().forEach(setting => {
|
||||||
|
if (G_CHINA_VERSION && setting.id === "language") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/** @type {HTMLElement} */
|
/** @type {HTMLElement} */
|
||||||
const element = this.htmlElement.querySelector("[data-setting='" + setting.id + "']");
|
const element = this.htmlElement.querySelector("[data-setting='" + setting.id + "']");
|
||||||
setting.bind(this.app, element, this.dialogs);
|
setting.bind(this.app, element, this.dialogs);
|
||||||
|
@ -291,7 +291,7 @@ ingame:
|
|||||||
interactiveTutorial:
|
interactiveTutorial:
|
||||||
title: Tutoriál
|
title: Tutoriál
|
||||||
hints:
|
hints:
|
||||||
1_1_extractor: Umístěte <strong>extraktor</strong> na naleziště<strong>kruhového
|
1_1_extractor: Umístěte <strong>extraktor</strong> na naleziště <strong>kruhového
|
||||||
tvaru</strong> a vytěžte jej!
|
tvaru</strong> a vytěžte jej!
|
||||||
1_2_conveyor: "Připojte extraktor pomocí <strong>dopravníkového pásu</strong> k
|
1_2_conveyor: "Připojte extraktor pomocí <strong>dopravníkového pásu</strong> k
|
||||||
vašemu HUBu!<br><br>Tip: <strong>Klikněte a táhněte</strong>
|
vašemu HUBu!<br><br>Tip: <strong>Klikněte a táhněte</strong>
|
||||||
@ -404,7 +404,7 @@ buildings:
|
|||||||
name: Extraktor
|
name: Extraktor
|
||||||
description: Umístěte na naleziště tvaru nebo barvy pro zahájení těžby.
|
description: Umístěte na naleziště tvaru nebo barvy pro zahájení těžby.
|
||||||
chainable:
|
chainable:
|
||||||
name: Extraktor (Navazující)
|
name: Extraktor (Řetěz)
|
||||||
description: Umístěte na naleziště tvaru nebo barvy pro zahájení těžby. Lze
|
description: Umístěte na naleziště tvaru nebo barvy pro zahájení těžby. Lze
|
||||||
zapojit po skupinách.
|
zapojit po skupinách.
|
||||||
underground_belt:
|
underground_belt:
|
||||||
@ -449,10 +449,10 @@ buildings:
|
|||||||
name: Barvič
|
name: Barvič
|
||||||
description: Obarví celý tvar v levém vstupu barvou z pravého vstupu.
|
description: Obarví celý tvar v levém vstupu barvou z pravého vstupu.
|
||||||
double:
|
double:
|
||||||
name: Barvič (dvojnásobný)
|
name: Barvič (2x)
|
||||||
description: Obarví tvary z levých vstupů barvou z horního vstupu.
|
description: Obarví tvary z levých vstupů barvou z horního vstupu.
|
||||||
quad:
|
quad:
|
||||||
name: Barvič (čtyřnásobný)
|
name: Barvič (4x)
|
||||||
description: Umožňuje obarvit každou čtvrtinu tvaru individuálně. Jen čtvrtiny
|
description: Umožňuje obarvit každou čtvrtinu tvaru individuálně. Jen čtvrtiny
|
||||||
se vstupy barev s <strong>logickým signálem</strong> na vrstvě
|
se vstupy barev s <strong>logickým signálem</strong> na vrstvě
|
||||||
kabelů budou obarveny!
|
kabelů budou obarveny!
|
||||||
@ -475,16 +475,16 @@ buildings:
|
|||||||
name: Vyvažovač
|
name: Vyvažovač
|
||||||
description: Multifunkční - Rovnoměrně rozděluje vstupy na výstupech.
|
description: Multifunkční - Rovnoměrně rozděluje vstupy na výstupech.
|
||||||
merger:
|
merger:
|
||||||
name: Spojovač (kompaktní)
|
name: Spojovač
|
||||||
description: Spojí dva pásy do jednoho.
|
description: Spojí dva pásy do jednoho.
|
||||||
merger-inverse:
|
merger-inverse:
|
||||||
name: Spojovač (kompaktní)
|
name: Spojovač
|
||||||
description: Spojí dva pásy do jednoho.
|
description: Spojí dva pásy do jednoho.
|
||||||
splitter:
|
splitter:
|
||||||
name: Rozdělovač (kompaktní)
|
name: Rozdělovač
|
||||||
description: Rozdělí jeden pás na dva.
|
description: Rozdělí jeden pás na dva.
|
||||||
splitter-inverse:
|
splitter-inverse:
|
||||||
name: Rozdělovač (kompaktní)
|
name: Rozdělovač
|
||||||
description: Rozdělí jeden pás na dva.
|
description: Rozdělí jeden pás na dva.
|
||||||
storage:
|
storage:
|
||||||
default:
|
default:
|
||||||
@ -558,12 +558,12 @@ buildings:
|
|||||||
name: Virtuální rotor
|
name: Virtuální rotor
|
||||||
description: Virtuálně otáčí tvary o 90 stupňů po směru hodinových ručiček.
|
description: Virtuálně otáčí tvary o 90 stupňů po směru hodinových ručiček.
|
||||||
unstacker:
|
unstacker:
|
||||||
name: Virtuální extrahátor
|
name: Virt. extrahátor
|
||||||
description: Virtuálně extrahuje nejvyšší vrstvu do pravého výstupu a zbývající
|
description: Virtuálně extrahuje nejvyšší vrstvu do pravého výstupu a zbývající
|
||||||
do levé.
|
do levé.
|
||||||
stacker:
|
stacker:
|
||||||
name: Virtuální kombinátor
|
name: Virt. kombinátor
|
||||||
description: Virtuálně Spojí tvary dohromady. Pokud nemohou být spojeny, pravý
|
description: Virtuálně spojí tvary dohromady. Pokud nemohou být spojeny, pravý
|
||||||
tvar je položen na levý.
|
tvar je položen na levý.
|
||||||
painter:
|
painter:
|
||||||
name: Virtuální barvič
|
name: Virtuální barvič
|
||||||
@ -615,8 +615,8 @@ storyRewards:
|
|||||||
proti směru hodinových ručiček. Vyberte rotor a <strong>zmáčkněte
|
proti směru hodinových ručiček. Vyberte rotor a <strong>zmáčkněte
|
||||||
'T' pro přepnutí mezi variantami</strong>!
|
'T' pro přepnutí mezi variantami</strong>!
|
||||||
reward_miner_chainable:
|
reward_miner_chainable:
|
||||||
title: Napojovací extraktor
|
title: Řetězový extraktor
|
||||||
desc: "Právě jste odemkli <strong>napojovací extraktor</strong>! Může
|
desc: "Právě jste odemkli <strong>řetězový extraktor</strong>! Může
|
||||||
<strong>předat své zdroje</strong> ostatním extraktorům, čímž můžete
|
<strong>předat své zdroje</strong> ostatním extraktorům, čímž můžete
|
||||||
efektivněji těžit více zdrojů!<br><br> PS: Starý extraktor bude od
|
efektivněji těžit více zdrojů!<br><br> PS: Starý extraktor bude od
|
||||||
teď nahrazen ve vašem panelu nástrojů!"
|
teď nahrazen ve vašem panelu nástrojů!"
|
||||||
@ -1008,7 +1008,7 @@ tips:
|
|||||||
- Můžete proplétat různé úrovně tunelů.
|
- Můžete proplétat různé úrovně tunelů.
|
||||||
- Snažte se postavit kompaktní továrny - vyplatí se to!
|
- Snažte se postavit kompaktní továrny - vyplatí se to!
|
||||||
- Barvič má zrcadlově otočenou variantu, kterou můžete vybrat klávesou
|
- Barvič má zrcadlově otočenou variantu, kterou můžete vybrat klávesou
|
||||||
<b>T</b>
|
<b>T</b>.
|
||||||
- Užití správné kombinace vylepšení maximalizuje efektivitu.
|
- Užití správné kombinace vylepšení maximalizuje efektivitu.
|
||||||
- Na maximální úrovní, 5 extraktorů zaplní jeden celý pás.
|
- Na maximální úrovní, 5 extraktorů zaplní jeden celý pás.
|
||||||
- Nezapomeňte na tunely!
|
- Nezapomeňte na tunely!
|
||||||
|
@ -30,49 +30,20 @@ steamPage:
|
|||||||
intro: >-
|
intro: >-
|
||||||
Do you like automation games? Then you are in the right place!
|
Do you like automation games? Then you are in the right place!
|
||||||
|
|
||||||
shapez.io is a relaxed game in which you have to build factories for the automated production of geometric shapes. As the level increases, the shapes become more and more complex, and you
|
shapez.io is a relaxed game in which you have to build factories for the automated production of geometric shapes. As the level increases, the shapes become more and more complex, and you have to spread out on the infinite map.
|
||||||
have to spread out on the infinite map.
|
|
||||||
|
|
||||||
And as if that wasn't enough, you also have to produce exponentially more to satisfy the demands - the only thing that helps is scaling! While you only have to process shapes at the
|
And as if that wasn't enough, you also have to produce exponentially more to satisfy the demands - the only thing that helps is scaling! While you only have to process shapes at the beginning, you will later have to color them - by extracting and mixing colors!
|
||||||
beginning, you will later have to color them - by extracting and mixing colors!
|
|
||||||
|
|
||||||
Buying the game on Steam gives you access to the full version, but you can also play a demo at shapez.io first and decide later!
|
Buying the game on Steam gives you access to the full version, but you can also play a demo at shapez.io first and decide later!
|
||||||
|
|
||||||
title_advantages: Standalone Advantages
|
what_others_say: What people say about shapez.io
|
||||||
advantages:
|
|
||||||
- <b>12 New Levels</b> for a total of 26 levels
|
|
||||||
- <b>18 New Buildings</b> for a fully automated factory!
|
|
||||||
- <b>Unlimited Upgrade Tiers</b> for many hours of fun!
|
|
||||||
- <b>Wires Update</b> for an entirely new dimension!
|
|
||||||
- <b>Dark Mode</b>!
|
|
||||||
- Unlimited Savegames
|
|
||||||
- Unlimited Markers
|
|
||||||
- Support me! ❤️
|
|
||||||
|
|
||||||
title_future: Planned Content
|
nothernlion_comment: >-
|
||||||
planned:
|
This game is great - I'm having a wonderful time playing, and time has flown by.
|
||||||
- Blueprint Library
|
notch_comment: >-
|
||||||
- Steam Achievements
|
Oh crap. I really should sleep, but I think I just figured out how to make a computer in shapez.io
|
||||||
- Puzzle Mode
|
steam_review_comment: >-
|
||||||
- Minimap
|
This game has stolen my life and I don't want it back. Very chill factory game that won't let me stop making my lines more efficient.
|
||||||
- Mods
|
|
||||||
- Sandbox mode
|
|
||||||
- ... and a lot more!
|
|
||||||
|
|
||||||
title_open_source: This game is open source!
|
|
||||||
text_open_source: >-
|
|
||||||
Anybody can contribute, I'm actively involved in the community and attempt to review all suggestions and take feedback into consideration where possible.
|
|
||||||
|
|
||||||
Be sure to check out my trello board for the full roadmap!
|
|
||||||
|
|
||||||
title_links: Links
|
|
||||||
|
|
||||||
links:
|
|
||||||
discord: Official Discord
|
|
||||||
roadmap: Roadmap
|
|
||||||
subreddit: Subreddit
|
|
||||||
source_code: Source code (GitHub)
|
|
||||||
translate: Help translate
|
|
||||||
|
|
||||||
global:
|
global:
|
||||||
loading: Loading
|
loading: Loading
|
||||||
@ -482,9 +453,9 @@ ingame:
|
|||||||
title: 18 New Buildings
|
title: 18 New Buildings
|
||||||
desc: Fully automate your factory!
|
desc: Fully automate your factory!
|
||||||
|
|
||||||
savegames:
|
achievements:
|
||||||
title: ∞ Savegames
|
title: Achievements
|
||||||
desc: As many as your heart desires!
|
desc: Hunt them all!
|
||||||
|
|
||||||
upgrades:
|
upgrades:
|
||||||
title: ∞ Upgrade Tiers
|
title: ∞ Upgrade Tiers
|
||||||
|
@ -168,13 +168,14 @@ dialogs:
|
|||||||
desc: Suorita taso 12 avataksesi piirustukset!
|
desc: Suorita taso 12 avataksesi piirustukset!
|
||||||
keybindingsIntroduction:
|
keybindingsIntroduction:
|
||||||
title: Hyödyllisiä pikanäppäimiä
|
title: Hyödyllisiä pikanäppäimiä
|
||||||
desc: "Tässä pelissä on paljon pikanäppäimiä, jotka tekevät isojen tehtaiden
|
desc: >-
|
||||||
|
Tässä pelissä on paljon pikanäppäimiä, jotka tekevät isojen tehtaiden
|
||||||
rakentamisesta helpompaa. Tässä on muutama, mutta <strong>katso
|
rakentamisesta helpompaa. Tässä on muutama, mutta <strong>katso
|
||||||
kaikki pikanäppäimet</strong>!<br><br> <code
|
kaikki pikanäppäimet</strong>!<br><br> <code
|
||||||
class='keybinding'>CTRL</code> + Raahaus: Valitse alue.<br> <code
|
class='keybinding'>CTRL</code> + Raahaus: Valitse alue.<br> <code
|
||||||
class='keybinding'>SHIFT</code>: Pidä pohjassa sijoittaaksesi
|
class='keybinding'>SHIFT</code>: Pidä pohjassa sijoittaaksesi
|
||||||
useita samoja rakennuksia.<br> <code class='keybinding'>ALT</code>:
|
useita samoja rakennuksia.<br> <code class='keybinding'>ALT</code>:
|
||||||
Käännä sijoitettavien kuljettimien suunta.<br>"
|
Käännä sijoitettavien kuljettimien suunta.<br>
|
||||||
createMarker:
|
createMarker:
|
||||||
title: Uusi merkki
|
title: Uusi merkki
|
||||||
desc: Anna merkille kuvaava nimi. Voit myös liittää <strong>lyhyen koodin</strong>
|
desc: Anna merkille kuvaava nimi. Voit myös liittää <strong>lyhyen koodin</strong>
|
||||||
@ -306,26 +307,31 @@ ingame:
|
|||||||
hints:
|
hints:
|
||||||
1_1_extractor: Laita <strong>Poimija</strong> <strong>ympyrämuodon</strong>
|
1_1_extractor: Laita <strong>Poimija</strong> <strong>ympyrämuodon</strong>
|
||||||
päälle käyttääksesi sitä!
|
päälle käyttääksesi sitä!
|
||||||
1_2_conveyor: "Yhdistä poimija <strong>kuljettimella</strong>
|
1_2_conveyor: >-
|
||||||
|
Yhdistä poimija <strong>kuljettimella</strong>
|
||||||
keskusrakennukseen!<br><br>Vihje: <strong>Paina ja
|
keskusrakennukseen!<br><br>Vihje: <strong>Paina ja
|
||||||
raahaa</strong> kuljetinta hiirellä!"
|
raahaa</strong> kuljetinta hiirellä!
|
||||||
1_3_expand: "Tämä <strong>EI OLE</strong> tyhjäkäyntipeli! Rakenna lisää
|
1_3_expand: >-
|
||||||
|
Tämä <strong>EI OLE</strong> tyhjäkäyntipeli! Rakenna lisää
|
||||||
poimijoita ja kuljettimia saavuttaaksesi tavoitteen nopeammin
|
poimijoita ja kuljettimia saavuttaaksesi tavoitteen nopeammin
|
||||||
valmiiksi.<br><br>Vihje: Pidä <strong>SHIFT</strong> pohjassa
|
valmiiksi.<br><br>Vihje: Pidä <strong>SHIFT</strong> pohjassa
|
||||||
laittaaksesi useampia poimijoita ja käytä <strong>R</strong>
|
laittaaksesi useampia poimijoita ja käytä <strong>R</strong>
|
||||||
kääntääksesi niitä."
|
kääntääksesi niitä.
|
||||||
2_1_place_cutter: "Nyt aseta <strong>Leikkuri</strong> leikataksesi ympyrä
|
2_1_place_cutter: >-
|
||||||
|
Nyt aseta <strong>Leikkuri</strong> leikataksesi ympyrä
|
||||||
puoliksi!<br><br> PS: Leikkuri aina leikkaa <strong>ylhäältä alaspäin</strong>
|
puoliksi!<br><br> PS: Leikkuri aina leikkaa <strong>ylhäältä alaspäin</strong>
|
||||||
riippumatta sen asennosta."
|
riippumatta sen asennosta.
|
||||||
2_2_place_trash: Leikkuri voi <strong>tukkeutua</strong>!<br><br> Käytä
|
2_2_place_trash: Leikkuri voi <strong>tukkeutua</strong>!<br><br> Käytä
|
||||||
<strong>roskakoria</strong> hävittääksesi (vielä!) tarpeeton jäte.
|
<strong>roskakoria</strong> hävittääksesi (vielä!) tarpeeton jäte.
|
||||||
2_3_more_cutters: "Hienoa! Lisää <strong>kaksi leikkuria</strong> nopeuttaaksesi
|
2_3_more_cutters: >-
|
||||||
|
Hienoa! Lisää <strong>kaksi leikkuria</strong> nopeuttaaksesi
|
||||||
hidasta prosessia!<br><br> PS: Käytä <strong>pikanäppäimiä 0-9</strong>
|
hidasta prosessia!<br><br> PS: Käytä <strong>pikanäppäimiä 0-9</strong>
|
||||||
valitaksesi rakennuksen nopeammin!"
|
valitaksesi rakennuksen nopeammin!
|
||||||
3_1_rectangles: "Poimitaanpa nyt neliöitä! <strong>Rakenna 4
|
3_1_rectangles: >-
|
||||||
|
Poimitaanpa nyt neliöitä! <strong>Rakenna 4
|
||||||
poimijaa</strong> ja yhdistä ne keskusrakennukseen.<br><br> PS:
|
poimijaa</strong> ja yhdistä ne keskusrakennukseen.<br><br> PS:
|
||||||
Pidä <strong>SHIFT</strong> painettuna, kun raahaat kuljetinta
|
Pidä <strong>SHIFT</strong> painettuna, kun raahaat kuljetinta
|
||||||
aktivoidaksesi kuljetinsuunnittelijan!"
|
aktivoidaksesi kuljetinsuunnittelijan!
|
||||||
21_1_place_quad_painter: Aseta <strong>nelimaalain</strong> ja hanki
|
21_1_place_quad_painter: Aseta <strong>nelimaalain</strong> ja hanki
|
||||||
<strong>ympyröitä</strong>, <strong>valkoista</strong> ja
|
<strong>ympyröitä</strong>, <strong>valkoista</strong> ja
|
||||||
<strong>punaista</strong> väriä!
|
<strong>punaista</strong> väriä!
|
||||||
@ -333,9 +339,10 @@ ingame:
|
|||||||
<strong>E</strong>!<br><br> Sitten <strong>yhdistä kaikki neljä tuloa</strong> maalaimeen johdoilla!
|
<strong>E</strong>!<br><br> Sitten <strong>yhdistä kaikki neljä tuloa</strong> maalaimeen johdoilla!
|
||||||
21_3_place_button: MahtaVATA! Aseta nyt <strong>kytkin</strong> ja yhdistä
|
21_3_place_button: MahtaVATA! Aseta nyt <strong>kytkin</strong> ja yhdistä
|
||||||
se johdoilla!
|
se johdoilla!
|
||||||
21_4_press_button: "Paina kytkintä <strong>lähettääksesi tosi-
|
21_4_press_button: >-
|
||||||
|
Paina kytkintä <strong>lähettääksesi tosi-
|
||||||
signaalin</strong> ja aktivoidaksesi maalaimen.<br><br> PS: Kaikkia
|
signaalin</strong> ja aktivoidaksesi maalaimen.<br><br> PS: Kaikkia
|
||||||
tuloja ei tarvitse kytkeä! Kokeile vaikka vain kahta."
|
tuloja ei tarvitse kytkeä! Kokeile vaikka vain kahta.
|
||||||
connectedMiners:
|
connectedMiners:
|
||||||
one_miner: 1 poimija
|
one_miner: 1 poimija
|
||||||
n_miners: <amount> poimijaa
|
n_miners: <amount> poimijaa
|
||||||
@ -587,10 +594,11 @@ storyRewards:
|
|||||||
desc: Avasit <strong>Kääntäjän</strong>! Se kääntää muotoja myötäpäivään 90 astetta.
|
desc: Avasit <strong>Kääntäjän</strong>! Se kääntää muotoja myötäpäivään 90 astetta.
|
||||||
reward_painter:
|
reward_painter:
|
||||||
title: Värjäys
|
title: Värjäys
|
||||||
desc: "Avasit <strong>Maalaimen</strong> - Poimi joitain värialueita
|
desc: >-
|
||||||
|
Avasit <strong>Maalaimen</strong> - Poimi joitain värialueita
|
||||||
(Samoin kuin muotoja) ja yhdistä se muotoon maalaimen
|
(Samoin kuin muotoja) ja yhdistä se muotoon maalaimen
|
||||||
avulla!<br><br>PS: Jos olet värisokea, asetuksissa on <strong> tila
|
avulla!<br><br>PS: Jos olet värisokea, asetuksissa on <strong> tila
|
||||||
värisokeille</strong>!"
|
värisokeille</strong>!
|
||||||
reward_mixer:
|
reward_mixer:
|
||||||
title: Värin Sekoitus
|
title: Värin Sekoitus
|
||||||
desc: Avasit <strong>Värinsekoittajan</strong> - Yhdistä kaksi väriä
|
desc: Avasit <strong>Värinsekoittajan</strong> - Yhdistä kaksi väriä
|
||||||
@ -617,10 +625,11 @@ storyRewards:
|
|||||||
<strong>painamalla 'T' vaihtaaksesi sen versioita</strong>!
|
<strong>painamalla 'T' vaihtaaksesi sen versioita</strong>!
|
||||||
reward_miner_chainable:
|
reward_miner_chainable:
|
||||||
title: Sarjapoimija
|
title: Sarjapoimija
|
||||||
desc: "Avasit juuri <strong>Sarjapoimijan</strong>! Se voi
|
desc: >-
|
||||||
|
Avasit juuri <strong>Sarjapoimijan</strong>! Se voi
|
||||||
<strong>siirtää resurssejaan</strong> muihin poimijoihin, joten
|
<strong>siirtää resurssejaan</strong> muihin poimijoihin, joten
|
||||||
voit hankkia resursseja tehokkaammin!<br><br> PS: Vanha
|
voit hankkia resursseja tehokkaammin!<br><br> PS: Vanha
|
||||||
poimija on nyt korvattu työkalupalkissa!"
|
poimija on nyt korvattu työkalupalkissa!
|
||||||
reward_underground_belt_tier_2:
|
reward_underground_belt_tier_2:
|
||||||
title: Tunneli Taso II
|
title: Tunneli Taso II
|
||||||
desc: Avasit uuden version <strong>Tunnelista</strong> - Siinä on <strong>pidempi
|
desc: Avasit uuden version <strong>Tunnelista</strong> - Siinä on <strong>pidempi
|
||||||
@ -657,9 +666,10 @@ storyRewards:
|
|||||||
jotta sinulla on varaa siihen! (Ne mitkä juuri toimitit).
|
jotta sinulla on varaa siihen! (Ne mitkä juuri toimitit).
|
||||||
no_reward:
|
no_reward:
|
||||||
title: Seuraava taso
|
title: Seuraava taso
|
||||||
desc: "Et saanut palkintoa tältä tasolta, mutta seuraavalta tasolta saat! <br><br> PS: Parempi
|
desc: >-
|
||||||
|
Et saanut palkintoa tältä tasolta, mutta seuraavalta tasolta saat! <br><br> PS: Parempi
|
||||||
olla tuhoamatta vanhoja tehtaita - Tarvitset <strong>kaikkia</strong>
|
olla tuhoamatta vanhoja tehtaita - Tarvitset <strong>kaikkia</strong>
|
||||||
muotoja myöhemmin <strong>avataksesi päivityksiä</strong>!"
|
muotoja myöhemmin <strong>avataksesi päivityksiä</strong>!
|
||||||
no_reward_freeplay:
|
no_reward_freeplay:
|
||||||
title: Seuraava taso
|
title: Seuraava taso
|
||||||
desc: Onnittelut! Muuten, lisää sisältöä on suunniteltu täysversioon!
|
desc: Onnittelut! Muuten, lisää sisältöä on suunniteltu täysversioon!
|
||||||
@ -682,8 +692,9 @@ storyRewards:
|
|||||||
kääntää muotoa 180 astetta (Ylläripylläri! :D)
|
kääntää muotoa 180 astetta (Ylläripylläri! :D)
|
||||||
reward_display:
|
reward_display:
|
||||||
title: Näyttö
|
title: Näyttö
|
||||||
desc: "Avasit juuri <strong>Näytön</strong> - Yhdistä signaali näyttöön
|
desc: >-
|
||||||
Johto-tasolla visualisoidaksesi sen<br><br> PS: Huomasitko, että kuljetinanturi ja varasto näyttävät viimeisimmän esineen? Yritäpä saada se näkyviin näytölle!"
|
Avasit juuri <strong>Näytön</strong> - Yhdistä signaali näyttöön
|
||||||
|
Johto-tasolla visualisoidaksesi sen<br><br> PS: Huomasitko, että kuljetinanturi ja varasto näyttävät viimeisimmän esineen? Yritäpä saada se näkyviin näytölle!
|
||||||
reward_constant_signal:
|
reward_constant_signal:
|
||||||
title: Jatkuva Signaali
|
title: Jatkuva Signaali
|
||||||
desc: Avasit <strong>Jatkuvan Signaalin</strong> laitteen johtotasolla!
|
desc: Avasit <strong>Jatkuvan Signaalin</strong> laitteen johtotasolla!
|
||||||
@ -708,12 +719,13 @@ storyRewards:
|
|||||||
tavallisesti.<br><br> Mitä valitsetkin, muista pitää hauskaa!
|
tavallisesti.<br><br> Mitä valitsetkin, muista pitää hauskaa!
|
||||||
reward_wires_painter_and_levers:
|
reward_wires_painter_and_levers:
|
||||||
title: Johdot & Nelimaalain
|
title: Johdot & Nelimaalain
|
||||||
desc: "Avasit juuri <strong>Johtotason</strong>: Se on erillinen
|
desc: >-
|
||||||
|
Avasit juuri <strong>Johtotason</strong>: Se on erillinen
|
||||||
taso tavallisen tason päällä ja sieltä löytyy useita uusia
|
taso tavallisen tason päällä ja sieltä löytyy useita uusia
|
||||||
mekaniikkoja!<br><br> Aluksi avasin sinulle <strong>Nelimaalaimen</strong>
|
mekaniikkoja!<br><br> Aluksi avasin sinulle <strong>Nelimaalaimen</strong>
|
||||||
- Yhdistä johtotasolla lokerot, joihin haluat maalia<br><br>
|
- Yhdistä johtotasolla lokerot, joihin haluat maalia<br><br>
|
||||||
Vaihtaaksesi johtotasolle, paina <strong>E</strong>. <br><br>
|
Vaihtaaksesi johtotasolle, paina <strong>E</strong>. <br><br>
|
||||||
PS: <strong>Aktivoi vinkit</strong> asetuksissa nähdäksesi Johdot-tutoriaalin!"
|
PS: <strong>Aktivoi vinkit</strong> asetuksissa nähdäksesi Johdot-tutoriaalin!
|
||||||
reward_filter:
|
reward_filter:
|
||||||
title: Esinesuodatin
|
title: Esinesuodatin
|
||||||
desc: Olet avannut <strong>Esinesuodattimen</strong>! Se lähettää esineet
|
desc: Olet avannut <strong>Esinesuodattimen</strong>! Se lähettää esineet
|
||||||
@ -878,8 +890,9 @@ settings:
|
|||||||
rangeSliderPercentage: <amount> %
|
rangeSliderPercentage: <amount> %
|
||||||
keybindings:
|
keybindings:
|
||||||
title: Pikanäppäimet
|
title: Pikanäppäimet
|
||||||
hint: "Vinkki: Muista käyttää CTRL, SHIFT ja ALT! Ne ottavat käyttöön erilaisia
|
hint: >-
|
||||||
sijoitteluvaihtoehtoja."
|
Vinkki: Muista käyttää CTRL, SHIFT ja ALT! Ne ottavat käyttöön erilaisia
|
||||||
|
sijoitteluvaihtoehtoja.
|
||||||
resetKeybindings: Nollaa pikanäppäimet
|
resetKeybindings: Nollaa pikanäppäimet
|
||||||
categoryLabels:
|
categoryLabels:
|
||||||
general: Sovellus
|
general: Sovellus
|
||||||
|
@ -495,7 +495,7 @@ buildings:
|
|||||||
description: 入力された信号をディスプレイに表示します。 形状、色、真偽値のいずれでも可能です。
|
description: 入力された信号をディスプレイに表示します。 形状、色、真偽値のいずれでも可能です。
|
||||||
reader:
|
reader:
|
||||||
default:
|
default:
|
||||||
name: ベルトリーダ
|
name: ベルトリーダー
|
||||||
description: 平均スループットを計測できます。 アンロック後は、 最後に通過したアイテムの情報を出力します。
|
description: 平均スループットを計測できます。 アンロック後は、 最後に通過したアイテムの情報を出力します。
|
||||||
analyzer:
|
analyzer:
|
||||||
default:
|
default:
|
||||||
@ -577,8 +577,8 @@ storyRewards:
|
|||||||
desc: <strong>分配機</strong>の<strong>コンパクトバージョン</strong>が利用可能になりました! -
|
desc: <strong>分配機</strong>の<strong>コンパクトバージョン</strong>が利用可能になりました! -
|
||||||
1つの入力を2つの出力に分配します!
|
1つの入力を2つの出力に分配します!
|
||||||
reward_belt_reader:
|
reward_belt_reader:
|
||||||
title: ベルトリーダ
|
title: ベルトリーダー
|
||||||
desc: <strong>ベルトリーダ</strong>が利用可能になりました!ベルトのスループットを計測できます。<br><br>ワイヤーのロックが解除されれば、より便利になります!
|
desc: <strong>ベルトリーダー</strong>が利用可能になりました!ベルトのスループットを計測できます。<br><br>ワイヤーのロックが解除されれば、より便利になります!
|
||||||
reward_cutter_quad:
|
reward_cutter_quad:
|
||||||
title: 四分割
|
title: 四分割
|
||||||
desc: <strong>切断機</strong>のバリエーションが利用可能になりました。 -
|
desc: <strong>切断機</strong>のバリエーションが利用可能になりました。 -
|
||||||
@ -846,7 +846,7 @@ keybindings:
|
|||||||
filter: アイテムフィルタ
|
filter: アイテムフィルタ
|
||||||
wire_tunnel: 交差ワイヤ
|
wire_tunnel: 交差ワイヤ
|
||||||
display: ディスプレイ
|
display: ディスプレイ
|
||||||
reader: ベルトリーダ
|
reader: ベルトリーダー
|
||||||
virtual_processor: 仮想切断機
|
virtual_processor: 仮想切断機
|
||||||
transistor: トランジスタ
|
transistor: トランジスタ
|
||||||
analyzer: 形状解析機
|
analyzer: 形状解析機
|
||||||
@ -894,58 +894,57 @@ demo:
|
|||||||
settingNotAvailable: デモ版では利用できません。
|
settingNotAvailable: デモ版では利用できません。
|
||||||
tips:
|
tips:
|
||||||
- ハブは現在指定されている形状だけではなく、あらゆる種類の入力を受け付けることができます。
|
- ハブは現在指定されている形状だけではなく、あらゆる種類の入力を受け付けることができます。
|
||||||
- あなたの工場が拡張可能か確認してください - あとで報われるでしょう!
|
- あなたの工場が部品単位で増築可能か確認してください。あとできっと役に立ちます!
|
||||||
- ハブのすぐ近くに建設しないでください。ぐちゃぐちゃになりますよ。
|
- ハブのすぐ近くに建設しないでください。あとでぐちゃぐちゃになりますよ!
|
||||||
- 積層が上手く行かない場合は、入力を入れ替えてみてください。
|
- 積層が上手く行かない場合は、入力を入れ替えてみてください。
|
||||||
- <b>R</b>を押すと、ベルトプランナーの経由方向を切り替えることができます。
|
- <b>R</b>を押すと、ベルトプランナーの経由方向を切り替えることができます。
|
||||||
- <b>CTRL</b>を押したままドラッグすると、向きを保ったままベルトを設置できます。
|
- <b>CTRL</b>を押したままドラッグすると、向きを保ったままベルトを設置できます。
|
||||||
- アップグレードが同じティアなら、お互いの比率は同じです。
|
- アップグレード段階が同じなら、比率も同じに保たれます。
|
||||||
- 直列処理は、並列処理より効率的です。
|
- 直列処理は、並列処理より効率的です。
|
||||||
- 後半になると、より多くの建物のバリエーションを解除できます。
|
- 後半になると、より多くの建物のバリエーションを解除できます。
|
||||||
- <b>T</b>を押すと、建物のバリエーションを切り替えることができます。
|
- <b>T</b>を押すと、建物のバリエーションを切り替えることができます。
|
||||||
- 対称性が重要です!
|
- 対称性が重要です!
|
||||||
- ティアの違うトンネル同士は、同じラインに重ねることができます。
|
- 別の種類のトンネル同士は、同じラインに重ねることができます。
|
||||||
- コンパクトに工場を作ってみてください - あとで報われるでしょう!
|
- コンパクトに工場を作ってみてください。あとできっと役に立ちます!
|
||||||
- 着色機には鏡写しのバリエーションがあり、<b>T</b>で選択できます。
|
- 着色機には鏡写しのバリエーションがあり、<b>T</b>で選択できます。
|
||||||
- 適切な比率で建設することで、効率が最大化できます。
|
- 適切な比率で建設することで、効率を最大化できます。
|
||||||
- 最大レベルでは、1つのベルトは5つの抽出機で満たすことができます。
|
- 最大レベルでは、1つのベルトは5つの抽出機で満たすことができます。
|
||||||
- トンネルを忘れないでください。
|
- トンネルを忘れないでください!
|
||||||
- 最大限の効率を得るためには、アイテムを均等に分割する必要はありません。
|
- アイテムを均等に分割することは、最大効率を得るために必須ではありません。
|
||||||
- <b>SHIFT</b>を押したままベルトを設置するとベルトプランナーが有効になり、
|
- <b>SHIFT</b>を押したままにするとベルトプランナーが有効になり、長距離のベルトを簡単に配置できます。
|
||||||
- 切断機は向きを考慮せず、常に垂直に切断します。
|
- 切断機は配置された向きを考慮せず、常に垂直に切断します。
|
||||||
- 白を作るためには、3色全てを混ぜます。
|
- ストレージは左側の出力を優先します。
|
||||||
- ストレージは優先出力を優先して出力します。
|
- 増築可能なデザインを作るために時間を使ってください。それだけの価値があります!
|
||||||
- 増築可能なデザインを作るために時間を使ってください - それには価値があります!
|
- <b>SHIFT</b>を使用すると複数の建物を一度に配置できます。
|
||||||
- <b>SHIFT</b>を使用すると複数の建物を配置できます。
|
|
||||||
- <b>ALT</b>を押しながらベルトを設置すると、逆向きに設置できます。
|
- <b>ALT</b>を押しながらベルトを設置すると、逆向きに設置できます。
|
||||||
- 効率が重要です!
|
- 効率が重要です!
|
||||||
- ハブから遠くに離れるほど、形状資源はより複雑な形になります。
|
- ハブから遠くに離れるほど、形状資源はより複雑な形になります。
|
||||||
- 機械の速度には上限があるので、最大効率を得るためには入力を分割します。
|
- 機械の速度には上限があるので、最大効率を得るためには入力を分割してください。
|
||||||
- 効率を最大化するために分配機/合流機を使用できます。
|
- 効率を最大化するために分配機/合流機を使用できます。
|
||||||
- 構成が重要です。ベルトを交差させすぎないようにしてください。
|
- 構成が重要です。ベルトを交差させすぎないようにしてください。
|
||||||
- 事前設計が重要です。さもないとぐちゃぐちゃになりますよ!
|
- 事前設計が重要です。さもないとぐちゃぐちゃになりますよ!
|
||||||
- 旧い工場を撤去しないでください!アップグレードを行うために、それらが必要になります。
|
- 古い工場を撤去しないでください! 各種アップグレードに必要になります。
|
||||||
- 助けなしでレベル20をクリアしてみてください!
|
- 自力でレベル20やレベル26をクリアしてみてください!
|
||||||
- 複雑にしないでください。単純に保つことができれば、成功することができるでしょう。
|
- 複雑にしないでください。単純に保つことが成功の秘訣です。
|
||||||
- ゲームの後半で工場を再利用する必要があるかもしれません。
|
- あとで工場を再利用する必要が出てくるかもしれません。
|
||||||
- 積層機を使用することなく、必要な形状資源を発見することができるかもしれません。
|
- 積層機を使用することなく、必要な形状資源を発見できるかもしれません。
|
||||||
- 完全な風車の形は資源としては生成されません。
|
- 完全な風車の形状は資源としては生成されません。
|
||||||
- 最大の効率を得るためには、切断する前に着色をしてください。
|
- 最大の効率を得るためには、切断する前に着色をしてください。
|
||||||
- モジュールとは、知覚こそが空間を生むものである。これは、人間である限り。
|
- モジュールがあれば、空間はただの認識に過ぎなくなる――生ある人間に対する気遣いだ。
|
||||||
- 工場の設計図を蓄えておいてください。それらを再利用することで、新たな工場が作成できます。
|
- 設計図としての工場を別に作っておくと、工場のモジュール化において重要な役割を果たします。
|
||||||
- 混合機をよく見ると、色の混ぜ方が解ります。
|
- 混合機をよく見ると、色の混ぜ方が解ります。
|
||||||
- <b>CTRL</b> + クリックで範囲選択ができます。
|
- <b>CTRL</b> + クリックで範囲選択ができます。
|
||||||
- ハブに近すぎる設計物を作ると、のちの設計の邪魔になる可能性があります。
|
- ハブに近すぎる設計物を作ると、のちの設計の邪魔になるかもしれません。
|
||||||
- アップグレードリストの各形状の横にあるピンのアイコンは、それを画面左に固定します。
|
- アップグレードリストの各形状の横にあるピンのアイコンは、その形状を画面左に固定表示します。
|
||||||
- 原色全てを混ぜ合わせると白になります!
|
- 三原色全てを混ぜ合わせると白になります!
|
||||||
- マップは無限の広さがあります。臆せずに拡張してください。
|
- マップは無限の広さがあります。臆せずに拡張してください。
|
||||||
- Factorioもプレイしてみてください!私のお気に入りのゲームです。
|
- Factorioもプレイしてみてください! 私のお気に入りのゲームです。
|
||||||
- 切断機(四分割)は右上から時計回りに切断します!
|
- 切断機(四分割)は右上から時計回りに切断します。
|
||||||
- メインメニューからセーブデータを保存できます!
|
- メインメニューからセーブデータを保存できます!
|
||||||
- このゲームには便利なキーバインドがたくさんあります!設定ページを見てみてください。
|
- このゲームには便利なキーバインドがたくさんあります! 設定ページを見てみてください。
|
||||||
- このゲームにはたくさんの設定があります!是非チェックしてみてください!
|
- このゲームにはたくさんの設定があります。是非チェックしてみてください!
|
||||||
- ハブを示すマーカーには、その方向を示す小さなコンパスがあります。
|
- ハブのマーカーには、その方向を示す小さなコンパスがついています。
|
||||||
- ベルトをクリアするには、範囲選択して同じ場所に貼り付けをします。
|
- ベルトの中身をクリアするには、範囲選択して同じ場所に貼り付けをします。
|
||||||
- F4を押すことで、FPSとTickレートを表示することができます。
|
- F4を押すことで、FPSとTickレートを表示することができます。
|
||||||
- F4を2回押すと、マウスとカメラの座標を表示することができます。
|
- F4を2回押すと、マウスとカメラの座標を表示することができます。
|
||||||
- 左のピン留めされた図形をクリックして、固定を解除できます。
|
- 左のピン留めされた図形をクリックすると、固定を解除できます。
|
||||||
|
@ -186,7 +186,7 @@ dialogs:
|
|||||||
wersję gry dla nielimitowanych znaczników!
|
wersję gry dla nielimitowanych znaczników!
|
||||||
massCutConfirm:
|
massCutConfirm:
|
||||||
title: Potwierdź wycinanie
|
title: Potwierdź wycinanie
|
||||||
desc: Wycinasz sporą ilość maszyn (<count> gwoli ścisłości)! Czy na pewno chcesz
|
desc: Wycinasz sporą ilość maszyn (<count> w roli ścisłości)! Czy na pewno chcesz
|
||||||
kontynuować?
|
kontynuować?
|
||||||
exportScreenshotWarning:
|
exportScreenshotWarning:
|
||||||
title: Tworzenie zrzutu fabryki
|
title: Tworzenie zrzutu fabryki
|
||||||
|
@ -215,21 +215,21 @@ ingame:
|
|||||||
selectBuildings: Alan seç
|
selectBuildings: Alan seç
|
||||||
stopPlacement: Yerleştİrmeyİ durdur
|
stopPlacement: Yerleştİrmeyİ durdur
|
||||||
rotateBuilding: Yapıyı döndür
|
rotateBuilding: Yapıyı döndür
|
||||||
placeMultiple: Çoklu yerleştİr
|
placeMultiple: Çoklu yerleştir
|
||||||
reverseOrientation: Yönünü ters çevİr
|
reverseOrientation: Yönünü ters çevir
|
||||||
disableAutoOrientation: Otomatik yönü devre dışı bırak
|
disableAutoOrientation: Otomatik yönü devre dışı bırak
|
||||||
toggleHud: Kullanıcı arayüzünü aç/kapa
|
toggleHud: Kullanıcı arayüzünü aç/kapa
|
||||||
placeBuilding: Yapı yerleştİr
|
placeBuilding: Yapı yerleştir
|
||||||
createMarker: Yer İmi oluştur
|
createMarker: Yer imi oluştur
|
||||||
delete: SİL
|
delete: SİL
|
||||||
pasteLastBlueprint: Son taslağı yapıştır
|
pasteLastBlueprint: Son taslağı yapıştır
|
||||||
lockBeltDirection: Taşıma bandı planlayıcısını kullan
|
lockBeltDirection: Taşıma bandı planlayıcısını kullan
|
||||||
plannerSwitchSide: Planlayıcıyı ters çevir
|
plannerSwitchSide: Planlayıcıyı ters çevir
|
||||||
cutSelection: Kes
|
cutSelection: Kes
|
||||||
copySelection: Kopyala
|
copySelection: Kopyala
|
||||||
clearSelection: Seçİmİ temİzle
|
clearSelection: Seçimi temİzle
|
||||||
pipette: Pİpet
|
pipette: Pipet
|
||||||
switchLayers: Katman değİştİr
|
switchLayers: Katman değiştir
|
||||||
buildingPlacement:
|
buildingPlacement:
|
||||||
cycleBuildingVariants: Yapının farklı türlerine geçmek için <key> tuşuna bas.
|
cycleBuildingVariants: Yapının farklı türlerine geçmek için <key> tuşuna bas.
|
||||||
hotkeyLabel: "Kısayol: <key>"
|
hotkeyLabel: "Kısayol: <key>"
|
||||||
@ -245,14 +245,14 @@ ingame:
|
|||||||
levelTitle: SEVİYE <level>
|
levelTitle: SEVİYE <level>
|
||||||
completed: Tamamlandı
|
completed: Tamamlandı
|
||||||
unlockText: Açıldı <reward>!
|
unlockText: Açıldı <reward>!
|
||||||
buttonNextLevel: Sonrakİ Sevİye
|
buttonNextLevel: Sonraki Seviye
|
||||||
notifications:
|
notifications:
|
||||||
newUpgrade: Yeni geliştirme mevcut!
|
newUpgrade: Yeni geliştirme mevcut!
|
||||||
gameSaved: Oyun kaydedildi.
|
gameSaved: Oyun kaydedildi.
|
||||||
freeplayLevelComplete: Seviye <level> tamamlandı!
|
freeplayLevelComplete: Seviye <level> tamamlandı!
|
||||||
shop:
|
shop:
|
||||||
title: Gelİştİrmeler
|
title: Geliştirmeler
|
||||||
buttonUnlock: Gelİştİr
|
buttonUnlock: Geliştİr
|
||||||
tier: Aşama <x>
|
tier: Aşama <x>
|
||||||
maximumLevel: SON SEVİYE (Hız x<currentMult>)
|
maximumLevel: SON SEVİYE (Hız x<currentMult>)
|
||||||
statistics:
|
statistics:
|
||||||
@ -283,19 +283,19 @@ ingame:
|
|||||||
blueprintPlacer:
|
blueprintPlacer:
|
||||||
cost: Bedel
|
cost: Bedel
|
||||||
waypoints:
|
waypoints:
|
||||||
waypoints: Yer İmi
|
waypoints: Yer imler
|
||||||
hub: MERKEZ
|
hub: MERKEZ
|
||||||
description: Sol-tık ile Yer İmlerine git, sağ-tık ile yer imini
|
description: Sol-tık ile Yer imlerine git, sağ-tık ile yer imini
|
||||||
sil.<br><br>Mevcut konumdan yer imi oluşturmak için <keybinding>'a
|
sil.<br><br>Mevcut konumdan yer imi oluşturmak için <keybinding>'a
|
||||||
bas, <strong>sağ-tık</strong> ile mevcut konumda yer imi oluştur.
|
bas, <strong>sağ-tık</strong> ile mevcut konumda yer imi oluştur.
|
||||||
creationSuccessNotification: Yer İmi oluşturuldu.
|
creationSuccessNotification: Yer imi oluşturuldu.
|
||||||
interactiveTutorial:
|
interactiveTutorial:
|
||||||
title: Eğİtİm
|
title: Eğitim
|
||||||
hints:
|
hints:
|
||||||
1_1_extractor: Daire üretmek için <strong>daire şekli</strong> üzerine bir
|
1_1_extractor: Daire üretmek için <strong>daire şekli</strong> üzerine bir
|
||||||
<strong>üretici</strong> yerleştir!
|
<strong>üretici</strong> yerleştir!
|
||||||
1_2_conveyor: "Üreticiyi <strong>taşıma bandı</strong> ile merkezine
|
1_2_conveyor: "Üreticiyi <strong>taşıma bandı</strong> ile merkezine
|
||||||
bağla!<br><br>İpucu: Taşıma bandı nı seç ve taşıma bandını
|
bağla!<br><br>İpucu: Taşıma bandını seç ve taşıma bandını
|
||||||
farenin sol tuşu ile <strong>tıkla ve sürükle</strong>!"
|
farenin sol tuşu ile <strong>tıkla ve sürükle</strong>!"
|
||||||
1_3_expand: "Bu bir boşta kalma oyunu (idle game) <strong>değil</strong>! Hedefe
|
1_3_expand: "Bu bir boşta kalma oyunu (idle game) <strong>değil</strong>! Hedefe
|
||||||
ulaşmak için daha fazla üretici ve taşıma bandı
|
ulaşmak için daha fazla üretici ve taşıma bandı
|
||||||
@ -394,7 +394,7 @@ shopUpgrades:
|
|||||||
description: Hız x<currentMult> → x<newMult>
|
description: Hız x<currentMult> → x<newMult>
|
||||||
buildings:
|
buildings:
|
||||||
hub:
|
hub:
|
||||||
deliver: Teslİm et
|
deliver: Teslim et
|
||||||
toUnlock: Açılacak
|
toUnlock: Açılacak
|
||||||
levelShortcut: SVY
|
levelShortcut: SVY
|
||||||
endOfDemo: Deneme Sürümünün Sonu
|
endOfDemo: Deneme Sürümünün Sonu
|
||||||
@ -405,10 +405,10 @@ buildings:
|
|||||||
sürükle.
|
sürükle.
|
||||||
miner:
|
miner:
|
||||||
default:
|
default:
|
||||||
name: Üretİcİ
|
name: Üretici
|
||||||
description: Bir şekli veya rengi üretmek için üzerlerini yerleştir.
|
description: Bir şekli veya rengi üretmek için üzerlerini yerleştir.
|
||||||
chainable:
|
chainable:
|
||||||
name: Üretİcİ (Zİncİrleme)
|
name: Üretici (Zİncİrleme)
|
||||||
description: Bir şekli veya rengi üretmek için üzerlerini yerleştir. Zincirleme
|
description: Bir şekli veya rengi üretmek için üzerlerini yerleştir. Zincirleme
|
||||||
bağlanabilir.
|
bağlanabilir.
|
||||||
underground_belt:
|
underground_belt:
|
||||||
@ -421,12 +421,12 @@ buildings:
|
|||||||
aktarımı sağlar.
|
aktarımı sağlar.
|
||||||
cutter:
|
cutter:
|
||||||
default:
|
default:
|
||||||
name: Kesİcİ
|
name: Kesici
|
||||||
description: Şekilleri yukarıdan aşağıya böler ve iki yarım parçayı çıktı olarak
|
description: Şekilleri yukarıdan aşağıya böler ve iki yarım parçayı çıktı olarak
|
||||||
verir. <strong>Eğer sadece bir çıktıyı kullanıyorsanız diğer
|
verir. <strong>Eğer sadece bir çıktıyı kullanıyorsanız diğer
|
||||||
çıkan parçayı yok etmeyi unutmayın, yoksa kesim durur!</strong>
|
çıkan parçayı yok etmeyi unutmayın, yoksa kesim durur!</strong>
|
||||||
quad:
|
quad:
|
||||||
name: Kesİcİ (Dörtlü)
|
name: Kesici (Dörtlü)
|
||||||
description: Şekilleri dört parçaya böler. <strong>Eğer sadece bir çıktıyı
|
description: Şekilleri dört parçaya böler. <strong>Eğer sadece bir çıktıyı
|
||||||
kullanıyorsanız diğer çıkan parçaları yok etmeyi unutmayın,
|
kullanıyorsanız diğer çıkan parçaları yok etmeyi unutmayın,
|
||||||
yoksa kesim durur!</strong>
|
yoksa kesim durur!</strong>
|
||||||
@ -435,10 +435,10 @@ buildings:
|
|||||||
name: Döndürücü
|
name: Döndürücü
|
||||||
description: Şekilleri saat yönünde 90 derece döndürür.
|
description: Şekilleri saat yönünde 90 derece döndürür.
|
||||||
ccw:
|
ccw:
|
||||||
name: Döndürücü (Saat Yönünün Tersİ)
|
name: Döndürücü (Saat Yönünün Tersi)
|
||||||
description: Şekilleri saat yönünün tersinde 90 derece döndürür.
|
description: Şekilleri saat yönünün tersinde 90 derece döndürür.
|
||||||
rotate180:
|
rotate180:
|
||||||
name: Dödürücü (180 Derece)
|
name: Döndürücü (180 Derece)
|
||||||
description: Şekilleri 180 derece döndürür.
|
description: Şekilleri 180 derece döndürür.
|
||||||
stacker:
|
stacker:
|
||||||
default:
|
default:
|
||||||
@ -486,10 +486,10 @@ buildings:
|
|||||||
name: Dengeleyici
|
name: Dengeleyici
|
||||||
description: Çok işlevli - bütün girdileri eşit olarak bütün çıkışlara dağıtır.
|
description: Çok işlevli - bütün girdileri eşit olarak bütün çıkışlara dağıtır.
|
||||||
merger:
|
merger:
|
||||||
name: Bİrleştİrİcİ (tekİl)
|
name: Birleştirici (tekil)
|
||||||
description: İki taşıma bandını bir çıktı verecek şekilde birleştirir.
|
description: İki taşıma bandını bir çıktı verecek şekilde birleştirir.
|
||||||
merger-inverse:
|
merger-inverse:
|
||||||
name: Birleştİrİcİ (tekİl)
|
name: Birleştirici (tekil)
|
||||||
description: İki taşıma bandını bir çıktı verecek şekilde birleştirir.
|
description: İki taşıma bandını bir çıktı verecek şekilde birleştirir.
|
||||||
splitter:
|
splitter:
|
||||||
name: Ayırıcı (tekİl)
|
name: Ayırıcı (tekİl)
|
||||||
@ -499,7 +499,7 @@ buildings:
|
|||||||
description: Bir taşıma bandını iki çıktı verecek şekilde ayırır.
|
description: Bir taşıma bandını iki çıktı verecek şekilde ayırır.
|
||||||
storage:
|
storage:
|
||||||
default:
|
default:
|
||||||
name: Storage
|
name: Depo
|
||||||
description: Belirli bir sınıra kadar fazla eşyaları depolar. Taşırma kapısı
|
description: Belirli bir sınıra kadar fazla eşyaları depolar. Taşırma kapısı
|
||||||
olarak kullanıla bilir.
|
olarak kullanıla bilir.
|
||||||
constant_signal:
|
constant_signal:
|
||||||
@ -589,7 +589,7 @@ buildings:
|
|||||||
katmanda çıktı olarak verir.
|
katmanda çıktı olarak verir.
|
||||||
storyRewards:
|
storyRewards:
|
||||||
reward_cutter_and_trash:
|
reward_cutter_and_trash:
|
||||||
title: Şekİllerİ Kesmek
|
title: Şekilleri Kesmek
|
||||||
desc: <strong>Kesici</strong> açıldı, bu alet şekilleri <strong>yönelimi ne
|
desc: <strong>Kesici</strong> açıldı, bu alet şekilleri <strong>yönelimi ne
|
||||||
olursa olsun</strong> ortadan ikiye böler!<br><br> Çıkan şekilleri
|
olursa olsun</strong> ortadan ikiye böler!<br><br> Çıkan şekilleri
|
||||||
kullanmayı veya çöpe atmayı unutma yoksa <strong>makine
|
kullanmayı veya çöpe atmayı unutma yoksa <strong>makine
|
||||||
@ -645,7 +645,7 @@ storyRewards:
|
|||||||
desc: <strong>Kesicinin</strong> yeni bir türünü açtın - Bu tür şekilleri iki
|
desc: <strong>Kesicinin</strong> yeni bir türünü açtın - Bu tür şekilleri iki
|
||||||
parça yerine <strong>dört parçaya</strong> ayırabilir!
|
parça yerine <strong>dört parçaya</strong> ayırabilir!
|
||||||
reward_painter_double:
|
reward_painter_double:
|
||||||
title: Çİfte Boyama
|
title: Çifte Boyama
|
||||||
desc: <strong>Boyayıcının<strong> başka bir türünü açtın - Sıradan bir boyayıcı
|
desc: <strong>Boyayıcının<strong> başka bir türünü açtın - Sıradan bir boyayıcı
|
||||||
gibi çalışır, fakat <strong>iki şekli birden</strong> boyayarak iki
|
gibi çalışır, fakat <strong>iki şekli birden</strong> boyayarak iki
|
||||||
boya yerine sadece bir boya harcar!
|
boya yerine sadece bir boya harcar!
|
||||||
@ -665,13 +665,13 @@ storyRewards:
|
|||||||
yapıştırabilmek için <strong>taslak şekilleri</strong> üretmelisin!
|
yapıştırabilmek için <strong>taslak şekilleri</strong> üretmelisin!
|
||||||
(Az önce teslim ettiğin şekiller).
|
(Az önce teslim ettiğin şekiller).
|
||||||
no_reward:
|
no_reward:
|
||||||
title: Sonrakİ Sevİye
|
title: Sonraki Seviye
|
||||||
desc: "Bu seviyenin bir ödülü yok ama bir sonrakinin olacak!<br><br> Not: Şu
|
desc: "Bu seviyenin bir ödülü yok ama bir sonrakinin olacak!<br><br> Not: Şu
|
||||||
anki fabrikalarını yok etmemeni öneririm - Daha sonra
|
anki fabrikalarını yok etmemeni öneririm - Daha sonra
|
||||||
<strong>Geliştirmeleri açmak için </strong> <strong>bütün
|
<strong>Geliştirmeleri açmak için </strong> <strong>bütün
|
||||||
hepsine</strong> ihtiyacın olacak!"
|
hepsine</strong> ihtiyacın olacak!"
|
||||||
no_reward_freeplay:
|
no_reward_freeplay:
|
||||||
title: Sonrakİ Sevİye
|
title: Sonraki Seviye
|
||||||
desc: Tebrikler!
|
desc: Tebrikler!
|
||||||
reward_freeplay:
|
reward_freeplay:
|
||||||
title: Özgür Mod
|
title: Özgür Mod
|
||||||
@ -701,7 +701,7 @@ storyRewards:
|
|||||||
hızını ölçmeyi sağlar.<br><br>Kabloları açana kadar bekle - o zaman
|
hızını ölçmeyi sağlar.<br><br>Kabloları açana kadar bekle - o zaman
|
||||||
çok kullanışlı olacak.
|
çok kullanışlı olacak.
|
||||||
reward_rotater_180:
|
reward_rotater_180:
|
||||||
title: Dödürücü (180 derece)
|
title: Döndürücü (180 derece)
|
||||||
desc: 180 derece <strong>döndürücüyü</strong> açtınız! - Şekilleri 180 derece
|
desc: 180 derece <strong>döndürücüyü</strong> açtınız! - Şekilleri 180 derece
|
||||||
döndürür (Süpriz! :D)
|
döndürür (Süpriz! :D)
|
||||||
reward_display:
|
reward_display:
|
||||||
@ -754,7 +754,7 @@ settings:
|
|||||||
categories:
|
categories:
|
||||||
general: Genel
|
general: Genel
|
||||||
userInterface: Kullanıcı Arayüzü
|
userInterface: Kullanıcı Arayüzü
|
||||||
advanced: Gelİşmİş
|
advanced: Gelişmİş
|
||||||
performance: Performans
|
performance: Performans
|
||||||
versionBadges:
|
versionBadges:
|
||||||
dev: Geliştirme
|
dev: Geliştirme
|
||||||
@ -764,7 +764,7 @@ settings:
|
|||||||
rangeSliderPercentage: <amount> %
|
rangeSliderPercentage: <amount> %
|
||||||
labels:
|
labels:
|
||||||
uiScale:
|
uiScale:
|
||||||
title: Arayüz Ölçeğİ
|
title: Arayüz Ölçeği
|
||||||
description: Kullanıcı arayüzünün boyutunu değiştirir. Arayüz cihazınızın
|
description: Kullanıcı arayüzünün boyutunu değiştirir. Arayüz cihazınızın
|
||||||
çözünürlüğüne göre ölçeklendirilir, ama ölçeğin miktarı burada
|
çözünürlüğüne göre ölçeklendirilir, ama ölçeğin miktarı burada
|
||||||
ayarlanabilir.
|
ayarlanabilir.
|
||||||
@ -775,7 +775,7 @@ settings:
|
|||||||
large: Büyük
|
large: Büyük
|
||||||
huge: Çok Büyük
|
huge: Çok Büyük
|
||||||
scrollWheelSensitivity:
|
scrollWheelSensitivity:
|
||||||
title: Yakınlaştırma Hassasİyeti
|
title: Yakınlaştırma Hassasiyeti
|
||||||
description: Yakınlaştırmanın ne kadar hassas olduğunu ayarlar (Fare tekerleği
|
description: Yakınlaştırmanın ne kadar hassas olduğunu ayarlar (Fare tekerleği
|
||||||
veya dokunmatik farketmez).
|
veya dokunmatik farketmez).
|
||||||
sensitivity:
|
sensitivity:
|
||||||
@ -785,7 +785,7 @@ settings:
|
|||||||
fast: Hızlı
|
fast: Hızlı
|
||||||
super_fast: Çok Hızlı
|
super_fast: Çok Hızlı
|
||||||
language:
|
language:
|
||||||
title: Dİl
|
title: Dil
|
||||||
description: Dili değiştirir. Bütün çevirmeler kullanıcı katkılarıyla
|
description: Dili değiştirir. Bütün çevirmeler kullanıcı katkılarıyla
|
||||||
oluşturulmuştur ve tam olmayabilir!
|
oluşturulmuştur ve tam olmayabilir!
|
||||||
fullscreen:
|
fullscreen:
|
||||||
@ -793,10 +793,10 @@ settings:
|
|||||||
description: En iyi oyun tecrübesi için oyunun tam ekranda oynanması tavsiye
|
description: En iyi oyun tecrübesi için oyunun tam ekranda oynanması tavsiye
|
||||||
edilir. Sadece tam sürümde mevcut.
|
edilir. Sadece tam sürümde mevcut.
|
||||||
soundsMuted:
|
soundsMuted:
|
||||||
title: Ses Efektlerİnİ Sustur
|
title: Ses Efektlerini Sustur
|
||||||
description: Aktif edildiğinde bütün ses efektleri susturulur.
|
description: Aktif edildiğinde bütün ses efektleri susturulur.
|
||||||
musicMuted:
|
musicMuted:
|
||||||
title: Müzİğİ Sustur
|
title: Müziği Sustur
|
||||||
description: Aktif edildiğinde bütün müzikler susturulur.
|
description: Aktif edildiğinde bütün müzikler susturulur.
|
||||||
theme:
|
theme:
|
||||||
title: Renk Teması
|
title: Renk Teması
|
||||||
@ -805,7 +805,7 @@ settings:
|
|||||||
dark: Karanlık
|
dark: Karanlık
|
||||||
light: Aydınlık
|
light: Aydınlık
|
||||||
refreshRate:
|
refreshRate:
|
||||||
title: Sİmülasyon Hızı
|
title: Simülasyon Hızı
|
||||||
description: Eğer mönitörünüzün yenileme hızı (Hz) yüksekse oyunun akıcı bir
|
description: Eğer mönitörünüzün yenileme hızı (Hz) yüksekse oyunun akıcı bir
|
||||||
şekilde çalışması için yenileme hızını buradan değiştirin. Eğer
|
şekilde çalışması için yenileme hızını buradan değiştirin. Eğer
|
||||||
bilgisayarınız yavaşsa bu ayar oyunun gösterdiği kare hızını
|
bilgisayarınız yavaşsa bu ayar oyunun gösterdiği kare hızını
|
||||||
@ -817,7 +817,7 @@ settings:
|
|||||||
özellik SHIFT tuşuna sürekli basılı tutup yapı yerleştirmeyle
|
özellik SHIFT tuşuna sürekli basılı tutup yapı yerleştirmeyle
|
||||||
aynıdır.
|
aynıdır.
|
||||||
offerHints:
|
offerHints:
|
||||||
title: İpuçları ve Eğİtİmler
|
title: İpuçları ve Eğitimler
|
||||||
description: İpuçları ve eğitimleri açar. Ayrıca bazı arayüz elemanlarını oyunun
|
description: İpuçları ve eğitimleri açar. Ayrıca bazı arayüz elemanlarını oyunun
|
||||||
daha kolay öğrenilebilmesi için gizler.
|
daha kolay öğrenilebilmesi için gizler.
|
||||||
enableTunnelSmartplace:
|
enableTunnelSmartplace:
|
||||||
@ -827,11 +827,11 @@ settings:
|
|||||||
tünellerin çekilerek inşa edilmesi ve aşırı uzağa yerleştirilen
|
tünellerin çekilerek inşa edilmesi ve aşırı uzağa yerleştirilen
|
||||||
tünel uçlarının silinmesini de sağlar.
|
tünel uçlarının silinmesini de sağlar.
|
||||||
vignette:
|
vignette:
|
||||||
title: Gölgelendİrme
|
title: Gölgelendirme
|
||||||
description: Gölgelendirmeyi açar. Gölgelendirme ekranın köşelerini karartır ve
|
description: Gölgelendirmeyi açar. Gölgelendirme ekranın köşelerini karartır ve
|
||||||
yazıları daha kolay okuyabilmeinizi sağlar.
|
yazıları daha kolay okuyabilmeinizi sağlar.
|
||||||
autosaveInterval:
|
autosaveInterval:
|
||||||
title: Otomatİk Kayıt Sıklığı
|
title: Otomatik Kayıt Sıklığı
|
||||||
description: Oyunun hangi sıklıkta kaydedileceğini belirler. Ayrıca otomatik
|
description: Oyunun hangi sıklıkta kaydedileceğini belirler. Ayrıca otomatik
|
||||||
kayıt tamamen kapatılabilir.
|
kayıt tamamen kapatılabilir.
|
||||||
intervals:
|
intervals:
|
||||||
@ -842,11 +842,11 @@ settings:
|
|||||||
twenty_minutes: 20 Dakika
|
twenty_minutes: 20 Dakika
|
||||||
disabled: Devredışı
|
disabled: Devredışı
|
||||||
compactBuildingInfo:
|
compactBuildingInfo:
|
||||||
title: Derlİ Toplu Yapı Bİlgİlerİ
|
title: Derli Toplu Yapı Bilgileri
|
||||||
description: Yapıların bilgi kutularını sadece oranlarını göstecek şekilde
|
description: Yapıların bilgi kutularını sadece oranlarını göstecek şekilde
|
||||||
kısaltır. Aksi taktirde yapının açıklaması ve resmi gösterilir.
|
kısaltır. Aksi taktirde yapının açıklaması ve resmi gösterilir.
|
||||||
disableCutDeleteWarnings:
|
disableCutDeleteWarnings:
|
||||||
title: Kes/Sİl Uyarılarını Devredışı Bırak
|
title: Kes/Sil Uyarılarını Devre dışı Bırak
|
||||||
description: 100 birimden fazla parçayı kesme/silme işlemlerinde beliren uyarı
|
description: 100 birimden fazla parçayı kesme/silme işlemlerinde beliren uyarı
|
||||||
pencerelerini devredışı bırakır.
|
pencerelerini devredışı bırakır.
|
||||||
enableColorBlindHelper:
|
enableColorBlindHelper:
|
||||||
@ -872,36 +872,36 @@ settings:
|
|||||||
title: Ses Ayarı
|
title: Ses Ayarı
|
||||||
description: Ses efektlerinin seviyesini ayarlar
|
description: Ses efektlerinin seviyesini ayarlar
|
||||||
musicVolume:
|
musicVolume:
|
||||||
title: Müzİk Ayarı
|
title: Müzik Ayarı
|
||||||
description: Müzik seviyesini ayarlar
|
description: Müzik seviyesini ayarlar
|
||||||
lowQualityMapResources:
|
lowQualityMapResources:
|
||||||
title: Düşük Kalİte Harİta Kaynakları
|
title: Düşük Kalite Harita Kaynakları
|
||||||
description: Oyun performansını artırmak için haritada görünen kaynakların çizim
|
description: Oyun performansını artırmak için haritada görünen kaynakların çizim
|
||||||
kalitesinin sadeleştirir. Kaynaklar daha açık görüneceğinde bu
|
kalitesinin sadeleştirir. Kaynaklar daha açık görüneceğinde bu
|
||||||
özelliği bir dene!
|
özelliği bir dene!
|
||||||
disableTileGrid:
|
disableTileGrid:
|
||||||
title: Harİta Çİzgİlerİnİ Gizle
|
title: Harita Çizgilerini Gizle
|
||||||
description: Harita çizgilerini gizlemek oyun performansına yardımcı olabilir.
|
description: Harita çizgilerini gizlemek oyun performansına yardımcı olabilir.
|
||||||
Aynı zamanda oyunun daha açık görünmesini sağlar!
|
Aynı zamanda oyunun daha açık görünmesini sağlar!
|
||||||
clearCursorOnDeleteWhilePlacing:
|
clearCursorOnDeleteWhilePlacing:
|
||||||
title: Sağ Tık İnşa İptalİ
|
title: Sağ Tık İnşa İptali
|
||||||
description: Varsayılan olarak açık. Özellik açıksa, inşa modundayken sağ yık
|
description: Varsayılan olarak açık. Özellik açıksa, inşa modundayken sağ yık
|
||||||
yapıldığında inşa modundan çıkar. Eğer özellik kapalıysa, inşa
|
yapıldığında inşa modundan çıkar. Eğer özellik kapalıysa, inşa
|
||||||
modundan çıkmadan var olan yapıları sağ tık ile silebilirsiniz.
|
modundan çıkmadan var olan yapıları sağ tık ile silebilirsiniz.
|
||||||
lowQualityTextures:
|
lowQualityTextures:
|
||||||
title: Düşük Kalİte Görüntü (Çirkin)
|
title: Düşük Kalite Görüntü (Çirkin)
|
||||||
description: Performans için düşük kalite görüntü kullanır. Bu oyunun daha
|
description: Performans için düşük kalite görüntü kullanır. Bu oyunun daha
|
||||||
çirkin görünmesine sebep olur!
|
çirkin görünmesine sebep olur!
|
||||||
displayChunkBorders:
|
displayChunkBorders:
|
||||||
title: Harİta Alan Sınırlarını Göster
|
title: Harita Alan Sınırlarını Göster
|
||||||
description: Oyun 16'ya 16 alanlardan oluşur. Bu seçenek aktif olduğunda alan
|
description: Oyun 16'ya 16 alanlardan oluşur. Bu seçenek aktif olduğunda alan
|
||||||
sınırları görüntülenir.
|
sınırları görüntülenir.
|
||||||
pickMinerOnPatch:
|
pickMinerOnPatch:
|
||||||
title: Kaynak Üzerinde Üretİcİ Seç
|
title: Kaynak Üzerinde Üretici Seç
|
||||||
description: Varsayılan olarak açık. Eğer pipet bir kaynağın üzerinde
|
description: Varsayılan olarak açık. Eğer pipet bir kaynağın üzerinde
|
||||||
kullanılırsa, üreteç yapısı inşa için seçilir.
|
kullanılırsa, üreteç yapısı inşa için seçilir.
|
||||||
simplifiedBelts:
|
simplifiedBelts:
|
||||||
title: Sadeleştİrİlmİş Bantlar (Çirkin)
|
title: Sadeleştirilmiş Bantlar (Çirkin)
|
||||||
description: Taşıma bandı üzerindeki eşyalar fare imleci üzerinde değilse
|
description: Taşıma bandı üzerindeki eşyalar fare imleci üzerinde değilse
|
||||||
görüntülenmez. Eğer gerçekten performansa ihtiyacınız yoksa bu
|
görüntülenmez. Eğer gerçekten performansa ihtiyacınız yoksa bu
|
||||||
ayarla oynamanız tavsiye edilmez.
|
ayarla oynamanız tavsiye edilmez.
|
||||||
@ -929,7 +929,7 @@ keybindings:
|
|||||||
placement: Yerleştİrme
|
placement: Yerleştİrme
|
||||||
massSelect: Çoklu Seçim
|
massSelect: Çoklu Seçim
|
||||||
buildings: Yapı Kısayolları
|
buildings: Yapı Kısayolları
|
||||||
placementModifiers: Yerleştİrme Özellİklerİ
|
placementModifiers: Yerleştirme Özellikleri
|
||||||
mappings:
|
mappings:
|
||||||
confirm: Kabul
|
confirm: Kabul
|
||||||
back: Geri
|
back: Geri
|
||||||
@ -947,8 +947,8 @@ keybindings:
|
|||||||
toggleFPSInfo: FPS ve Debug Bilgisini Aç/Kapat
|
toggleFPSInfo: FPS ve Debug Bilgisini Aç/Kapat
|
||||||
belt: Taşıma Bandı
|
belt: Taşıma Bandı
|
||||||
underground_belt: Tünel
|
underground_belt: Tünel
|
||||||
miner: Üretİcİ
|
miner: Üretici
|
||||||
cutter: Kesİcİ
|
cutter: Kesici
|
||||||
rotater: Döndürücü
|
rotater: Döndürücü
|
||||||
stacker: Kaynaştırıcı
|
stacker: Kaynaştırıcı
|
||||||
mixer: Renk Karıştırıcısı
|
mixer: Renk Karıştırıcısı
|
||||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user