Merge branch 'master' into master
4
.gitattributes
vendored
@ -1,4 +0,0 @@
|
|||||||
*.wav filter=lfs diff=lfs merge=lfs -text
|
|
||||||
*.webm filter=lfs diff=lfs merge=lfs -text
|
|
||||||
*.mp3 filter=lfs diff=lfs merge=lfs -text
|
|
||||||
*.psd filter=lfs diff=lfs merge=lfs -text
|
|
||||||
@ -22,8 +22,6 @@ Your goal is to produce shapes by cutting, rotating, merging and painting parts
|
|||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
- Make sure git `git lfs` extension is on your path
|
|
||||||
- Run `git lfs pull` to download sound assets
|
|
||||||
- Make sure `ffmpeg` is on your path
|
- Make sure `ffmpeg` is on your path
|
||||||
- Install Node.js and Yarn
|
- Install Node.js and Yarn
|
||||||
- Run `yarn` in the root folder
|
- Run `yarn` in the root folder
|
||||||
|
|||||||
1
gulp/.gitattributes
vendored
@ -1 +0,0 @@
|
|||||||
*.wav filter=lfs diff=lfs merge=lfs -text
|
|
||||||
@ -25,6 +25,14 @@ module.exports = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getTag() {
|
||||||
|
try {
|
||||||
|
return execSync("git describe --tag --exact-match").toString("ascii");
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error('Current git HEAD is not a version tag');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
getVersion() {
|
getVersion() {
|
||||||
return trim(fs.readFileSync(path.join(__dirname, "..", "version")).toString());
|
return trim(fs.readFileSync(path.join(__dirname, "..", "version")).toString());
|
||||||
},
|
},
|
||||||
|
|||||||
12
gulp/entitlements.plist
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>com.apple.security.cs.allow-jit</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.cs.debugger</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@ -42,6 +42,10 @@ const envVars = [
|
|||||||
"SHAPEZ_CLI_STAGING_FTP_PW",
|
"SHAPEZ_CLI_STAGING_FTP_PW",
|
||||||
"SHAPEZ_CLI_LIVE_FTP_USER",
|
"SHAPEZ_CLI_LIVE_FTP_USER",
|
||||||
"SHAPEZ_CLI_LIVE_FTP_PW",
|
"SHAPEZ_CLI_LIVE_FTP_PW",
|
||||||
|
"SHAPEZ_CLI_APPLE_ID",
|
||||||
|
"SHAPEZ_CLI_APPLE_CERT_NAME",
|
||||||
|
"SHAPEZ_CLI_GITHUB_USER",
|
||||||
|
"SHAPEZ_CLI_GITHUB_TOKEN",
|
||||||
];
|
];
|
||||||
|
|
||||||
for (let i = 0; i < envVars.length; ++i) {
|
for (let i = 0; i < envVars.length; ++i) {
|
||||||
@ -78,6 +82,9 @@ docs.gulptasksDocs($, gulp, buildFolder);
|
|||||||
const standalone = require("./standalone");
|
const standalone = require("./standalone");
|
||||||
standalone.gulptasksStandalone($, gulp, buildFolder);
|
standalone.gulptasksStandalone($, gulp, buildFolder);
|
||||||
|
|
||||||
|
const releaseUploader = require("./release-uploader");
|
||||||
|
releaseUploader.gulptasksReleaseUploader($, gulp, buildFolder);
|
||||||
|
|
||||||
const translations = require("./translations");
|
const translations = require("./translations");
|
||||||
translations.gulptasksTranslations($, gulp, buildFolder);
|
translations.gulptasksTranslations($, gulp, buildFolder);
|
||||||
|
|
||||||
@ -299,6 +306,17 @@ gulp.task(
|
|||||||
gulp.series("utils.cleanup", "step.standalone-prod.all", "step.postbuild")
|
gulp.series("utils.cleanup", "step.standalone-prod.all", "step.postbuild")
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// OS X build and release upload
|
||||||
|
gulp.task(
|
||||||
|
"build.darwin64-prod",
|
||||||
|
gulp.series(
|
||||||
|
"build.standalone-prod",
|
||||||
|
"standalone.prepare",
|
||||||
|
"standalone.package.prod.darwin64",
|
||||||
|
"standalone.uploadRelease.darwin64"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
// Deploying!
|
// Deploying!
|
||||||
gulp.task(
|
gulp.task(
|
||||||
"main.deploy.alpha",
|
"main.deploy.alpha",
|
||||||
|
|||||||
26
gulp/html.js
@ -54,19 +54,19 @@ function gulptasksHTML($, gulp, buildFolder) {
|
|||||||
document.head.appendChild(css);
|
document.head.appendChild(css);
|
||||||
|
|
||||||
// Append async css
|
// Append async css
|
||||||
const asyncCss = document.createElement("link");
|
// const asyncCss = document.createElement("link");
|
||||||
asyncCss.rel = "stylesheet";
|
// asyncCss.rel = "stylesheet";
|
||||||
asyncCss.type = "text/css";
|
// asyncCss.type = "text/css";
|
||||||
asyncCss.media = "none";
|
// asyncCss.media = "none";
|
||||||
asyncCss.setAttribute("onload", "this.media='all'");
|
// asyncCss.setAttribute("onload", "this.media='all'");
|
||||||
asyncCss.href = cachebust("async-resources.css");
|
// asyncCss.href = cachebust("async-resources.css");
|
||||||
if (integrity) {
|
// if (integrity) {
|
||||||
asyncCss.setAttribute(
|
// asyncCss.setAttribute(
|
||||||
"integrity",
|
// "integrity",
|
||||||
computeIntegrityHash(path.join(buildFolder, "async-resources.css"))
|
// computeIntegrityHash(path.join(buildFolder, "async-resources.css"))
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
document.head.appendChild(asyncCss);
|
// document.head.appendChild(asyncCss);
|
||||||
|
|
||||||
if (app) {
|
if (app) {
|
||||||
// Append cordova link
|
// Append cordova link
|
||||||
|
|||||||
66
gulp/release-uploader.js
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
const path = require("path");
|
||||||
|
const fs = require("fs");
|
||||||
|
const execSync = require("child_process").execSync;
|
||||||
|
const { Octokit } = require("@octokit/rest");
|
||||||
|
const buildutils = require("./buildutils");
|
||||||
|
|
||||||
|
function gulptasksReleaseUploader($, gulp, buildFolder) {
|
||||||
|
const standaloneDir = path.join(__dirname, "..", "tmp_standalone_files");
|
||||||
|
const darwinApp = path.join(standaloneDir, "shapez.io-standalone-darwin-x64", "shapez.io-standalone.app");
|
||||||
|
const dmgName = "shapez.io-standalone.dmg";
|
||||||
|
const dmgPath = path.join(standaloneDir, "shapez.io-standalone-darwin-x64", dmgName);
|
||||||
|
|
||||||
|
gulp.task("standalone.uploadRelease.darwin64.cleanup", () => {
|
||||||
|
return gulp.src(dmgPath, { read: false, allowEmpty: true }).pipe($.clean({ force: true }));
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task("standalone.uploadRelease.darwin64.compress", cb => {
|
||||||
|
console.log("Packaging disk image", dmgPath);
|
||||||
|
execSync(`hdiutil create -format UDBZ -srcfolder ${darwinApp} ${dmgPath}`);
|
||||||
|
cb();
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task("standalone.uploadRelease.darwin64.upload", async cb => {
|
||||||
|
const currentTag = buildutils.getTag();
|
||||||
|
|
||||||
|
const octokit = new Octokit({
|
||||||
|
auth: process.env.SHAPEZ_CLI_GITHUB_TOKEN
|
||||||
|
});
|
||||||
|
|
||||||
|
const createdRelease = await octokit.request("POST /repos/{owner}/{repo}/releases", {
|
||||||
|
owner: process.env.SHAPEZ_CLI_GITHUB_USER,
|
||||||
|
repo: "shapez.io",
|
||||||
|
tag_name: currentTag,
|
||||||
|
name: currentTag,
|
||||||
|
draft: true
|
||||||
|
});
|
||||||
|
|
||||||
|
const { data: { id, upload_url } } = createdRelease;
|
||||||
|
console.log(`Created release ${id} for tag ${currentTag}`);
|
||||||
|
|
||||||
|
const dmgContents = fs.readFileSync(dmgPath);
|
||||||
|
const dmgSize = fs.statSync(dmgPath).size;
|
||||||
|
console.log("Uploading", dmgContents.length / 1024 / 1024, "MB to", upload_url);
|
||||||
|
|
||||||
|
await octokit.request({
|
||||||
|
method: "POST",
|
||||||
|
url: upload_url,
|
||||||
|
headers: {
|
||||||
|
"content-type": "application/x-apple-diskimage"
|
||||||
|
},
|
||||||
|
name: dmgName,
|
||||||
|
data: dmgContents
|
||||||
|
});
|
||||||
|
|
||||||
|
cb();
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task("standalone.uploadRelease.darwin64",
|
||||||
|
gulp.series(
|
||||||
|
"standalone.uploadRelease.darwin64.cleanup",
|
||||||
|
"standalone.uploadRelease.darwin64.compress",
|
||||||
|
"standalone.uploadRelease.darwin64.upload"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { gulptasksReleaseUploader };
|
||||||
@ -1,3 +1,4 @@
|
|||||||
|
require('colors');
|
||||||
const packager = require("electron-packager");
|
const packager = require("electron-packager");
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
const { getVersion } = require("./buildutils");
|
const { getVersion } = require("./buildutils");
|
||||||
@ -80,8 +81,9 @@ function gulptasksStandalone($, gulp) {
|
|||||||
* @param {'win32'|'linux'|'darwin'} platform
|
* @param {'win32'|'linux'|'darwin'} platform
|
||||||
* @param {'x64'|'ia32'} arch
|
* @param {'x64'|'ia32'} arch
|
||||||
* @param {function():void} cb
|
* @param {function():void} cb
|
||||||
|
* @param {boolean=} isRelease
|
||||||
*/
|
*/
|
||||||
function packageStandalone(platform, arch, cb) {
|
function packageStandalone(platform, arch, cb, isRelease = true) {
|
||||||
const tomlFile = fs.readFileSync(path.join(__dirname, ".itch.toml"));
|
const tomlFile = fs.readFileSync(path.join(__dirname, ".itch.toml"));
|
||||||
|
|
||||||
packager({
|
packager({
|
||||||
@ -99,6 +101,20 @@ function gulptasksStandalone($, gulp) {
|
|||||||
overwrite: true,
|
overwrite: true,
|
||||||
appBundleId: "io.shapez.standalone",
|
appBundleId: "io.shapez.standalone",
|
||||||
appCategoryType: "public.app-category.games",
|
appCategoryType: "public.app-category.games",
|
||||||
|
...(isRelease && platform === "darwin" && {
|
||||||
|
osxSign: {
|
||||||
|
identity: process.env.SHAPEZ_CLI_APPLE_CERT_NAME,
|
||||||
|
"hardened-runtime": true,
|
||||||
|
hardenedRuntime: true,
|
||||||
|
entitlements: 'entitlements.plist',
|
||||||
|
'entitlements-inherit': 'entitlements.plist',
|
||||||
|
'signature-flags': 'library'
|
||||||
|
},
|
||||||
|
osxNotarize: {
|
||||||
|
appleId: process.env.SHAPEZ_CLI_APPLE_ID,
|
||||||
|
appleIdPassword: "@keychain:SHAPEZ_CLI_APPLE_ID"
|
||||||
|
}
|
||||||
|
})
|
||||||
}).then(
|
}).then(
|
||||||
appPaths => {
|
appPaths => {
|
||||||
console.log("Packages created:", appPaths);
|
console.log("Packages created:", appPaths);
|
||||||
@ -123,7 +139,11 @@ function gulptasksStandalone($, gulp) {
|
|||||||
fs.chmodSync(path.join(appPath, "play.sh"), 0o775);
|
fs.chmodSync(path.join(appPath, "play.sh"), 0o775);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (platform === "darwin") {
|
if (process.platform === "win32" && platform === "darwin") {
|
||||||
|
console.warn("Cross-building for macOS on Windows: dereferencing symlinks.\n".red +
|
||||||
|
"This will nearly double app size and make code signature invalid. Sorry!\n".red.bold +
|
||||||
|
"For more information, see " + "https://github.com/electron/electron-packager/issues/71".underline);
|
||||||
|
|
||||||
// Clear up framework folders
|
// Clear up framework folders
|
||||||
fs.writeFileSync(
|
fs.writeFileSync(
|
||||||
path.join(appPath, "play.sh"),
|
path.join(appPath, "play.sh"),
|
||||||
@ -175,6 +195,7 @@ function gulptasksStandalone($, gulp) {
|
|||||||
gulp.task("standalone.package.prod.linux64", cb => packageStandalone("linux", "x64", 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.linux32", cb => packageStandalone("linux", "ia32", cb));
|
||||||
gulp.task("standalone.package.prod.darwin64", cb => packageStandalone("darwin", "x64", 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(
|
gulp.task(
|
||||||
"standalone.package.prod",
|
"standalone.package.prod",
|
||||||
|
|||||||
@ -71,6 +71,7 @@
|
|||||||
"yawn-yaml": "^1.5.0"
|
"yawn-yaml": "^1.5.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@octokit/rest": "^18.0.6",
|
||||||
"@typescript-eslint/eslint-plugin": "3.0.1",
|
"@typescript-eslint/eslint-plugin": "3.0.1",
|
||||||
"@typescript-eslint/parser": "3.0.1",
|
"@typescript-eslint/parser": "3.0.1",
|
||||||
"autoprefixer": "^9.4.3",
|
"autoprefixer": "^9.4.3",
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 8.0 KiB After Width: | Height: | Size: 4.0 KiB |
BIN
res/ui/building_tutorials/comparator.png
Normal file
|
After Width: | Height: | Size: 114 KiB |
|
Before Width: | Height: | Size: 106 KiB After Width: | Height: | Size: 114 KiB |
|
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 1.3 MiB |
|
Before Width: | Height: | Size: 279 KiB After Width: | Height: | Size: 283 KiB |
|
Before Width: | Height: | Size: 700 KiB After Width: | Height: | Size: 706 KiB |
@ -118,7 +118,7 @@
|
|||||||
<key>textureFormat</key>
|
<key>textureFormat</key>
|
||||||
<enum type="SettingsBase::TextureFormat">png</enum>
|
<enum type="SettingsBase::TextureFormat">png</enum>
|
||||||
<key>borderPadding</key>
|
<key>borderPadding</key>
|
||||||
<uint>3</uint>
|
<uint>4</uint>
|
||||||
<key>maxTextureSize</key>
|
<key>maxTextureSize</key>
|
||||||
<QSize>
|
<QSize>
|
||||||
<key>width</key>
|
<key>width</key>
|
||||||
|
|||||||
BIN
res_raw/sounds/music/theme-short.mp3
Normal file
@ -1,3 +0,0 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
|
||||||
oid sha256:df7487fb5e8cb34cecee2519b9c3162a5107d2d7b1301c4a550904cfb108a015
|
|
||||||
size 223361394
|
|
||||||
BIN
res_raw/sounds/sfx/copy.wav
Normal file
|
Before Width: | Height: | Size: 7.8 KiB After Width: | Height: | Size: 9.9 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 9.3 KiB After Width: | Height: | Size: 9.6 KiB |
|
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 4.7 KiB |
@ -1,8 +1,24 @@
|
|||||||
#ingame_HUD_BetaOverlay {
|
#ingame_HUD_BetaOverlay {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
@include S(top, 10px);
|
@include S(top, 10px);
|
||||||
@include S(right, 15px);
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
color: $colorRedBright;
|
color: $colorRedBright;
|
||||||
@include Heading;
|
@include Heading;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
@include PlainText;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
color: #555;
|
||||||
|
@include SuperSmallText;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -178,6 +178,27 @@
|
|||||||
display: list-item;
|
display: list-item;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ingameItemChooser {
|
||||||
|
@include S(margin, 20px, 0);
|
||||||
|
display: grid;
|
||||||
|
grid-auto-flow: column;
|
||||||
|
grid-auto-columns: 1fr;
|
||||||
|
@include S(grid-column-gap, 3px);
|
||||||
|
|
||||||
|
canvas {
|
||||||
|
pointer-events: all;
|
||||||
|
@include S(width, 25px);
|
||||||
|
@include S(height, 25px);
|
||||||
|
position: relative;
|
||||||
|
cursor: pointer;
|
||||||
|
@include IncreasedClickArea(3px);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
> .buttons {
|
> .buttons {
|
||||||
@ -220,7 +241,7 @@
|
|||||||
content: " ";
|
content: " ";
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
background: rgba(#fff, 0.6);
|
background: rgba(#fff, 0.6);
|
||||||
@include InlineAnimation(5s linear) {
|
@include InlineAnimation(3s linear) {
|
||||||
0% {
|
0% {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,7 +13,7 @@ $buildingsAndVariants: belt, balancer, underground_belt, underground_belt-tier2,
|
|||||||
cutter, cutter-quad, rotater, rotater-ccw, stacker, mixer, painter-double, painter-quad, trash, storage,
|
cutter, cutter-quad, rotater, rotater-ccw, stacker, mixer, painter-double, painter-quad, trash, storage,
|
||||||
reader, rotater-rotate180, display, constant_signal, wire, wire_tunnel, logic_gate-or, logic_gate-not,
|
reader, rotater-rotate180, display, constant_signal, wire, wire_tunnel, logic_gate-or, logic_gate-not,
|
||||||
logic_gate-xor, analyzer, virtual_processor-rotater, virtual_processor-unstacker,
|
logic_gate-xor, analyzer, virtual_processor-rotater, virtual_processor-unstacker,
|
||||||
virtual_processor-stacker, virtual_processor-painter, wire-second, painter, painter-mirrored;
|
virtual_processor-stacker, virtual_processor-painter, wire-second, painter, painter-mirrored, comparator;
|
||||||
@each $building in $buildingsAndVariants {
|
@each $building in $buildingsAndVariants {
|
||||||
[data-icon="building_tutorials/#{$building}.png"] {
|
[data-icon="building_tutorials/#{$building}.png"] {
|
||||||
/* @load-async */
|
/* @load-async */
|
||||||
|
|||||||
@ -1,43 +1,9 @@
|
|||||||
export const CHANGELOG = [
|
export const CHANGELOG = [
|
||||||
{
|
{
|
||||||
version: "1.2.0",
|
version: "1.2.0",
|
||||||
date: "unreleased",
|
date: "09.10.2020",
|
||||||
entries: [
|
entries: [
|
||||||
"WIRES",
|
"⚠️⚠️This update is HUGE, view the full changelog <a href='https://shapez.io/wires/' target='_blank'>here</a>! ⚠️⚠️",
|
||||||
"Reworked menu UI design (by dengr1605)",
|
|
||||||
"Allow holding ALT in belt planner to reverse direction (by jakobhellermann)",
|
|
||||||
"Clear cursor when trying to pipette the same building twice (by hexy)",
|
|
||||||
"Fixed level 18 stacker bug: If you experienced it already, you know it, if not, I don't want to spoiler (by hexy)",
|
|
||||||
"Added keybinding to close menus (by isaisstillalive / Sandwichs-del)",
|
|
||||||
"Fix rare crash regarding the buildings toolbar (by isaisstillalive)",
|
|
||||||
"Fixed some phrases (by EnderDoom77)",
|
|
||||||
"Zoom towards mouse cursor (by Dimava)",
|
|
||||||
"Added multiple settings to optimize the performance",
|
|
||||||
"Updated the soundtrack again, it is now 40 minutes in total!",
|
|
||||||
"Added a button to the statistics dialog to disable the sorting (by squeek502)",
|
|
||||||
"Tier 2 tunnels are now 9 tiles wide, so the gap between is 8 tiles (double the tier 1 range)",
|
|
||||||
"Updated and added new translations (Thanks to all contributors!)",
|
|
||||||
"Show connected chained miners on hover",
|
|
||||||
"Added setting to be able to delete buildings while placing (inspired by hexy)",
|
|
||||||
"You can now adjust the sound and music volumes! (inspired by Yoshie2000)",
|
|
||||||
"Some hud elements now have reduced opacity when hovering, so you can see through (inspired by mvb005)",
|
|
||||||
"Mark pinned shapes in statistics dialog and show them first (inspired by davidburhans)",
|
|
||||||
"Added setting to show chunk borders",
|
|
||||||
"Quad painters have been reworked! They now are integrated with the wires, and only paint the shape when the value is 1 (inspired by dengr1605)",
|
|
||||||
"There are now compact 1x1 balancers available to be unlocked!",
|
|
||||||
"Replaced level completion sound to be less distracting",
|
|
||||||
"Allow editing waypoints (by isaisstillalive)",
|
|
||||||
"Show confirmation when cutting area which is too expensive to get pasted again (by isaisstillalive)",
|
|
||||||
"Show mouse and camera tile on debug overlay (F4) (by dengr)",
|
|
||||||
"Fix belt planner placing the belt when a dialog opens in the meantime",
|
|
||||||
"Added confirmation when deleting a savegame",
|
|
||||||
"Make chained mainer the default and only option after unlocking it",
|
|
||||||
"Fixed tunnels entrances connecting to exits sometimes when they shouldn't",
|
|
||||||
"You can now pan the map with your mouse by moving the cursor to the edges of the screen!",
|
|
||||||
"Added setting to auto select the extractor when pipetting a resource patch (by Exund)",
|
|
||||||
"You can now change the unit (seconds / minutes / hours) in the statistics dialog",
|
|
||||||
"The initial belt planner direction is now based on the cursor movement (by MizardX)",
|
|
||||||
"Fix preferred variant not getting saved when clicking on the hud (by Danacus)",
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,215 +1,232 @@
|
|||||||
/* typehints:start */
|
/* typehints:start */
|
||||||
import { Application } from "../application";
|
import { Application } from "../application";
|
||||||
/* typehints:end */
|
/* typehints:end */
|
||||||
|
|
||||||
import { Loader } from "./loader";
|
import { Loader } from "./loader";
|
||||||
import { createLogger } from "./logging";
|
import { createLogger } from "./logging";
|
||||||
import { Signal } from "./signal";
|
import { Signal } from "./signal";
|
||||||
import { SOUNDS, MUSIC } from "../platform/sound";
|
import { SOUNDS, MUSIC } from "../platform/sound";
|
||||||
import { AtlasDefinition, atlasFiles } from "./atlas_definitions";
|
import { AtlasDefinition, atlasFiles } from "./atlas_definitions";
|
||||||
import { initBuildingCodesAfterResourcesLoaded } from "../game/meta_building_registry";
|
import { initBuildingCodesAfterResourcesLoaded } from "../game/meta_building_registry";
|
||||||
|
import { cachebust } from "./cachebust";
|
||||||
const logger = createLogger("background_loader");
|
|
||||||
|
const logger = createLogger("background_loader");
|
||||||
const essentialMainMenuSprites = [
|
|
||||||
"logo.png",
|
const essentialMainMenuSprites = [
|
||||||
...G_ALL_UI_IMAGES.filter(src => src.startsWith("ui/") && src.indexOf(".gif") < 0),
|
"logo.png",
|
||||||
];
|
...G_ALL_UI_IMAGES.filter(src => src.startsWith("ui/") && src.indexOf(".gif") < 0),
|
||||||
const essentialMainMenuSounds = [
|
];
|
||||||
SOUNDS.uiClick,
|
const essentialMainMenuSounds = [
|
||||||
SOUNDS.uiError,
|
SOUNDS.uiClick,
|
||||||
SOUNDS.dialogError,
|
SOUNDS.uiError,
|
||||||
SOUNDS.dialogOk,
|
SOUNDS.dialogError,
|
||||||
SOUNDS.swishShow,
|
SOUNDS.dialogOk,
|
||||||
SOUNDS.swishHide,
|
SOUNDS.swishShow,
|
||||||
];
|
SOUNDS.swishHide,
|
||||||
|
];
|
||||||
const essentialBareGameAtlases = atlasFiles;
|
|
||||||
const essentialBareGameSprites = G_ALL_UI_IMAGES.filter(src => src.indexOf(".gif") < 0);
|
const essentialBareGameAtlases = atlasFiles;
|
||||||
const essentialBareGameSounds = [MUSIC.theme];
|
const essentialBareGameSprites = G_ALL_UI_IMAGES.filter(src => src.indexOf(".gif") < 0);
|
||||||
|
const essentialBareGameSounds = [MUSIC.theme];
|
||||||
const additionalGameSprites = [];
|
|
||||||
// @ts-ignore
|
const additionalGameSprites = [];
|
||||||
const additionalGameSounds = [...Object.values(SOUNDS), ...Object.values(MUSIC)];
|
// @ts-ignore
|
||||||
|
const additionalGameSounds = [...Object.values(SOUNDS), ...Object.values(MUSIC)];
|
||||||
export class BackgroundResourcesLoader {
|
|
||||||
/**
|
export class BackgroundResourcesLoader {
|
||||||
*
|
/**
|
||||||
* @param {Application} app
|
*
|
||||||
*/
|
* @param {Application} app
|
||||||
constructor(app) {
|
*/
|
||||||
this.app = app;
|
constructor(app) {
|
||||||
|
this.app = app;
|
||||||
this.registerReady = false;
|
|
||||||
this.mainMenuReady = false;
|
this.registerReady = false;
|
||||||
this.bareGameReady = false;
|
this.mainMenuReady = false;
|
||||||
this.additionalReady = false;
|
this.bareGameReady = false;
|
||||||
|
this.additionalReady = false;
|
||||||
this.signalMainMenuLoaded = new Signal();
|
|
||||||
this.signalBareGameLoaded = new Signal();
|
this.signalMainMenuLoaded = new Signal();
|
||||||
this.signalAdditionalLoaded = new Signal();
|
this.signalBareGameLoaded = new Signal();
|
||||||
|
this.signalAdditionalLoaded = new Signal();
|
||||||
this.numAssetsLoaded = 0;
|
|
||||||
this.numAssetsToLoadTotal = 0;
|
this.numAssetsLoaded = 0;
|
||||||
|
this.numAssetsToLoadTotal = 0;
|
||||||
// Avoid loading stuff twice
|
|
||||||
this.spritesLoaded = [];
|
// Avoid loading stuff twice
|
||||||
this.soundsLoaded = [];
|
this.spritesLoaded = [];
|
||||||
}
|
this.soundsLoaded = [];
|
||||||
|
}
|
||||||
getNumAssetsLoaded() {
|
|
||||||
return this.numAssetsLoaded;
|
getNumAssetsLoaded() {
|
||||||
}
|
return this.numAssetsLoaded;
|
||||||
|
}
|
||||||
getNumAssetsTotal() {
|
|
||||||
return this.numAssetsToLoadTotal;
|
getNumAssetsTotal() {
|
||||||
}
|
return this.numAssetsToLoadTotal;
|
||||||
|
}
|
||||||
getPromiseForMainMenu() {
|
|
||||||
if (this.mainMenuReady) {
|
getPromiseForMainMenu() {
|
||||||
return Promise.resolve();
|
if (this.mainMenuReady) {
|
||||||
}
|
return Promise.resolve();
|
||||||
|
}
|
||||||
return new Promise(resolve => {
|
|
||||||
this.signalMainMenuLoaded.add(resolve);
|
return new Promise(resolve => {
|
||||||
});
|
this.signalMainMenuLoaded.add(resolve);
|
||||||
}
|
});
|
||||||
|
}
|
||||||
getPromiseForBareGame() {
|
|
||||||
if (this.bareGameReady) {
|
getPromiseForBareGame() {
|
||||||
return Promise.resolve();
|
if (this.bareGameReady) {
|
||||||
}
|
return Promise.resolve();
|
||||||
|
}
|
||||||
return new Promise(resolve => {
|
|
||||||
this.signalBareGameLoaded.add(resolve);
|
return new Promise(resolve => {
|
||||||
});
|
this.signalBareGameLoaded.add(resolve);
|
||||||
}
|
});
|
||||||
|
}
|
||||||
startLoading() {
|
|
||||||
this.internalStartLoadingEssentialsForMainMenu();
|
startLoading() {
|
||||||
}
|
this.internalStartLoadingEssentialsForMainMenu();
|
||||||
|
}
|
||||||
internalStartLoadingEssentialsForMainMenu() {
|
|
||||||
logger.log("⏰ Start load: main menu");
|
internalStartLoadingEssentialsForMainMenu() {
|
||||||
this.internalLoadSpritesAndSounds(essentialMainMenuSprites, essentialMainMenuSounds)
|
logger.log("⏰ Start load: main menu");
|
||||||
.catch(err => {
|
this.internalLoadSpritesAndSounds(essentialMainMenuSprites, essentialMainMenuSounds)
|
||||||
logger.warn("⏰ Failed to load essentials for main menu:", err);
|
.catch(err => {
|
||||||
})
|
logger.warn("⏰ Failed to load essentials for main menu:", err);
|
||||||
.then(() => {
|
})
|
||||||
logger.log("⏰ Finish load: main menu");
|
.then(() => {
|
||||||
this.mainMenuReady = true;
|
logger.log("⏰ Finish load: main menu");
|
||||||
this.signalMainMenuLoaded.dispatch();
|
this.mainMenuReady = true;
|
||||||
this.internalStartLoadingEssentialsForBareGame();
|
this.signalMainMenuLoaded.dispatch();
|
||||||
});
|
this.internalStartLoadingEssentialsForBareGame();
|
||||||
}
|
});
|
||||||
|
}
|
||||||
internalStartLoadingEssentialsForBareGame() {
|
|
||||||
logger.log("⏰ Start load: bare game");
|
internalStartLoadingEssentialsForBareGame() {
|
||||||
this.internalLoadSpritesAndSounds(
|
logger.log("⏰ Start load: bare game");
|
||||||
essentialBareGameSprites,
|
this.internalLoadSpritesAndSounds(
|
||||||
essentialBareGameSounds,
|
essentialBareGameSprites,
|
||||||
essentialBareGameAtlases
|
essentialBareGameSounds,
|
||||||
)
|
essentialBareGameAtlases
|
||||||
.catch(err => {
|
)
|
||||||
logger.warn("⏰ Failed to load essentials for bare game:", err);
|
.then(() => this.internalPreloadCss("async-resources.scss"))
|
||||||
})
|
.catch(err => {
|
||||||
.then(() => {
|
logger.warn("⏰ Failed to load essentials for bare game:", err);
|
||||||
logger.log("⏰ Finish load: bare game");
|
})
|
||||||
this.bareGameReady = true;
|
.then(() => {
|
||||||
initBuildingCodesAfterResourcesLoaded();
|
logger.log("⏰ Finish load: bare game");
|
||||||
this.signalBareGameLoaded.dispatch();
|
this.bareGameReady = true;
|
||||||
this.internalStartLoadingAdditionalGameAssets();
|
initBuildingCodesAfterResourcesLoaded();
|
||||||
});
|
this.signalBareGameLoaded.dispatch();
|
||||||
}
|
this.internalStartLoadingAdditionalGameAssets();
|
||||||
|
});
|
||||||
internalStartLoadingAdditionalGameAssets() {
|
}
|
||||||
const additionalAtlases = [];
|
|
||||||
logger.log("⏰ Start load: additional assets (", additionalAtlases.length, "images)");
|
internalStartLoadingAdditionalGameAssets() {
|
||||||
this.internalLoadSpritesAndSounds(additionalGameSprites, additionalGameSounds, additionalAtlases)
|
const additionalAtlases = [];
|
||||||
.catch(err => {
|
logger.log("⏰ Start load: additional assets (", additionalAtlases.length, "images)");
|
||||||
logger.warn("⏰ Failed to load additional assets:", err);
|
this.internalLoadSpritesAndSounds(additionalGameSprites, additionalGameSounds, additionalAtlases)
|
||||||
})
|
.catch(err => {
|
||||||
.then(() => {
|
logger.warn("⏰ Failed to load additional assets:", err);
|
||||||
logger.log("⏰ Finish load: additional assets");
|
})
|
||||||
this.additionalReady = true;
|
.then(() => {
|
||||||
this.signalAdditionalLoaded.dispatch();
|
logger.log("⏰ Finish load: additional assets");
|
||||||
});
|
this.additionalReady = true;
|
||||||
}
|
this.signalAdditionalLoaded.dispatch();
|
||||||
|
});
|
||||||
/**
|
}
|
||||||
* @param {Array<string>} sprites
|
|
||||||
* @param {Array<string>} sounds
|
internalPreloadCss(name) {
|
||||||
* @param {Array<AtlasDefinition>} atlases
|
return new Promise((resolve, reject) => {
|
||||||
* @returns {Promise<void>}
|
const link = document.createElement("link");
|
||||||
*/
|
|
||||||
internalLoadSpritesAndSounds(sprites, sounds, atlases = []) {
|
link.onload = resolve;
|
||||||
this.numAssetsToLoadTotal = sprites.length + sounds.length + atlases.length;
|
link.onerror = reject;
|
||||||
this.numAssetsLoaded = 0;
|
|
||||||
|
link.setAttribute("rel", "stylesheet");
|
||||||
let promises = [];
|
link.setAttribute("media", "all");
|
||||||
|
link.setAttribute("type", "text/css");
|
||||||
for (let i = 0; i < sounds.length; ++i) {
|
link.setAttribute("href", cachebust("async-resources.css"));
|
||||||
if (this.soundsLoaded.indexOf(sounds[i]) >= 0) {
|
document.head.appendChild(link);
|
||||||
// Already loaded
|
});
|
||||||
continue;
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
this.soundsLoaded.push(sounds[i]);
|
* @param {Array<string>} sprites
|
||||||
promises.push(
|
* @param {Array<string>} sounds
|
||||||
this.app.sound
|
* @param {Array<AtlasDefinition>} atlases
|
||||||
.loadSound(sounds[i])
|
* @returns {Promise<void>}
|
||||||
.catch(err => {
|
*/
|
||||||
logger.warn("Failed to load sound:", sounds[i]);
|
internalLoadSpritesAndSounds(sprites, sounds, atlases = []) {
|
||||||
})
|
this.numAssetsToLoadTotal = sprites.length + sounds.length + atlases.length;
|
||||||
.then(() => {
|
this.numAssetsLoaded = 0;
|
||||||
this.numAssetsLoaded++;
|
|
||||||
})
|
let promises = [];
|
||||||
);
|
|
||||||
}
|
for (let i = 0; i < sounds.length; ++i) {
|
||||||
|
if (this.soundsLoaded.indexOf(sounds[i]) >= 0) {
|
||||||
for (let i = 0; i < sprites.length; ++i) {
|
// Already loaded
|
||||||
if (this.spritesLoaded.indexOf(sprites[i]) >= 0) {
|
continue;
|
||||||
// Already loaded
|
}
|
||||||
continue;
|
|
||||||
}
|
this.soundsLoaded.push(sounds[i]);
|
||||||
this.spritesLoaded.push(sprites[i]);
|
promises.push(
|
||||||
promises.push(
|
this.app.sound
|
||||||
Loader.preloadCSSSprite(sprites[i])
|
.loadSound(sounds[i])
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
logger.warn("Failed to load css sprite:", sprites[i]);
|
logger.warn("Failed to load sound:", sounds[i]);
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.numAssetsLoaded++;
|
this.numAssetsLoaded++;
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < atlases.length; ++i) {
|
for (let i = 0; i < sprites.length; ++i) {
|
||||||
const atlas = atlases[i];
|
if (this.spritesLoaded.indexOf(sprites[i]) >= 0) {
|
||||||
promises.push(
|
// Already loaded
|
||||||
Loader.preloadAtlas(atlas)
|
continue;
|
||||||
.catch(err => {
|
}
|
||||||
logger.warn("Failed to load atlas:", atlas.sourceFileName);
|
this.spritesLoaded.push(sprites[i]);
|
||||||
})
|
promises.push(
|
||||||
.then(() => {
|
Loader.preloadCSSSprite(sprites[i])
|
||||||
this.numAssetsLoaded++;
|
.catch(err => {
|
||||||
})
|
logger.warn("Failed to load css sprite:", sprites[i]);
|
||||||
);
|
})
|
||||||
}
|
.then(() => {
|
||||||
|
this.numAssetsLoaded++;
|
||||||
return (
|
})
|
||||||
Promise.all(promises)
|
);
|
||||||
|
}
|
||||||
// // Remove some pressure by waiting a bit
|
|
||||||
// .then(() => {
|
for (let i = 0; i < atlases.length; ++i) {
|
||||||
// return new Promise(resolve => {
|
const atlas = atlases[i];
|
||||||
// setTimeout(resolve, 200);
|
promises.push(
|
||||||
// });
|
Loader.preloadAtlas(atlas)
|
||||||
// })
|
.catch(err => {
|
||||||
.then(() => {
|
logger.warn("Failed to load atlas:", atlas.sourceFileName);
|
||||||
this.numAssetsToLoadTotal = 0;
|
})
|
||||||
this.numAssetsLoaded = 0;
|
.then(() => {
|
||||||
})
|
this.numAssetsLoaded++;
|
||||||
);
|
})
|
||||||
}
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
Promise.all(promises)
|
||||||
|
|
||||||
|
// // Remove some pressure by waiting a bit
|
||||||
|
// .then(() => {
|
||||||
|
// return new Promise(resolve => {
|
||||||
|
// setTimeout(resolve, 200);
|
||||||
|
// });
|
||||||
|
// })
|
||||||
|
.then(() => {
|
||||||
|
this.numAssetsToLoadTotal = 0;
|
||||||
|
this.numAssetsLoaded = 0;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -20,6 +20,7 @@ export const THIRDPARTY_URLS = {
|
|||||||
discord: "https://discord.gg/HN7EVzV",
|
discord: "https://discord.gg/HN7EVzV",
|
||||||
github: "https://github.com/tobspr/shapez.io",
|
github: "https://github.com/tobspr/shapez.io",
|
||||||
reddit: "https://www.reddit.com/r/shapezio",
|
reddit: "https://www.reddit.com/r/shapezio",
|
||||||
|
shapeViewer: "https://viewer.shapez.io",
|
||||||
|
|
||||||
standaloneStorePage: "https://store.steampowered.com/app/1318690/shapezio/",
|
standaloneStorePage: "https://store.steampowered.com/app/1318690/shapezio/",
|
||||||
};
|
};
|
||||||
|
|||||||
@ -60,6 +60,8 @@ export class Dialog {
|
|||||||
this.buttonSignals[buttonId] = new Signal();
|
this.buttonSignals[buttonId] = new Signal();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.valueChosen = new Signal();
|
||||||
|
|
||||||
this.timeouts = [];
|
this.timeouts = [];
|
||||||
this.clickDetectors = [];
|
this.clickDetectors = [];
|
||||||
|
|
||||||
@ -164,7 +166,7 @@ export class Dialog {
|
|||||||
const timeout = setTimeout(() => {
|
const timeout = setTimeout(() => {
|
||||||
button.classList.remove("timedButton");
|
button.classList.remove("timedButton");
|
||||||
arrayDeleteValue(this.timeouts, timeout);
|
arrayDeleteValue(this.timeouts, timeout);
|
||||||
}, 5000);
|
}, 3000);
|
||||||
this.timeouts.push(timeout);
|
this.timeouts.push(timeout);
|
||||||
}
|
}
|
||||||
if (isEnter || isEscape) {
|
if (isEnter || isEscape) {
|
||||||
@ -431,10 +433,12 @@ export class DialogWithForm extends Dialog {
|
|||||||
for (let i = 0; i < this.formElements.length; ++i) {
|
for (let i = 0; i < this.formElements.length; ++i) {
|
||||||
const elem = this.formElements[i];
|
const elem = this.formElements[i];
|
||||||
elem.bindEvents(div, this.clickDetectors);
|
elem.bindEvents(div, this.clickDetectors);
|
||||||
|
elem.valueChosen.add(this.closeRequested.dispatch, this.closeRequested);
|
||||||
|
elem.valueChosen.add(this.valueChosen.dispatch, this.valueChosen);
|
||||||
}
|
}
|
||||||
|
|
||||||
waitNextFrame().then(() => {
|
waitNextFrame().then(() => {
|
||||||
this.formElements[0].focus();
|
this.formElements[this.formElements.length - 1].focus();
|
||||||
});
|
});
|
||||||
|
|
||||||
return div;
|
return div;
|
||||||
|
|||||||
@ -1,150 +1,221 @@
|
|||||||
import { ClickDetector } from "./click_detector";
|
import { BaseItem } from "../game/base_item";
|
||||||
|
import { ClickDetector } from "./click_detector";
|
||||||
export class FormElement {
|
import { Signal } from "./signal";
|
||||||
constructor(id, label) {
|
|
||||||
this.id = id;
|
export class FormElement {
|
||||||
this.label = label;
|
constructor(id, label) {
|
||||||
}
|
this.id = id;
|
||||||
|
this.label = label;
|
||||||
getHtml() {
|
|
||||||
abstract;
|
this.valueChosen = new Signal();
|
||||||
return "";
|
}
|
||||||
}
|
|
||||||
|
getHtml() {
|
||||||
getFormElement(parent) {
|
abstract;
|
||||||
return parent.querySelector("[data-formId='" + this.id + "']");
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
bindEvents(parent, clickTrackers) {
|
getFormElement(parent) {
|
||||||
abstract;
|
return parent.querySelector("[data-formId='" + this.id + "']");
|
||||||
}
|
}
|
||||||
|
|
||||||
focus() {}
|
bindEvents(parent, clickTrackers) {
|
||||||
|
abstract;
|
||||||
isValid() {
|
}
|
||||||
return true;
|
|
||||||
}
|
focus() {}
|
||||||
|
|
||||||
/** @returns {any} */
|
isValid() {
|
||||||
getValue() {
|
return true;
|
||||||
abstract;
|
}
|
||||||
}
|
|
||||||
}
|
/** @returns {any} */
|
||||||
|
getValue() {
|
||||||
export class FormElementInput extends FormElement {
|
abstract;
|
||||||
constructor({ id, label = null, placeholder, defaultValue = "", inputType = "text", validator = null }) {
|
}
|
||||||
super(id, label);
|
}
|
||||||
this.placeholder = placeholder;
|
|
||||||
this.defaultValue = defaultValue;
|
export class FormElementInput extends FormElement {
|
||||||
this.inputType = inputType;
|
constructor({ id, label = null, placeholder, defaultValue = "", inputType = "text", validator = null }) {
|
||||||
this.validator = validator;
|
super(id, label);
|
||||||
|
this.placeholder = placeholder;
|
||||||
this.element = null;
|
this.defaultValue = defaultValue;
|
||||||
}
|
this.inputType = inputType;
|
||||||
|
this.validator = validator;
|
||||||
getHtml() {
|
|
||||||
let classes = [];
|
this.element = null;
|
||||||
let inputType = "text";
|
}
|
||||||
let maxlength = 256;
|
|
||||||
switch (this.inputType) {
|
getHtml() {
|
||||||
case "text": {
|
let classes = [];
|
||||||
classes.push("input-text");
|
let inputType = "text";
|
||||||
break;
|
let maxlength = 256;
|
||||||
}
|
switch (this.inputType) {
|
||||||
|
case "text": {
|
||||||
case "email": {
|
classes.push("input-text");
|
||||||
classes.push("input-email");
|
break;
|
||||||
inputType = "email";
|
}
|
||||||
break;
|
|
||||||
}
|
case "email": {
|
||||||
|
classes.push("input-email");
|
||||||
case "token": {
|
inputType = "email";
|
||||||
classes.push("input-token");
|
break;
|
||||||
inputType = "text";
|
}
|
||||||
maxlength = 4;
|
|
||||||
break;
|
case "token": {
|
||||||
}
|
classes.push("input-token");
|
||||||
}
|
inputType = "text";
|
||||||
|
maxlength = 4;
|
||||||
return `
|
break;
|
||||||
<div class="formElement input">
|
}
|
||||||
${this.label ? `<label>${this.label}</label>` : ""}
|
}
|
||||||
<input
|
|
||||||
type="${inputType}"
|
return `
|
||||||
value="${this.defaultValue.replace(/["\\]+/gi, "")}"
|
<div class="formElement input">
|
||||||
maxlength="${maxlength}"
|
${this.label ? `<label>${this.label}</label>` : ""}
|
||||||
autocomplete="off"
|
<input
|
||||||
autocorrect="off"
|
type="${inputType}"
|
||||||
autocapitalize="off"
|
value="${this.defaultValue.replace(/["\\]+/gi, "")}"
|
||||||
spellcheck="false"
|
maxlength="${maxlength}"
|
||||||
class="${classes.join(" ")}"
|
autocomplete="off"
|
||||||
placeholder="${this.placeholder.replace(/["\\]+/gi, "")}"
|
autocorrect="off"
|
||||||
data-formId="${this.id}">
|
autocapitalize="off"
|
||||||
</div>
|
spellcheck="false"
|
||||||
`;
|
class="${classes.join(" ")}"
|
||||||
}
|
placeholder="${this.placeholder.replace(/["\\]+/gi, "")}"
|
||||||
|
data-formId="${this.id}">
|
||||||
bindEvents(parent, clickTrackers) {
|
</div>
|
||||||
this.element = this.getFormElement(parent);
|
`;
|
||||||
this.element.addEventListener("input", event => this.updateErrorState());
|
}
|
||||||
this.updateErrorState();
|
|
||||||
}
|
bindEvents(parent, clickTrackers) {
|
||||||
|
this.element = this.getFormElement(parent);
|
||||||
updateErrorState() {
|
this.element.addEventListener("input", event => this.updateErrorState());
|
||||||
this.element.classList.toggle("errored", !this.isValid());
|
this.updateErrorState();
|
||||||
}
|
}
|
||||||
|
|
||||||
isValid() {
|
updateErrorState() {
|
||||||
return !this.validator || this.validator(this.element.value);
|
this.element.classList.toggle("errored", !this.isValid());
|
||||||
}
|
}
|
||||||
|
|
||||||
getValue() {
|
isValid() {
|
||||||
return this.element.value;
|
return !this.validator || this.validator(this.element.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
focus() {
|
getValue() {
|
||||||
this.element.focus();
|
return this.element.value;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
focus() {
|
||||||
export class FormElementCheckbox extends FormElement {
|
this.element.focus();
|
||||||
constructor({ id, label, defaultValue = true }) {
|
}
|
||||||
super(id, label);
|
}
|
||||||
this.defaultValue = defaultValue;
|
|
||||||
this.value = this.defaultValue;
|
export class FormElementCheckbox extends FormElement {
|
||||||
|
constructor({ id, label, defaultValue = true }) {
|
||||||
this.element = null;
|
super(id, label);
|
||||||
}
|
this.defaultValue = defaultValue;
|
||||||
|
this.value = this.defaultValue;
|
||||||
getHtml() {
|
|
||||||
return `
|
this.element = null;
|
||||||
<div class="formElement checkBoxFormElem">
|
}
|
||||||
${this.label ? `<label>${this.label}</label>` : ""}
|
|
||||||
<div class="checkbox ${this.defaultValue ? "checked" : ""}" data-formId='${this.id}'>
|
getHtml() {
|
||||||
<span class="knob"></span >
|
return `
|
||||||
</div >
|
<div class="formElement checkBoxFormElem">
|
||||||
</div>
|
${this.label ? `<label>${this.label}</label>` : ""}
|
||||||
`;
|
<div class="checkbox ${this.defaultValue ? "checked" : ""}" data-formId='${this.id}'>
|
||||||
}
|
<span class="knob"></span >
|
||||||
|
</div >
|
||||||
bindEvents(parent, clickTrackers) {
|
</div>
|
||||||
this.element = this.getFormElement(parent);
|
`;
|
||||||
const detector = new ClickDetector(this.element, {
|
}
|
||||||
consumeEvents: false,
|
|
||||||
preventDefault: false,
|
bindEvents(parent, clickTrackers) {
|
||||||
});
|
this.element = this.getFormElement(parent);
|
||||||
clickTrackers.push(detector);
|
const detector = new ClickDetector(this.element, {
|
||||||
detector.click.add(this.toggle, this);
|
consumeEvents: false,
|
||||||
}
|
preventDefault: false,
|
||||||
|
});
|
||||||
getValue() {
|
clickTrackers.push(detector);
|
||||||
return this.value;
|
detector.click.add(this.toggle, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
toggle() {
|
getValue() {
|
||||||
this.value = !this.value;
|
return this.value;
|
||||||
this.element.classList.toggle("checked", this.value);
|
}
|
||||||
}
|
|
||||||
|
toggle() {
|
||||||
focus(parent) {}
|
this.value = !this.value;
|
||||||
}
|
this.element.classList.toggle("checked", this.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
focus(parent) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class FormElementItemChooser extends FormElement {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {object} param0
|
||||||
|
* @param {string} param0.id
|
||||||
|
* @param {string=} param0.label
|
||||||
|
* @param {Array<BaseItem>} param0.items
|
||||||
|
*/
|
||||||
|
constructor({ id, label, items = [] }) {
|
||||||
|
super(id, label);
|
||||||
|
this.items = items;
|
||||||
|
this.element = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {BaseItem}
|
||||||
|
*/
|
||||||
|
this.chosenItem = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
getHtml() {
|
||||||
|
let classes = [];
|
||||||
|
|
||||||
|
return `
|
||||||
|
<div class="formElement">
|
||||||
|
${this.label ? `<label>${this.label}</label>` : ""}
|
||||||
|
<div class="ingameItemChooser input" data-formId="${this.id}"></div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {HTMLElement} parent
|
||||||
|
* @param {Array<ClickDetector>} clickTrackers
|
||||||
|
*/
|
||||||
|
bindEvents(parent, clickTrackers) {
|
||||||
|
this.element = this.getFormElement(parent);
|
||||||
|
|
||||||
|
for (let i = 0; i < this.items.length; ++i) {
|
||||||
|
const item = this.items[i];
|
||||||
|
|
||||||
|
const canvas = document.createElement("canvas");
|
||||||
|
canvas.width = 128;
|
||||||
|
canvas.height = 128;
|
||||||
|
const context = canvas.getContext("2d");
|
||||||
|
item.drawFullSizeOnCanvas(context, 128);
|
||||||
|
this.element.appendChild(canvas);
|
||||||
|
|
||||||
|
const detector = new ClickDetector(canvas, {});
|
||||||
|
clickTrackers.push(detector);
|
||||||
|
detector.click.add(() => {
|
||||||
|
this.chosenItem = item;
|
||||||
|
this.valueChosen.dispatch(item);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isValid() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
getValue() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
focus() {}
|
||||||
|
}
|
||||||
|
|||||||
@ -667,3 +667,14 @@ export function safeModulo(n, m) {
|
|||||||
export function smoothPulse(time) {
|
export function smoothPulse(time) {
|
||||||
return Math.sin(time * 4) * 0.5 + 0.5;
|
return Math.sin(time * 4) * 0.5 + 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fills in a <link> tag
|
||||||
|
* @param {string} translation
|
||||||
|
* @param {string} link
|
||||||
|
*/
|
||||||
|
export function fillInLinkIntoTranslation(translation, link) {
|
||||||
|
return translation
|
||||||
|
.replace("<link>", "<a href='" + link + "' target='_blank'>")
|
||||||
|
.replace("</link>", "</a>");
|
||||||
|
}
|
||||||
|
|||||||
@ -1,82 +1,100 @@
|
|||||||
import { globalConfig } from "../core/config";
|
import { globalConfig } from "../core/config";
|
||||||
import { DrawParameters } from "../core/draw_parameters";
|
import { DrawParameters } from "../core/draw_parameters";
|
||||||
import { BasicSerializableObject } from "../savegame/serialization";
|
import { BasicSerializableObject } from "../savegame/serialization";
|
||||||
|
|
||||||
/** @type {ItemType[]} **/
|
/** @type {ItemType[]} **/
|
||||||
export const itemTypes = ["shape", "color", "boolean"];
|
export const itemTypes = ["shape", "color", "boolean"];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class for items on belts etc. Not an entity for performance reasons
|
* Class for items on belts etc. Not an entity for performance reasons
|
||||||
*/
|
*/
|
||||||
export class BaseItem extends BasicSerializableObject {
|
export class BaseItem extends BasicSerializableObject {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
static getId() {
|
static getId() {
|
||||||
return "base_item";
|
return "base_item";
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @returns {object} */
|
/** @returns {object} */
|
||||||
static getSchema() {
|
static getSchema() {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @returns {ItemType} **/
|
/** @returns {ItemType} **/
|
||||||
getItemType() {
|
getItemType() {
|
||||||
abstract;
|
abstract;
|
||||||
return "shape";
|
return "shape";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns if the item equals the other itme
|
* Returns a string id of the item
|
||||||
* @param {BaseItem} other
|
* @returns {string}
|
||||||
* @returns {boolean}
|
*/
|
||||||
*/
|
getAsCopyableKey() {
|
||||||
equals(other) {
|
abstract;
|
||||||
if (this.getItemType() !== other.getItemType()) {
|
return "";
|
||||||
return false;
|
}
|
||||||
}
|
|
||||||
return this.equalsImpl(other);
|
/**
|
||||||
}
|
* Returns if the item equals the other itme
|
||||||
|
* @param {BaseItem} other
|
||||||
/**
|
* @returns {boolean}
|
||||||
* Override for custom comparison
|
*/
|
||||||
* @abstract
|
equals(other) {
|
||||||
* @param {BaseItem} other
|
if (this.getItemType() !== other.getItemType()) {
|
||||||
* @returns {boolean}
|
return false;
|
||||||
*/
|
}
|
||||||
equalsImpl(other) {
|
return this.equalsImpl(other);
|
||||||
abstract;
|
}
|
||||||
return false;
|
|
||||||
}
|
/**
|
||||||
|
* Override for custom comparison
|
||||||
/**
|
* @abstract
|
||||||
* Draws the item at the given position
|
* @param {BaseItem} other
|
||||||
* @param {number} x
|
* @returns {boolean}
|
||||||
* @param {number} y
|
*/
|
||||||
* @param {DrawParameters} parameters
|
equalsImpl(other) {
|
||||||
* @param {number=} diameter
|
abstract;
|
||||||
*/
|
return false;
|
||||||
drawItemCenteredClipped(x, y, parameters, diameter = globalConfig.defaultItemDiameter) {
|
}
|
||||||
if (parameters.visibleRect.containsCircle(x, y, diameter / 2)) {
|
|
||||||
this.drawItemCenteredImpl(x, y, parameters, diameter);
|
/**
|
||||||
}
|
* Draws the item to a canvas
|
||||||
}
|
* @param {CanvasRenderingContext2D} context
|
||||||
|
* @param {number} size
|
||||||
/**
|
*/
|
||||||
* INTERNAL
|
drawFullSizeOnCanvas(context, size) {
|
||||||
* @param {number} x
|
abstract;
|
||||||
* @param {number} y
|
}
|
||||||
* @param {DrawParameters} parameters
|
|
||||||
* @param {number=} diameter
|
/**
|
||||||
*/
|
* Draws the item at the given position
|
||||||
drawItemCenteredImpl(x, y, parameters, diameter = globalConfig.defaultItemDiameter) {
|
* @param {number} x
|
||||||
abstract;
|
* @param {number} y
|
||||||
}
|
* @param {DrawParameters} parameters
|
||||||
|
* @param {number=} diameter
|
||||||
getBackgroundColorAsResource() {
|
*/
|
||||||
abstract;
|
drawItemCenteredClipped(x, y, parameters, diameter = globalConfig.defaultItemDiameter) {
|
||||||
return "";
|
if (parameters.visibleRect.containsCircle(x, y, diameter / 2)) {
|
||||||
}
|
this.drawItemCenteredImpl(x, y, parameters, diameter);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* INTERNAL
|
||||||
|
* @param {number} x
|
||||||
|
* @param {number} y
|
||||||
|
* @param {DrawParameters} parameters
|
||||||
|
* @param {number=} diameter
|
||||||
|
*/
|
||||||
|
drawItemCenteredImpl(x, y, parameters, diameter = globalConfig.defaultItemDiameter) {
|
||||||
|
abstract;
|
||||||
|
}
|
||||||
|
|
||||||
|
getBackgroundColorAsResource() {
|
||||||
|
abstract;
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,12 +1,11 @@
|
|||||||
|
import { globalConfig } from "../core/config";
|
||||||
import { DrawParameters } from "../core/draw_parameters";
|
import { DrawParameters } from "../core/draw_parameters";
|
||||||
import { Loader } from "../core/loader";
|
|
||||||
import { createLogger } from "../core/logging";
|
import { createLogger } from "../core/logging";
|
||||||
|
import { findNiceIntegerValue } from "../core/utils";
|
||||||
import { Vector } from "../core/vector";
|
import { Vector } from "../core/vector";
|
||||||
import { Entity } from "./entity";
|
import { Entity } from "./entity";
|
||||||
import { GameRoot } from "./root";
|
import { GameRoot } from "./root";
|
||||||
import { findNiceIntegerValue } from "../core/utils";
|
|
||||||
import { blueprintShape } from "./upgrades";
|
import { blueprintShape } from "./upgrades";
|
||||||
import { globalConfig } from "../core/config";
|
|
||||||
|
|
||||||
const logger = createLogger("blueprint");
|
const logger = createLogger("blueprint");
|
||||||
|
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import { enumPinSlotType, WiredPinsComponent } from "../components/wired_pins";
|
|||||||
import { Entity } from "../entity";
|
import { Entity } from "../entity";
|
||||||
import { MetaBuilding } from "../meta_building";
|
import { MetaBuilding } from "../meta_building";
|
||||||
import { GameRoot } from "../root";
|
import { GameRoot } from "../root";
|
||||||
|
import { enumHubGoalRewards } from "../tutorial_goals";
|
||||||
|
|
||||||
const overlayMatrix = generateMatrixRotations([1, 1, 0, 1, 1, 1, 0, 1, 0]);
|
const overlayMatrix = generateMatrixRotations([1, 1, 0, 1, 1, 1, 0, 1, 0]);
|
||||||
|
|
||||||
@ -21,8 +22,7 @@ export class MetaAnalyzerBuilding extends MetaBuilding {
|
|||||||
* @param {GameRoot} root
|
* @param {GameRoot} root
|
||||||
*/
|
*/
|
||||||
getIsUnlocked(root) {
|
getIsUnlocked(root) {
|
||||||
// @todo
|
return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_virtual_processing);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @returns {"wires"} **/
|
/** @returns {"wires"} **/
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { enumPinSlotType, WiredPinsComponent } from "../components/wired_pins";
|
|||||||
import { Entity } from "../entity";
|
import { Entity } from "../entity";
|
||||||
import { MetaBuilding } from "../meta_building";
|
import { MetaBuilding } from "../meta_building";
|
||||||
import { GameRoot } from "../root";
|
import { GameRoot } from "../root";
|
||||||
|
import { enumHubGoalRewards } from "../tutorial_goals";
|
||||||
|
|
||||||
export class MetaComparatorBuilding extends MetaBuilding {
|
export class MetaComparatorBuilding extends MetaBuilding {
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -18,8 +19,7 @@ export class MetaComparatorBuilding extends MetaBuilding {
|
|||||||
* @param {GameRoot} root
|
* @param {GameRoot} root
|
||||||
*/
|
*/
|
||||||
getIsUnlocked(root) {
|
getIsUnlocked(root) {
|
||||||
// @todo
|
return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_virtual_processing);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @returns {"wires"} **/
|
/** @returns {"wires"} **/
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import { MetaBuilding, defaultBuildingVariant } from "../meta_building";
|
|||||||
import { GameRoot } from "../root";
|
import { GameRoot } from "../root";
|
||||||
import { enumLogicGateType, LogicGateComponent } from "../components/logic_gate";
|
import { enumLogicGateType, LogicGateComponent } from "../components/logic_gate";
|
||||||
import { generateMatrixRotations } from "../../core/utils";
|
import { generateMatrixRotations } from "../../core/utils";
|
||||||
|
import { enumHubGoalRewards } from "../tutorial_goals";
|
||||||
|
|
||||||
/** @enum {string} */
|
/** @enum {string} */
|
||||||
export const enumLogicGateVariants = {
|
export const enumLogicGateVariants = {
|
||||||
@ -48,8 +49,7 @@ export class MetaLogicGateBuilding extends MetaBuilding {
|
|||||||
* @param {GameRoot} root
|
* @param {GameRoot} root
|
||||||
*/
|
*/
|
||||||
getIsUnlocked(root) {
|
getIsUnlocked(root) {
|
||||||
// @todo
|
return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_logic_gates);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @returns {"wires"} **/
|
/** @returns {"wires"} **/
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import { enumPinSlotType, WiredPinsComponent } from "../components/wired_pins";
|
|||||||
import { Entity } from "../entity";
|
import { Entity } from "../entity";
|
||||||
import { defaultBuildingVariant, MetaBuilding } from "../meta_building";
|
import { defaultBuildingVariant, MetaBuilding } from "../meta_building";
|
||||||
import { GameRoot } from "../root";
|
import { GameRoot } from "../root";
|
||||||
|
import { enumHubGoalRewards } from "../tutorial_goals";
|
||||||
|
|
||||||
/** @enum {string} */
|
/** @enum {string} */
|
||||||
export const enumTransistorVariants = {
|
export const enumTransistorVariants = {
|
||||||
@ -29,8 +30,7 @@ export class MetaTransistorBuilding extends MetaBuilding {
|
|||||||
* @param {GameRoot} root
|
* @param {GameRoot} root
|
||||||
*/
|
*/
|
||||||
getIsUnlocked(root) {
|
getIsUnlocked(root) {
|
||||||
// @todo
|
return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_logic_gates);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @returns {"wires"} **/
|
/** @returns {"wires"} **/
|
||||||
|
|||||||
@ -110,7 +110,12 @@ export class MetaVirtualProcessorBuilding extends MetaBuilding {
|
|||||||
pinComp.setSlots([
|
pinComp.setSlots([
|
||||||
{
|
{
|
||||||
pos: new Vector(0, 0),
|
pos: new Vector(0, 0),
|
||||||
direction: enumDirection.top,
|
direction: enumDirection.left,
|
||||||
|
type: enumPinSlotType.logicalEjector,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pos: new Vector(0, 0),
|
||||||
|
direction: enumDirection.right,
|
||||||
type: enumPinSlotType.logicalEjector,
|
type: enumPinSlotType.logicalEjector,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@ -53,6 +53,6 @@ export class MetaWireTunnelBuilding extends MetaBuilding {
|
|||||||
* @param {Entity} entity
|
* @param {Entity} entity
|
||||||
*/
|
*/
|
||||||
setupEntityComponents(entity) {
|
setupEntityComponents(entity) {
|
||||||
entity.addComponent(new WireTunnelComponent({}));
|
entity.addComponent(new WireTunnelComponent());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,10 @@
|
|||||||
import { globalConfig } from "../core/config";
|
import { globalConfig } from "../core/config";
|
||||||
|
import { RandomNumberGenerator } from "../core/rng";
|
||||||
import { clamp, findNiceIntegerValue, randomChoice, randomInt } from "../core/utils";
|
import { clamp, findNiceIntegerValue, randomChoice, randomInt } from "../core/utils";
|
||||||
import { BasicSerializableObject, types } from "../savegame/serialization";
|
import { BasicSerializableObject, types } from "../savegame/serialization";
|
||||||
import { enumColors } from "./colors";
|
import { enumColors } from "./colors";
|
||||||
import { enumItemProcessorTypes } from "./components/item_processor";
|
import { enumItemProcessorTypes } from "./components/item_processor";
|
||||||
|
import { enumAnalyticsDataSource } from "./production_analytics";
|
||||||
import { GameRoot } from "./root";
|
import { GameRoot } from "./root";
|
||||||
import { enumSubShape, ShapeDefinition } from "./shape_definition";
|
import { enumSubShape, ShapeDefinition } from "./shape_definition";
|
||||||
import { enumHubGoalRewards, tutorialGoals } from "./tutorial_goals";
|
import { enumHubGoalRewards, tutorialGoals } from "./tutorial_goals";
|
||||||
@ -18,12 +20,6 @@ export class HubGoals extends BasicSerializableObject {
|
|||||||
level: types.uint,
|
level: types.uint,
|
||||||
storedShapes: types.keyValueMap(types.uint),
|
storedShapes: types.keyValueMap(types.uint),
|
||||||
upgradeLevels: types.keyValueMap(types.uint),
|
upgradeLevels: types.keyValueMap(types.uint),
|
||||||
|
|
||||||
currentGoal: types.structured({
|
|
||||||
definition: types.knownType(ShapeDefinition),
|
|
||||||
required: types.uint,
|
|
||||||
reward: types.nullable(types.enum(enumHubGoalRewards)),
|
|
||||||
}),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,15 +49,7 @@ export class HubGoals extends BasicSerializableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Compute current goal
|
// Compute current goal
|
||||||
const goal = tutorialGoals[this.level - 1];
|
this.computeNextGoal();
|
||||||
if (goal) {
|
|
||||||
this.currentGoal = {
|
|
||||||
/** @type {ShapeDefinition} */
|
|
||||||
definition: this.root.shapeDefinitionMgr.getShapeFromShortKey(goal.shape),
|
|
||||||
required: goal.required,
|
|
||||||
reward: goal.reward,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -92,6 +80,11 @@ export class HubGoals extends BasicSerializableObject {
|
|||||||
*/
|
*/
|
||||||
this.upgradeLevels = {};
|
this.upgradeLevels = {};
|
||||||
|
|
||||||
|
// Reset levels
|
||||||
|
for (const key in UPGRADES) {
|
||||||
|
this.upgradeLevels[key] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores the improvements for all upgrades
|
* Stores the improvements for all upgrades
|
||||||
* @type {Object<string, number>}
|
* @type {Object<string, number>}
|
||||||
@ -101,7 +94,7 @@ export class HubGoals extends BasicSerializableObject {
|
|||||||
this.upgradeImprovements[key] = 1;
|
this.upgradeImprovements[key] = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.createNextGoal();
|
this.computeNextGoal();
|
||||||
|
|
||||||
// Allow quickly switching goals in dev mode
|
// Allow quickly switching goals in dev mode
|
||||||
if (G_IS_DEV) {
|
if (G_IS_DEV) {
|
||||||
@ -150,6 +143,13 @@ export class HubGoals extends BasicSerializableObject {
|
|||||||
* Returns how much of the current goal was already delivered
|
* Returns how much of the current goal was already delivered
|
||||||
*/
|
*/
|
||||||
getCurrentGoalDelivered() {
|
getCurrentGoalDelivered() {
|
||||||
|
if (this.currentGoal.throughputOnly) {
|
||||||
|
return this.root.productionAnalytics.getCurrentShapeRate(
|
||||||
|
enumAnalyticsDataSource.delivered,
|
||||||
|
this.currentGoal.definition
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return this.getShapesStored(this.currentGoal.definition);
|
return this.getShapesStored(this.currentGoal.definition);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,9 +184,8 @@ export class HubGoals extends BasicSerializableObject {
|
|||||||
this.root.signals.shapeDelivered.dispatch(definition);
|
this.root.signals.shapeDelivered.dispatch(definition);
|
||||||
|
|
||||||
// Check if we have enough for the next level
|
// Check if we have enough for the next level
|
||||||
const targetHash = this.currentGoal.definition.getHash();
|
|
||||||
if (
|
if (
|
||||||
this.storedShapes[targetHash] >= this.currentGoal.required ||
|
this.getCurrentGoalDelivered() >= this.currentGoal.required ||
|
||||||
(G_IS_DEV && globalConfig.debug.rewardsInstant)
|
(G_IS_DEV && globalConfig.debug.rewardsInstant)
|
||||||
) {
|
) {
|
||||||
this.onGoalCompleted();
|
this.onGoalCompleted();
|
||||||
@ -196,24 +195,28 @@ export class HubGoals extends BasicSerializableObject {
|
|||||||
/**
|
/**
|
||||||
* Creates the next goal
|
* Creates the next goal
|
||||||
*/
|
*/
|
||||||
createNextGoal() {
|
computeNextGoal() {
|
||||||
const storyIndex = this.level - 1;
|
const storyIndex = this.level - 1;
|
||||||
if (storyIndex < tutorialGoals.length) {
|
if (storyIndex < tutorialGoals.length) {
|
||||||
const { shape, required, reward } = tutorialGoals[storyIndex];
|
const { shape, required, reward, throughputOnly } = tutorialGoals[storyIndex];
|
||||||
this.currentGoal = {
|
this.currentGoal = {
|
||||||
/** @type {ShapeDefinition} */
|
/** @type {ShapeDefinition} */
|
||||||
definition: this.root.shapeDefinitionMgr.getShapeFromShortKey(shape),
|
definition: this.root.shapeDefinitionMgr.getShapeFromShortKey(shape),
|
||||||
required,
|
required,
|
||||||
reward,
|
reward,
|
||||||
|
throughputOnly,
|
||||||
};
|
};
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const required = 4 + (this.level - 27) * 0.25;
|
||||||
|
|
||||||
this.currentGoal = {
|
this.currentGoal = {
|
||||||
/** @type {ShapeDefinition} */
|
/** @type {ShapeDefinition} */
|
||||||
definition: this.createRandomShape(),
|
definition: this.computeFreeplayShape(this.level),
|
||||||
required: findNiceIntegerValue(1000 + Math.pow(this.level * 2000, 0.8)),
|
required,
|
||||||
reward: enumHubGoalRewards.no_reward_freeplay,
|
reward: enumHubGoalRewards.no_reward_freeplay,
|
||||||
|
throughputOnly: true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -226,7 +229,7 @@ export class HubGoals extends BasicSerializableObject {
|
|||||||
|
|
||||||
this.root.app.gameAnalytics.handleLevelCompleted(this.level);
|
this.root.app.gameAnalytics.handleLevelCompleted(this.level);
|
||||||
++this.level;
|
++this.level;
|
||||||
this.createNextGoal();
|
this.computeNextGoal();
|
||||||
|
|
||||||
this.root.signals.storyGoalCompleted.dispatch(this.level - 1, reward);
|
this.root.signals.storyGoalCompleted.dispatch(this.level - 1, reward);
|
||||||
}
|
}
|
||||||
@ -320,15 +323,85 @@ export class HubGoals extends BasicSerializableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Picks random colors which are close to each other
|
||||||
|
* @param {RandomNumberGenerator} rng
|
||||||
|
*/
|
||||||
|
generateRandomColorSet(rng, allowUncolored = false) {
|
||||||
|
const colorWheel = [
|
||||||
|
enumColors.red,
|
||||||
|
enumColors.yellow,
|
||||||
|
enumColors.green,
|
||||||
|
enumColors.cyan,
|
||||||
|
enumColors.blue,
|
||||||
|
enumColors.purple,
|
||||||
|
enumColors.red,
|
||||||
|
enumColors.yellow,
|
||||||
|
];
|
||||||
|
|
||||||
|
const universalColors = [enumColors.white];
|
||||||
|
if (allowUncolored) {
|
||||||
|
universalColors.push(enumColors.uncolored);
|
||||||
|
}
|
||||||
|
const index = rng.nextIntRangeInclusive(0, colorWheel.length - 3);
|
||||||
|
const pickedColors = colorWheel.slice(index, index + 3);
|
||||||
|
pickedColors.push(rng.choice(universalColors));
|
||||||
|
return pickedColors;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a (seeded) random shape
|
||||||
|
* @param {number} level
|
||||||
* @returns {ShapeDefinition}
|
* @returns {ShapeDefinition}
|
||||||
*/
|
*/
|
||||||
createRandomShape() {
|
computeFreeplayShape(level) {
|
||||||
const layerCount = clamp(this.level / 25, 2, 4);
|
const layerCount = clamp(this.level / 25, 2, 4);
|
||||||
|
|
||||||
/** @type {Array<import("./shape_definition").ShapeLayer>} */
|
/** @type {Array<import("./shape_definition").ShapeLayer>} */
|
||||||
let layers = [];
|
let layers = [];
|
||||||
|
|
||||||
const randomColor = () => randomChoice(Object.values(enumColors));
|
const rng = new RandomNumberGenerator(this.root.map.seed + "/" + level);
|
||||||
const randomShape = () => randomChoice(Object.values(enumSubShape));
|
|
||||||
|
const colors = this.generateRandomColorSet(rng, level > 35);
|
||||||
|
|
||||||
|
let pickedSymmetry = null; // pairs of quadrants that must be the same
|
||||||
|
let availableShapes = [enumSubShape.rect, enumSubShape.circle, enumSubShape.star];
|
||||||
|
if (rng.next() < 0.5) {
|
||||||
|
pickedSymmetry = [
|
||||||
|
// radial symmetry
|
||||||
|
[0, 2],
|
||||||
|
[1, 3],
|
||||||
|
];
|
||||||
|
availableShapes.push(enumSubShape.windmill); // windmill looks good only in radial symmetry
|
||||||
|
} else {
|
||||||
|
const symmetries = [
|
||||||
|
[
|
||||||
|
// horizontal axis
|
||||||
|
[0, 3],
|
||||||
|
[1, 2],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
// vertical axis
|
||||||
|
[0, 1],
|
||||||
|
[2, 3],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
// diagonal axis
|
||||||
|
[0, 2],
|
||||||
|
[1],
|
||||||
|
[3],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
// other diagonal axis
|
||||||
|
[1, 3],
|
||||||
|
[0],
|
||||||
|
[2],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
pickedSymmetry = rng.choice(symmetries);
|
||||||
|
}
|
||||||
|
|
||||||
|
const randomColor = () => rng.choice(colors);
|
||||||
|
const randomShape = () => rng.choice(Object.values(enumSubShape));
|
||||||
|
|
||||||
let anyIsMissingTwo = false;
|
let anyIsMissingTwo = false;
|
||||||
|
|
||||||
@ -336,23 +409,24 @@ export class HubGoals extends BasicSerializableObject {
|
|||||||
/** @type {import("./shape_definition").ShapeLayer} */
|
/** @type {import("./shape_definition").ShapeLayer} */
|
||||||
const layer = [null, null, null, null];
|
const layer = [null, null, null, null];
|
||||||
|
|
||||||
for (let quad = 0; quad < 4; ++quad) {
|
for (let j = 0; j < pickedSymmetry.length; ++j) {
|
||||||
layer[quad] = {
|
const group = pickedSymmetry[j];
|
||||||
subShape: randomShape(),
|
const shape = randomShape();
|
||||||
color: randomColor(),
|
const color = randomColor();
|
||||||
};
|
for (let k = 0; k < group.length; ++k) {
|
||||||
}
|
const quad = group[k];
|
||||||
|
layer[quad] = {
|
||||||
// Sometimes shapes are missing
|
subShape: shape,
|
||||||
if (Math.random() > 0.85) {
|
color,
|
||||||
layer[randomInt(0, 3)] = null;
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sometimes they actually are missing *two* ones!
|
// Sometimes they actually are missing *two* ones!
|
||||||
// Make sure at max only one layer is missing it though, otherwise we could
|
// Make sure at max only one layer is missing it though, otherwise we could
|
||||||
// create an uncreateable shape
|
// create an uncreateable shape
|
||||||
if (Math.random() > 0.95 && !anyIsMissingTwo) {
|
if (level > 75 && rng.next() > 0.95 && !anyIsMissingTwo) {
|
||||||
layer[randomInt(0, 3)] = null;
|
layer[rng.nextIntRange(0, 4)] = null;
|
||||||
anyIsMissingTwo = true;
|
anyIsMissingTwo = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -44,6 +44,8 @@ import { HUDWireInfo } from "./parts/wire_info";
|
|||||||
import { HUDLeverToggle } from "./parts/lever_toggle";
|
import { HUDLeverToggle } from "./parts/lever_toggle";
|
||||||
import { HUDLayerPreview } from "./parts/layer_preview";
|
import { HUDLayerPreview } from "./parts/layer_preview";
|
||||||
import { HUDMinerHighlight } from "./parts/miner_highlight";
|
import { HUDMinerHighlight } from "./parts/miner_highlight";
|
||||||
|
import { HUDBetaOverlay } from "./parts/beta_overlay";
|
||||||
|
import { HUDPerformanceWarning } from "./parts/performance_warning";
|
||||||
|
|
||||||
export class GameHUD {
|
export class GameHUD {
|
||||||
/**
|
/**
|
||||||
@ -75,7 +77,6 @@ export class GameHUD {
|
|||||||
pinnedShapes: new HUDPinnedShapes(this.root),
|
pinnedShapes: new HUDPinnedShapes(this.root),
|
||||||
notifications: new HUDNotifications(this.root),
|
notifications: new HUDNotifications(this.root),
|
||||||
settingsMenu: new HUDSettingsMenu(this.root),
|
settingsMenu: new HUDSettingsMenu(this.root),
|
||||||
// betaOverlay: new HUDBetaOverlay(this.root),
|
|
||||||
debugInfo: new HUDDebugInfo(this.root),
|
debugInfo: new HUDDebugInfo(this.root),
|
||||||
dialogs: new HUDModalDialogs(this.root),
|
dialogs: new HUDModalDialogs(this.root),
|
||||||
screenshotExporter: new HUDScreenshotExporter(this.root),
|
screenshotExporter: new HUDScreenshotExporter(this.root),
|
||||||
@ -85,6 +86,7 @@ export class GameHUD {
|
|||||||
layerPreview: new HUDLayerPreview(this.root),
|
layerPreview: new HUDLayerPreview(this.root),
|
||||||
|
|
||||||
minerHighlight: new HUDMinerHighlight(this.root),
|
minerHighlight: new HUDMinerHighlight(this.root),
|
||||||
|
performanceWarning: new HUDPerformanceWarning(this.root),
|
||||||
|
|
||||||
// Typing hints
|
// Typing hints
|
||||||
/* typehints:start */
|
/* typehints:start */
|
||||||
@ -137,6 +139,10 @@ export class GameHUD {
|
|||||||
this.parts.sandboxController = new HUDSandboxController(this.root);
|
this.parts.sandboxController = new HUDSandboxController(this.root);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!G_IS_RELEASE) {
|
||||||
|
this.parts.betaOverlay = new HUDBetaOverlay(this.root);
|
||||||
|
}
|
||||||
|
|
||||||
const frag = document.createDocumentFragment();
|
const frag = document.createDocumentFragment();
|
||||||
for (const key in this.parts) {
|
for (const key in this.parts) {
|
||||||
this.parts[key].createElements(frag);
|
this.parts[key].createElements(frag);
|
||||||
|
|||||||
@ -3,7 +3,12 @@ import { makeDiv } from "../../../core/utils";
|
|||||||
|
|
||||||
export class HUDBetaOverlay extends BaseHUDPart {
|
export class HUDBetaOverlay extends BaseHUDPart {
|
||||||
createElements(parent) {
|
createElements(parent) {
|
||||||
this.element = makeDiv(parent, "ingame_HUD_BetaOverlay", [], "CLOSED BETA");
|
this.element = makeDiv(
|
||||||
|
parent,
|
||||||
|
"ingame_HUD_BetaOverlay",
|
||||||
|
[],
|
||||||
|
"<h2>CLOSED BETA VERSION</h2><span>This version is unstable, might crash and is not final!</span>"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
initialize() {}
|
initialize() {}
|
||||||
|
|||||||
@ -334,7 +334,11 @@ export class HUDBuildingPlacerLogic extends BaseHUDPart {
|
|||||||
const tileBelow = this.root.map.getLowerLayerContentXY(tile.x, tile.y);
|
const tileBelow = this.root.map.getLowerLayerContentXY(tile.x, tile.y);
|
||||||
|
|
||||||
// Check if there's a shape or color item below, if so select the miner
|
// Check if there's a shape or color item below, if so select the miner
|
||||||
if (tileBelow && this.root.app.settings.getAllSettings().pickMinerOnPatch) {
|
if (
|
||||||
|
tileBelow &&
|
||||||
|
this.root.app.settings.getAllSettings().pickMinerOnPatch &&
|
||||||
|
this.root.currentLayer === "regular"
|
||||||
|
) {
|
||||||
this.currentMetaBuilding.set(gMetaBuildingRegistry.findByClass(MetaMinerBuilding));
|
this.currentMetaBuilding.set(gMetaBuildingRegistry.findByClass(MetaMinerBuilding));
|
||||||
|
|
||||||
// Select chained miner if available, since that's always desired once unlocked
|
// Select chained miner if available, since that's always desired once unlocked
|
||||||
|
|||||||
16
src/js/game/hud/parts/performance_warning.js
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { T } from "../../../translations";
|
||||||
|
import { BaseHUDPart } from "../base_hud_part";
|
||||||
|
|
||||||
|
export class HUDPerformanceWarning extends BaseHUDPart {
|
||||||
|
initialize() {
|
||||||
|
this.warningShown = false;
|
||||||
|
this.root.signals.entityManuallyPlaced.add(this.checkAfterPlace, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
checkAfterPlace() {
|
||||||
|
if (!this.warningShown && this.root.entityMgr.entities.length > 10000) {
|
||||||
|
this.root.hud.parts.dialogs.showInfo(T.dialogs.entityWarning.title, T.dialogs.entityWarning.desc);
|
||||||
|
this.warningShown = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -4,6 +4,8 @@ import { ShapeDefinition } from "../../shape_definition";
|
|||||||
import { BaseHUDPart } from "../base_hud_part";
|
import { BaseHUDPart } from "../base_hud_part";
|
||||||
import { blueprintShape, UPGRADES } from "../../upgrades";
|
import { blueprintShape, UPGRADES } from "../../upgrades";
|
||||||
import { enumHubGoalRewards } from "../../tutorial_goals";
|
import { enumHubGoalRewards } from "../../tutorial_goals";
|
||||||
|
import { enumAnalyticsDataSource } from "../../production_analytics";
|
||||||
|
import { T } from "../../../translations";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages the pinned shapes on the left side of the screen
|
* Manages the pinned shapes on the left side of the screen
|
||||||
@ -22,11 +24,13 @@ export class HUDPinnedShapes extends BaseHUDPart {
|
|||||||
* convenient. Also allows for cleaning up handles.
|
* convenient. Also allows for cleaning up handles.
|
||||||
* @type {Array<{
|
* @type {Array<{
|
||||||
* key: string,
|
* key: string,
|
||||||
|
* definition: ShapeDefinition,
|
||||||
* amountLabel: HTMLElement,
|
* amountLabel: HTMLElement,
|
||||||
* lastRenderedValue: string,
|
* lastRenderedValue: string,
|
||||||
* element: HTMLElement,
|
* element: HTMLElement,
|
||||||
* detector?: ClickDetector,
|
* detector?: ClickDetector,
|
||||||
* infoDetector?: ClickDetector
|
* infoDetector?: ClickDetector,
|
||||||
|
* throughputOnly?: boolean
|
||||||
* }>}
|
* }>}
|
||||||
*/
|
*/
|
||||||
this.handles = [];
|
this.handles = [];
|
||||||
@ -163,29 +167,40 @@ export class HUDPinnedShapes extends BaseHUDPart {
|
|||||||
this.handles = [];
|
this.handles = [];
|
||||||
|
|
||||||
// Pin story goal
|
// Pin story goal
|
||||||
this.internalPinShape(currentKey, false, "goal");
|
this.internalPinShape({
|
||||||
|
key: currentKey,
|
||||||
|
canUnpin: false,
|
||||||
|
className: "goal",
|
||||||
|
throughputOnly: currentGoal.throughputOnly,
|
||||||
|
});
|
||||||
|
|
||||||
// Pin blueprint shape as well
|
// Pin blueprint shape as well
|
||||||
if (this.root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_blueprints)) {
|
if (this.root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_blueprints)) {
|
||||||
this.internalPinShape(blueprintShape, false, "blueprint");
|
this.internalPinShape({
|
||||||
|
key: blueprintShape,
|
||||||
|
canUnpin: false,
|
||||||
|
className: "blueprint",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pin manually pinned shapes
|
// Pin manually pinned shapes
|
||||||
for (let i = 0; i < this.pinnedShapes.length; ++i) {
|
for (let i = 0; i < this.pinnedShapes.length; ++i) {
|
||||||
const key = this.pinnedShapes[i];
|
const key = this.pinnedShapes[i];
|
||||||
if (key !== currentKey) {
|
if (key !== currentKey) {
|
||||||
this.internalPinShape(key);
|
this.internalPinShape({ key });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pins a new shape
|
* Pins a new shape
|
||||||
* @param {string} key
|
* @param {object} param0
|
||||||
* @param {boolean} canUnpin
|
* @param {string} param0.key
|
||||||
* @param {string=} className
|
* @param {boolean=} param0.canUnpin
|
||||||
|
* @param {string=} param0.className
|
||||||
|
* @param {boolean=} param0.throughputOnly
|
||||||
*/
|
*/
|
||||||
internalPinShape(key, canUnpin = true, className = null) {
|
internalPinShape({ key, canUnpin = true, className = null, throughputOnly = false }) {
|
||||||
const definition = this.root.shapeDefinitionMgr.getShapeFromShortKey(key);
|
const definition = this.root.shapeDefinitionMgr.getShapeFromShortKey(key);
|
||||||
|
|
||||||
const element = makeDiv(this.element, null, ["shape"]);
|
const element = makeDiv(this.element, null, ["shape"]);
|
||||||
@ -229,11 +244,13 @@ export class HUDPinnedShapes extends BaseHUDPart {
|
|||||||
|
|
||||||
this.handles.push({
|
this.handles.push({
|
||||||
key,
|
key,
|
||||||
|
definition,
|
||||||
element,
|
element,
|
||||||
amountLabel,
|
amountLabel,
|
||||||
lastRenderedValue: "",
|
lastRenderedValue: "",
|
||||||
detector,
|
detector,
|
||||||
infoDetector,
|
infoDetector,
|
||||||
|
throughputOnly,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -244,8 +261,20 @@ export class HUDPinnedShapes extends BaseHUDPart {
|
|||||||
for (let i = 0; i < this.handles.length; ++i) {
|
for (let i = 0; i < this.handles.length; ++i) {
|
||||||
const handle = this.handles[i];
|
const handle = this.handles[i];
|
||||||
|
|
||||||
const currentValue = this.root.hubGoals.getShapesStoredByKey(handle.key);
|
let currentValue = this.root.hubGoals.getShapesStoredByKey(handle.key);
|
||||||
const currentValueFormatted = formatBigNumber(currentValue);
|
let currentValueFormatted = formatBigNumber(currentValue);
|
||||||
|
|
||||||
|
if (handle.throughputOnly) {
|
||||||
|
currentValue = this.root.productionAnalytics.getCurrentShapeRate(
|
||||||
|
enumAnalyticsDataSource.delivered,
|
||||||
|
handle.definition
|
||||||
|
);
|
||||||
|
currentValueFormatted = T.ingame.statistics.shapesDisplayUnits.second.replace(
|
||||||
|
"<shapes>",
|
||||||
|
String(currentValue)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (currentValueFormatted !== handle.lastRenderedValue) {
|
if (currentValueFormatted !== handle.lastRenderedValue) {
|
||||||
handle.lastRenderedValue = currentValueFormatted;
|
handle.lastRenderedValue = currentValueFormatted;
|
||||||
handle.amountLabel.innerText = currentValueFormatted;
|
handle.amountLabel.innerText = currentValueFormatted;
|
||||||
|
|||||||
@ -113,7 +113,7 @@ export class HUDSandboxController extends BaseHUDPart {
|
|||||||
modifyLevel(amount) {
|
modifyLevel(amount) {
|
||||||
const hubGoals = this.root.hubGoals;
|
const hubGoals = this.root.hubGoals;
|
||||||
hubGoals.level = Math.max(1, hubGoals.level + amount);
|
hubGoals.level = Math.max(1, hubGoals.level + amount);
|
||||||
hubGoals.createNextGoal();
|
hubGoals.computeNextGoal();
|
||||||
|
|
||||||
// Clear all shapes of this level
|
// Clear all shapes of this level
|
||||||
hubGoals.storedShapes[hubGoals.currentGoal.definition.getHash()] = 0;
|
hubGoals.storedShapes[hubGoals.currentGoal.definition.getHash()] = 0;
|
||||||
|
|||||||
@ -90,17 +90,15 @@ export class HUDShop extends BaseHUDPart {
|
|||||||
// Max level
|
// Max level
|
||||||
handle.elemDescription.innerText = T.ingame.shop.maximumLevel.replace(
|
handle.elemDescription.innerText = T.ingame.shop.maximumLevel.replace(
|
||||||
"<currentMult>",
|
"<currentMult>",
|
||||||
currentTierMultiplier.toString()
|
formatBigNumber(currentTierMultiplier)
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set description
|
// Set description
|
||||||
handle.elemDescription.innerText = T.shopUpgrades[upgradeId].description
|
handle.elemDescription.innerText = T.shopUpgrades[upgradeId].description
|
||||||
.replace("<currentMult>", currentTierMultiplier.toString())
|
.replace("<currentMult>", formatBigNumber(currentTierMultiplier))
|
||||||
.replace("<newMult>", (currentTierMultiplier + tierHandle.improvement).toString())
|
.replace("<newMult>", formatBigNumber(currentTierMultiplier + tierHandle.improvement));
|
||||||
// Backwards compatibility
|
|
||||||
.replace("<gain>", (tierHandle.improvement * 100.0).toString());
|
|
||||||
|
|
||||||
tierHandle.required.forEach(({ shape, amount }) => {
|
tierHandle.required.forEach(({ shape, amount }) => {
|
||||||
const container = makeDiv(handle.elemRequirements, null, ["requirement"]);
|
const container = makeDiv(handle.elemRequirements, null, ["requirement"]);
|
||||||
|
|||||||
@ -4,11 +4,12 @@ import { makeDiv } from "../../../core/utils";
|
|||||||
import { SOUNDS } from "../../../platform/sound";
|
import { SOUNDS } from "../../../platform/sound";
|
||||||
import { T } from "../../../translations";
|
import { T } from "../../../translations";
|
||||||
import { defaultBuildingVariant } from "../../meta_building";
|
import { defaultBuildingVariant } from "../../meta_building";
|
||||||
import { enumHubGoalRewards } from "../../tutorial_goals";
|
import { enumHubGoalRewards, tutorialGoals } from "../../tutorial_goals";
|
||||||
import { BaseHUDPart } from "../base_hud_part";
|
import { BaseHUDPart } from "../base_hud_part";
|
||||||
import { DynamicDomAttach } from "../dynamic_dom_attach";
|
import { DynamicDomAttach } from "../dynamic_dom_attach";
|
||||||
import { enumHubGoalRewardsToContentUnlocked } from "../../tutorial_goals_mappings";
|
import { enumHubGoalRewardsToContentUnlocked } from "../../tutorial_goals_mappings";
|
||||||
import { InputReceiver } from "../../../core/input_receiver";
|
import { InputReceiver } from "../../../core/input_receiver";
|
||||||
|
import { enumNotificationType } from "./notifications";
|
||||||
|
|
||||||
export class HUDUnlockNotification extends BaseHUDPart {
|
export class HUDUnlockNotification extends BaseHUDPart {
|
||||||
initialize() {
|
initialize() {
|
||||||
@ -50,6 +51,14 @@ export class HUDUnlockNotification extends BaseHUDPart {
|
|||||||
* @param {enumHubGoalRewards} reward
|
* @param {enumHubGoalRewards} reward
|
||||||
*/
|
*/
|
||||||
showForLevel(level, reward) {
|
showForLevel(level, reward) {
|
||||||
|
if (level > tutorialGoals.length) {
|
||||||
|
this.root.hud.signals.notification.dispatch(
|
||||||
|
T.ingame.notifications.freeplayLevelComplete.replace("<level>", String(level)),
|
||||||
|
enumNotificationType.success
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.root.app.inputMgr.makeSureAttachedAndOnTop(this.inputReciever);
|
this.root.app.inputMgr.makeSureAttachedAndOnTop(this.inputReciever);
|
||||||
this.elemTitle.innerText = T.ingame.levelCompleteNotification.levelTitle.replace(
|
this.elemTitle.innerText = T.ingame.levelCompleteNotification.levelTitle.replace(
|
||||||
"<level>",
|
"<level>",
|
||||||
|
|||||||
@ -1,12 +1,18 @@
|
|||||||
import { makeOffscreenBuffer } from "../../../core/buffer_utils";
|
import { makeOffscreenBuffer } from "../../../core/buffer_utils";
|
||||||
import { globalConfig, IS_DEMO } from "../../../core/config";
|
import { globalConfig, IS_DEMO, THIRDPARTY_URLS } from "../../../core/config";
|
||||||
import { DrawParameters } from "../../../core/draw_parameters";
|
import { DrawParameters } from "../../../core/draw_parameters";
|
||||||
import { Loader } from "../../../core/loader";
|
import { Loader } from "../../../core/loader";
|
||||||
import { DialogWithForm } from "../../../core/modal_dialog_elements";
|
import { DialogWithForm } from "../../../core/modal_dialog_elements";
|
||||||
import { FormElementInput } from "../../../core/modal_dialog_forms";
|
import { FormElementInput } from "../../../core/modal_dialog_forms";
|
||||||
import { Rectangle } from "../../../core/rectangle";
|
import { Rectangle } from "../../../core/rectangle";
|
||||||
import { STOP_PROPAGATION } from "../../../core/signal";
|
import { STOP_PROPAGATION } from "../../../core/signal";
|
||||||
import { arrayDeleteValue, lerp, makeDiv, removeAllChildren } from "../../../core/utils";
|
import {
|
||||||
|
arrayDeleteValue,
|
||||||
|
fillInLinkIntoTranslation,
|
||||||
|
lerp,
|
||||||
|
makeDiv,
|
||||||
|
removeAllChildren,
|
||||||
|
} from "../../../core/utils";
|
||||||
import { Vector } from "../../../core/vector";
|
import { Vector } from "../../../core/vector";
|
||||||
import { T } from "../../../translations";
|
import { T } from "../../../translations";
|
||||||
import { BaseItem } from "../../base_item";
|
import { BaseItem } from "../../base_item";
|
||||||
@ -272,7 +278,7 @@ export class HUDWaypoints extends BaseHUDPart {
|
|||||||
const dialog = new DialogWithForm({
|
const dialog = new DialogWithForm({
|
||||||
app: this.root.app,
|
app: this.root.app,
|
||||||
title: waypoint ? T.dialogs.createMarker.titleEdit : T.dialogs.createMarker.title,
|
title: waypoint ? T.dialogs.createMarker.titleEdit : T.dialogs.createMarker.title,
|
||||||
desc: T.dialogs.createMarker.desc,
|
desc: fillInLinkIntoTranslation(T.dialogs.createMarker.desc, THIRDPARTY_URLS.shapeViewer),
|
||||||
formElements: [markerNameInput],
|
formElements: [markerNameInput],
|
||||||
buttons: waypoint ? ["delete:bad", "cancel", "ok:good"] : ["cancel", "ok:good"],
|
buttons: waypoint ? ["delete:bad", "cancel", "ok:good"] : ["cancel", "ok:good"],
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,13 +1,14 @@
|
|||||||
import { makeOffscreenBuffer } from "../../../core/buffer_utils";
|
import { makeOffscreenBuffer } from "../../../core/buffer_utils";
|
||||||
import { globalConfig } from "../../../core/config";
|
import { globalConfig } from "../../../core/config";
|
||||||
import { DrawParameters } from "../../../core/draw_parameters";
|
import { DrawParameters } from "../../../core/draw_parameters";
|
||||||
import { KEYMAPPINGS } from "../../key_action_mapper";
|
|
||||||
import { THEME } from "../../theme";
|
|
||||||
import { BaseHUDPart } from "../base_hud_part";
|
|
||||||
import { Loader } from "../../../core/loader";
|
import { Loader } from "../../../core/loader";
|
||||||
import { lerp } from "../../../core/utils";
|
import { lerp } from "../../../core/utils";
|
||||||
|
import { SOUNDS } from "../../../platform/sound";
|
||||||
|
import { KEYMAPPINGS } from "../../key_action_mapper";
|
||||||
import { enumHubGoalRewards } from "../../tutorial_goals";
|
import { enumHubGoalRewards } from "../../tutorial_goals";
|
||||||
|
import { BaseHUDPart } from "../base_hud_part";
|
||||||
|
|
||||||
|
const copy = require("clipboard-copy");
|
||||||
const wiresBackgroundDpi = 4;
|
const wiresBackgroundDpi = 4;
|
||||||
|
|
||||||
export class HUDWiresOverlay extends BaseHUDPart {
|
export class HUDWiresOverlay extends BaseHUDPart {
|
||||||
@ -16,6 +17,7 @@ export class HUDWiresOverlay extends BaseHUDPart {
|
|||||||
initialize() {
|
initialize() {
|
||||||
// Probably not the best location, but the one which makes most sense
|
// Probably not the best location, but the one which makes most sense
|
||||||
this.root.keyMapper.getBinding(KEYMAPPINGS.ingame.switchLayers).add(this.switchLayers, this);
|
this.root.keyMapper.getBinding(KEYMAPPINGS.ingame.switchLayers).add(this.switchLayers, this);
|
||||||
|
this.root.keyMapper.getBinding(KEYMAPPINGS.placement.copyWireValue).add(this.copyWireValue, this);
|
||||||
|
|
||||||
this.generateTilePattern();
|
this.generateTilePattern();
|
||||||
|
|
||||||
@ -54,7 +56,53 @@ export class HUDWiresOverlay extends BaseHUDPart {
|
|||||||
|
|
||||||
update() {
|
update() {
|
||||||
const desiredAlpha = this.root.currentLayer === "wires" ? 1.0 : 0.0;
|
const desiredAlpha = this.root.currentLayer === "wires" ? 1.0 : 0.0;
|
||||||
this.currentAlpha = lerp(this.currentAlpha, desiredAlpha, 0.12);
|
|
||||||
|
// On low performance, skip the fade
|
||||||
|
if (this.root.entityMgr.entities.length > 5000 || this.root.dynamicTickrate.averageFps < 50) {
|
||||||
|
this.currentAlpha = desiredAlpha;
|
||||||
|
} else {
|
||||||
|
this.currentAlpha = lerp(this.currentAlpha, desiredAlpha, 0.12);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copies the wires value below the cursor
|
||||||
|
*/
|
||||||
|
copyWireValue() {
|
||||||
|
if (this.root.currentLayer !== "wires") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const mousePos = this.root.app.mousePosition;
|
||||||
|
if (!mousePos) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tile = this.root.camera.screenToWorld(mousePos).toTileSpace();
|
||||||
|
const contents = this.root.map.getLayerContentXY(tile.x, tile.y, "wires");
|
||||||
|
if (!contents) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let value = null;
|
||||||
|
if (contents.components.Wire) {
|
||||||
|
const network = contents.components.Wire.linkedNetwork;
|
||||||
|
if (network && network.hasValue()) {
|
||||||
|
value = network.currentValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (contents.components.ConstantSignal) {
|
||||||
|
value = contents.components.ConstantSignal.signal;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value) {
|
||||||
|
copy(value.getAsCopyableKey());
|
||||||
|
this.root.soundProxy.playUi(SOUNDS.copy);
|
||||||
|
} else {
|
||||||
|
copy("");
|
||||||
|
this.root.soundProxy.playUiError();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -8,6 +8,9 @@ import { MetaVirtualProcessorBuilding } from "../../buildings/virtual_processor"
|
|||||||
import { MetaTransistorBuilding } from "../../buildings/transistor";
|
import { MetaTransistorBuilding } from "../../buildings/transistor";
|
||||||
import { MetaAnalyzerBuilding } from "../../buildings/analyzer";
|
import { MetaAnalyzerBuilding } from "../../buildings/analyzer";
|
||||||
import { MetaComparatorBuilding } from "../../buildings/comparator";
|
import { MetaComparatorBuilding } from "../../buildings/comparator";
|
||||||
|
import { MetaReaderBuilding } from "../../buildings/reader";
|
||||||
|
import { MetaFilterBuilding } from "../../buildings/filter";
|
||||||
|
import { MetaDisplayBuilding } from "../../buildings/display";
|
||||||
|
|
||||||
export class HUDWiresToolbar extends HUDBaseToolbar {
|
export class HUDWiresToolbar extends HUDBaseToolbar {
|
||||||
constructor(root) {
|
constructor(root) {
|
||||||
@ -16,12 +19,17 @@ export class HUDWiresToolbar extends HUDBaseToolbar {
|
|||||||
MetaWireBuilding,
|
MetaWireBuilding,
|
||||||
MetaWireTunnelBuilding,
|
MetaWireTunnelBuilding,
|
||||||
MetaConstantSignalBuilding,
|
MetaConstantSignalBuilding,
|
||||||
MetaLeverBuilding,
|
|
||||||
MetaTransistorBuilding,
|
|
||||||
MetaLogicGateBuilding,
|
MetaLogicGateBuilding,
|
||||||
MetaAnalyzerBuilding,
|
|
||||||
MetaVirtualProcessorBuilding,
|
MetaVirtualProcessorBuilding,
|
||||||
|
MetaAnalyzerBuilding,
|
||||||
MetaComparatorBuilding,
|
MetaComparatorBuilding,
|
||||||
|
MetaTransistorBuilding,
|
||||||
|
],
|
||||||
|
secondaryBuildings: [
|
||||||
|
MetaReaderBuilding,
|
||||||
|
MetaLeverBuilding,
|
||||||
|
MetaFilterBuilding,
|
||||||
|
MetaDisplayBuilding,
|
||||||
],
|
],
|
||||||
visibilityCondition: () =>
|
visibilityCondition: () =>
|
||||||
!this.root.camera.getIsMapOverlayActive() && this.root.currentLayer === "wires",
|
!this.root.camera.getIsMapOverlayActive() && this.root.currentLayer === "wires",
|
||||||
|
|||||||
@ -26,6 +26,13 @@ export class BooleanItem extends BaseItem {
|
|||||||
return "boolean";
|
return "boolean";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
getAsCopyableKey() {
|
||||||
|
return this.value ? "1" : "0";
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {number} value
|
* @param {number} value
|
||||||
*/
|
*/
|
||||||
@ -56,6 +63,21 @@ export class BooleanItem extends BaseItem {
|
|||||||
}
|
}
|
||||||
sprite.drawCachedCentered(parameters, x, y, diameter);
|
sprite.drawCachedCentered(parameters, x, y, diameter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws the item to a canvas
|
||||||
|
* @param {CanvasRenderingContext2D} context
|
||||||
|
* @param {number} size
|
||||||
|
*/
|
||||||
|
drawFullSizeOnCanvas(context, size) {
|
||||||
|
let sprite;
|
||||||
|
if (this.value) {
|
||||||
|
sprite = Loader.getSprite("sprites/wires/boolean_true.png");
|
||||||
|
} else {
|
||||||
|
sprite = Loader.getSprite("sprites/wires/boolean_false.png");
|
||||||
|
}
|
||||||
|
sprite.drawCentered(context, size / 2, size / 2, size);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const BOOL_FALSE_SINGLETON = new BooleanItem(0);
|
export const BOOL_FALSE_SINGLETON = new BooleanItem(0);
|
||||||
|
|||||||
@ -28,6 +28,13 @@ export class ColorItem extends BaseItem {
|
|||||||
return "color";
|
return "color";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
getAsCopyableKey() {
|
||||||
|
return this.color;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {BaseItem} other
|
* @param {BaseItem} other
|
||||||
*/
|
*/
|
||||||
@ -47,6 +54,18 @@ export class ColorItem extends BaseItem {
|
|||||||
return THEME.map.resources[this.color];
|
return THEME.map.resources[this.color];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws the item to a canvas
|
||||||
|
* @param {CanvasRenderingContext2D} context
|
||||||
|
* @param {number} size
|
||||||
|
*/
|
||||||
|
drawFullSizeOnCanvas(context, size) {
|
||||||
|
if (!this.cachedSprite) {
|
||||||
|
this.cachedSprite = Loader.getSprite("sprites/colors/" + this.color + ".png");
|
||||||
|
}
|
||||||
|
this.cachedSprite.drawCentered(context, size / 2, size / 2, size);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {number} x
|
* @param {number} x
|
||||||
* @param {number} y
|
* @param {number} y
|
||||||
|
|||||||
@ -1,62 +1,78 @@
|
|||||||
import { DrawParameters } from "../../core/draw_parameters";
|
import { DrawParameters } from "../../core/draw_parameters";
|
||||||
import { types } from "../../savegame/serialization";
|
import { types } from "../../savegame/serialization";
|
||||||
import { BaseItem } from "../base_item";
|
import { BaseItem } from "../base_item";
|
||||||
import { ShapeDefinition } from "../shape_definition";
|
import { ShapeDefinition } from "../shape_definition";
|
||||||
import { THEME } from "../theme";
|
import { THEME } from "../theme";
|
||||||
import { globalConfig } from "../../core/config";
|
import { globalConfig } from "../../core/config";
|
||||||
|
|
||||||
export class ShapeItem extends BaseItem {
|
export class ShapeItem extends BaseItem {
|
||||||
static getId() {
|
static getId() {
|
||||||
return "shape";
|
return "shape";
|
||||||
}
|
}
|
||||||
|
|
||||||
static getSchema() {
|
static getSchema() {
|
||||||
return types.string;
|
return types.string;
|
||||||
}
|
}
|
||||||
|
|
||||||
serialize() {
|
serialize() {
|
||||||
return this.definition.getHash();
|
return this.definition.getHash();
|
||||||
}
|
}
|
||||||
|
|
||||||
deserialize(data) {
|
deserialize(data) {
|
||||||
this.definition = ShapeDefinition.fromShortKey(data);
|
this.definition = ShapeDefinition.fromShortKey(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @returns {"shape"} **/
|
/** @returns {"shape"} **/
|
||||||
getItemType() {
|
getItemType() {
|
||||||
return "shape";
|
return "shape";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {BaseItem} other
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
equalsImpl(other) {
|
getAsCopyableKey() {
|
||||||
return this.definition.getHash() === /** @type {ShapeItem} */ (other).definition.getHash();
|
return this.definition.getHash();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {ShapeDefinition} definition
|
* @param {BaseItem} other
|
||||||
*/
|
*/
|
||||||
constructor(definition) {
|
equalsImpl(other) {
|
||||||
super();
|
return this.definition.getHash() === /** @type {ShapeItem} */ (other).definition.getHash();
|
||||||
|
}
|
||||||
/**
|
|
||||||
* This property must not be modified on runtime, you have to clone the class in order to change the definition
|
/**
|
||||||
*/
|
* @param {ShapeDefinition} definition
|
||||||
this.definition = definition;
|
*/
|
||||||
}
|
constructor(definition) {
|
||||||
|
super();
|
||||||
getBackgroundColorAsResource() {
|
|
||||||
return THEME.map.resources.shape;
|
/**
|
||||||
}
|
* This property must not be modified on runtime, you have to clone the class in order to change the definition
|
||||||
|
*/
|
||||||
/**
|
this.definition = definition;
|
||||||
* @param {number} x
|
}
|
||||||
* @param {number} y
|
|
||||||
* @param {DrawParameters} parameters
|
getBackgroundColorAsResource() {
|
||||||
* @param {number=} diameter
|
return THEME.map.resources.shape;
|
||||||
*/
|
}
|
||||||
drawItemCenteredImpl(x, y, parameters, diameter = globalConfig.defaultItemDiameter) {
|
|
||||||
this.definition.drawCentered(x, y, parameters, diameter);
|
/**
|
||||||
}
|
* Draws the item to a canvas
|
||||||
}
|
* @param {CanvasRenderingContext2D} context
|
||||||
|
* @param {number} size
|
||||||
|
*/
|
||||||
|
drawFullSizeOnCanvas(context, size) {
|
||||||
|
this.definition.drawFullSizeOnCanvas(context, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {number} x
|
||||||
|
* @param {number} y
|
||||||
|
* @param {DrawParameters} parameters
|
||||||
|
* @param {number=} diameter
|
||||||
|
*/
|
||||||
|
drawItemCenteredImpl(x, y, parameters, diameter = globalConfig.defaultItemDiameter) {
|
||||||
|
this.definition.drawCentered(x, y, parameters, diameter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -67,12 +67,11 @@ export const KEYMAPPINGS = {
|
|||||||
wire: { keyCode: key("1") },
|
wire: { keyCode: key("1") },
|
||||||
wire_tunnel: { keyCode: key("2") },
|
wire_tunnel: { keyCode: key("2") },
|
||||||
constant_signal: { keyCode: key("3") },
|
constant_signal: { keyCode: key("3") },
|
||||||
lever_wires: { keyCode: key("4") },
|
logic_gate: { keyCode: key("4") },
|
||||||
logic_gate: { keyCode: key("5") },
|
virtual_processor: { keyCode: key("5") },
|
||||||
virtual_processor: { keyCode: key("6") },
|
analyzer: { keyCode: key("6") },
|
||||||
transistor: { keyCode: key("7") },
|
comparator: { keyCode: key("7") },
|
||||||
analyzer: { keyCode: key("8") },
|
transistor: { keyCode: key("8") },
|
||||||
comparator: { keyCode: key("9") },
|
|
||||||
},
|
},
|
||||||
|
|
||||||
placement: {
|
placement: {
|
||||||
@ -82,6 +81,8 @@ export const KEYMAPPINGS = {
|
|||||||
cycleBuildingVariants: { keyCode: key("T") },
|
cycleBuildingVariants: { keyCode: key("T") },
|
||||||
cycleBuildings: { keyCode: 9 }, // TAB
|
cycleBuildings: { keyCode: 9 }, // TAB
|
||||||
switchDirectionLockSide: { keyCode: key("R") },
|
switchDirectionLockSide: { keyCode: key("R") },
|
||||||
|
|
||||||
|
copyWireValue: { keyCode: key("Z") },
|
||||||
},
|
},
|
||||||
|
|
||||||
massSelect: {
|
massSelect: {
|
||||||
|
|||||||
@ -297,6 +297,15 @@ export class ShapeDefinition extends BasicSerializableObject {
|
|||||||
parameters.context.drawImage(canvas, x - diameter / 2, y - diameter / 2, diameter, diameter);
|
parameters.context.drawImage(canvas, x - diameter / 2, y - diameter / 2, diameter, diameter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws the item to a canvas
|
||||||
|
* @param {CanvasRenderingContext2D} context
|
||||||
|
* @param {number} size
|
||||||
|
*/
|
||||||
|
drawFullSizeOnCanvas(context, size) {
|
||||||
|
this.internalGenerateShapeBuffer(null, context, size, size, 1);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates this shape as a canvas
|
* Generates this shape as a canvas
|
||||||
* @param {number} size
|
* @param {number} size
|
||||||
|
|||||||
@ -1,6 +1,9 @@
|
|||||||
import trim from "trim";
|
import trim from "trim";
|
||||||
|
import { THIRDPARTY_URLS } from "../../core/config";
|
||||||
import { DialogWithForm } from "../../core/modal_dialog_elements";
|
import { DialogWithForm } from "../../core/modal_dialog_elements";
|
||||||
import { FormElementInput } from "../../core/modal_dialog_forms";
|
import { FormElementInput, FormElementItemChooser } from "../../core/modal_dialog_forms";
|
||||||
|
import { fillInLinkIntoTranslation } from "../../core/utils";
|
||||||
|
import { T } from "../../translations";
|
||||||
import { BaseItem } from "../base_item";
|
import { BaseItem } from "../base_item";
|
||||||
import { enumColors } from "../colors";
|
import { enumColors } from "../colors";
|
||||||
import { ConstantSignalComponent } from "../components/constant_signal";
|
import { ConstantSignalComponent } from "../components/constant_signal";
|
||||||
@ -9,6 +12,7 @@ import { GameSystemWithFilter } from "../game_system_with_filter";
|
|||||||
import { BOOL_FALSE_SINGLETON, BOOL_TRUE_SINGLETON } from "../items/boolean_item";
|
import { BOOL_FALSE_SINGLETON, BOOL_TRUE_SINGLETON } from "../items/boolean_item";
|
||||||
import { COLOR_ITEM_SINGLETONS } from "../items/color_item";
|
import { COLOR_ITEM_SINGLETONS } from "../items/color_item";
|
||||||
import { ShapeDefinition } from "../shape_definition";
|
import { ShapeDefinition } from "../shape_definition";
|
||||||
|
import { blueprintShape } from "../upgrades";
|
||||||
|
|
||||||
export class ConstantSignalSystem extends GameSystemWithFilter {
|
export class ConstantSignalSystem extends GameSystemWithFilter {
|
||||||
constructor(root) {
|
constructor(root) {
|
||||||
@ -41,23 +45,35 @@ export class ConstantSignalSystem extends GameSystemWithFilter {
|
|||||||
|
|
||||||
const signalValueInput = new FormElementInput({
|
const signalValueInput = new FormElementInput({
|
||||||
id: "signalValue",
|
id: "signalValue",
|
||||||
label: null,
|
label: fillInLinkIntoTranslation(T.dialogs.editSignal.descShortKey, THIRDPARTY_URLS.shapeViewer),
|
||||||
placeholder: "",
|
placeholder: "",
|
||||||
defaultValue: "",
|
defaultValue: "",
|
||||||
validator: val => this.parseSignalCode(val),
|
validator: val => this.parseSignalCode(val),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const itemInput = new FormElementItemChooser({
|
||||||
|
id: "signalItem",
|
||||||
|
label: null,
|
||||||
|
items: [
|
||||||
|
BOOL_FALSE_SINGLETON,
|
||||||
|
BOOL_TRUE_SINGLETON,
|
||||||
|
...Object.values(COLOR_ITEM_SINGLETONS),
|
||||||
|
this.root.shapeDefinitionMgr.getShapeItemFromShortKey(blueprintShape),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
const dialog = new DialogWithForm({
|
const dialog = new DialogWithForm({
|
||||||
app: this.root.app,
|
app: this.root.app,
|
||||||
title: "Set Signal",
|
title: T.dialogs.editSignal.title,
|
||||||
desc: "Enter a shape code, color or '0' or '1'",
|
desc: T.dialogs.editSignal.descItems,
|
||||||
formElements: [signalValueInput],
|
formElements: [itemInput, signalValueInput],
|
||||||
buttons: ["cancel:bad:escape", "ok:good:enter"],
|
buttons: ["cancel:bad:escape", "ok:good:enter"],
|
||||||
closeButton: false,
|
closeButton: false,
|
||||||
});
|
});
|
||||||
this.root.hud.parts.dialogs.internalShowDialog(dialog);
|
this.root.hud.parts.dialogs.internalShowDialog(dialog);
|
||||||
|
|
||||||
// When confirmed, set the signal
|
// When confirmed, set the signal
|
||||||
dialog.buttonSignals.ok.add(() => {
|
const closeHandler = () => {
|
||||||
if (!this.root || !this.root.entityMgr) {
|
if (!this.root || !this.root.entityMgr) {
|
||||||
// Game got stopped
|
// Game got stopped
|
||||||
return;
|
return;
|
||||||
@ -75,8 +91,16 @@ export class ConstantSignalSystem extends GameSystemWithFilter {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
constantComp.signal = this.parseSignalCode(signalValueInput.getValue());
|
if (itemInput.chosenItem) {
|
||||||
});
|
console.log(itemInput.chosenItem);
|
||||||
|
constantComp.signal = itemInput.chosenItem;
|
||||||
|
} else {
|
||||||
|
constantComp.signal = this.parseSignalCode(signalValueInput.getValue());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
dialog.buttonSignals.ok.add(closeHandler);
|
||||||
|
dialog.valueChosen.add(closeHandler);
|
||||||
|
|
||||||
// When cancelled, destroy the entity again
|
// When cancelled, destroy the entity again
|
||||||
dialog.buttonSignals.cancel.add(() => {
|
dialog.buttonSignals.cancel.add(() => {
|
||||||
|
|||||||
@ -1,15 +1,15 @@
|
|||||||
|
import { globalConfig } from "../../core/config";
|
||||||
|
import { smoothenDpi } from "../../core/dpi_manager";
|
||||||
import { DrawParameters } from "../../core/draw_parameters";
|
import { DrawParameters } from "../../core/draw_parameters";
|
||||||
|
import { drawSpriteClipped } from "../../core/draw_utils";
|
||||||
import { Loader } from "../../core/loader";
|
import { Loader } from "../../core/loader";
|
||||||
|
import { Rectangle } from "../../core/rectangle";
|
||||||
|
import { ORIGINAL_SPRITE_SCALE } from "../../core/sprites";
|
||||||
import { formatBigNumber } from "../../core/utils";
|
import { formatBigNumber } from "../../core/utils";
|
||||||
import { T } from "../../translations";
|
import { T } from "../../translations";
|
||||||
import { HubComponent } from "../components/hub";
|
import { HubComponent } from "../components/hub";
|
||||||
import { Entity } from "../entity";
|
import { Entity } from "../entity";
|
||||||
import { GameSystemWithFilter } from "../game_system_with_filter";
|
import { GameSystemWithFilter } from "../game_system_with_filter";
|
||||||
import { globalConfig } from "../../core/config";
|
|
||||||
import { smoothenDpi } from "../../core/dpi_manager";
|
|
||||||
import { drawSpriteClipped } from "../../core/draw_utils";
|
|
||||||
import { Rectangle } from "../../core/rectangle";
|
|
||||||
import { ORIGINAL_SPRITE_SCALE } from "../../core/sprites";
|
|
||||||
|
|
||||||
const HUB_SIZE_TILES = 4;
|
const HUB_SIZE_TILES = 4;
|
||||||
const HUB_SIZE_PIXELS = HUB_SIZE_TILES * globalConfig.tileSize;
|
const HUB_SIZE_PIXELS = HUB_SIZE_TILES * globalConfig.tileSize;
|
||||||
@ -73,23 +73,36 @@ export class HubSystem extends GameSystemWithFilter {
|
|||||||
const textOffsetX = 70;
|
const textOffsetX = 70;
|
||||||
const textOffsetY = 61;
|
const textOffsetY = 61;
|
||||||
|
|
||||||
// Deliver count
|
if (goals.throughputOnly) {
|
||||||
const delivered = this.root.hubGoals.getCurrentGoalDelivered();
|
// Throughput
|
||||||
const deliveredText = "" + formatBigNumber(delivered);
|
const deliveredText = T.ingame.statistics.shapesDisplayUnits.second.replace(
|
||||||
|
"<shapes>",
|
||||||
|
formatBigNumber(goals.required)
|
||||||
|
);
|
||||||
|
|
||||||
if (delivered > 999) {
|
context.font = "bold 12px GameFont";
|
||||||
context.font = "bold 16px GameFont";
|
context.fillStyle = "#64666e";
|
||||||
|
context.textAlign = "left";
|
||||||
|
context.fillText(deliveredText, textOffsetX, textOffsetY);
|
||||||
} else {
|
} else {
|
||||||
context.font = "bold 25px GameFont";
|
// Deliver count
|
||||||
}
|
const delivered = this.root.hubGoals.getCurrentGoalDelivered();
|
||||||
context.fillStyle = "#64666e";
|
const deliveredText = "" + formatBigNumber(delivered);
|
||||||
context.textAlign = "left";
|
|
||||||
context.fillText(deliveredText, textOffsetX, textOffsetY);
|
|
||||||
|
|
||||||
// Required
|
if (delivered > 999) {
|
||||||
context.font = "13px GameFont";
|
context.font = "bold 16px GameFont";
|
||||||
context.fillStyle = "#a4a6b0";
|
} else {
|
||||||
context.fillText("/ " + formatBigNumber(goals.required), textOffsetX, textOffsetY + 13);
|
context.font = "bold 25px GameFont";
|
||||||
|
}
|
||||||
|
context.fillStyle = "#64666e";
|
||||||
|
context.textAlign = "left";
|
||||||
|
context.fillText(deliveredText, textOffsetX, textOffsetY);
|
||||||
|
|
||||||
|
// Required
|
||||||
|
context.font = "13px GameFont";
|
||||||
|
context.fillStyle = "#a4a6b0";
|
||||||
|
context.fillText("/ " + formatBigNumber(goals.required), textOffsetX, textOffsetY + 13);
|
||||||
|
}
|
||||||
|
|
||||||
// Reward
|
// Reward
|
||||||
const rewardText = T.storyRewards[goals.reward].title.toUpperCase();
|
const rewardText = T.storyRewards[goals.reward].title.toUpperCase();
|
||||||
@ -167,4 +180,4 @@ export class HubSystem extends GameSystemWithFilter {
|
|||||||
originalH: HUB_SIZE_PIXELS * dpi,
|
originalH: HUB_SIZE_PIXELS * dpi,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3,8 +3,9 @@ import { enumColors } from "../colors";
|
|||||||
import { enumLogicGateType, LogicGateComponent } from "../components/logic_gate";
|
import { enumLogicGateType, LogicGateComponent } from "../components/logic_gate";
|
||||||
import { enumPinSlotType } from "../components/wired_pins";
|
import { enumPinSlotType } from "../components/wired_pins";
|
||||||
import { GameSystemWithFilter } from "../game_system_with_filter";
|
import { GameSystemWithFilter } from "../game_system_with_filter";
|
||||||
import { BOOL_FALSE_SINGLETON, BOOL_TRUE_SINGLETON, isTruthyItem } from "../items/boolean_item";
|
import { BOOL_FALSE_SINGLETON, BOOL_TRUE_SINGLETON, BooleanItem, isTruthyItem } from "../items/boolean_item";
|
||||||
import { COLOR_ITEM_SINGLETONS } from "../items/color_item";
|
import { ColorItem, COLOR_ITEM_SINGLETONS } from "../items/color_item";
|
||||||
|
import { ShapeItem } from "../items/shape_item";
|
||||||
import { ShapeDefinition } from "../shape_definition";
|
import { ShapeDefinition } from "../shape_definition";
|
||||||
|
|
||||||
export class LogicGateSystem extends GameSystemWithFilter {
|
export class LogicGateSystem extends GameSystemWithFilter {
|
||||||
@ -153,18 +154,22 @@ export class LogicGateSystem extends GameSystemWithFilter {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Array<BaseItem|null>} parameters
|
* @param {Array<BaseItem|null>} parameters
|
||||||
* @returns {BaseItem}
|
* @returns {[BaseItem, BaseItem]}
|
||||||
*/
|
*/
|
||||||
compute_ROTATE(parameters) {
|
compute_ROTATE(parameters) {
|
||||||
const item = parameters[0];
|
const item = parameters[0];
|
||||||
if (!item || item.getItemType() !== "shape") {
|
if (!item || item.getItemType() !== "shape") {
|
||||||
// Not a shape
|
// Not a shape
|
||||||
return null;
|
return [null, null];
|
||||||
}
|
}
|
||||||
|
|
||||||
const definition = /** @type {ShapeItem} */ (item).definition;
|
const definition = /** @type {ShapeItem} */ (item).definition;
|
||||||
const rotatedDefinition = this.root.shapeDefinitionMgr.shapeActionRotateCW(definition);
|
const rotatedDefinitionCCW = this.root.shapeDefinitionMgr.shapeActionRotateCCW(definition);
|
||||||
return this.root.shapeDefinitionMgr.getShapeItemFromDefinition(rotatedDefinition);
|
const rotatedDefinitionCW = this.root.shapeDefinitionMgr.shapeActionRotateCW(definition);
|
||||||
|
return [
|
||||||
|
this.root.shapeDefinitionMgr.getShapeItemFromDefinition(rotatedDefinitionCCW),
|
||||||
|
this.root.shapeDefinitionMgr.getShapeItemFromDefinition(rotatedDefinitionCW),
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -43,7 +43,7 @@ export const tutorialGoals = [
|
|||||||
// Circle
|
// Circle
|
||||||
{
|
{
|
||||||
shape: "CuCuCuCu", // belts t1
|
shape: "CuCuCuCu", // belts t1
|
||||||
required: 40,
|
required: 30,
|
||||||
reward: enumHubGoalRewards.reward_cutter_and_trash,
|
reward: enumHubGoalRewards.reward_cutter_and_trash,
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -59,14 +59,14 @@ export const tutorialGoals = [
|
|||||||
// Rectangle
|
// Rectangle
|
||||||
{
|
{
|
||||||
shape: "RuRuRuRu", // miners t1
|
shape: "RuRuRuRu", // miners t1
|
||||||
required: 85,
|
required: 70,
|
||||||
reward: enumHubGoalRewards.reward_balancer,
|
reward: enumHubGoalRewards.reward_balancer,
|
||||||
},
|
},
|
||||||
|
|
||||||
// 4
|
// 4
|
||||||
{
|
{
|
||||||
shape: "RuRu----", // processors t2
|
shape: "RuRu----", // processors t2
|
||||||
required: 100,
|
required: 70,
|
||||||
reward: enumHubGoalRewards.reward_rotater,
|
reward: enumHubGoalRewards.reward_rotater,
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -74,14 +74,14 @@ export const tutorialGoals = [
|
|||||||
// Rotater
|
// Rotater
|
||||||
{
|
{
|
||||||
shape: "Cu----Cu", // belts t2
|
shape: "Cu----Cu", // belts t2
|
||||||
required: 175,
|
required: 170,
|
||||||
reward: enumHubGoalRewards.reward_tunnel,
|
reward: enumHubGoalRewards.reward_tunnel,
|
||||||
},
|
},
|
||||||
|
|
||||||
// 6
|
// 6
|
||||||
{
|
{
|
||||||
shape: "Cu------", // miners t2
|
shape: "Cu------", // miners t2
|
||||||
required: 250,
|
required: 270,
|
||||||
reward: enumHubGoalRewards.reward_painter,
|
reward: enumHubGoalRewards.reward_painter,
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -89,14 +89,14 @@ export const tutorialGoals = [
|
|||||||
// Painter
|
// Painter
|
||||||
{
|
{
|
||||||
shape: "CrCrCrCr", // unused
|
shape: "CrCrCrCr", // unused
|
||||||
required: 500,
|
required: 300,
|
||||||
reward: enumHubGoalRewards.reward_rotater_ccw,
|
reward: enumHubGoalRewards.reward_rotater_ccw,
|
||||||
},
|
},
|
||||||
|
|
||||||
// 8
|
// 8
|
||||||
{
|
{
|
||||||
shape: "RbRb----", // painter t2
|
shape: "RbRb----", // painter t2
|
||||||
required: 700,
|
required: 480,
|
||||||
reward: enumHubGoalRewards.reward_mixer,
|
reward: enumHubGoalRewards.reward_mixer,
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -104,7 +104,7 @@ export const tutorialGoals = [
|
|||||||
// Mixing (purple)
|
// Mixing (purple)
|
||||||
{
|
{
|
||||||
shape: "CpCpCpCp", // belts t3
|
shape: "CpCpCpCp", // belts t3
|
||||||
required: 800,
|
required: 600,
|
||||||
reward: enumHubGoalRewards.reward_merger,
|
reward: enumHubGoalRewards.reward_merger,
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -112,7 +112,7 @@ export const tutorialGoals = [
|
|||||||
// STACKER: Star shape + cyan
|
// STACKER: Star shape + cyan
|
||||||
{
|
{
|
||||||
shape: "ScScScSc", // miners t3
|
shape: "ScScScSc", // miners t3
|
||||||
required: 900,
|
required: 800,
|
||||||
reward: enumHubGoalRewards.reward_stacker,
|
reward: enumHubGoalRewards.reward_stacker,
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -128,7 +128,7 @@ export const tutorialGoals = [
|
|||||||
// Blueprints
|
// Blueprints
|
||||||
{
|
{
|
||||||
shape: "CbCbCbRb:CwCwCwCw",
|
shape: "CbCbCbRb:CwCwCwCw",
|
||||||
required: 1250,
|
required: 1000,
|
||||||
reward: enumHubGoalRewards.reward_blueprints,
|
reward: enumHubGoalRewards.reward_blueprints,
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -136,24 +136,24 @@ export const tutorialGoals = [
|
|||||||
// Tunnel Tier 2
|
// Tunnel Tier 2
|
||||||
{
|
{
|
||||||
shape: "RpRpRpRp:CwCwCwCw", // painting t3
|
shape: "RpRpRpRp:CwCwCwCw", // painting t3
|
||||||
required: 5000,
|
required: 3800,
|
||||||
reward: enumHubGoalRewards.reward_underground_belt_tier_2,
|
reward: enumHubGoalRewards.reward_underground_belt_tier_2,
|
||||||
},
|
},
|
||||||
|
|
||||||
// 14
|
// 14
|
||||||
// Belt reader
|
// Belt reader
|
||||||
{
|
{
|
||||||
// @todo
|
shape: "--Cg----:--Cr----", // unused
|
||||||
shape: "CuCuCuCu",
|
required: 16, // Per second!
|
||||||
required: 7000,
|
|
||||||
reward: enumHubGoalRewards.reward_belt_reader,
|
reward: enumHubGoalRewards.reward_belt_reader,
|
||||||
|
throughputOnly: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
// 15
|
// 15
|
||||||
// Storage
|
// Storage
|
||||||
{
|
{
|
||||||
shape: "SrSrSrSr:CyCyCyCy", // unused
|
shape: "SrSrSrSr:CyCyCyCy", // unused
|
||||||
required: 7500,
|
required: 10000,
|
||||||
reward: enumHubGoalRewards.reward_storage,
|
reward: enumHubGoalRewards.reward_storage,
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -161,7 +161,7 @@ export const tutorialGoals = [
|
|||||||
// Quad Cutter
|
// Quad Cutter
|
||||||
{
|
{
|
||||||
shape: "SrSrSrSr:CyCyCyCy:SwSwSwSw", // belts t4 (two variants)
|
shape: "SrSrSrSr:CyCyCyCy:SwSwSwSw", // belts t4 (two variants)
|
||||||
required: 12500,
|
required: 6000,
|
||||||
reward: enumHubGoalRewards.reward_cutter_quad,
|
reward: enumHubGoalRewards.reward_cutter_quad,
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -169,15 +169,14 @@ export const tutorialGoals = [
|
|||||||
// Double painter
|
// Double painter
|
||||||
{
|
{
|
||||||
shape: "CbRbRbCb:CwCwCwCw:WbWbWbWb", // miner t4 (two variants)
|
shape: "CbRbRbCb:CwCwCwCw:WbWbWbWb", // miner t4 (two variants)
|
||||||
required: 15000,
|
required: 20000,
|
||||||
reward: enumHubGoalRewards.reward_painter_double,
|
reward: enumHubGoalRewards.reward_painter_double,
|
||||||
},
|
},
|
||||||
|
|
||||||
// 18
|
// 18
|
||||||
// Rotater (180deg)
|
// Rotater (180deg)
|
||||||
{
|
{
|
||||||
// @TODO
|
shape: "Sg----Sg:CgCgCgCg:--CyCy--", // unused
|
||||||
shape: "CuCuCuCu",
|
|
||||||
required: 20000,
|
required: 20000,
|
||||||
reward: enumHubGoalRewards.reward_rotater_180,
|
reward: enumHubGoalRewards.reward_rotater_180,
|
||||||
},
|
},
|
||||||
@ -185,8 +184,7 @@ export const tutorialGoals = [
|
|||||||
// 19
|
// 19
|
||||||
// Compact splitter
|
// Compact splitter
|
||||||
{
|
{
|
||||||
// @TODO
|
shape: "CpRpCp--:SwSwSwSw",
|
||||||
shape: "CuCuCuCu",
|
|
||||||
required: 25000,
|
required: 25000,
|
||||||
reward: enumHubGoalRewards.reward_splitter,
|
reward: enumHubGoalRewards.reward_splitter,
|
||||||
},
|
},
|
||||||
@ -195,15 +193,14 @@ export const tutorialGoals = [
|
|||||||
// WIRES
|
// WIRES
|
||||||
{
|
{
|
||||||
shape: finalGameShape,
|
shape: finalGameShape,
|
||||||
required: 50000,
|
required: 25000,
|
||||||
reward: enumHubGoalRewards.reward_wires_filters_and_levers,
|
reward: enumHubGoalRewards.reward_wires_filters_and_levers,
|
||||||
},
|
},
|
||||||
|
|
||||||
// 21
|
// 21
|
||||||
// Display
|
// Display
|
||||||
{
|
{
|
||||||
// @TODO
|
shape: "CrCrCrCr:CwCwCwCw:CrCrCrCr:CwCwCwCw",
|
||||||
shape: "CuCuCuCu",
|
|
||||||
required: 25000,
|
required: 25000,
|
||||||
reward: enumHubGoalRewards.reward_display,
|
reward: enumHubGoalRewards.reward_display,
|
||||||
},
|
},
|
||||||
@ -211,43 +208,37 @@ export const tutorialGoals = [
|
|||||||
// 22
|
// 22
|
||||||
// Constant signal
|
// Constant signal
|
||||||
{
|
{
|
||||||
// @TODO
|
shape: "Cg----Cr:Cw----Cw:Sy------:Cy----Cy",
|
||||||
shape: "CuCuCuCu",
|
required: 25000,
|
||||||
required: 30000,
|
|
||||||
reward: enumHubGoalRewards.reward_constant_signal,
|
reward: enumHubGoalRewards.reward_constant_signal,
|
||||||
},
|
},
|
||||||
|
|
||||||
// 23
|
// 23
|
||||||
// Quad Painter
|
// Quad Painter
|
||||||
{
|
{
|
||||||
// @TODO
|
shape: "CcSyCcSy:SyCcSyCc:CcSyCcSy",
|
||||||
shape: "CuCuCuCu",
|
required: 5000,
|
||||||
// shape: "WrRgWrRg:CwCrCwCr:SgSgSgSg", // processors t4 (two variants)
|
|
||||||
required: 35000,
|
|
||||||
reward: enumHubGoalRewards.reward_painter_quad,
|
reward: enumHubGoalRewards.reward_painter_quad,
|
||||||
},
|
},
|
||||||
|
|
||||||
// 24 Logic gates
|
// 24 Logic gates
|
||||||
{
|
{
|
||||||
// @TODO
|
shape: "CcRcCcRc:RwCwRwCw:Sr--Sw--:CyCyCyCy",
|
||||||
shape: "CuCuCuCu",
|
required: 10000,
|
||||||
required: 40000,
|
|
||||||
reward: enumHubGoalRewards.reward_logic_gates,
|
reward: enumHubGoalRewards.reward_logic_gates,
|
||||||
},
|
},
|
||||||
|
|
||||||
// 25 Virtual Processing
|
// 25 Virtual Processing
|
||||||
{
|
{
|
||||||
// @TODO
|
shape: "Rg--Rg--:CwRwCwRw:--Rg--Rg",
|
||||||
shape: "CuCuCuCu",
|
required: 10000,
|
||||||
required: 45000,
|
|
||||||
reward: enumHubGoalRewards.reward_virtual_processing,
|
reward: enumHubGoalRewards.reward_virtual_processing,
|
||||||
},
|
},
|
||||||
|
|
||||||
// 26 Freeplay
|
// 26 Freeplay
|
||||||
{
|
{
|
||||||
// @TODO
|
shape: "CbCuCbCu:Sr------:--CrSrCr:CwCwCwCw",
|
||||||
shape: "CuCuCuCu",
|
required: 10000,
|
||||||
required: 100000,
|
|
||||||
reward: enumHubGoalRewards.reward_freeplay,
|
reward: enumHubGoalRewards.reward_freeplay,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { MetaConstantSignalBuilding } from "./buildings/constant_signal";
|
|||||||
import { enumCutterVariants, MetaCutterBuilding } from "./buildings/cutter";
|
import { enumCutterVariants, MetaCutterBuilding } from "./buildings/cutter";
|
||||||
import { MetaDisplayBuilding } from "./buildings/display";
|
import { MetaDisplayBuilding } from "./buildings/display";
|
||||||
import { MetaLeverBuilding } from "./buildings/lever";
|
import { MetaLeverBuilding } from "./buildings/lever";
|
||||||
|
import { MetaLogicGateBuilding } from "./buildings/logic_gate";
|
||||||
import { enumMinerVariants, MetaMinerBuilding } from "./buildings/miner";
|
import { enumMinerVariants, MetaMinerBuilding } from "./buildings/miner";
|
||||||
import { MetaMixerBuilding } from "./buildings/mixer";
|
import { MetaMixerBuilding } from "./buildings/mixer";
|
||||||
import { enumPainterVariants, MetaPainterBuilding } from "./buildings/painter";
|
import { enumPainterVariants, MetaPainterBuilding } from "./buildings/painter";
|
||||||
@ -53,7 +54,7 @@ export const enumHubGoalRewardsToContentUnlocked = {
|
|||||||
[enumHubGoalRewards.reward_constant_signal]: typed([
|
[enumHubGoalRewards.reward_constant_signal]: typed([
|
||||||
[MetaConstantSignalBuilding, defaultBuildingVariant],
|
[MetaConstantSignalBuilding, defaultBuildingVariant],
|
||||||
]),
|
]),
|
||||||
[enumHubGoalRewards.reward_logic_gates]: null, // @TODO!
|
[enumHubGoalRewards.reward_logic_gates]: typed([[MetaLogicGateBuilding, defaultBuildingVariant]]),
|
||||||
[enumHubGoalRewards.reward_virtual_processing]: null, // @TODO!
|
[enumHubGoalRewards.reward_virtual_processing]: null, // @TODO!
|
||||||
|
|
||||||
[enumHubGoalRewards.reward_wires_filters_and_levers]: typed([
|
[enumHubGoalRewards.reward_wires_filters_and_levers]: typed([
|
||||||
|
|||||||
@ -2,10 +2,28 @@ import { findNiceIntegerValue } from "../core/utils";
|
|||||||
import { ShapeDefinition } from "./shape_definition";
|
import { ShapeDefinition } from "./shape_definition";
|
||||||
|
|
||||||
export const finalGameShape = "RuCw--Cw:----Ru--";
|
export const finalGameShape = "RuCw--Cw:----Ru--";
|
||||||
|
export const rocketShape = "CbCuCbCu:Sr------:--CrSrCr:CwCwCwCw";
|
||||||
export const blueprintShape = "CbCbCbRb:CwCwCwCw";
|
export const blueprintShape = "CbCbCbRb:CwCwCwCw";
|
||||||
|
|
||||||
const fixedImprovements = [0.5, 0.5, 1, 1, 2, 2];
|
const fixedImprovements = [0.5, 0.5, 1, 1, 2, 2];
|
||||||
|
|
||||||
|
const numEndgameUpgrades = G_IS_DEV || G_IS_STANDALONE ? 20 - fixedImprovements.length - 1 : 0;
|
||||||
|
|
||||||
|
function generateEndgameUpgrades() {
|
||||||
|
return new Array(numEndgameUpgrades).fill(null).map((_, i) => ({
|
||||||
|
required: [
|
||||||
|
{ shape: blueprintShape, amount: 30000 + i * 10000 },
|
||||||
|
{ shape: finalGameShape, amount: 20000 + i * 5000 },
|
||||||
|
{ shape: rocketShape, amount: 20000 + i * 5000 },
|
||||||
|
],
|
||||||
|
excludePrevious: true,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < numEndgameUpgrades; ++i) {
|
||||||
|
fixedImprovements.push(0.1);
|
||||||
|
}
|
||||||
|
|
||||||
/** @typedef {{
|
/** @typedef {{
|
||||||
* shape: string,
|
* shape: string,
|
||||||
* amount: number
|
* amount: number
|
||||||
@ -23,95 +41,99 @@ const fixedImprovements = [0.5, 0.5, 1, 1, 2, 2];
|
|||||||
export const UPGRADES = {
|
export const UPGRADES = {
|
||||||
belt: [
|
belt: [
|
||||||
{
|
{
|
||||||
required: [{ shape: "CuCuCuCu", amount: 150 }],
|
required: [{ shape: "CuCuCuCu", amount: 60 }],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
required: [{ shape: "--CuCu--", amount: 1000 }],
|
required: [{ shape: "--CuCu--", amount: 500 }],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
required: [{ shape: "CpCpCpCp", amount: 5000 }],
|
required: [{ shape: "CpCpCpCp", amount: 1000 }],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
required: [{ shape: "SrSrSrSr:CyCyCyCy", amount: 12000 }],
|
required: [{ shape: "SrSrSrSr:CyCyCyCy", amount: 6000 }],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
required: [{ shape: "SrSrSrSr:CyCyCyCy:SwSwSwSw", amount: 20000 }],
|
required: [{ shape: "SrSrSrSr:CyCyCyCy:SwSwSwSw", amount: 25000 }],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
required: [{ shape: finalGameShape, amount: 50000 }],
|
required: [{ shape: finalGameShape, amount: 50000 }],
|
||||||
excludePrevious: true,
|
excludePrevious: true,
|
||||||
},
|
},
|
||||||
|
...generateEndgameUpgrades(),
|
||||||
],
|
],
|
||||||
|
|
||||||
miner: [
|
miner: [
|
||||||
{
|
{
|
||||||
required: [{ shape: "RuRuRuRu", amount: 400 }],
|
required: [{ shape: "RuRuRuRu", amount: 300 }],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
required: [{ shape: "Cu------", amount: 3000 }],
|
required: [{ shape: "Cu------", amount: 800 }],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
required: [{ shape: "ScScScSc", amount: 7000 }],
|
required: [{ shape: "ScScScSc", amount: 3500 }],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
required: [{ shape: "CwCwCwCw:WbWbWbWb", amount: 15000 }],
|
required: [{ shape: "CwCwCwCw:WbWbWbWb", amount: 23000 }],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
required: [{ shape: "CbRbRbCb:CwCwCwCw:WbWbWbWb", amount: 30000 }],
|
required: [{ shape: "CbRbRbCb:CwCwCwCw:WbWbWbWb", amount: 50000 }],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
required: [{ shape: finalGameShape, amount: 65000 }],
|
required: [{ shape: finalGameShape, amount: 50000 }],
|
||||||
excludePrevious: true,
|
excludePrevious: true,
|
||||||
},
|
},
|
||||||
|
...generateEndgameUpgrades(),
|
||||||
],
|
],
|
||||||
|
|
||||||
processors: [
|
processors: [
|
||||||
{
|
{
|
||||||
required: [{ shape: "SuSuSuSu", amount: 600 }],
|
required: [{ shape: "SuSuSuSu", amount: 500 }],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
required: [{ shape: "RuRu----", amount: 2000 }],
|
required: [{ shape: "RuRu----", amount: 600 }],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
required: [{ shape: "CgScScCg", amount: 15000 }],
|
required: [{ shape: "CgScScCg", amount: 3500 }],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
required: [{ shape: "CwCrCwCr:SgSgSgSg", amount: 20000 }],
|
required: [{ shape: "CwCrCwCr:SgSgSgSg", amount: 25000 }],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
required: [{ shape: "WrRgWrRg:CwCrCwCr:SgSgSgSg", amount: 30000 }],
|
required: [{ shape: "WrRgWrRg:CwCrCwCr:SgSgSgSg", amount: 50000 }],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
required: [{ shape: finalGameShape, amount: 75000 }],
|
required: [{ shape: finalGameShape, amount: 50000 }],
|
||||||
excludePrevious: true,
|
excludePrevious: true,
|
||||||
},
|
},
|
||||||
|
...generateEndgameUpgrades(),
|
||||||
],
|
],
|
||||||
|
|
||||||
painting: [
|
painting: [
|
||||||
{
|
{
|
||||||
required: [{ shape: "RbRb----", amount: 1000 }],
|
required: [{ shape: "RbRb----", amount: 600 }],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
required: [{ shape: "WrWrWrWr", amount: 3000 }],
|
required: [{ shape: "WrWrWrWr", amount: 3800 }],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
required: [{ shape: "RpRpRpRp:CwCwCwCw", amount: 15000 }],
|
required: [{ shape: "RpRpRpRp:CwCwCwCw", amount: 6500 }],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
required: [{ shape: "WpWpWpWp:CwCwCwCw:WpWpWpWp", amount: 20000 }],
|
required: [{ shape: "WpWpWpWp:CwCwCwCw:WpWpWpWp", amount: 25000 }],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
required: [{ shape: "WpWpWpWp:CwCwCwCw:WpWpWpWp:CwCwCwCw", amount: 30000 }],
|
required: [{ shape: "WpWpWpWp:CwCwCwCw:WpWpWpWp:CwCwCwCw", amount: 50000 }],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
required: [{ shape: finalGameShape, amount: 100000 }],
|
required: [{ shape: finalGameShape, amount: 50000 }],
|
||||||
excludePrevious: true,
|
excludePrevious: true,
|
||||||
},
|
},
|
||||||
|
...generateEndgameUpgrades(),
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
// Tiers need % of the previous tier as requirement too
|
// Tiers need % of the previous tier as requirement too
|
||||||
const tierGrowth = 1.8;
|
const tierGrowth = 2.5;
|
||||||
|
|
||||||
// Automatically generate tier levels
|
// Automatically generate tier levels
|
||||||
for (const upgradeId in UPGRADES) {
|
for (const upgradeId in UPGRADES) {
|
||||||
|
|||||||
3
src/js/globals.d.ts
vendored
@ -19,9 +19,6 @@ 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;
|
||||||
|
|
||||||
// Node require
|
|
||||||
declare function require(...args): any;
|
|
||||||
|
|
||||||
// Polyfills
|
// Polyfills
|
||||||
declare interface String {
|
declare interface String {
|
||||||
replaceAll(search: string, replacement: string): string;
|
replaceAll(search: string, replacement: string): string;
|
||||||
|
|||||||
@ -25,6 +25,7 @@ export const SOUNDS = {
|
|||||||
destroyBuilding: "destroy_building",
|
destroyBuilding: "destroy_building",
|
||||||
placeBuilding: "place_building",
|
placeBuilding: "place_building",
|
||||||
placeBelt: "place_belt",
|
placeBelt: "place_belt",
|
||||||
|
copy: "copy",
|
||||||
};
|
};
|
||||||
|
|
||||||
export const MUSIC = {
|
export const MUSIC = {
|
||||||
|
|||||||
@ -19,6 +19,7 @@ import { getCodeFromBuildingData } from "../../game/building_codes.js";
|
|||||||
import { StaticMapEntityComponent } from "../../game/components/static_map_entity.js";
|
import { StaticMapEntityComponent } from "../../game/components/static_map_entity.js";
|
||||||
import { Entity } from "../../game/entity.js";
|
import { Entity } from "../../game/entity.js";
|
||||||
import { defaultBuildingVariant, MetaBuilding } from "../../game/meta_building.js";
|
import { defaultBuildingVariant, MetaBuilding } from "../../game/meta_building.js";
|
||||||
|
import { finalGameShape } from "../../game/upgrades.js";
|
||||||
import { SavegameInterface_V1005 } from "./1005.js";
|
import { SavegameInterface_V1005 } from "./1005.js";
|
||||||
|
|
||||||
const schema = require("./1006.json");
|
const schema = require("./1006.json");
|
||||||
@ -151,11 +152,26 @@ export class SavegameInterface_V1006 extends SavegameInterface_V1005 {
|
|||||||
stored[shapeKey] = rebalance(stored[shapeKey]);
|
stored[shapeKey] = rebalance(stored[shapeKey]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stored[finalGameShape] = 0;
|
||||||
|
|
||||||
// Reduce goals
|
// Reduce goals
|
||||||
if (dump.hubGoals.currentGoal) {
|
if (dump.hubGoals.currentGoal) {
|
||||||
dump.hubGoals.currentGoal.required = rebalance(dump.hubGoals.currentGoal.required);
|
dump.hubGoals.currentGoal.required = rebalance(dump.hubGoals.currentGoal.required);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let level = Math.min(19, dump.hubGoals.level);
|
||||||
|
|
||||||
|
const levelMapping = {
|
||||||
|
14: 15,
|
||||||
|
15: 16,
|
||||||
|
16: 17,
|
||||||
|
17: 18,
|
||||||
|
18: 19,
|
||||||
|
19: 20,
|
||||||
|
};
|
||||||
|
|
||||||
|
dump.hubGoals.level = levelMapping[level] || level;
|
||||||
|
|
||||||
// Update entities
|
// Update entities
|
||||||
const entities = dump.entities;
|
const entities = dump.entities;
|
||||||
for (let i = 0; i < entities.length; ++i) {
|
for (let i = 0; i < entities.length; ++i) {
|
||||||
|
|||||||
@ -1,82 +1,80 @@
|
|||||||
// Synchronizes all translations
|
// Synchronizes all translations
|
||||||
|
|
||||||
const fs = require("fs");
|
const fs = require("fs");
|
||||||
const matchAll = require("match-all");
|
const matchAll = require("match-all");
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
const YAWN = require("yawn-yaml/cjs");
|
const YAML = require("yaml");
|
||||||
const YAML = require("yaml");
|
|
||||||
|
const files = fs
|
||||||
const files = fs
|
.readdirSync(path.join(__dirname, "translations"))
|
||||||
.readdirSync(path.join(__dirname, "translations"))
|
.filter(x => x.endsWith(".yaml"))
|
||||||
.filter(x => x.endsWith(".yaml"))
|
.filter(x => x.indexOf("base-en") < 0);
|
||||||
.filter(x => x.indexOf("base-en") < 0);
|
|
||||||
|
const originalContents = fs
|
||||||
const originalContents = fs
|
.readFileSync(path.join(__dirname, "translations", "base-en.yaml"))
|
||||||
.readFileSync(path.join(__dirname, "translations", "base-en.yaml"))
|
.toString("utf-8");
|
||||||
.toString("utf-8");
|
|
||||||
|
const original = YAML.parse(originalContents);
|
||||||
const original = YAML.parse(originalContents);
|
|
||||||
|
const placeholderRegexp = /[[<]([a-zA-Z_0-9]+)[\]<]/gi;
|
||||||
const placeholderRegexp = /[[<]([a-zA-Z_0-9]+)[\]<]/gi;
|
|
||||||
|
function match(originalObj, translatedObj, path = "/") {
|
||||||
function match(originalObj, translatedObj, path = "/") {
|
for (const key in originalObj) {
|
||||||
for (const key in originalObj) {
|
if (!translatedObj.hasOwnProperty(key)) {
|
||||||
if (!translatedObj.hasOwnProperty(key)) {
|
console.warn(" | Missing key", path + key);
|
||||||
console.warn(" | Missing key", path + key);
|
translatedObj[key] = originalObj[key];
|
||||||
translatedObj[key] = originalObj[key];
|
continue;
|
||||||
continue;
|
}
|
||||||
}
|
const valueOriginal = originalObj[key];
|
||||||
const valueOriginal = originalObj[key];
|
const valueMatching = translatedObj[key];
|
||||||
const valueMatching = translatedObj[key];
|
if (typeof valueOriginal !== typeof valueMatching) {
|
||||||
if (typeof valueOriginal !== typeof valueMatching) {
|
console.warn(" | MISMATCHING type (obj|non-obj) in", path + key);
|
||||||
console.warn(" | MISMATCHING type (obj|non-obj) in", path + key);
|
continue;
|
||||||
continue;
|
}
|
||||||
}
|
|
||||||
|
if (typeof valueOriginal === "object") {
|
||||||
if (typeof valueOriginal === "object") {
|
match(valueOriginal, valueMatching, path + key + "/");
|
||||||
match(valueOriginal, valueMatching, path + key + "/");
|
} else if (typeof valueOriginal === "string") {
|
||||||
} else if (typeof valueOriginal === "string") {
|
// todo
|
||||||
// todo
|
const originalPlaceholders = matchAll(valueOriginal, placeholderRegexp).toArray();
|
||||||
const originalPlaceholders = matchAll(valueOriginal, placeholderRegexp).toArray();
|
const translatedPlaceholders = matchAll(valueMatching, placeholderRegexp).toArray();
|
||||||
const translatedPlaceholders = matchAll(valueMatching, placeholderRegexp).toArray();
|
|
||||||
|
if (originalPlaceholders.length !== translatedPlaceholders.length) {
|
||||||
if (originalPlaceholders.length !== translatedPlaceholders.length) {
|
console.warn(
|
||||||
console.warn(
|
" | Mismatching placeholders in",
|
||||||
" | Mismatching placeholders in",
|
path + key,
|
||||||
path + key,
|
"->",
|
||||||
"->",
|
originalPlaceholders,
|
||||||
originalPlaceholders,
|
"vs",
|
||||||
"vs",
|
translatedPlaceholders
|
||||||
translatedPlaceholders
|
);
|
||||||
);
|
translatedObj[key] = originalObj[key];
|
||||||
translatedObj[key] = originalObj[key];
|
continue;
|
||||||
continue;
|
}
|
||||||
}
|
} else {
|
||||||
} else {
|
console.warn(" | Unknown type: ", typeof valueOriginal);
|
||||||
console.warn(" | Unknown type: ", typeof valueOriginal);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// const matching = translatedObj[key];
|
for (const key in translatedObj) {
|
||||||
}
|
if (!originalObj.hasOwnProperty(key)) {
|
||||||
|
console.warn(" | Obsolete key", path + key);
|
||||||
for (const key in translatedObj) {
|
delete translatedObj[key];
|
||||||
if (!originalObj.hasOwnProperty(key)) {
|
}
|
||||||
console.warn(" | Obsolete key", path + key);
|
}
|
||||||
delete translatedObj[key];
|
}
|
||||||
}
|
|
||||||
}
|
for (let i = 0; i < files.length; ++i) {
|
||||||
}
|
const filePath = path.join(__dirname, "translations", files[i]);
|
||||||
|
console.log("Processing", files[i]);
|
||||||
for (let i = 0; i < files.length; ++i) {
|
const translatedContents = fs.readFileSync(filePath).toString("utf-8");
|
||||||
const filePath = path.join(__dirname, "translations", files[i]);
|
|
||||||
console.log("Processing", files[i]);
|
const json = YAML.parse(translatedContents);
|
||||||
const translatedContents = fs.readFileSync(filePath).toString("utf-8");
|
match(original, json, "/");
|
||||||
const translated = YAML.parse(translatedContents);
|
|
||||||
const handle = new YAWN(translatedContents);
|
const stringified = YAML.stringify(json, {
|
||||||
|
indent: 4,
|
||||||
const json = handle.json;
|
simpleKeys: true,
|
||||||
match(original, json, "/");
|
});
|
||||||
handle.json = json;
|
fs.writeFileSync(filePath, stringified, "utf-8");
|
||||||
|
}
|
||||||
fs.writeFileSync(filePath, handle.yaml, "utf-8");
|
|
||||||
}
|
|
||||||
|
|||||||
@ -268,7 +268,13 @@ dialogs:
|
|||||||
createMarker:
|
createMarker:
|
||||||
title: New Marker
|
title: New Marker
|
||||||
titleEdit: Edit Marker
|
titleEdit: Edit Marker
|
||||||
desc: Give it a meaningful name, you can also include a <strong>short key</strong> of a shape (Which you can generate <a href="https://viewer.shapez.io" target="_blank">here</a>)
|
desc: Give it a meaningful name, you can also include a <strong>short key</strong> of a shape (Which you can generate <link>here</link>)
|
||||||
|
|
||||||
|
editSignal:
|
||||||
|
title: Set Signal
|
||||||
|
descItems: >-
|
||||||
|
Choose a pre-defined item:
|
||||||
|
descShortKey: ... or enter the <strong>short key</strong> of a shape (Which you can generate <link>here</link>)
|
||||||
|
|
||||||
markerDemoLimit:
|
markerDemoLimit:
|
||||||
desc: You can only create two custom markers in the demo. Get the standalone for unlimited markers!
|
desc: You can only create two custom markers in the demo. Get the standalone for unlimited markers!
|
||||||
@ -281,6 +287,10 @@ dialogs:
|
|||||||
title: Rename Savegame
|
title: Rename Savegame
|
||||||
desc: You can rename your savegame here.
|
desc: You can rename your savegame here.
|
||||||
|
|
||||||
|
entityWarning:
|
||||||
|
title: Performance Warning
|
||||||
|
desc: You have placed a lot of buildings, this is just a friendly reminder that the game can not handle an endless count of buildings - So try to keep your factories compact!
|
||||||
|
|
||||||
ingame:
|
ingame:
|
||||||
# This is shown in the top left corner and displays useful keybindings in
|
# This is shown in the top left corner and displays useful keybindings in
|
||||||
# every situation
|
# every situation
|
||||||
@ -350,6 +360,7 @@ ingame:
|
|||||||
notifications:
|
notifications:
|
||||||
newUpgrade: A new upgrade is available!
|
newUpgrade: A new upgrade is available!
|
||||||
gameSaved: Your game has been saved.
|
gameSaved: Your game has been saved.
|
||||||
|
freeplayLevelComplete: Level <level> has been completed!
|
||||||
|
|
||||||
# The "Upgrades" window
|
# The "Upgrades" window
|
||||||
shop:
|
shop:
|
||||||
@ -360,7 +371,8 @@ ingame:
|
|||||||
tier: Tier <x>
|
tier: Tier <x>
|
||||||
|
|
||||||
# The roman number for each tier
|
# The roman number for each tier
|
||||||
tierLabels: [I, II, III, IV, V, VI, VII, VIII, IX, X]
|
tierLabels:
|
||||||
|
[I, II, III, IV, V, VI, VII, VIII, IX, X, XI, XII, XIII, XIV, XV, XVI, XVII, XVIII, XIX, XX]
|
||||||
|
|
||||||
maximumLevel: MAXIMUM LEVEL (Speed x<currentMult>)
|
maximumLevel: MAXIMUM LEVEL (Speed x<currentMult>)
|
||||||
|
|
||||||
@ -573,7 +585,7 @@ buildings:
|
|||||||
|
|
||||||
wire_tunnel:
|
wire_tunnel:
|
||||||
default:
|
default:
|
||||||
name: &wire_tunnel Wire Tunnel
|
name: &wire_tunnel Wire Crossing
|
||||||
description: Allows to cross two wires without connecting them.
|
description: Allows to cross two wires without connecting them.
|
||||||
|
|
||||||
constant_signal:
|
constant_signal:
|
||||||
@ -589,16 +601,16 @@ buildings:
|
|||||||
logic_gate:
|
logic_gate:
|
||||||
default:
|
default:
|
||||||
name: AND Gate
|
name: AND Gate
|
||||||
description: Emits a boolean "1" if both inputs are truthy.
|
description: Emits a boolean "1" if both inputs are truthy. (Truthy means shape, color or boolean "1")
|
||||||
not:
|
not:
|
||||||
name: NOT Gate
|
name: NOT Gate
|
||||||
description: Emits a boolean "1" if the input is not truthy.
|
description: Emits a boolean "1" if the input is not truthy. (Truthy means shape, color or boolean "1")
|
||||||
xor:
|
xor:
|
||||||
name: XOR Gate
|
name: XOR Gate
|
||||||
description: Emits a boolean "1" if one of the inputs is truthy, but not both.
|
description: Emits a boolean "1" if one of the inputs is truthy, but not both. (Truthy means shape, color or boolean "1")
|
||||||
or:
|
or:
|
||||||
name: OR Gate
|
name: OR Gate
|
||||||
description: Emits a boolean "1" if one of the inputs is truthy.
|
description: Emits a boolean "1" if one of the inputs is truthy. (Truthy means shape, color or boolean "1")
|
||||||
|
|
||||||
transistor:
|
transistor:
|
||||||
default:
|
default:
|
||||||
@ -622,7 +634,7 @@ buildings:
|
|||||||
reader:
|
reader:
|
||||||
default:
|
default:
|
||||||
name: &reader Belt Reader
|
name: &reader Belt Reader
|
||||||
description: Allows to measure belt throughput. Outputs the last read item on the wires layer (once unlocked).
|
description: Allows to measure the average belt throughput. Outputs the last read item on the wires layer (once unlocked).
|
||||||
|
|
||||||
analyzer:
|
analyzer:
|
||||||
default:
|
default:
|
||||||
@ -632,20 +644,20 @@ buildings:
|
|||||||
comparator:
|
comparator:
|
||||||
default:
|
default:
|
||||||
name: &comparator Compare
|
name: &comparator Compare
|
||||||
description: Returns boolean "1" if both items are exactly equal. Can compare shapes, items and booleans.
|
description: Returns boolean "1" if both signals are exactly equal. Can compare shapes, items and booleans.
|
||||||
|
|
||||||
virtual_processor:
|
virtual_processor:
|
||||||
default:
|
default:
|
||||||
name: &virtual_processor Virtual Cutter
|
name: &virtual_processor Virtual Cutter
|
||||||
description: Computes
|
description: Virtually cuts the shape into two halves.
|
||||||
|
|
||||||
rotater:
|
rotater:
|
||||||
name: Virtual Rotater
|
name: Virtual Rotater
|
||||||
description: Virtually rotates the shape by 90 degrees clockwise.
|
description: Virtually rotates the shape, both clockwise and counter-clockwise.
|
||||||
|
|
||||||
unstacker:
|
unstacker:
|
||||||
name: Virtual Unstacker
|
name: Virtual Unstacker
|
||||||
description: Returns the topmost layer to the right, and the remaining ones on the left.
|
description: Virtually extracts the topmost layer to the right output and the remaining ones to the left.
|
||||||
|
|
||||||
stacker:
|
stacker:
|
||||||
name: Virtual Stacker
|
name: Virtual Stacker
|
||||||
@ -753,7 +765,7 @@ storyRewards:
|
|||||||
reward_display:
|
reward_display:
|
||||||
title: Display
|
title: Display
|
||||||
desc: >-
|
desc: >-
|
||||||
You have unlocked the <strong>Display</strong>! Connect a wires signal to it to present a color, shape or boolean!
|
You have unlocked the <strong>Display</strong> - Connect a signal on the wires layer to visualize its contents!
|
||||||
|
|
||||||
reward_constant_signal:
|
reward_constant_signal:
|
||||||
title: Constant Signal
|
title: Constant Signal
|
||||||
@ -765,17 +777,19 @@ storyRewards:
|
|||||||
title: Logic Gates
|
title: Logic Gates
|
||||||
desc: >-
|
desc: >-
|
||||||
You unlocked <strong>logic gates</strong>! You don't have to be excited about this, but it's actually super cool!<br><br>
|
You unlocked <strong>logic gates</strong>! You don't have to be excited about this, but it's actually super cool!<br><br>
|
||||||
With those gates you can now perform AND, OR, XOR and NOT boolean operations!
|
With those gates you can now compute AND, OR, XOR and NOT operations.<br><br>
|
||||||
|
As a bonus on top I also just gave you a <strong>transistor</strong>!
|
||||||
|
|
||||||
reward_virtual_processing:
|
reward_virtual_processing:
|
||||||
title: Virtual Processing
|
title: Virtual Processing
|
||||||
desc: >-
|
desc: >-
|
||||||
I just gave a whole bunch of new buildings which allow you to <strong>simulate the processing of shapes</strong>!<br><br>
|
I just gave a whole bunch of new buildings which allow you to <strong>simulate the processing of shapes</strong>!<br><br>
|
||||||
You can now simulate a cutter, rotater, stacker and more on the wires layer!<br><br>
|
You can now simulate a cutter, rotater, stacker and more on the wires layer!
|
||||||
With this you now have three options to continue the game:<br><br>
|
With this you now have three options to continue the game:<br><br>
|
||||||
- Build an <strong>automated machine</strong> to create any possible shape requested by the hub (This is cool, I swear!).<br><br>
|
- Build an <strong>automated machine</strong> to create any possible shape requested by the HUB (I recommend to try it!).<br><br>
|
||||||
- Build something cool with wires.<br><br>
|
- Build something cool with wires.<br><br>
|
||||||
- Continue to play regulary.
|
- Continue to play regulary.<br><br>
|
||||||
|
Whatever you choose, remember to have fun!
|
||||||
|
|
||||||
# Special reward, which is shown when there is no reward actually
|
# Special reward, which is shown when there is no reward actually
|
||||||
no_reward:
|
no_reward:
|
||||||
@ -786,13 +800,14 @@ storyRewards:
|
|||||||
no_reward_freeplay:
|
no_reward_freeplay:
|
||||||
title: Next level
|
title: Next level
|
||||||
desc: >-
|
desc: >-
|
||||||
Congratulations! By the way, more content is planned for the standalone!
|
Congratulations!
|
||||||
|
|
||||||
reward_freeplay:
|
reward_freeplay:
|
||||||
title: Freeplay
|
title: Freeplay
|
||||||
desc: >-
|
desc: >-
|
||||||
You did it! You unlocked the <strong>free-play mode</strong>! This means that shapes are now <strong>randomly</strong> generated!<br><br>
|
You did it! You unlocked the <strong>free-play mode</strong>! This means that shapes are now <strong>randomly</strong> generated!<br><br>
|
||||||
Since the hub will only require low quantities, I highly recommend to build a machine which automatically delivers the requested shape!
|
Since the hub will require a <strong>throughput</strong> from now on, I highly recommend to build a machine which automatically delivers the requested shape!<br><br>
|
||||||
|
The HUB outputs the requested shape on the wires layer, so all you have to do is to analyze it and automatically configure your factory based on that.
|
||||||
|
|
||||||
settings:
|
settings:
|
||||||
title: Settings
|
title: Settings
|
||||||
@ -1036,8 +1051,7 @@ keybindings:
|
|||||||
wire: *wire
|
wire: *wire
|
||||||
constant_signal: *constant_signal
|
constant_signal: *constant_signal
|
||||||
logic_gate: Logic Gate
|
logic_gate: Logic Gate
|
||||||
lever: Switch (regular)
|
lever: *lever
|
||||||
lever_wires: Switch (wires)
|
|
||||||
filter: *filter
|
filter: *filter
|
||||||
wire_tunnel: *wire_tunnel
|
wire_tunnel: *wire_tunnel
|
||||||
display: *display
|
display: *display
|
||||||
@ -1059,7 +1073,8 @@ keybindings:
|
|||||||
lockBeltDirection: Enable belt planner
|
lockBeltDirection: Enable belt planner
|
||||||
switchDirectionLockSide: >-
|
switchDirectionLockSide: >-
|
||||||
Planner: Switch side
|
Planner: Switch side
|
||||||
|
copyWireValue: >-
|
||||||
|
Wires: Copy value below cursor
|
||||||
massSelectStart: Hold and drag to start
|
massSelectStart: Hold and drag to start
|
||||||
massSelectSelectMultiple: Select multiple areas
|
massSelectSelectMultiple: Select multiple areas
|
||||||
massSelectCopy: Copy area
|
massSelectCopy: Copy area
|
||||||
@ -1128,7 +1143,7 @@ tips:
|
|||||||
- Organization is important. Try not to cross conveyors too much.
|
- Organization is important. Try not to cross conveyors too much.
|
||||||
- Plan in advance, or it will be a huge chaos!
|
- Plan in advance, or it will be a huge chaos!
|
||||||
- Don't remove your old factories! You'll need them to unlock upgrades.
|
- Don't remove your old factories! You'll need them to unlock upgrades.
|
||||||
- Try beating level 18 on your own before seeking for help!
|
- Try beating level 20 on your own before seeking for help!
|
||||||
- Don't complicate things, try to stay simple and you'll go far.
|
- Don't complicate things, try to stay simple and you'll go far.
|
||||||
- You may need to re-use factories later in the game. Plan your factories to be re-usable.
|
- You may need to re-use factories later in the game. Plan your factories to be re-usable.
|
||||||
- Sometimes, you can find a needed shape in the map without creating it with stackers.
|
- Sometimes, you can find a needed shape in the map without creating it with stackers.
|
||||||
@ -1149,3 +1164,5 @@ tips:
|
|||||||
- This game has a lot of settings, be sure to check them out!
|
- This game has a lot of settings, be sure to check them out!
|
||||||
- The marker to your hub has a small compass to indicate its direction!
|
- The marker to your hub has a small compass to indicate its direction!
|
||||||
- To clear belts, cut the area and then paste it at the same location.
|
- To clear belts, cut the area and then paste it at the same location.
|
||||||
|
- Press F4 to show your FPS and Tick Rate.
|
||||||
|
- Press F4 twice to show the tile of your mouse and camera.
|
||||||
|
|||||||